Wikipedia
suwiki
https://su.wikipedia.org/wiki/Tepas
MediaWiki 1.48.0-wmf.3
first-letter
Média
Husus
Obrolan
Pamaké
Obrolan pamaké
Wikipedia
Obrolan Wikipedia
Gambar
Obrolan gambar
MédiaWiki
Obrolan MédiaWiki
Citakan
Obrolan citakan
Pitulung
Obrolan pitulung
Kategori
Obrolan kategori
Portal
Obrolan portal
TimedText
TimedText talk
Modul
Pembicaraan Modul
Acara
Pembicaraan Acara
Yamagata Aritomo
0
25951
731543
711614
2026-11-06T22:56:35Z
InternetArchiveBot
25926
Add 1 book for [[en:Wikipedia:Verifiability|verifiability]] (20261106) #IABot (v2.0.9.7) ([[User:GreenC bot|GreenC bot]])
731543
wikitext
text/x-wiki
{{Infobox Military Person
|name=Prince Yamagata Aritomo
|lived=[[14 June]] [[1838]] - [[1 February]] [[1922]]
|placeofbirth=[[Hagi]], [[Chōshū]] domain [[Japan]]
|placeofdeath=[[Tokyo]], [[Japan]]
|image=[[Image:Yamagata_Aritomo.jpg|center|200px]]
|caption=Meiji-period postcard of Yamagata Aritomo
|nickname=
|allegiance=[[Empire of Japan]]
|branch=[[Imperial Japanese Army]]
|serviceyears=1868 -1898
|rank=[[Field Marshal]]
|commands=
|unit=
|battles=[[Boshin War]]<br>[[Satsuma Rebellion]]<br>[[First Sino-Japanese War]]<br>[[Russo-Japanese War]]
|awards=[[Order of Merit (Commonwealth)|Order of Merit]]<br> [[Order of the Golden Kite]] (1st class)<br>[[Order of the Rising Sun]] (1st class with Paulownia Blossoms, Grand Cordon)<br>[[Order of the Chrysanthemum]].
|family=
|laterwork=[[Prime Minister of Japan]]
}}
{{Japanese name|Yamagata}}
Pangeran {{nihongo|'''Yamagata Aritomo'''|山縣 有朋| Yamagata Aritomo|extra=[[14 Juni]] [[1838]]–[[1 Pébruari]] [[1922]]}} salah saurang [[Field Marshal (Japan)|laksamana]] di [[Imperial Japanese Army|Tentara Kaisar Jepang]] tur dua kali [[Perdana Mentri Jepang]]. Anjeunna salah saurang arsitek militer jeung pulitik Jepang modérn. Yamagata Aritomo diaku ogé bapak [[Japanese militarism|militer Jepang]]. <!--His support for many autocratic and aggressive policies directly undermined the development of an open society, and contributed to the coming of the [[Second World War]].-->
==Rujukan==
{{Commonscat|Aritomo Yamagata}}
*{{cite book
| last = Craig
| first = Albert M.
| year = 1961
| title = Chōshū in the Meiji Restoration
| url = https://archive.org/details/choshuinmeijires0000crai
| publisher = Harvard University Press
| location =
| id = ISBN 0739101935
}}
*{{cite book
| last = Dupuy
| first = Trevor N.
| year = 1992
| title = Encyclopedia of Military Biography
| publisher = I B Tauris & Co Ltd
| location =
| id = ISBN 1-85043-569-3
}}
*{{cite book
| last = Hackett
| first = Roger F.
| year = 1971
| title = Yamagata Aritomo in the Rise of Modern Japan 1838 - 1922
| url = https://archive.org/details/yamagataaritomoi0000hack
| publisher = Harvard University Press
| location =
| id = SBN 674-96301-6
}}
*{{cite book
| last = Jansen
| first = Marius B.
| year = 2000
| title = The Making of Modern Japan
| url = https://archive.org/details/makingofmodernja00jans
| publisher = Belknap Press
| location =
| id = ISBN 0674009916
}}
==Tumbu kaluar==
{{clear}}
* {{Japanese prime ministers-NDL|208}}
==Catetan==
{{reflist}}
{{start box}}
{{succession box| title=[[Perdana Mentri Jepang]]| before=[[Kuroda Kiyotaka]]| after=[[Matsukata Masayoshi]]| years=}}
{{succession box| title=[[Perdana Mentri Jepang]]| before=[[Ōkuma Shigenobu]]| after=[[Itō Hirobumi]]| years=}}
{{end box}}
{{Japanese prime ministers}}
{{pondok}}
<!-- note that Yamagata is the family name! -->
{{DEFAULTSORT:Yamagata, Aritomo}}
[[Kategori:Perdana Mentri Jepang]]
[[Kategori:Jéndral Jepang]]
[[Kategori:Marsekal Jepang]]
[[Kategori:Jalma dina Perang Boshin]]
[[Kategori:Anggota Orde Kahadéan]]
[[Kategori:Nu babar taun 1838]]
[[Kategori:Nu pupus taun 1922]]
[[Kategori:Urang Préféktur Yamaguchi]]
[[Kategori:Kazoku]]
[[Kategori:Jalma dina Perang Cina-Jepang Munggaran]]
[[Kategori:Jalma dina Perang Rusia-Jepang]]
[[Kategori:Urang Jepang dina période Méiji]]
tl48xlgwxdytyoyq5jnqeyd18ai9bc7
Suharto
0
31116
731540
712314
2026-11-06T22:52:25Z
InternetArchiveBot
25926
Add 1 book for [[en:Wikipedia:Verifiability|verifiability]] (20261106) #IABot (v2.0.9.7) ([[User:GreenC bot|GreenC bot]])
731540
wikitext
text/x-wiki
{{Unreferenced}}
{{Infobox President
| honorific-prefix=
| name=Soeharto
| nationality=
| image=President Suharto, 1993.jpg
| order=Presiden Indonesia
| office=
| term_start= 12 Maret 1967
| term_end=21 Mei 1998
| predecessor=[[Soekarno]]
| successor=[[Bacharuddin Jusuf Habibie]]
| order2=Sekretaris Jendral Gerakan Non Blok
| term_start2= 7 September 1992
| term_end2=20 Oktober 1995
| predecessor2=[[Dobrica Ćosić]]
| successor2=[[Ernesto Samper Pizano]]
| birth_date={{birth date|1921|6|8}}
| birth_place=[[Kemusuk]], [[Yogyakarta]], [[Indonesia]]
| death_date= {{death date and age|2008|1|27|1921|6|8}}
| death_place= [[Jakarta]], [[Indonesia]]
| religion=[[Islam]]
| spouse=[[Siti Hartinah]] (d. 1996)
| children=[[Siti Hardiyanti Rukmana|Siti Hardiyanti Hastuti]]<br />Sigit Harjojudanto<br />Bambang Trihatmodjo<br />Siti Hediyati Hariyadi<br />[[Tommy Suharto|Hutomo Mandala Putra]]<br />Siti Hutami Endang Adiningsih
| party=[[Partey Golkar]]
| vicepresident= [[Hamengkubuwono IX]]<br />[[Adam Malik]]<br />[[Umar Wirahadikusumah]]<br />[[Sudharmono]]<br />[[Try Sutrisno]]<br />[[Bacharuddin Jusuf Habibie]]
| occupation=[[Militer]], [[Politikus]]
| signature= Suharto signature.svg
}}
'''Soeharto''' ({{lahirmati|Dusun [[Kemusuk]], Desa [[Argomulyo, Sedayu, Bantul|Argomulyo]], Kecamatan [[Sedayu, Bantul|Sedayu]], [[Kabupaten Bantul|Bantul]], [[Yogyakarta]]|8|6|1921|[[Jakarta]]|27|1|2008}}) nyaéta présidén Indonesia kadua nu ngajabat ti taun 1967 dugi ka 1998.
==Daftar pustaka==
* {{cite news |date=22 February 2006 |title=Two former strongmen, Soeharto-Lee Kuan Yew meet again |publisher=ANTARA |access-date=22 February 2006 |url=http://www.antara.co.id/en/seenws/?id=9296 |archive-date=25 March 2007 |archive-url=https://archive.today/20070325054315/http://www.antara.co.id/en/seenws/?id=9296 }}
* {{cite book |last=Aarons |first=Mark |date=2008 |chapter=Justice Betrayed: Post-1945 Responses to Genocide |chapter-url=https://books.google.com/books?id=dg0hWswKgTIC&pg=PA69 |editor1=David A. Blumenthal |editor2=Timothy L. H. McCormack |title=The Legacy of Nuremberg: Civilising Influence or Institutionalised Vengeance? |series=International Humanitarian Law |publisher=[[Martinus Nijhoff Publishers]] |isbn=978-9004156913 }}
* {{cite book |last1=Anderson |first1=Benedict R. |last2=McVey |date=2009 |first2=Ruth T. |title=A Preliminary Analysis of 1 October 1965 Coup in Indonesia |publisher=Equinox Publishing |isbn=9786028397520 |url=https://books.google.com/books?id=CVcwPV3NvDMC }}
* {{cite news |date=19 October 1965 |title=Army in Jakarta Imposes a Ban on Communists |newspaper=The New York Times |url-access=registration |url=https://www.nytimes.com/1965/10/19/archives/army-in-jakarta-imposes-a-ban-on-communists-extent-of-order-unclear.html |access-date=9 March 2021 |archive-date=29 August 2021 |archive-url=https://web.archive.org/web/20210829193148/https://www.nytimes.com/1965/10/19/archives/army-in-jakarta-imposes-a-ban-on-communists-extent-of-order-unclear.html |url-status=live }}
* {{cite news |last=Aspinall |first=Ed |date=October–December 1996 |title=What happened before the riots? |newspaper=[[Inside Indonesia]] |issue=48 |archive-date=5 May 2005 |url=http://www.insideindonesia.org/edit48/ed.htm |archive-url=https://web.archive.org/web/20050505222646/http://www.insideindonesia.org/edit48/ed.htm }} {{Webarchive|url=https://web.archive.org/web/20050505222646/http://www.insideindonesia.org/edit48/ed.htm |date=5 May 2005 }}
* {{cite book |editor1-last=Aspinall |editor1-first=Ed |editor2-last=Klinken |editor2-first=Gerry van |editor3-last=Feith |editor3-first=Herb |date=1999 |title=The Last Days of President Suharto |location=[[Clayton, Victoria|Clayton AU-VIC]] |publisher=Monash Asia Institute |isbn=073261175X}}
*{{cite book |last1=Bevins |first1=Vincent |title= [[The Jakarta Method|The Jakarta Method: Washington's Anticommunist Crusade and the Mass Murder Program that Shaped Our World]] |date=2020 |publisher= [[PublicAffairs]] |isbn= 978-1541742406}}
* {{cite news| title=Attorney general doubts Soeharto can be prosecuted |work=The Jakarta Post| date=27 May 2005}}
* {{cite book| first = William| last = Blum| date = 1995| title = Killing Hope: US Military and CIA Interventions Since World War II| publisher = Common Courage Press| location = Monroe, Me.| isbn = 1-56751-052-3| url = https://archive.org/details/killinghopeusmil00blum_0}}
* ''[http://www.imf.org/external/np/sec/nb/1997/nb9722.htm Camdessus Commends Indonesian Actions]{{Webarchive|url=https://web.archive.org/web/20161109110205/http://www.imf.org/external/np/sec/nb/1997/nb9722.htm |date=9 November 2016 }}''. Press Release. [[International Monetary Fund]]. (31 October 1997)
* {{cite web| title=CIA Stalling State Department Histories| work=The National Security Archive| url=http://www.gwu.edu/~nsarchiv/NSAEBB/NSAEBB52/| access-date=23 May 2005| archive-date=8 March 2012| archive-url=https://web.archive.org/web/20120308035956/http://www.gwu.edu/~nsarchiv/NSAEBB/NSAEBB52/| url-status=live}}
* {{cite news| last=Colmey| first=John| title=The Family Firm| date=24 May 1999| publisher=TIME Asia| url=http://www.time.com/time/asia/asia/magazine/1999/990524/cover1.html| archive-url=https://web.archive.org/web/20010208155758/http://www.time.com/time/asia/asia/magazine/1999/990524/cover1.html| url-status=dead| archive-date=8 February 2001}} {{Webarchive|url=https://web.archive.org/web/20010208155758/http://www.time.com/time/asia/asia/magazine/1999/990524/cover1.html |date=8 February 2001 }}
* {{cite book |last=Conboy |first=Kenneth J. |date=2003 |title=Kopassus: Inside Indonesia's Special Forces |publisher=Equinox Publishing |isbn=9789799589880 |url=https://books.google.com/books?id=lf5TUoHfeM8C |access-date=20 April 2021 |archive-date=23 March 2023 |archive-url=https://web.archive.org/web/20230323145048/https://books.google.com/books?id=lf5TUoHfeM8C |url-status=live }}
* Robert Cribb, "Genocide in Indonesia,1965–1966". Journal of Genocide Research no.2:219–239, 2001.
* {{cite book |last=Elson |first=Robert E. |date=2001 |title = Suharto: A Political Biography |url=https://archive.org/details/suhartopolitical0000elso |location=Cambridge UK |publisher=Cambridge University Press |isbn=0-521-77326-1}}
* {{cite book |last=Dijk |first=Kees van |date=2001 |title=A country in despair : Indonesia between 1997 and 2000 |url=https://archive.org/details/countryindespair0000unse |location=[[Leiden]] |publisher=KITLV Press |isbn=9789067181600 }}
* {{cite book |last=Friend |first=Theodore |date=2003 |title=Indonesian Destinies |publisher=The Belknap Press of Harvard University Press |isbn=0-674-01834-6 |url=https://archive.org/details/indonesiandestin00theo |url-access=registration }}
* {{cite web | title=H.AMDT.647 (A003): An amendment to prohibit any funds appropriated in the bill to be used for military education and training assistance to Indonesia | url=http://thomas.loc.gov/cgi-bin/bdquery/z?d102:HZ00647: | publisher=THOMAS (Library of Congress) | access-date=4 February 2006 | url-status=dead | archive-url=https://web.archive.org/web/20160113050730/http://thomas.loc.gov/cgi-bin/bdquery/z?d102:HZ00647: | archive-date=13 January 2016 | df=dmy-all }} {{Webarchive|url=https://web.archive.org/web/20160113050730/http://thomas.loc.gov/cgi-bin/bdquery/z?d102:HZ00647: |date=13 January 2016 }}
* {{cite book| last=Hughes |first=John |title=The End of Sukarno – A Coup that Misfired: A Purge that Ran Wild| publisher=Archipelago Press|date=2002 | orig-year=1976| isbn=981-4068-65-9}}
* {{cite news |title=Indonesia: Arrests, torture and intimidation: The Government's response to its critics |date=27 November 1996 |publisher=Amnesty International |url=http://web.amnesty.org/library/eng-idn/index&start=391 |url-status=dead |archive-url=https://web.archive.org/web/20051109023133/http://web.amnesty.org/library/eng-idn/index |archive-date=9 November 2005 }}
* {{cite web | title=Indonesia Economic | work=Commanding Heights | url=https://www.pbs.org/wgbh/commandingheights/lo/countries/id/id_economic.html | access-date=23 May 2005 | archive-date=24 February 2012 | archive-url=https://web.archive.org/web/20120224182929/http://www.pbs.org/wgbh/commandingheights/lo/countries/id/id_economic.html | url-status=live }}
* {{cite news| title=Jakarta Cabinet Faces Challenge|work=The New York Times| date=16 December 1965}}
* {{cite news| title=Jakarta Leftist Out As Army Chief| work=[[New York Times]]| date=15 October 1965}}
* {{cite news| title=Jakarta Cabinet Faces Challenge| work=The New York Times| date=16 December 1965}}
* {{cite news| last1=Lashmar |first1=Paul |last2=Oliver |first2=James| name-list-style=amp | title=MI6 Spread Lies to Put Killer in Power| work=The Independent |location=UK| date=16 April 2000}}
* {{cite book| last1=Lashmar |first1=Paul |last2=Oliver |first2=James |title=Britain's Secret Propaganda War| publisher=Sutton Pub Ltd|date=1999| isbn=0-7509-1668-0}}
* {{cite book|last=McDonald |first=H. |author-link=Hamish McDonald |date=1980 |title=Suharto's Indonesia |url=https://archive.org/details/suhartoindonesia0000mcdo |location=[[Blackburn, Victoria|Blackburn]] AU|publisher=Fontana Books |isbn=0-00-635721-0}}
* {{cite book|last=Melvin|first=Jess|date=2018|title=The Army and the Indonesian Genocide: Mechanics of Mass Murder|url=https://www.routledge.com/The-Army-and-the-Indonesian-Genocide-Mechanics-of-Mass-Murder/Melvin/p/book/9781138574694|publisher=[[Routledge]]|isbn=978-1-138-57469-4|access-date=21 November 2021|archive-date=8 June 2019|archive-url=https://web.archive.org/web/20190608032221/https://www.routledge.com/The-Army-and-the-Indonesian-Genocide-Mechanics-of-Mass-Murder/Melvin/p/book/9781138574694|url-status=live}}
*{{cite news |date=1993 |title=Public Expenditures, Prices and the Poor |publisher=World Bank |url=http://wbln0018.worldbank.org/dg/povertys.nsf/0/2f56edbf2ef22ff185256b2100754284?OpenDocument |archive-url=https://web.archive.org/web/20070323210822/http://wbln0018.worldbank.org/dg/povertys.nsf/0/2f56edbf2ef22ff185256b2100754284?OpenDocument |archive-date=23 March 2007 }}
* {{cite book |last=Ricklefs |first=M.C. |date=1991 |title=A History of Modern Indonesia since c. 1300 |url=https://archive.org/details/historyofmoderni0000rick_i0z4 |edition=2nd |publisher=Stanford University Press |isbn=0-333-57690-X}}
* {{cite book |last=Rock |first=Michael T. |date=2003 |title=The Politics of Development Policy and Development Policy Reform |others=William Davidson Institute Working Paper Number 632 |publisher=The University of Michigan Business School |url=http://deepblue.lib.umich.edu/bitstream/handle/2027.42/40018/wp632.pdf?sequence=3 |access-date=11 June 2013 |archive-date=12 December 2013 |archive-url=https://web.archive.org/web/20131212120254/http://deepblue.lib.umich.edu/bitstream/handle/2027.42/40018/wp632.pdf?sequence=3 |url-status=live }}
* {{cite book |last=Purdey |first=Jemma |date=2006 |title=Anti-Chinese Violence in Indonesia, 1996–1999 |location=[[Honolulu]] HI |publisher=[[University of Hawaii Press]]|isbn=9780824830571}}
*{{cite book |last=Robinson |first=Geoffrey B. |date=2018 |title=The Killing Season: A History of the Indonesian Massacres, 1965-66 |publisher=[[Princeton University Press]] |isbn=9781400888863 |url=https://books.google.com/books?id=jW0wDwAAQBAJ }}
* John Roosa, Pretext for Mass Murder: 30 September Movement & Suharto's Coup D'état. The University of Wisconsin Press, 2006. {{ISBN|978-0-299-22034-1}}.
* {{cite news| last=Simpson| first=Brad| title=Indonesia's 1969 Takeover of West Papua Not by "Free Choice"| date=9 July 2004| publisher=National Security Archive| url=http://www.gwu.edu/~nsarchiv/NSAEBB/NSAEBB128/index.htm| access-date=17 June 2005| archive-date=5 February 2013| archive-url=https://web.archive.org/web/20130205191327/http://www.gwu.edu/%7Ensarchiv/NSAEBB/NSAEBB128/index.htm| url-status=live}}
* {{cite book |last=Schwarz |first=Adam |date=1994 |title=A Nation in Waiting: Indonesia in the 1990s |publisher=Westview Press |isbn=9780813388816 |url=https://archive.org/details/nationinwaitingi00schw }}
* {{cite book |last=Simpson |first=Bradley |date=2010 |title=Economists with Guns: Authoritarian Development and U.S.–Indonesian Relations, 1960–1968 |url=https://www.sup.org/books/title/?id=7853 |location= |publisher=[[Stanford University Press]] |isbn=978-0804771825 |access-date=21 November 2021 |archive-date=25 June 2018 |archive-url=https://web.archive.org/web/20180625213245/https://www.sup.org/books/title/?id=7853 |url-status=live }} {{Webarchive|url=https://web.archive.org/web/20180625213245/https://www.sup.org/books/title/?id=7853 |date=25 June 2018 }}
* {{cite news| url=http://news.bbc.co.uk/2/hi/business/3567745.stm| title=Suharto tops corruption rankings| work=BBC News| date=25 March 2004| access-date=4 February 2006| archive-date=13 November 2020| archive-url=https://web.archive.org/web/20201113042444/http://news.bbc.co.uk/2/hi/business/3567745.stm| url-status=live}}
* {{cite news| title=Sukarno Removes His Defense Chief| work=[[New York Times]]| date=22 February 1966}}
* {{cite news| title=Tapol Troubles: When Will They End?| date=April–June 1999| publisher=Inside Indonesia| url=http://insideindonesia.org/index.php/component/content/693?task=view| url-status=dead| archive-url=https://web.archive.org/web/20120305190845/http://insideindonesia.org/index.php/component/content/693?task=view| archive-date=5 March 2012| df=dmy-all}} {{Webarchive|url=https://web.archive.org/web/20120305190845/http://insideindonesia.org/index.php/component/content/693?task=view |date=5 March 2012 }}
* {{cite book| author=Pramoedya Ananta Toer| title=The Mute's Soliloquy: A Memoir| url=https://archive.org/details/mutessoliloquyme0000toer| publisher=Penguin|date=2000| isbn=0-14-028904-6}}
* {{cite web| title=United Nations High Commission on Human Rights resolution 1993/97: Situation in East Timor| url=http://www.unhchr.ch/Huridocda/Huridoca.nsf/0/81427c9bacaf9847c1256c6800603ff2?Opendocument| publisher=United Nations| access-date=4 February 2006| archive-date=4 February 2012| archive-url=https://web.archive.org/web/20120204035327/http://www.unhchr.ch/Huridocda/Huridoca.nsf/0/81427c9bacaf9847c1256c6800603ff2?Opendocument| url-status=live}}
*{{Cite book |last=Vickers |first=Adrian |date=2005 |title=A History of Modern Indonesia |publisher=Cambridge University Press |isbn=9780521542623 |url-access=registration |url=https://archive.org/details/historyofmoderni00adri }}
* {{cite book |last=Wanandi |first=Jusuf |author-link=Jusuf Wanandi |date=2012 |title=Shades of Grey: A Political Memoir of Modern Indonesia 1965–1998 |url=https://archive.org/details/shadesofgreypoli0000wana |location=Singapore |publisher=Equinox Publishing |isbn=9789793780924
}}
* {{cite book| title=Legacy of Ashes: The History of the CIA| last=Weiner| first=Tim| publisher=Doubleday| place=New York|date=2007| isbn=978-3-596-17865-0| chapter=Chapter 15, CIA and Indonesia| title-link=Legacy of Ashes: The History of the CIA}}
* {{cite journal| last1=Wertheim| first1=W.F.| title=Whose plot?-New light on the 1965 events| url=https://archive.org/details/sim_journal-of-contemporary-asia_1979_9_2/page/197| journal=Journal of Contemporary Asia|date=1979| volume=9| issue=2| pages=197–215| doi=10.1080/00472337985390191}}
* {{cite book |last=Wiranto |author-link=Wiranto |date=2011 |title=7 Tahun Menggali Pemikiran dan Tindakan Pak Harto 1991–1997 |trans-title=7 Years Exploring the Thoughts and Actions of Pak Harto 1991–1997 |location=Jakarta |publisher=PT Citra Kharisma Bunda |isbn=9786028112123}}
{{start box}}
{{s-off}}
{{succession box | title=Presiden Indonesia | before=[[Sukarno]] | after=[[Bacharuddin Jusuf Habibie]] | years=1945 - 1967 }}
{{succession box | title=Sekretaris Jendral<br> Gerakan Non Blok | before=[[Dobrica Ćosić]] | after=[[Ernesto Samper Pizano]] | years=1992 - 1995 }}
{{end box}}
{{Presiden Indonesia}}
[[Kategori:Inohong Indonésia]]
[[Kategori:Présidén Indonésia]]
gwoeyltzy52600usn84l4nouaiit9mq
Bawang bodas
0
33416
731538
626836
2026-11-06T15:52:59Z
Dinototosugiarto
11449
731538
wikitext
text/x-wiki
{{Taxobox
| color = lightgreen
| name = Bawang Bodas
| image = Bawang Putih.jpg
| image_width = 240px
| image_caption = Bawang putih
| regnum = [[Plantae]]
| divisio = [[Flowering plant|Magnoliophyta]]
| classis = [[Liliopsida]]
| ordo = [[Asparagales]]
| familia = [[Alliaceae]]
| subfamilia = Allioideae
| tribus = Allieae
| genus = ''[[Allium]]''
| species = '''''A. sativum'''''
| binomial = ''Allium sativum''
| binomial_authority = [[Carolus Linnaeus|L.]]
}}
'''Bawang bodas''' atawa ''Allium sativum L'', mangrupa pepelakan anggota ''familia lilaceae'' anu loba tumuwuh di [[Indonésia]] utamana di [[Jawa Tengah]] sarta [[Nusa Tenggara]].<ref name="sumber1">{{id}} Kartasapoetra, G.2006.Budidaya Tanaman Berkhasiat Obat.Jakarta:PT Rineka Cipta</ref><ref name="Buku1">{{Cite book|title =Buku Pintar Tanaman Obat: 431 jenis tanaman penggempur aneka penyakit| last= Damayanti| first= Dewi| coauthors =dr Prapti utami, Novi Widianti, Nina Wulandari, Agung Sugiarto, Tinton Dwi Putra| year= 2008|page=63|location=Jakarta| publisher=AgroMedia| isbn=9789790061941|url=https://books.google.co.id/books?id=iO0ldwKoXvQC&pg=PA21&dq=bawang+batak+khasiat&hl=id&sa=X&ved=2ahUKEwinj4f98pXvAhVYAXIKHSO8B40Q6AEwAXoECAUQAg#v=onepage&q=bawang%20batak%20khasiat&f=false}}</ref>
==Asal Muasal==
[[Asia tengah]] nyaéta lokasi anu mangrupa pusat asal muasalna bawang bodas sarta ''A Longicuspis'' anu boga sipat éndemik di Asia tengah, bisa mangrupa tipe nini moyang liarna.<ref name="sumber2">{{id}} Rubatzky, Vincent E.1998.Sayuran Dunia 2.Bandung:ITB Bandung</ref> Bisa dicindekkeun ''A Longicuspis'' ngahasilkeun [[siki]] anu bisa tumuwuh, samentara bawang bodas biasana henteu tapi sawatara klon ''A Sativum'' bisa ngahasilkeun sababaraha siki anu bisa tumuwuh.<ref name="sumber2"/> Kiwari keur diupayakeun [[pepelakan]] anu bisa dikembangkeun ku [[siki]].<ref name="sumber2"/> Bukti ''isozom'' terkini nunjukkeun yén misahkeun bawang bodas ti ''A Longicuspis'' kurang bener.<ref name="sumber2"/> Aya bukti deui yén bawang bodas téh geus biasa digunakeun di [[Mesir]] saméméh taun 2000 SM, jeung di [[Cina]] ogé di [[India]] leuwih ti 1000 taun ka tukang.<ref name="sumber2"/> Padang ti [[Eropa]] mantuan nyebarkeun bawang bodas terus-terusan nepi ka jadi komoditas [[sayuran]] anu penting di dunia, minangka bumbu pikeun masakan.<ref name="sumber2"/> Sanajan ngan ukur dipaké sapersapuluhna ti batan bawang beureum, bawang bodas mangrupa ''Allium'' kadua anu loba digunakeun.<ref name="sumber2"/> Bawang bodas loba diproduksi hampir ku sakabéh negara,jeung dibudidayakeun anu dimimitian ti khatulistiwa nepi ka lintang 50°.<ref name="sumber2"/> Sanajan tumuwuh sarta rupana rada jiga [[bawang bombay]], bawang bodas béda dina hal daunna anu padet, wangunna ngabentuk hurup V, jeung ngagulung sacar longitudinal.<ref name="sumber2"/> Nu ngabédakeun tinu séjénna nyaéta yén ujung [[daun]] bawang bodas teu nendeun kadaharan.
==Ciri-ciri==
Bawang bodas anu penting pikeun ubar nyaéta [[beuti]] [[watang]]na anu miboga bau anu khas [[aromatik]], rasana lada, uraiana makroskopikna :
#Mangrupa beuti majemuk anu bentukna rata-rata rada buleud, miboga garis tengah 4 cm nepika 6 cm.<ref name="sumber1"/>
#Warnana [[bodas]] diwangun ku sababaraha siung (8 nepika 20 siung), anu sakabéhna kabungkus ku 3-5 selaput ipis warna bodas.<ref name="sumber1"/>
#Unggal siungna dibeulit ku 2 selaput ipis, selaput luar warnana ngadeukeutan warna bodas jeung rada logor, sedengkeun selaput luar ngabungkus rakeut dina bagian luar daging siung, warnana [[jambon]], anu babari diesotkeun.<ref name="sumber1"/>
Kandungan zat-zat dina beuti bawang bodas, nyaéta :
#Minyak atsiri antara 0,1% nepika 0,5% anu eusina [[dialildisulfida]], [[alilpropildisufida]] jeung [[senyawa sulfur organik]] séjéna.<ref name="sumber1"/>
#Alin (teu miboga bau) anu dina [[hidrolisa]] bisa nimbulkeun bau bawang, ku dosis antara 2 gram - 8 gram bisa digunakeun pikeun obat [[antiseptika]], [[antispasmodika]], [[antiiritansia]].<ref name="sumber1"/>
==Khasiatna==
#Ngahambat morosotna [[uteuk]] sarta sistem kekebalan
#Mantuan ngahambat prosés ngolotan
#Ngahambat tumuwuhna sél [[kangker]]
#Kalayan ngonsumsi bawang bodas, resiko katularan kanker bisa dikurangan
#Bawang bodas anu dikonsumsi sacara rutin dina jangka waktu nu tangtu bisa mantuan nurunkeun kadar [[kolésterol]]
#Zat anti-kolesterol dina bawang bodas anu ngaranna ajoene nulungan nyegah penggumpalan [[getih]]
#Bawang bodas bisa mantuan meredakan stress, kahariwang, sarta déprési, jeung éfék anu leuwih lembut
#Bawang bodas ngandung [[vitamin A]]
#Bawang bodas ngandung [[vitamin B]]
#Bawang bodas ngandung [[vitamin C]]
#Bawang bodas ngandung [[kalsium]]
#Bawang bodas ngandung [[potasium]]
#Bawang bodas ngandung [[antioksidan]]
#Bawang bodas ngandung [[karotén]] sarta [[selenium]]
#Ngonsumsi 2-3 siung bawang bodas sapoé, baris nyingcetkeun tina kamungkinan miboga panyakit [[jantung]],
#Nyageurkeun tekenan darah tinggi
#Ngaringankeun tukak [[lambung]]
#Nurunkeun [[kolesterol]] dina getih
#Ngaronjatkeun [[insulin]] [[getih]] pikeun penderita [[diabetes]]
#Ngalumpuhkeun radikal bébas anu ngaganggu sistem kekebalan awak
#Méré mangpaat pikeun penawar racun (detoxifier) anu nangtayungan awak tina sagala rupa panyakit
#Mantuan nambahan napsu dahar lamun didahar atah-atah
#Ngajaga stamina awak
#Ngandung khasiat [[antimikroba]], [[antitrombotik]], [[hipolipidemik]], [[antiarthritis]], [[hipoglikemik]], sarta ogé miboga antivitas minangka [[antitumor]] <ref name="sumber3">{{id}} [http://kabarinews.com/khasiat Khasiat Bawang] (diaksés tanggal 07 Oktober 2011)</ref>
Tapi sakumaha hasiatna hiji ubar, lamun henteu dimbangan ku pola hirup nu bener bakalan sia-sia.<ref name="sumber3"/><ref name="Jonathan">{{Citation | last =Jonathan | first = Rigg| coauthors =Jonathan | year = 1862| title =A Dictionary of the Sunda Language of Java|page=194|location=Universitas Harvard| publisher =Bataviaasch Genootschap van Kunsten en Wetenschappen| Url =https://books.google.co.id/books?id=1PIUAAAAYAAJ&pg=PA213&dq=KEHKEL&hl=id&sa=X&ved=0ahUKEwj7qpOVitnTAhWBO48KHXrzCD8Q6AEIQDAD#v=onepage&q=KEHKEL&f=false}}</ref>
==Referensi==
{{Reflist|2}}
{{pondok}}
{{Commons|Allium sativum|Bawang bodas}}
[[Kategori:Alliaceae]]
[[Kategori:Tutuwuhan bungbu]]
[[Kategori:Beubeutian]]
[[Kategori:Tutuwuhan ubar]]
25sz1b1nzelu38mamcn2753dg8v74fo
Wortel
0
33444
731536
497392
2026-11-06T12:44:45Z
Dinototosugiarto
11449
731536
wikitext
text/x-wiki
[[Gambar:Daucus carota Wortel Amsterdamse bak gladheid.jpg|thumb|150px|Wortel]]
'''Wortel''' atawa ''Aconilum napellus L,'' nyaéta sajenis [[tutuwuhan]] anu kaasup ''familia Ranunculaceae'', anu pentingna nyaéta [[beuti]]na anu sok disebut wortel.<ref name="sumber1">{{id}} Kartasapoetra, G.2006.Budidaya Tanaman Berkhasiat Obat.Jakarta:PT Rineka Cipta</ref> Tempat tumuwuhna dina [[taneuh]] utamana di dataran anu luhur di [[Jawa Barat]], [[Jawa Tengah]], [[Jawa Timur]] utamana anu miboga hawa anu tiis.<ref name="sumber1"/> Wortel miboga bau anu khas, mimitina mah baris karasa amis lamun digegel, tapi sateurusna bakal karasa lada, uraian makroskopikna nyaéta :
#Wangun beuti wortel nyaéta jiga kerucut anu panjang, dina umumna bagian ujungna lencob jeung sering miboga anak beuti.<ref name="sumber1"/>
#Belah luarna warna [[coklat]] rada ngora, sedengkeun belah jerona warna [[beureum]] rada coklat.<ref name="sumber1"/>
#Loba [[akar serabut]]na, panjang wortel 4 cm - 10 cm, rubakna 1,5 cm - 3 cm.<ref name="sumber1"/>
Wortel ngandung zat-zat, di handap ieu :
#[[Alkaloida]], [[akonitina]] jeung [[asetbencilakonin]].<ref name="sumber1"/>
#[[Benzoilakonina]], [[akonina]], jeung [[neopelina]].<ref name="sumber1"/>
Dosis dina ngagunakeun biasana 20 mg salaku antineuralgia alus pikeun ubar luar jeung ubar jero.<ref name="sumber1"/>
== Referensi ==
{{Reflist}}
{{Commons|Daucus carota|Kumpulan média ngeunaan wortel}}
{{pondok}}
[[Kategori:Beubeutian]]
[[Kategori:Apiaceae]]
bieryx6b9ondns0puiom5i2itqwntrb
Engkol
0
33778
731537
660709
2026-11-06T15:47:32Z
Dinototosugiarto
11449
731537
wikitext
text/x-wiki
[[Gambar:Cabbage.jpg|200px|thumb|rigt|nuju dipelak]]
'''Engkol''' ''(Brassica oleracea)'' téh [[sayuran]] anu [[buleud]] jeung disusun ku [[daun]]-[[daun]] anu [[héjo]] sarta ''patumpang tindih''<ref name="sumber1">[http://www.anneahira.com/manfaat-sayur-kol.htm Manfaat Sayur Kol untuk Kesehatan] {{Webarchive|url=https://web.archive.org/web/20110905215354/http://www.anneahira.com/manfaat-sayur-kol.htm |date=2011-09-05 }}(diakses 17 Oktober 2011)</ref>.Engkol atawa basa [[Walanda]]na mah ''kool'' nyaéta ngaran sayuran anu sok disebut ogé kol, sok dipaké [[lalab]], di[[angeun]] jeung sajabana<ref name="sumber2">Danadibrata,R.A.2006.Kamus Basa Sunda.Bandung:Kiblat Kaca 192</ref>.<ref name="Buku1">{{Cite book|title =Buku Pintar Tanaman Obat: 431 jenis tanaman penggempur aneka penyakit| last= Damayanti| first= Dewi| coauthors =dr Prapti utami, Novi Widianti, Nina Wulandari, Agung Sugiarto, Tinton Dwi Putra| year= 2008|page=63|location=Jakarta| publisher=AgroMedia| isbn=9789790061941|url=https://books.google.co.id/books?id=iO0ldwKoXvQC&pg=PA21&dq=bawang+batak+khasiat&hl=id&sa=X&ved=2ahUKEwinj4f98pXvAhVYAXIKHSO8B40Q6AEwAXoECAUQAg#v=onepage&q=bawang%20batak%20khasiat&f=false}}</ref>
== Mangpaat ==
Sanajan dina engkol sok kaambeu bau [[walirang]] jalaran ayana kandungan [[walirang]] anu loba tapi engkol,boga mangpaat keur [[kaséhatan]] di antarana ngurangan beurat awak,[[kulit]] buruk jeung ''sembelit''<ref name="sumber1"/>.
Ngadahar sayur kol atah atawa setengah asak supaya [[vitamin]] jeung mangpaatna angger aya<ref name="sumber3"/>. Engkol kaasup sayuran anu luhur kandungan serat jeung [[vitamin C]] na. Lamun didahar atah-atah bisa nyingkahan [[kangker]] jeung ngubaran panyakit [[maag]]<ref name="sumber3">[http://kesehatan.liputan6.com/read/305483/Obati.Maag.dengan.Sayur.Kol%2017%20Okt%202011 Obati maag dengan sayur kol](diakses 17 Oktober 2011)</ref>. Anu rumasa boga panyakit [[maag]] leuwih hadé ngadahar engkol ku cara dijieun [[jus]]<ref name="sumber4">[http://www.jurug.com/tips-kesehatan/khasiat-sayur-kol-untuk-mengobati-sakit-maag.html Khasiat Sayur Kol Untuk Mengobati Sakit Maag] {{Webarchive|url=https://web.archive.org/web/20110101143846/http://www.jurug.com/tips-kesehatan/khasiat-sayur-kol-untuk-mengobati-sakit-maag.html |date=2011-01-01 }}(diakses 17 Oktober 2011)</ref>. Husus keur anu panyakit [[maag]], bakal leuwih hadé lamun satengah jam atawa sajam saméméh dahar nginum heula jus engkol<ref name="sumber4"/>. Cara nyieunna téh [[300 gram]] [[bonténg]] ditambah [[150 gram]] engkol bodas anu geus dibérésihkeun saméméhna tuluy dijus sabada kitu disaring tuluy diinum<ref name="sumber4"/>. Engkol mah leuwih hadé didahar atah ti batan diseupan atawa diasakan nepi ka hipu, ku sabab bisa galuarkeun gas nu ngabalukarkeun beuteung nu ngadaharna bunghak<ref name="sumber4"/>.
== Kandungan Gizi ==
[[Gambar:כרוב מרוכך.jpg|right|200px|thumb|engkol dina wadah]]
*[[Kalium]], saporsi sayur engkol ngandung [[269 miligram]] kalium, nyaéta kurang leuwih [[6%]] tina kabutuhan lalaki sawawa unggal poé<ref name="sumber1"/>.kalium boga fungsi keur ngontrol ''tekanan darah'' jeung ngurangan resiko [[batu ginjal]] ogé nyaimbangkeun cairan-cairan anu aya dina jero awak<ref name="sumber1"/>.
*[[Zat besi]],engkol per porsi ngadung [[samiligram]] atawa [[2%]] anu dibutuhkeun ku [[lalaki]] sawawa unggal poé<ref name="sumber1"/>. Zat besi nyingkahan panyakit kurang darah (anemia)<ref name="sumber1"/> . Keur mantuan nyerep zat besi anu aya dina engkol bisa dibarengan ku ngadahar jeruk<ref name="sumber1"/>.
*Vitamin A,saporsi engkol ngandung kurang leuwih 7% tina jumlah anu disarankeun keur lalaki sawawa<ref name="sumber1"/>. Mangpaat engkol nyaéta ngarojong kaséhatan panon,kalemesan kulit jeung ''kalembaban'' selaput lendir<ref name="sumber1"/>.Vitamin A ogé ngaronjatkeun sistem imun awak ku cara nganetralisir radikal bébas anu ngabalukarkeun panyakit jeung ruksakna jaringan<ref name="sumber1"/>.
*Vitamin C,dina kol aya 55 miligram Vitamin C per porsi, nyaéta kurang leuwih 61% tina jumlah anu diperlukeun sapopoéna.<ref name="sumber1"/> Vitamin C ngawangun fungsi imun awak, gancang nyageurkeun raheut, ngaronjatkeun kaséhatan huntu jeung gusi, jeung ngungkulan sel anu ruksak dina awak<ref name="sumber1"/>.
*Kalsium, saporsi engkol ngandung 67 miligram kalsium atawa 7% tina jumlah anu disarankeun keur lalaki sawawa unggal poéna<ref name="sumber1"/>. Kalsium miboga punsi keur nguatkeun tulang jeung huntu,ngaronjatkeun pembekuan getih, ngababarikeun kontraksi otot, ogé mantuan transmisi sinyal saraf<ref name="sumber1"/>.
*Fitonutrisi, engkol ngandung fitonutrisi anu disebut ''indole-3-carbinol'' keur nurunkeun kolesterol<ref name="sumber1"/>.
== Dicutat tina ==
{{Reflist|2}}
{{pondok}}
{{Commons|Brassica oleracea|Sabangsaning engkol}}
[[Kategori:Lalab]]
[[Kategori:Sayuran]]
[[Kategori:Tutuwuhan ubar]]
[[Kategori:Brassicaceae]]
9y84ljsamlefm4o5jc2b1j3gmtvoxxb
Brokoli
0
34382
731535
661048
2026-11-06T12:37:54Z
Dinototosugiarto
11449
731535
wikitext
text/x-wiki
[[Gambar:Broccoli closeup 'Fiesta'.jpg|thumb|200px]]
'''Brokoli''' nyaéta [[sayuran kembang]] kawas [[kembang engkol]], warnana héjo kolot rada kabodas-bodasan (biasana dipaké pikeun pasakan [[Éropa]] sarta [[Cina]]).<ref name=sumber1>{{id}} [http://kamusbahasaindonesia.org/brokoli] {{Webarchive|url=https://web.archive.org/web/20160312090436/http://kamusbahasaindonesia.org/brokoli |date=2016-03-12 }} (Dicutat tanggal 02 Oktober 2011)</ref> Brokoli mangrupa sayuran suku kubis-kubisan anu asalna ti sagara tengah sarta asup ka [[Indonésia]] kira-kira taun [[70]]-an.<ref name=sumber1 /> Sayuran héjo anu hiji ieu téh sayuran anu saeutik kalorina sarta bisa disebutkeun minangka kadaharan anu super.<ref name=sumber1 /> Bisa disebut super alatan sajaba kasohor ogé minangka bahan pangan, sayuran ieu ogé beunghar baris sagala rupa kandungan gizi anu pohara alus, sarta ogé mangrupa sayuran kalayan jajaran pangluhurna minangka ubar tradisional antikanker.<ref name=sumber2>{{id}} [http://informasitips.com/kandungan-gizi-dan-khasiat-sayuran-brokoli kandungan brokoli] {{Webarchive|url=https://web.archive.org/web/20111115095601/http://informasitips.com/kandungan-gizi-dan-khasiat-sayuran-brokoli |date=2011-11-15 }} (Dicutat tanggal 02 Oktober 2011)</ref>
==Kandungan Gizi Brokoli==
*Brokoli ngandung [[energi]], [[karbohidrat]], [[vitamin]], [[sarta mineral]] anu kapaké pikeun [[awak]].<ref name=sumber2 />
*Brokoli mangrupa sayuran kalayan kalori anu saeutik, saeutik [[lemak]], sarta leupas kandungan lemak jenuh, gedé kaliumna sarta saeutik ngandung sodium, henteu ngandung [[kolesterol]]
*Sayuran anu pangalusna tumuwuh di wewengkon cuaca tiis sarta taneuh anu beueus ieu mangrupa asal serat anu pohara alus.
*Kandungan serat dina brokoli komo leuwih loba batan serat anu aya dina [[roti gandum]]. Serat ieu pohara alus pikeun sistem pencernaan.
*Sajaba ti éta, brokoli ogé ngandung folat. Kawas anu urang nyaho, folat mangrupa salah sahiji vitamin anu pohara alus salah sahijina alus pikeun ibu hamil sarta [[janin]].
*Kandungan [[Vitamin C]] di jero brokoli pohara gedé komo Vitamin C anu aya dina brokoli leuwih luhur ti kandungan vitamin C dina hiji [[jeruk]]. Vitamin C anu luhur baris ngalancarkeun awak dina nyerep zat beusi.
*Kandungan kalsiumnya ampir saimbag kalayan kalsium dina sagelas [[usu]] (71.8 mg kalsium brokoli sarua kalayan 120 ml kalsium susu).
*Vitamin K anu kaeusi dina brokoli ogé penting pikeun nguatkeun tulang sarta mantuan ngabekukeun getih. Brokoli ogé ngandung [[karoten]], [[lutein]].
==Rujukan==
{{Reflist}}
{{pondok}}
{{Commons|Broccoli|Tempo brokoli anu sanésna}}
[[Kategori:Sayuran]]
n6hrh4aq0ogua6njhxp7o7kn6x4x0fx
Burundi
0
35723
731539
728521
2026-11-06T22:49:59Z
InternetArchiveBot
25926
Add 1 book for [[en:Wikipedia:Verifiability|verifiability]] (20261106) #IABot (v2.0.9.7) ([[User:GreenC bot|GreenC bot]])
731539
wikitext
text/x-wiki
{{Infobox Country
|native_name = Republika y'u Burundi<br />République du Burundi
|conventional_long_name = Républik Burundi
|common_name = Burundi
|image_flag = Flag of Burundi.svg
|image_coat = Coat of arms of Burundi.svg
|national_motto = <span style="line-height:1.33em;">''"Ubumwe, Ibikorwa, Iterambere"''<small>([[Basa Kirundi]])</small><br />''"Unité, Travail, Progrés"''<small>([[Basa Perancis]])<br />"Kahijian, Gawé, Kamajuan" <sup>1</sup></small></span>
|image_map = Location Burundi AU Africa.svg
|map_caption = {{map caption|countryprefix=|location_color=paul kolot|region=[[Afrika]]|region_color=kulawu kolor|subregion=[[Uni Afrika]]|subregion_color=paul ngora|legend=Location Burundi AU Africa.svg}}
|national_anthem = ''[[Burundi bwacu]]''<br /><small>(Burundi Urang)</small>
|official_languages = [[Basa Kirundi]], [[Basa Perancis]]
|capital = [[Bujumbura]]
|latd=3 |latm=30 |latNS=S |longd=30 |longm=00 |longEW=E
|largest_city = [[Bujumbura]]
|demonym = Burundian
|government_type = [[Républik]]
|leader_title1 = [[Daptar Présidén Burundi|Présidén]]
|leader_name1 = [[Évariste Ndayishimiye]]
|leader_title2 = [[Wakil Présidén Burundi|Wakil Présidén I]]
|leader_name2 = Prosper Bazombanza
|leader_title3 = [[Wakil Présidén Burundi|Wakil Présidén II]]
|leader_name3 = [[Gervais Rufyikiri]]
|sovereignty_type = [[Kamerdikaan]]
|sovereignty_note = ti [[Bélgia]]
|established_event1 = Kaping
|established_date1 = 1 Juli 1962
|area = 27.834
|areami² = 10.745 <!--Do not remove per [[WP:MOSNUM]]-->
|area_rank = ka-145
|area_magnitude = 1 E10
|percent_water = 7,8
|population_estimate = 10.216.190<ref name="cia">[https://www.cia.gov/library/publications/the-world-factbook/geos/by.html CIA – The World Factbook – Burundi] {{Webarchive|url=https://web.archive.org/web/20200424083404/https://www.cia.gov/library/publications/the-world-factbook/geos/by.html |date=2020-04-24 }} [[CIA]]. Ditarima 8 Juni 2008.</ref>
|population_estimate_year = [[2011]]
|population_estimate_rank = ka-89
|population_census = 8.053.574<ref>[http://www.presidence.bi/spip.php?article405 3rd general census (2008)] {{Webarchive|url=https://web.archive.org/web/20130523051745/http://www.presidence.bi/spip.php?article405 |date=2013-05-23 }}</ref>
|population_census_year = 2008
|population_density = 367,0
|population_densitymi² = 836,5 <!--Do not remove per [[WP:MOSNUM]]-->
|population_density_rank = ka-45
|GDP_PPP_year = 2010
|GDP_PPP = $3,397 milyar<ref name=imf2>{{cite web|url=http://www.imf.org/external/pubs/ft/weo/2011/01/weodata/weorept.aspx?sy=2008&ey=2011&scsm=1&ssd=1&sort=country&ds=.&br=1&c=618&s=NGDPD%2CNGDPDPC%2CPPPGDP%2CPPPPC%2CLP&grp=0&a=&pr.x=62&pr.y=14|title=Burundi|publisher=International Monetary Fund|accessdate=2011-04-21|archiveurl=https://web.archive.org/web/20110513231139/http://www.imf.org/external/pubs/ft/weo/2011/01/weodata/weorept.aspx?sy=2008&ey=2011&scsm=1&ssd=1&sort=country&ds=.&br=1&c=618&s=NGDPD%2CNGDPDPC%2CPPPGDP%2CPPPPC%2CLP&grp=0&a=&pr.x=62&pr.y=14|archivedate=2011-05-13}}</ref>
|GDP_PPP_rank =
|GDP_PPP_per_capita = $410<ref name=imf2/>
|GDP_PPP_per_capita_rank =
|GDP_nominal = $1,489 milyar<ref name=imf2/>
|GDP_nominal_rank =
|GDP_nominal_year = 2010
|GDP_nominal_per_capita = $180<ref name=imf2/>
|GDP_nominal_per_capita_rank =
|HDI_year = 2010
|HDI = {{increase}} 0,282
|HDI_rank = ka-166
|HDI_category = <font color="#e0584e">handap</font>
|Gini = 42,4<ref>{{cite web|url=https://www.cia.gov/library/publications/the-world-factbook/fields/2172.html|title=Distribution of family income – Gini index|work=The World Factbook|publisher=CIA|accessdate=2009-09-01|archiveurl=https://web.archive.org/web/20091002120506/https://www.cia.gov/library/publications/the-world-factbook/fields/2172.html|archivedate=2009-10-02}}</ref>
|Gini_year = 1998
|Gini_category = <span style="color:#fc0;">sedeng</span>
|FSI = 95,2 {{decrease}} 1,5
|FSI_year = 2007
|FSI_rank = ka-19
|FSI_category = <font color="#FF0000">Bahaya</font>
|currency = [[Franc Burundi]] (FBu)
|currency_code = BIF
|time_zone = [[Wanci Afrika Tengah|CAT]]
|utc_offset = +2
|time_zone_DST = teu kaobsérvasi
|utc_offset_DST = +2
|drives_on = katuhu
|cctld = [[.bi]]
|calling_code = 257
|footnote1 = Saméméh 1966, "Ganza Sabwa".
|footnote2 = Pakiraan dumasar kana régrési; inohong PPP nu lian diékstrapolasi ti Program Pabandingan Internasioanl pikeun pakiraan bandingan.
}}
'''Burundi''', resmina '''Républik Burundi''' ({{lang-rn|Republika y'u Burundi}}; {{lang-fr|République du Burundi}}), nyaéta hiji [[nagara nu dilingkung]] di wewengkon [[Dano-Dano Garedé (Afrika)|Dano-Dano Garedé]] di [[Afrika Wétan]], tepung wates jeung [[Rwanda]] di kalér, [[Tanzania]] di wétan jeung kidul, sarta [[Républik Démokratik Kongo]] di kulon. Ibu kotana nyaéta [[Bujumbura]]. Sanajan nagara ieu mangrupa [[nagara nu dilingkung]], kalobaanana wates kidul-kulon nepi ka [[Dano Tanganyika]].
== Propinsi ==
{{utama|Propinsi di Burundi}}
Burundi dibagi kana 17 propinsi, nyaéta:
{{col|2}}
* [[Propinsi Bubanza|Bubanza]]
* [[Bujumbura|Bujumbura Mairie]]
* [[Propinsi Bujumbura Désa|Bujumbura Désa]]
* [[Propinsi Bururi|Bururi]]
* [[Propinsi Cankuzo|Cankuzo]]
* [[Propinsi Cibitoke|Cibitoke]]
* [[Propinsi Gitega|Gitega]]
* [[Propinsi Karuzi|Karuzi]]
* [[Propinsi Kayanza|Kayanza]]
* [[Propinsi Kirundo|Kirundo]]
* [[Propinsi Makamba|Makamba]]
* [[Propinsi Muramvya|Muramvya]]
* [[Propinsi Muyinga|Muyinga]]
* [[Propinsi Mwaro|Mwaro]]
* [[Propinsi Ngozi|Ngozi]]
* [[Propinsi Rutana|Rutana]]
* [[Propinsi Ruyigi|Ruyigi]]
</div>
== Catetan ==
{{Reflist}}
== Réferénsi ==
{{Refbegin}}
* {{cite book | last =Allen | first =J.A. | authorlink = | coauthors =et al. | title=Africa South of the Sahara 2004: South of the Sahara |publisher = Taylor and Francis Group| year = 2003| location = New York, New York | pages = | url =https://archive.org/details/africasouthofsah0000unse_y7v6| doi = | id = | isbn = 1857431839 }}
* {{cite book | last = Cook | first = Chris | authorlink = | coauthors = Diccon Bewes | title = What Happened Where: A Guide to Places and Events in Twentieth-Century | publisher = Routledge | year = 1999 | location = London, England| pages = | url = | doi = | id = | isbn = 1857285336 }}
* {{cite book | last = Dinham | first = Barbara | authorlink = | coauthors = Colin Hines| title = Agribusiness in Africa| publisher = Africa World Press | year = 1984 | location = Trenton, New Jersey| pages = | url = | doi = | id = | isbn = 0865430039 }}
* {{cite book | last = Eggers| first = Ellen K. | authorlink = | coauthors = | title = Historical Dictionary of Burundi| publisher = Scarecrow Press, Incorporated| year = 2006| location = Lanham, Maryland| pages = | url =https://archive.org/details/historicaldictio0000egge| doi = | id = | isbn = 0810853027 }} 3rd. edition.
* {{cite book | last = Gates | first = Henry Lewis | authorlink = | coauthors = Anthony Appiah | title = Africana: The Encyclopedia of the African and African American Experience | publisher = Basic Civitas Books | year = 1999 | location = New York, New York | pages = | url =https://archive.org/details/africanaencyclop00appi| doi = | id = | isbn = 0465000711 }}
* {{cite book | last =Jessup | first =John E. | authorlink = | coauthors =| title=An Encyclopedic Dictionary of Conflict and Conflict Resolution, 1945–1996 |publisher = Greenwood Publishing Group| year = 1998| location = Westport, Connecticut| pages = | url =https://archive.org/details/encyclopedicdict0000jess| doi = | id = | isbn = 0313281122 }}
* {{cite book | last =Krueger | first =Ambassador Robert. | authorlink = | coauthors = Kathleen Tobin Krueger | title=From Bloodshed to Hope in Burundi: Our Embassy Years during Genocide |publisher = University of Texas Press| year = 2007| location = Austin, Texas| pages = | url =https://archive.org/details/frombloodshedtoh0000krue| doi = | id = | isbn = 0292714866}}
* {{cite book | last = MacDonald| first = Fiona | authorlink = | coauthors = et al. | title = Peoples of Africa| publisher = Marshall Cavendish | year = 2001| location = Tarrytown, New York| pages = | url =https://archive.org/details/isbn_0761471650_7| doi = | id = | isbn = 0761471588 }}
* {{cite book | last = Puddington| first = Arch | authorlink = | coauthors = | title = Freedom in the World: The Annual Survey of Political Rights and Civil Liberties| publisher = Lanham, Maryland | year = 2007| location = Syracuse University | pages = | url = | doi = | id = | isbn = 0742558975}}
* {{cite book | last = Weinstein| first = Warren | authorlink = | coauthors = Robert Schrere | title = Political Conflict and Ethnic Strategies: A Case Study of Burundi| publisher = Maxwell School of Citizenship and Public Affairs | year = 1976| location = Syracuse University | pages = | url = | doi = | id = | isbn = 0915984202 }}
* {{cite book | last = Weinstein| first = Warren | authorlink = | coauthors = | title = Historical Dictionary of Burundi| publisher = Scarecrow Press, Incorporated| year = 2006| location = Metuchen, New Jersey| pages = | url =https://archive.org/details/historicaldictio0000wein_e0d2| doi = | id = | isbn = 0810809621 }} 1st. edition.
{{Refend}}
== Bacaan salejengna ==
* Abdallah, Ahmedou Ould ''Burundi on the Brink, 1993-95: A UN Special Envoy Reflects on Preventive Diplomacy''
* Bentley, Kristina and Southall, Roger ''An African Peace Process: Mandela, South Africa, and Burundi''
* Chrétien, Jéan-Pierre ''The Great Lakes of Africa: Two Thousand Years of History''
* Daley, Patricia ''Gender and Genocide in Burundi: The Search for Spaces of Peace in the Great Lakes Region''
* Ewusi, Kale and Akwanga, Ebenezer ''Burundi's Negative Peace: The Shadow of a Broken Continent in the Era of Nepad''
* Jennings, Christian ''Across the Red River: Rwanda, Burundi and the Heart of Darkness''
* Kidder, Tracy, ''Strength in What Remains'' (Hiji biografi ngeunaan saurang imigran Burundi di Amérika Sarikat)
* Krueger, Robert and Krueger, Kathleen ''From Bloodshed to Hope in Burundi: Our Embassy Years during Genocide''
* Lemarchand, Rene ''Burundi: Ethnic Conflict and Genocide''
* Melady, Thomas Patrick ''Burundi: The Tragic Years''
* Nivonzima, David and Fendell, Len ''Unlocking Horns: Forgiveness and Reconciliation in Burundi''
* Uvin, Peter ''Life After Violence: A People's Story of Burundi''
* Watt, Nigel '' Burundi: The Biography of a Small African Country''
== Tumbu kaluar ==
{{SisterLinks|Burundi}}
* {{fr}} [http://www.burundi-gov.bi/ Loka resmi pamaréntah Burundi] {{Webarchive|url=https://web.archive.org/web/20081106204744/http://www.burundi-gov.bi/ |date=2008-11-06 }}
* {{CIA_World_Factbook link|by|Burundi}}
* [http://ucblibraries.colorado.edu/govpubs/for/burundi.htm Burundi] {{Webarchive|url=https://web.archive.org/web/20090523153822/http://ucblibraries.colorado.edu/govpubs/for/burundi.htm |date=2009-05-23 }} ti ''UCB Libraries GovPubs''
* {{dmoz|Regional/Africa/Burundi|Burundi}}
* {{Wikiatlas|Burundi}}
* {{wikivoyage|Burundi}}
{{Countries of Africa}}
{{African Union}}
{{La Francophonie|state=collapsed}}
{{Niger-Congo-speaking nations}}
[[Kategori:Burundi| ]]
[[Kategori:Anggota La Francophonie]]
[[Kategori:Anggota Pasarikatan Bangsa-Bangsa]]
[[Kategori:Anggota Uni Afrika]]
[[Kategori:Nagara di Afrika]]
[[Kategori:Nagara jeung téritori Bantu]]
[[Kategori:Nagara nu maké basa Perancis]]
[[Kategori:Nagara nu dilingkung]]
[[Kategori:Nagara nu kirang kawangun]]
[[Kategori:Nu ngadeg taun 1962]]
[[Kategori:Républik]]
[[Kategori:Wewengkon nu maké basa Swahili]]
{{nagara-geo-stub|Burundi}}
3ud0fs49p99vjw7q504cdn5qn4zwdhn
Kakaisaran Akéméniyah
0
37692
731545
711513
2026-11-06T23:02:17Z
InternetArchiveBot
25926
Add 1 book for [[en:Wikipedia:Verifiability|verifiability]] (20261106) #IABot (v2.0.9.7) ([[User:GreenC bot|GreenC bot]])
731545
wikitext
text/x-wiki
{{Redirect|Kakaisaran Pérsia}}
{{Kotak info urut nagara
|native_name=
|conventional_long_name = Kakaisaran Akéméniyah|common_name = Kakaisaran Pérsia
|continent = Eurasia
|region = Wétan deukeut
|country = Iran
|era = Kuna
|status = Kakaisaran
|status_text = <!---A free text to describe status the top of the infobox. Use sparingly.--->
|empire = <!---The empire or country to which the entity was in a state of dependency--->
|government_type = Monarki
|today = {{Collapsible list |titlestyle=font-weight:normal; background:transparent; text-align:left;|title=Nagara ayeuna|
{{flag|Apganistan}}|{{flag|Arab Saudi}}|{{flag|Arménia}}|{{flag|Azerbaijan}}|{{flag|Bahrain}}<ref name="Mojtahed-Zadeh">''Security and Territoriality in the Persian Gulf: A Maritime Political Geography'' by Pirouz Mojtahed-Zadeh, page 119</ref>|{{flag|Bulgaria}}|{{flag|Cina}}|{{flag|Géorgia}}|{{flag|Irak}}|{{flag|Iran}}|{{flag|Israél}}|{{flag|Kazakhstan}}|{{flag|Kirgizstan}}|{{flag|Kuwait}}|{{flag|Libanon}}|{{flag|Libya}}|{{flag|Makédonia}}|{{flag|Mesir}}|{{flag|Pakistan}}|{{flag|Paléstina}}|{{flag|Romania}}|{{flag|Rusia}}|{{flag|Sudan}}|{{flag|Suriah}}|{{flag|Tajikistan}}|{{flag|Turki}}|{{flag|Turkménistan}}|{{flag|Uzbékistan}}|{{flag|Yordania}}|{{flag|Yunani}}|
}}
<!--- Rise and fall, events, years and dates --->
<!-- only fill in the start/end event entry if a specific article exists. Don't just say "abolition" or "declaration"-->
|year_start = 550 SM (Cyrus nu ageung Great ngéléhan Astages ti Média)
|year_end = 336 SM (Aleksander nu Ageung ngawasaan Pérsia)
|life_span = <u>+</u> 550 SM–336 SM
|year_exile_start = <!--- Year of start of exile (if dealing with exiled government - status="Exile") --->
|year_exile_end = <!--- Year of end of exile (leave blank if still in exile) --->
|
|event_start = <!--- Default: "Established" --->
|date_start = <!--- Optional: Date of establishment, enter dates in this format 1 January 1801--->
|event_end = Conquered during [[Wars of Alexander the Great]]
|date_end = <!--- Optional: Date of disestablishment --->
|
|event1 = Mimiti pawangunan [[Pérsepolis]]
|date_event1 = 515 SM
|event2 = Panarajangan [[Mesir Kuna|Mesir]] ku [[Cambyses II ti Pérsia|Cambyses II]]
|date_event2 = 525 SM
|event3 = [[Perang Yunani-Pérsia]]
|date_event3 = 498–448 SM
|event4 =
|date_event4 =
|event5 = Panarajangan Mesir ku [[Artaxerxes III]]
|date_event5 = 343 SM
|event6 = Eléh ku [[Aleksander nu Ageung]]
|date_event6 = 334 SM–330 SM
|
|event_pre = <!--- Optional: A crucial event that took place before before "event_start"--->
|date_pre =
|event_post = [[Darius III]] ditelasan ku [[Bessus]]
|date_post = 330 SM
|
|p1 = Kakaisaran Média
|flag_p1 = Median Empire.svg
|p2 = Kakaisaran Babylonia Anyar
|flag_p2 = Neo babylonia empire 540 bc-2.png
|p3 = Lydia
|flag_p3 = Map Anatolia ancient regions (Indonesian version).svg
|p4 = Dinasti ka-26 Mesir
|flag_p4 = Ancient Egypt map-en.svg
|s1 = Kakaisaran Makédonia
|flag_s1 = Vergiasun.svg
|s2 =
|flag_s2 =
|image_flag =Standard of Cyrus the Great (White).svg
|flag = Bandéra-bandéra Pérsia
|flag_type = Standar Cyrus nu Ageung
|
|image_coat = <!--- Default: Coat of arms of {{{Common name}}}.svg --->
|symbol = <!--- Link target under symbol image. Default: Coat of arms of {{{Common name}}} --->
|symbol_type = <!--- Displayed text for link under symbol. Default "Coat of arms" --->
|
|image_map = Achaemenid_Empire.jpg
|image_map_caption = Kakaisaran Akéméniyah nalika kira-kira taun 500 SM saméméh kakawasaan maksimumna dina [[Darius I]] (tanpa panarajangan [[Punjab Kulon|Panjab Kulon]]).
|
|image_map2 = <!-- If second map is needed - does not appear by default -->
|image_map2_caption =
|
|capital = [[Pasargadae]], [[Ecbatana]], [[Persepolis]], [[Susa]], [[Babylon]]
|capital_exile = <!-- If status="Exile" -->
|latd= |latm= |latNS= |longd= |longm= |longEW=
|
|national_motto =
|national_anthem =
|common_languages = [[Basa Pérsia Kuna]] (resmi jeung basa asli)<br>[[Basa Aramaik Luhur]](<small>''lingua franca''</small>)<ref>Josef Wiesehöfer, ''Ancient Persia'', (I.B. Tauris Ltd, 2007), 119.</ref><br>[[Elamite]]<br>[[Akkadian language|Akkadian]]<ref>{{cite book|title=Traduction: encyclopédie internationale de la recherche sur la traduction|author=Harald Kittel, Juliane House, Brigitte Schultze|publisher=Walter de Gruyter|year=2007|pages=1194–5|url=http://books.google.com/?id=oD0dBqGDNscC&pg=PA1194&dq=Old+Persian+official+language+of+achaemenid+empire#v=onepage&q=Old%20Persian%20official%20language%20of%20achaemenid%20empire&f=false|isbn=9783110171457}}</ref>
|religion = [[Zoroastrianisme]]
|currency = [[Daric]] jeung [[Siglos]]
|
|<!--- Titles and names of the first and last leaders and their deputies --->
|leader1 = [[Cyrus nu Ageung]]
|leader2 = [[Darius III ti Pérsia|Darius III]]
|year_leader1 = 559–529 SM (kahiji)
|year_leader2 = 336–330 SM (pangahirna)
|title_leader = [[Raja Pérsia|Raja]]
|<!--- Area and population of a given year --->
|stat_year1 = <!--- year of the statistic, specify either area, population or both --->
|stat_area1 = <!--- area in square kílometres (w/o commas or spaces), area in square miles is calculated --->
|stat_pop1 = <!--- population (w/o commas or spaces), population density is calculated if area is also given --->
|footnotes = <!--- Accepts wikilinks --->
}}
<!-- {{History of Iran}} -->
'''Kakaisaran Akéméniyah''' ([[Basa Pérsia Kuna]]: ''Parsa'') (<u>+</u> 550–330 SM), dipikawanoh ogé minangka '''Kakaisaran Pérsia Kahiji''', nyaéta hiji kakaisaran di [[Asia Kidul-Kulon]], ngadeg dina [[abad ka-6 SM]] ku [[Cyrus nu Ageung]] anu ngéléhkeun [[Média (étnisitas)|konféderasi Média]]. Kakaisaran ieu ngalega nepi ka ahirna ngawasaan sabagian gedé dina dunya baheula nu mana kira-kira dina taun 500 SM ngalintang ti Lebak Indus di wétan, nepi ka [[Thracia]] jeung [[Makédon]] dina wates kalér-wétan [[Yunani]].
==Tingali ogé==
*[[Daptar raja-raja Pérsia]]
*[[Pohon kulawargi Akéméniyah]]
*[[Purim]], parayaan Yahudi nu patali jeung Kakaisaran Akéméniyah
==Catetan jeung rujukan==
{{Reflist}}
<div class="references-small">
*<cite id=refBriant>{{cite encyclopaedia|last=Briant|first=Pierre|title=Alexander|url=http://www.iranica.com/newsite/articles/unicode/v1f8/v1f8a031.html|encyclopedia=Encyclopaedia Iranica|volume=vol. 3|publisher=Routledge & Kegan Paul}} {{Webarchive|url=https://web.archive.org/web/20080517062736/http://www.iranica.com/newsite/articles/unicode/v1f8/v1f8a031.html |date=2008-05-17 }}
*<cite id=refariaramnes-EI>{{cite encyclopaedia|author=A. Sh. Shahbazi|title=ARIARAMNEIA|url=http://www.iranicaonline.org/articles/ariaramneia-a-city-in-cappadocia-mentioned-in-an-inscription|volume=vol. 2|publisher=Encyclopaedia Iranica (Routledge & Kegan Paul)}}
*<cite id=refachaemenids-EI>{{cite encyclopaedia|last=Schmitt|first=Rüdiger|title=Achaemenid dynasty|url=http://www.iranica.com/newsite/articles/unicode/v1f4/v1f4a109.html|encyclopedia=Encyclopaedia Iranica|volume=vol. 3|publisher=Routledge & Kegan Paul}} {{Webarchive|url=https://web.archive.org/web/20090317005252/http://www.iranica.com/newsite/articles/unicode/v1f4/v1f4a109.html |date=2009-03-17 }}
*<cite id=refschlerath>{{cite book|last=Schlerath|first=Bernfried|title=Die Indogermanen|publisher=Inst. f. Vergl. Sprachwiss.|year=1973|isbn=3851245164}}
*<cite id=refjtavernier> {{cite book |last=Tavernier|first=Jan| title=Iranica in the Achaemenid Period (ca. 550-330 B.C.): Linguistic Study of Old Iranian Proper Names and Loanwords, Attested in Non-Iranian Texts |publisher=Peeters Publishers |year=2007|isbn=9042918330}}
*Stronach, David "Darius at Pasargadae: A Neglected Source for the History of Early Persia," Topoi
*Stronach, David "Anshan and Parsa: Early Achaemenid History, Art and Architecture on the Iranian Plateau". In: John Curtis, ed., Mesopotamia and Iran in the Persian Period: Conquest and Imperialism 539–331, 35–53. London: British muséum Press 1997.
</div>
*<cite id=wiesehöfer>{{cite encyclopaedia|last=Wiesehöfer|first=Josef|title=History in pre-Islamic period|url=http://www.iranica.com/newsite/index.isc?Article=http://www.iranica.com/newsite/articles/v9f3/v9f393a.html|encyclopedia=Encyclopaedia Iranica}}{{Dead link|date=February 2024 |bot=InternetArchiveBot |fix-attempted=yes }}
==Bacaan salajengna==
* {{cite book|first=Josef|last=Wiesehöfer|authorlink=Josef Wiesehöfer|coauthors=Azizeh Azodi (translator)|title=Ancient Persia|url=https://archive.org/details/ancientpersiafro0000wies_b9x5|publisher=I.B. Tauris|location=London, New York|year=2001|isbn=1860646751}} There have been a number of editions since 1996.
* {{cite book|title=Forgotten Empire: The World of Ancient Persia|url=https://archive.org/details/forgottenempirew0000unse_c6q5|first=John E.|last=Curtis|coauthors=Nigel Tallis (editors)|year=2005|publisher=[[University of California Press]]|location=Berkeley and Los Angeles|isbn=0520247310}} A collection of articles by different authors.
*''[http://books.google.ca/books?id=lxQ9W6F1oSYC From Cyrus to Alexander: A History of the Persian Empire]'', [[Pierre Briant]], Eisenbrauns: 2002, ISBN 978-1-57506-0316
*''The Greco-Persian Wars'', Peter Green
*[http://books.google.ca/books?id=V5BeCF82gwUC ''The Greek and Persian Wars 499–386 BC'', Volume 36 of Essential histories. Philip De Souza. Osprey Publishing, 2003, ISBN 1841763586, 9781841763583]
*''The Heritage of Persia'', [[Richard N. Frye]]
*''History of the Persian Empire'', A.T. Olmstéad
*''The Persian Empire'', Lindsay Allen
*''The Persian Empire'', J.M. Cook
*''Persian Fire: The First World Empire and the Battle for the West'', Tom Holland
*''Pictorial History of Iran: Ancient Persia Before Islam 15000 B.C.–625 A.D.'', Amini Sam
*''Timelife Persians: Masters of the Empire (Lost Civilizations)''
* Dandamaev, M.A. ''A Political History of the Achaemenid Empire''. Leiden: [[Brill Academic Publishers]], 1989 (ISBN 90-04-09172-6).
*[[Richard Hallock (epigrapher)|Hallock, R.]], ''[http://oi.uchicago.edu/pdf/OIP92.pdf Persepolis Fortification Tablets] {{Webarchive|url=https://web.archive.org/web/20070621133316/http://oi.uchicago.edu/pdf/OIP92.pdf |date=2007-06-21 }}''
==Tumbu kaluar==
{{Commons category}}
{{Wiktionary}}
{{refbegin}}
*[http://www.persiansarenotarabs.com/persian-history Sajarh Pérsia]
*[http://www.livius.org/aa-ac/achaemenians/achaemenians.html Livius.org dina Achaemenids]
*[http://www.iranicaonline.org/articles/cispis-opers ČIŠPIŠ]
*[http://www.livius.org/be-bm/behistun/behistun01.html Prasasti Behistun] {{Webarchive|url=https://web.archive.org/web/20070510195821/http://www.livius.org/be-bm/behistun/behistun01.html |date=2007-05-10 }}
*[http://www.livius.org/aa-ac/achaemenians/inscriptions.html Livius.org dina Achaemenid Royal Inscriptions] {{Webarchive|url=https://web.archive.org/web/20161218193522/http://www.livius.org/aa-ac/achaemenians/inscriptions.html |date=2016-12-18 }}
{{refend}}
{{Median and Achaemenid kings}}
{{Iran topics}}
{{Empires}}
{{DEFAULTSORT:Akemeniyah}}
[[Kategori:Kakaisaran Akéméniyah| ]]
[[Kategori:Pérsia Kuna|+]]
[[Kategori:Sajarah Iran|+]]
[[Kategori:Sajarah Zoroastrianisme]]
[[Kategori:Sajarah Azerbaijan]]
[[Kategori:Sajarah Wétan Tengah]]
[[Kategori:Sajarah kuna Pakistan]]
[[Kategori:Sajarah kuna Turkey]]
[[Kategori:Mesir Kuna]]
[[Kategori:Sajarah Pra-Islam Apganistan]]
[[Kategori:Nu ngadeg taun 550 SM]]
[[Kategori:Nu bubar taun 330 SM di Iran]]
[[Kategori:Jaman Beusi]]
{{nagara-sajarah-stub|Wétan Deukeut}}
beg0df9lgdkwrfoq1hybd9dgogmhnwu
Vasco da Gama
0
38231
731541
711543
2026-11-06T22:54:23Z
InternetArchiveBot
25926
Add 2 books for [[en:Wikipedia:Verifiability|verifiability]] (20261106) #IABot (v2.0.9.7) ([[User:GreenC bot|GreenC bot]])
731541
wikitext
text/x-wiki
{{Orphan|date=April 2017}}
{{otheruses}}
{{Infobox nobility|type
| name = Vasco da Gama
| title = Laksamana Laut Arab, Pérsia, India, jeung sadaya Wewengkon Wétan
| image = Gregório Lopes - Vasco da Gama (ca 1524).jpg
| succession = [[Count Vidigueira]]
| CoA = [[File:Vasco da Gama o navegador escudo.png|100px]]
| reign = 29 Désémber 1519 - <br />23 Désémber 1524
| reign-type = Masa jabatan
| successor = Francisco da Gama
| spouse = Catarina de Ataíde
| issue = D. Francisco da Gama, Count Vidigueira II <br /> [[Estêvão da Gama (abad ka-16)|D. Estêvão da Gama, Raja Madya India]] <br /> [[Cristóvão da Gama|D. Cristóvão da Gama, Kaptén Malaka]]
| issue-link = #Nikah jeung murangkalih
| issue-pipe = Lianna
| full name = Vasco da Gama
| noble family = Gama
| house-type =
| father = [[Estêvão da Gama (abad ka-15)|Estêvão da Gama]]
| birth_date = 1460 atanapi 1469
| birth_place = [[Sines, Portugal|Sines]] atanapi [[Vidigueira]], [[Alentejo]], [[Karajaan Portugal]]
| death_date = 23 Désémber 1524
| death_place = [[Kochi (India)|Kochim]], [[India Portugal]]
| burial_place = [[Biara Jerónimos]], [[Lisbon]], [[Karajaan Portugal]]
| occupation = Penjelajah, [[daptar pupuhu kolonial India Portugal|Raja Madya India]]
}}
[[File:Sines06 edit1.jpg|thumb|left|Patung Vasco da Gama di [[Sines, Portugal]]]]
'''Vasco da Gama''', [[Count Vidigueira]] I ({{IPA-pt|ˈvaʃku ðɐ ˈɣɐmɐ}}) (<u>+</u> 1460 atanap 1469 – 24 Désémber 1524) nyaéta saurang [[Portugal dina Jaman Pamanggihan|penjelajah Portugis]], salah sahiji anu pangsuksésna dina [[Jaman Pamanggihan]] sarta komandan ti kapal-kapal kahiji anu indit langsung ti [[Éropa]] ka [[India]]. Pikeun hiji jangka wanci pondok dina 1524, anjeunna janten Gubernur [[India Portugal]], dina gelar [[Raja Madya]].
== Tingali ogé ==
*[[Kronologi penjelajahan Éropa kana Asia]]
*[[Count Vidigueira]]
* [[Armada India Portugal]]
*[[India Portugal]]
== Rujukan jeun catetan ==
{{Reflist}}
*{{Cite book |last=Ames |first=Glenn J. |title=Vasco da Gama: Renaissance Crusader |url=https://archive.org/details/vascodagamarenai0000ames |authorlink= |coauthors= |year=2004 |publisher=Longman |location= |isbn=0-321-09282-1 |pages= }}
*{{Cite book |last=Ames |first=Glenn J. |title=The Globe Encompassed: The Age of European Discovery, 1500–1700 |url=https://archive.org/details/globeencompassed0000ames |authorlink= |coauthors= |year=2007 |publisher=Prentice Hall |location= |isbn=978-0-13-193388-0 |pages= }}
* Castanhoso, M. de (1898) ''Dos feitos de D. Christovam da Gama em Ethiopia'' Lisbon: Imprensa nacional. [http://books.google.com/books?id=MiFXAAAAMAAJ&pg=PR3#v=onepage&q&f=false online]
*{{Cite book |last=Corrêa |first=Gaspar |title=The Three Voyages of Vasco da Gama, and His Viceroyalty |authorlink=Gaspar Correia |coauthors= |year=2001 |publisher=Adamant Media Corporation |location= |isbn=1-4021-9543-5 |pages= }} Facsimile reprint of a 1869 edition by the [[Hakluyt Society]], London.
*{{Cite book|last=Disney|first=Anthony|title= The Indian Ocean in World History|coauthors=Emily Booth (eds.) |year=2000 |publisher=Oxford University Press|location=New Delhi and New York |isbn= }}
*{{Cite book|last=Fernández-Armesto |first=Felipe |title=Civilizations |url=https://archive.org/details/civilizations0000fern_d8n4 |authorlink=Felipe Fernández-Armesto|year=2001|publisher=Macmillan|location=Basingstoke and Oxford, U.K.|isbn=0-7432-0248-1}}
*{{Cite book |last=Fernández-Armesto |first=Felipe |title=Pathfinders: A Global History of Exploration |url=https://archive.org/details/pathfindersgloba0000fern_o3w9 |year=2006 |publisher=W. W. Norton |location= |isbn=978-0-393-06259-5 |pages=[https://archive.org/details/pathfindersgloba0000fern_o3w9/page/177 177]–181 }}
* {{Cite book|url=http://ia360625.us.archive.org/3/items/vascodagamahissu00jaynuoft/vascodagamahissu00jaynuoft.pdf|first=Kingsley Garland|last=Jayne|title=Vasco Da Gama and His Successors 1460 to 1580|publisher=Meuthen & Co.Ltd.|location=London, England|year=1910|isbn = 978-0-548-00895-9}}
*{{Cite book |last=Panikkar |first=K. M. |title=Asia and Western Dominance: A Survey of the Vasco da Gama Epoch of Asian History, 1498–1945 |authorlink=Kavalam Madhava Panikkar |coauthors= |edition=new ed. |year=1959 |publisher=Allen & Unwin |location=London |isbn= |id={{ASIN|B000Q5T6X6}} |pages= |url=http://www.archive.org/details/asiaandwesterndo009963mbp }}
*{{Cite book |last=Ravenstein |first=E. G. |title=A Journal of the First Voyage of Vasco da Gama, 1497–1499 |authorlink=Ernst Georg Ravenstein |coauthors=ed. and trans. |year=1898 |publisher=Hakluyt Society |location=London|url=http://books.google.com/books?id=13stAAAAMAAJ&printsec=frontcover&dq=intitle:Vasco+inauthor:Ravenstein&lr=&num=30&as_brr=0&ei=Lzm2R6iwEo3AsQPrtv2RBQ }} (reissued by [[Cambridge University Press]], 2010. ISBN 978-1-108-01296-6)
*{{Cite book |last=Ravenstein |first=E. G. |title=Vasco da Gama |authorlink=Ernst Georg Ravenstein |coauthors= Gaspar Corrêa, Alvaro Velho |year=2010|isbn=978-1-906421-04-5}}
*{{Cite book|last=Russell-Wood|first=A. J. R. |title=A World on the Move: The Portuguese in Africa, Asia, and America, 1415–1808|url=https://archive.org/details/worldonmoveportu0000russ|year=1993 |publisher=Macmillan |location= |isbn=978-0-312-09427-0}}
*{{Cite book|last=Subrahmanyam |first=Sanjay |title=The Career and Legend of Vasco da Gama |url=https://archive.org/details/careerlegendofva0000subr |coauthors= |year=1997 |publisher=Cambridge University Press|isbn=978-0-521-47072-8 |pages= }}
* Teixeira de Aragão, A.C. (1887) ''Vasco da Gama e a Vidigueira: um estudo historico''. Lisbon: Sociedade de Géografia de Lisboa [http://books.google.com/books?id=cBAoAAAAYAAJ&pg=PP7#v=onepage&q&f=false online]
*{{Cite book |last=Towle |first=George Makepeace |title=Vasco da Gama, his voyages and adventures |authorlink= |coauthors= |year=c. 1878 |publisher=Lothrop, Lee & Shepard |location=Boston |isbn= |url=http://www.archive.org/details/vascodagamahisvo00towl }}
== Tumbu kaluar ==
{{commons|Vasco da Gama}}
* [http://www.prominentpeople.co.za/people/35.php Vasco da Gama] {{Webarchive|url=https://web.archive.org/web/20071104051501/http://www.prominentpeople.co.za/people/35.php |date=2007-11-04 }}, prominentpéople.co.za
* [http://www.fordham.edu/halsall/mod/1497degama.html Vasco da Gama's ''Round Africa to India''] {{Webarchive|url=https://web.archive.org/web/20110828160915/http://www.fordham.edu/halsall/mod/1497degama.html |date=2011-08-28 }}, fordham.edu
* [http://www.ucalgary.ca/applied_history/tutor/eurvoya/vasco.html Vasco da Gama web tutorial with animated maps] {{Webarchive|url=https://web.archive.org/web/20070703091222/http://www.ucalgary.ca/applied_history/tutor/eurvoya/vasco.html |date=2007-07-03 }}, [[University of Calgary|ucalgary.ca]]
* [http://sify.com/itihaas/fullstory.php?id=13233617 Brief description of Vasco da Gama's journeys], sify.com
* [http://expressbuzz.com/entertainment/news/urumi-santosh-sivans-new-film/188476.html Urumi Malayalam Movie]{{Dead link|date=April 2023 |bot=InternetArchiveBot |fix-attempted=yes }}, expressbuzz.com
{{DEFAULTSORT:Gama, Vasco da}}
[[Kategori:Nu pupus taun 1524]]
[[Kategori:Penjelajah Portugis]]
[[Kategori:Sajarah maritim Portugal]]
[[Kategori:Urang Portugis Katolik Romawi]]
[[Kategori:Penjelajah Asia]]
[[Kategori:Penjelajah Afrika]]
[[Kategori:Raja Madya India Portugal]]
[[Kategori:Jaman Pamanggihan]]
[[Kategori:Maot ku malaria]]
[[Kategori:Sajarah maritim Afrika Kidul]]
[[Kategori:Pangagem Katolik Romawi abad ka-15]]
[[Kategori:Sajarah Kerala]]
[[Kategori:Sajarah Goa]]
[[Kategori:Nu babar dina 1460-an]]
[[Kategori:Gubernur jeung administrator kolonial Portugal]]
[[Kategori:Urang Portugis abad ka-15]]
{{nagara-bio-stub|Portugal}}
cf4ezhgxkxk2aiwunl0amsv46ktvm0w
Modul:Citation/CS1
828
44263
731560
679494
2026-11-07T11:11:47Z
Rafka Aditia
29183
Ngamutahirkeun
731560
Scribunto
text/plain
require ('strict');
--[[--------------------------< F O R W A R D D E C L A R A T I O N S >--------------------------------------
each of these counts against the Lua upvalue limit
]]
local validation; -- functions in Module:Citation/CS1/Date_validation
local utilities; -- functions in Module:Citation/CS1/Utilities
local z = {}; -- table of tables in Module:Citation/CS1/Utilities
local identifiers; -- functions and tables in Module:Citation/CS1/Identifiers
local metadata; -- functions in Module:Citation/CS1/COinS
local cfg = {}; -- table of configuration tables that are defined in Module:Citation/CS1/Configuration
local whitelist = {}; -- table of tables listing valid template parameter names; defined in Module:Citation/CS1/Whitelist
--[[------------------< P A G E S C O P E V A R I A B L E S >---------------
declare variables here that have page-wide scope that are not brought in from
other modules; that are created here and used here
]]
local added_deprecated_cat; -- boolean flag so that the category is added only once
local added_vanc_errs; -- boolean flag so we only emit one Vancouver error / category
local added_generic_name_errs; -- boolean flag so we only emit one generic name error / category and stop testing names once an error is encountered
local added_numeric_name_errs; -- boolean flag so we only emit one numeric name error / category and stop testing names once an error is encountered
local added_numeric_name_maint; -- boolean flag so we only emit one numeric name maint category and stop testing names once a category has been emitted
local is_preview_mode; -- true when article is in preview mode; false when using 'Preview page with this template' (previewing the module)
local is_sandbox; -- true when using sandbox modules to render citation
local url_was_archive_today; -- boolean flag set to true when has_archive_today_url() has unset |url= because it held a deprecated archive.today url
--[[--------------------------< F I R S T _ S E T >------------------------------------------------------------
Locates and returns the first set value in a table of values where the order established in the table,
left-to-right (or top-to-bottom), is the order in which the values are evaluated. Returns nil if none are set.
This version replaces the original 'for _, val in pairs do' and a similar version that used ipairs. With the pairs
version the order of evaluation could not be guaranteed. With the ipairs version, a nil value would terminate
the for-loop before it reached the actual end of the list.
]]
local function first_set (list, count)
local i = 1;
while i <= count do -- loop through all items in list
if utilities.is_set( list[i] ) then
return list[i]; -- return the first set list member
end
i = i + 1; -- point to next
end
end
--[[--------------------------< A D D _ V A N C _ E R R O R >----------------------------------------------------
Adds a single Vancouver system error message to the template's output regardless of how many error actually exist.
To prevent duplication, added_vanc_errs is nil until an error message is emitted.
added_vanc_errs is a Boolean declared in page scope variables above
]]
local function add_vanc_error (source, position)
if added_vanc_errs then return end
added_vanc_errs = true; -- note that we've added this category
utilities.set_message ('err_vancouver', {source, position});
end
--[[--------------------------< I S _ S C H E M E >------------------------------------------------------------
does this thing that purports to be a URI scheme seem to be a valid scheme? The scheme is checked to see if it
is in agreement with http://tools.ietf.org/html/std66#section-3.1 which says:
Scheme names consist of a sequence of characters beginning with a
letter and followed by any combination of letters, digits, plus
("+"), period ("."), or hyphen ("-").
returns true if it does, else false
]]
local function is_scheme (scheme)
return scheme and scheme:match ('^%a[%a%d%+%.%-]*:'); -- true if scheme is set and matches the pattern
end
--[=[-------------------------< I S _ D O M A I N _ N A M E >--------------------------------------------------
Does this thing that purports to be a domain name seem to be a valid domain name?
Syntax defined here: http://tools.ietf.org/html/rfc1034#section-3.5
BNF defined here: https://tools.ietf.org/html/rfc4234
Single character names are generally reserved; see https://tools.ietf.org/html/draft-ietf-dnsind-iana-dns-01#page-15;
see also [[Single-letter second-level domain]]
list of TLDs: https://www.iana.org/domains/root/db
RFC 952 (modified by RFC 1123) requires the first and last character of a hostname to be a letter or a digit
(see http://tools.ietf.org/html/rfc952 §GRAMMATICAL HOST TABLE SPECIFICATION §§B. Lexical grammar <name>; see
http://tools.ietf.org/html/rfc1123#section-2). Between the first and last characters the name may use letters,
digits, and the hyphen.
Also allowed are IPv4 addresses. IPv6 not supported
domain is expected to be stripped of any path so that the last character in the last character of the TLD. tld
is two or more alpha characters. Any preceding '//' (from splitting a URL with a scheme) will be stripped
here. Perhaps not necessary but retained in case it is necessary for IPv4 dot decimal.
There are several tests:
the first character of the whole domain name including subdomains must be a letter or a digit
internationalized domain name (ASCII characters with .xn-- ASCII Compatible Encoding (ACE) prefix xn-- in the TLD) see https://tools.ietf.org/html/rfc3490
single-letter/digit second-level domains in the .org, .cash, and .today TLDs
q, x, and z SL domains in the .com TLD
i and q SL domains in the .net TLD
single-letter SL domains in the ccTLDs (where the ccTLD is two letters)
two-character SL domains in gTLDs (where the gTLD is two or more letters)
three-plus-character SL domains in gTLDs (where the gTLD is two or more letters)
IPv4 dot-decimal address format; TLD not allowed
returns true if domain appears to be a proper name and TLD or IPv4 address, else false
]=]
local function is_domain_name (domain)
if not domain then
return false; -- if not set, abandon
end
domain = domain:gsub ('^//', ''); -- strip '//' from domain name if present; done here so we only have to do it once
local sl_domain = domain:match ('[^%.]+') or domain; -- test the second level domain name
if not (sl_domain:match ('^%w') and sl_domain:match ('%w$') and -- first and last characters must be letter or digit
sl_domain:match ('^[%w%-]+$')) then -- all other characters must be letters, digits, or hyphen-minus
return false;
end
if domain:match ('^%a+:') then -- hack to detect things that look like s:Page:Title where Page: is namespace at Wikisource
return false;
end
local patterns = { -- patterns that look like URLs
'%f[%w][%w][%w%-]+[%w]%.%a%a+$', -- three or more character hostname.hostname or hostname.tld
'%f[%w][%w][%w%-]+[%w]%.xn%-%-[%w]+$', -- internationalized domain name with ACE prefix
'%f[%a][qxz]%.com$', -- assigned one character .com hostname (x.com times out 2015-12-10)
'%f[%a][iq]%.net$', -- assigned one character .net hostname (q.net registered but not active 2015-12-10)
'%f[%w][%w]%.%a%a$', -- one character hostname and ccTLD (2 chars)
'%f[%w][%w][%w]%.%a%a+$', -- two character hostname and TLD
'^%d%d?%d?%.%d%d?%d?%.%d%d?%d?%.%d%d?%d?', -- IPv4 address
'[%a%d]+%:?' -- IPv6 address
}
for _, pattern in ipairs (patterns) do -- loop through the patterns list
if domain:match (pattern) then
return true; -- if a match then we think that this thing that purports to be a URL is a URL
end
end
for _, d in ipairs (cfg.single_letter_2nd_lvl_domains_t) do -- look for single letter second level domain names for these top level domains
if domain:match ('%f[%w][%w]%.' .. d) then
return true
end
end
return false; -- no matches, we don't know what this thing is
end
--[[--------------------------< I S _ U R L >------------------------------------------------------------------
returns true if the scheme and domain parts of a URL appear to be a valid URL; else false.
This function is the last step in the validation process. This function is separate because there are cases that
are not covered by split_url(), for example is_parameter_ext_wikilink() which is looking for bracketted external
wikilinks.
]]
local function is_url (scheme, domain)
if utilities.is_set (scheme) then -- if scheme is set check it and domain
return is_scheme (scheme) and is_domain_name (domain);
else
return is_domain_name (domain); -- scheme not set when URL is protocol-relative
end
end
--[[--------------------------< S P L I T _ U R L >------------------------------------------------------------
Split a URL into a scheme, authority indicator, and domain.
First remove Fully Qualified Domain Name terminator (a dot following TLD) (if any) and any path(/), query(?) or fragment(#).
If protocol-relative URL, return nil scheme and domain else return nil for both scheme and domain.
When not protocol-relative, get scheme, authority indicator, and domain. If there is an authority indicator (one
or more '/' characters immediately following the scheme's colon), make sure that there are only 2.
Any URL that does not have news: scheme must have authority indicator (//). TODO: are there other common schemes
like news: that don't use authority indicator?
Strip off any port and path;
]]
local function split_url (url_str)
local scheme, authority, domain;
url_str = url_str:gsub ('([%a%d])%.?[/%?#].*$', '%1'); -- strip FQDN terminator and path(/), query(?), fragment (#) (the capture prevents false replacement of '//')
if url_str:match ('^//%S*') then -- if there is what appears to be a protocol-relative URL
domain = url_str:match ('^//(%S*)')
elseif url_str:match ('%S-:/*%S+') then -- if there is what appears to be a scheme, optional authority indicator, and domain name
scheme, authority, domain = url_str:match ('(%S-:)(/*)(%S+)'); -- extract the scheme, authority indicator, and domain portions
if utilities.is_set (authority) then
authority = authority:gsub ('//', '', 1); -- replace place 1 pair of '/' with nothing;
if utilities.is_set(authority) then -- if anything left (1 or 3+ '/' where authority should be) then
return scheme; -- return scheme only making domain nil which will cause an error message
end
else
if not scheme:match ('^news:') then -- except for news:..., MediaWiki won't link URLs that do not have authority indicator; TODO: a better way to do this test?
return scheme; -- return scheme only making domain nil which will cause an error message
end
end
domain = domain:gsub ('(%a):%d+', '%1'); -- strip port number if present
end
return scheme, domain;
end
--[[--------------------------< L I N K _ P A R A M _ O K >---------------------------------------------------
checks the content of |title-link=, |series-link=, |author-link=, etc. for properly formatted content: no wikilinks, no URLs
Link parameters are to hold the title of a Wikipedia article, so none of the WP:TITLESPECIALCHARACTERS are allowed:
# < > [ ] | { } _
except the underscore which is used as a space in wiki URLs and # which is used for section links
returns false when the value contains any of these characters.
When there are no illegal characters, this function returns TRUE if value DOES NOT appear to be a valid URL (the
|<param>-link= parameter is ok); else false when value appears to be a valid URL (the |<param>-link= parameter is NOT ok).
]]
local function link_param_ok (value)
local scheme, domain;
if value:find ('[<>%[%]|{}]') then -- if any prohibited characters
return false;
end
scheme, domain = split_url (value); -- get scheme or nil and domain or nil from URL;
return not is_url (scheme, domain); -- return true if value DOES NOT appear to be a valid URL
end
--[[--------------------------< L I N K _ T I T L E _ O K >---------------------------------------------------
Use link_param_ok() to validate |<param>-link= value and its matching |<title>= value.
|<title>= may be wiki-linked but not when |<param>-link= has a value. This function emits an error message when
that condition exists
check <link> for inter-language interwiki-link prefix. prefix must be a MediaWiki-recognized language
code and must begin with a colon.
]]
local function link_title_ok (link, lorig, title, torig)
local orig;
if utilities.is_set (link) then -- don't bother if <param>-link doesn't have a value
if not link_param_ok (link) then -- check |<param>-link= markup
orig = lorig; -- identify the failing link parameter
elseif title:find ('%[%[') then -- check |title= for wikilink markup
orig = torig; -- identify the failing |title= parameter
elseif link:match ('^%a+:') then -- if the link is what looks like an interwiki
local prefix = link:match ('^(%a+):'):lower(); -- get the interwiki prefix
if cfg.inter_wiki_map[prefix] then -- if prefix is in the map, must have preceding colon
orig = lorig; -- flag as error
end
end
end
if utilities.is_set (orig) then
link = ''; -- unset
utilities.set_message ('err_bad_paramlink', orig); -- URL or wikilink in |title= with |title-link=;
end
return link; -- link if ok, empty string else
end
--[[--------------------------< C H E C K _ U R L >------------------------------------------------------------
Determines whether a URL string appears to be valid.
First we test for space characters. If any are found, return false. Then split the URL into scheme and domain
portions, or for protocol-relative (//example.com) URLs, just the domain. Use is_url() to validate the two
portions of the URL. If both are valid, or for protocol-relative if domain is valid, return true, else false.
Because it is different from a standard URL, and because this module used external_link() to make external links
that work for standard and news: links, we validate newsgroup names here. The specification for a newsgroup name
is at https://tools.ietf.org/html/rfc5536#section-3.1.4
]]
local function check_url( url_str )
if nil == url_str:match ("^%S+$") then -- if there are any spaces in |url=value it can't be a proper URL
return false;
end
local scheme, domain;
scheme, domain = split_url (url_str); -- get scheme or nil and domain or nil from URL;
if 'news:' == scheme then -- special case for newsgroups
return domain:match('^[%a%d%+%-_]+%.[%a%d%+%-_%.]*[%a%d%+%-_]$');
end
return is_url (scheme, domain); -- return true if value appears to be a valid URL
end
--[=[-------------------------< I S _ P A R A M E T E R _ E X T _ W I K I L I N K >----------------------------
Return true if a parameter value has a string that begins and ends with square brackets [ and ] and the first
non-space characters following the opening bracket appear to be a URL. The test will also find external wikilinks
that use protocol-relative URLs. Also finds bare URLs.
The frontier pattern prevents a match on interwiki-links which are similar to scheme:path URLs. The tests that
find bracketed URLs are required because the parameters that call this test (currently |title=, |chapter=, |work=,
and |publisher=) may have wikilinks and there are articles or redirects like '//Hus' so, while uncommon, |title=[[//Hus]]
is possible as might be [[en://Hus]].
]=]
local function is_parameter_ext_wikilink (value)
local scheme, domain;
if value:match ('%f[%[]%[%a%S*:%S+.*%]') then -- if ext. wikilink with scheme and domain: [xxxx://yyyyy.zzz]
scheme, domain = split_url (value:match ('%f[%[]%[(%a%S*:%S+).*%]'));
elseif value:match ('%f[%[]%[//%S+.*%]') then -- if protocol-relative ext. wikilink: [//yyyyy.zzz]
scheme, domain = split_url (value:match ('%f[%[]%[(//%S+).*%]'));
elseif value:match ('%a%S*:%S+') then -- if bare URL with scheme; may have leading or trailing plain text
scheme, domain = split_url (value:match ('(%a%S*:%S+)'));
elseif value:match ('^//%S+') or value:match ('%s//%S+') then -- if protocol-relative bare URL: //yyyyy.zzz; authority indicator (//) must be be preceded nothing or by whitespace
scheme, domain = split_url (value:match ('(//%S+)')); -- what is left should be the domain
else
return false; -- didn't find anything that is obviously a URL
end
return is_url (scheme, domain); -- return true if value appears to be a valid URL
end
--[[--------------------------< I S _ A R C H I V E _ T O D A Y _ U R L >--------------------------------------
archive.today (and .ph, .is, .md, .li, .fo, .vn) are deprecated. See Wikipedia:Requests_for_comment/Archive.is_RFC_5
(Special:Permalink/1339352976)
<val> is a string that may or may not be a url
]]
local function is_archive_today_url (val)
if is_parameter_ext_wikilink (val) then -- is <val> a url?
local tld = val:match ('archive%.(%l+)/'); -- get the tld; real archive.today urls have a '/' after the tld
return tld and cfg.archive_today_tlds_t[tld]; -- return true if <tld> is one of the archive.today tlds; nil else
end
end
--[[--------------------------< H A S _ A R C H I V E _ T O D A Y _ U R L >------------------------------------
blank any parameter that holds one of the archive.today tlds (.today, .ph, .is, .md, .li, .fo, .vn) and also
blank parameters that are dependent on the offending parameter.
input <cite_args_t> is a table of all of this template's parameters This function modifies <cite_args_t>
when |url= or its alias |URL= hold an archive.today url, this function sets the flag <url_was_archive_today>
which is used later to suppress the missing url error message from {{citation}} when it has |website=<summat>
or from the templates {{cite web}}, {{cite podcast}}, {{cite mailing list}} which all require a url.
returns nothing
]]
local function has_archive_today_url (cite_args_t)
local unset_params_t = {}; -- to hold a sequence of parameters to unset from cite_args_t
for p, v in pairs (cite_args_t) do -- look at each parameter in the template
if is_archive_today_url (v:lower()) then -- if it has a parameter with an archive.today url
table.insert (unset_params_t, p); -- add the url-holding parameter name to our list of parameters to unset (may or may not be a legitimate url parameter)
if 'url' == p:lower() then -- if this parameter is |url= or |URL=
url_was_archive_today = true; -- set a flag so that cite web / podcast / mailinglist don't emit a missing url error message
end
if p:match ('archive%-?url') and ('usurped' == cite_args_t['url-status'] or 'unfit' == cite_args_t['url-status']) then -- if |archive-url=archive.today and |url-status=unfit/usurped
url_was_archive_today = true; -- set a flag so that cite web / podcast / mailinglist don't emit a missing url error message
table.insert (unset_params_t, 'url'); -- add the url parameter name to our list of parameters to unset (may or may not be a legitimate url parameter)
table.insert (unset_params_t, 'URL'); -- this one because both are possible
end
if cfg.dependencies_t[p] then -- if this url-holding parameter (<p>) has dependencies
for _, dependent in ipairs (cfg.dependencies_t[p]) do -- loop through the url-holding parameter's dependency sequence
table.insert (unset_params_t, dependent); -- add dependent parameter to the list to be unset
end
end
end
end
if 0 < #unset_params_t then -- when there are parameters to unset
if not is_preview_mode then -- when in preview mode do not suppress any parameters
for _, unset_param in ipairs (unset_params_t) do -- loop through the sequence
cite_args_t[unset_param] = nil; -- and unset the url-holding parameter and its dependent parameters to avoid parameter-requires-other-parameter errors
end
end
utilities.set_message ('maint_deprecated_archive'); -- add a maintenance message
end
end
--[[-------------------------< C H E C K _ F O R _ U R L >-----------------------------------------------------
loop through a list of parameters and their values. Look at the value and if it has an external link, emit an error message.
]]
local function check_for_url (parameter_list, error_list)
for k, v in pairs (parameter_list) do -- for each parameter in the list
if is_parameter_ext_wikilink (v) then -- look at the value; if there is a URL add an error message
table.insert (error_list, utilities.wrap_style ('parameter', k));
end
end
end
--[[--------------------------< S A F E _ F O R _ U R L >------------------------------------------------------
Escape sequences for content that will be used for URL descriptions
]]
local function safe_for_url( str )
if str:match( "%[%[.-%]%]" ) ~= nil then
utilities.set_message ('err_wikilink_in_url', {});
end
return str:gsub( '[%[%]\n]', {
['['] = '[',
[']'] = ']',
['\n'] = ' ' } );
end
--[[--------------------------< E X T E R N A L _ L I N K >----------------------------------------------------
Format an external link with error checking
]]
local function external_link (URL, label, source, access)
local domain;
local path;
local base_url;
if not utilities.is_set (label) then
label = URL;
if utilities.is_set (source) then
utilities.set_message ('err_bare_url_missing_title', {utilities.wrap_style ('parameter', source)});
else
error (cfg.messages["bare_url_no_origin"]); -- programmer error; valid parameter name does not have matching meta-parameter
end
end
if not check_url (URL) then
utilities.set_message ('err_bad_url', {utilities.wrap_style ('parameter', source)});
end
domain, path = URL:match ('^([/%.%-%+:%a%d]+)([/%?#].*)$'); -- split the URL into scheme plus domain and path
if path then -- if there is a path portion
path = path:gsub ('[%[%]]', {['['] = '%5b', [']'] = '%5d'}); -- replace '[' and ']' with their percent-encoded values
URL = table.concat ({domain, path}); -- and reassemble
end
base_url = table.concat ({ "[", URL, " ", safe_for_url (label), "]" }); -- assemble a wiki-markup URL
if utilities.is_set (access) then -- access level (subscription, registration, limited)
base_url = utilities.substitute (cfg.presentation['ext-link-access-signal'], {cfg.presentation[access].class, cfg.presentation[access].title, base_url}); -- add the appropriate icon
end
return base_url;
end
--[[--------------------------< D E P R E C A T E D _ P A R A M E T E R >--------------------------------------
Categorize and emit an error message when the citation contains one or more deprecated parameters. The function includes the
offending parameter name to the error message. Only one error message is emitted regardless of the number of deprecated
parameters in the citation.
added_deprecated_cat is a Boolean declared in page scope variables above
]]
local function deprecated_parameter(name)
if not added_deprecated_cat then
added_deprecated_cat = true; -- note that we've added this category
utilities.set_message ('err_deprecated_params', {name}); -- add error message
end
end
--[=[-------------------------< K E R N _ Q U O T E S >--------------------------------------------------------
Apply kerning to open the space between the quote mark provided by the module and a leading or trailing quote
mark contained in a |title= or |chapter= parameter's value.
This function will positive kern either single or double quotes:
"'Unkerned title with leading and trailing single quote marks'"
" 'Kerned title with leading and trailing single quote marks' " (in real life the kerning isn't as wide as this example)
Double single quotes (italic or bold wiki-markup) are not kerned.
Replaces Unicode quote marks in plain text or in the label portion of a [[L|D]] style wikilink with typewriter
quote marks regardless of the need for kerning. Unicode quote marks are not replaced in simple [[D]] wikilinks.
Call this function for chapter titles, for website titles, etc.; not for book titles.
]=]
local function kern_quotes (str)
local wl_type, label, link;
wl_type, label, link = utilities.is_wikilink (str); -- wl_type is: 0, no wl (text in label variable); 1, [[D]]; 2, [[L|D]]
if 1 == wl_type then -- [[D]] simple wikilink with or without quote marks
if mw.ustring.match (str, '%[%[[\"“”\'‘’].+[\"“”\'‘’]%]%]') then -- leading and trailing quote marks
str = utilities.substitute (cfg.presentation['kern-left'], str);
str = utilities.substitute (cfg.presentation['kern-right'], str);
elseif mw.ustring.match (str, '%[%[[\"“”\'‘’].+%]%]') then -- leading quote marks
str = utilities.substitute (cfg.presentation['kern-left'], str);
elseif mw.ustring.match (str, '%[%[.+[\"“”\'‘’]%]%]') then -- trailing quote marks
str = utilities.substitute (cfg.presentation['kern-right'], str);
end
else -- plain text or [[L|D]]; text in label variable
label = mw.ustring.gsub (label, '[“”]', '\"'); -- replace “” (U+201C & U+201D) with " (typewriter double quote mark)
label = mw.ustring.gsub (label, '[‘’]', '\''); -- replace ‘’ (U+2018 & U+2019) with ' (typewriter single quote mark)
local cap = mw.ustring.match (label, "^([\"\'][^\'].+)"); -- match leading double or single quote but not doubled single quotes (italic markup)
if utilities.is_set (cap) then
label = utilities.substitute (cfg.presentation['kern-left'], cap);
end
cap = mw.ustring.match (label, "^(.+[^\'][\"\'])$") -- match trailing double or single quote but not doubled single quotes (italic markup)
if utilities.is_set (cap) then
label = utilities.substitute (cfg.presentation['kern-right'], cap);
end
if 2 == wl_type then
str = utilities.make_wikilink (link, label); -- reassemble the wikilink
else
str = label;
end
end
return str;
end
--[[--------------------------< F O R M A T _ S C R I P T _ V A L U E >----------------------------------------
|script-title= holds title parameters that are not written in Latin-based scripts: Chinese, Japanese, Arabic, Hebrew, etc. These scripts should
not be italicized and may be written right-to-left. The value supplied by |script-title= is concatenated onto Title after Title has been wrapped
in italic markup.
Regardless of language, all values provided by |script-title= are wrapped in <bdi>...</bdi> tags to isolate RTL languages from the English left to right.
|script-title= provides a unique feature. The value in |script-title= may be prefixed with a two-character ISO 639-1 language code and a colon:
|script-title=ja:*** *** (where * represents a Japanese character)
Spaces between the two-character code and the colon and the colon and the first script character are allowed:
|script-title=ja : *** ***
|script-title=ja: *** ***
|script-title=ja :*** ***
Spaces preceding the prefix are allowed: |script-title = ja:*** ***
The prefix is checked for validity. If it is a valid ISO 639-1 language code, the lang attribute (lang="ja") is added to the <bdi> tag so that browsers can
know the language the tag contains. This may help the browser render the script more correctly. If the prefix is invalid, the lang attribute
is not added. At this time there is no error message for this condition.
Supports |script-title=, |script-chapter=, |script-<periodical>=
]]
local function format_script_value (script_value, script_param)
local lang=''; -- initialize to empty string
local name;
if script_value:match('^%l%l%l?%s*:') then -- if first 3 or 4 non-space characters are script language prefix
lang = script_value:match('^(%l%l%l?)%s*:%s*%S.*'); -- get the language prefix or nil if there is no script
if not utilities.is_set (lang) then
utilities.set_message ('err_script_parameter', {script_param, cfg.err_msg_supl['missing title part']}); -- prefix without 'title'; add error message
return ''; -- script_value was just the prefix so return empty string
end
-- if we get this far we have prefix and script
name = cfg.lang_tag_remap[lang] or mw.language.fetchLanguageName( lang, cfg.this_wiki_code ); -- get language name so that we can use it to categorize
if utilities.is_set (name) then -- is prefix a proper ISO 639-1 language code?
script_value = script_value:gsub ('^%l+%s*:%s*', ''); -- strip prefix from script
-- is prefix one of these language codes?
if utilities.in_array (lang, cfg.script_lang_codes) then
utilities.add_prop_cat ('script', {name, lang})
else
utilities.set_message ('err_script_parameter', {script_param, cfg.err_msg_supl['unknown language code']}); -- unknown script-language; add error message
end
lang = ' lang="' .. lang .. '" '; -- convert prefix into a lang attribute
else
utilities.set_message ('err_script_parameter', {script_param, cfg.err_msg_supl['invalid language code']}); -- invalid language code; add error message
lang = ''; -- invalid so set lang to empty string
end
else
utilities.set_message ('err_script_parameter', {script_param, cfg.err_msg_supl['missing prefix']}); -- no language code prefix; add error message
end
script_value = utilities.substitute (cfg.presentation['bdi'], {lang, script_value}); -- isolate in case script is RTL
return script_value;
end
--[[--------------------------< S C R I P T _ C O N C A T E N A T E >------------------------------------------
Initially for |title= and |script-title=, this function concatenates those two parameter values after the script
value has been wrapped in <bdi> tags.
]]
local function script_concatenate (title, script, script_param)
if utilities.is_set (script) then
script = format_script_value (script, script_param); -- <bdi> tags, lang attribute, categorization, etc.; returns empty string on error
if utilities.is_set (script) then
title = title .. ' ' .. script; -- concatenate title and script title
end
end
return title;
end
--[[--------------------------< W R A P _ M S G >--------------------------------------------------------------
Applies additional message text to various parameter values. Supplied string is wrapped using a message_list
configuration taking one argument. Supports lower case text for {{citation}} templates. Additional text taken
from citation_config.messages - the reason this function is similar to but separate from wrap_style().
]]
local function wrap_msg (key, str, lower)
if not utilities.is_set ( str ) then
return "";
end
if true == lower then
local msg;
msg = cfg.messages[key]:lower(); -- set the message to lower case before
return utilities.substitute ( msg, str ); -- including template text
else
return utilities.substitute ( cfg.messages[key], str );
end
end
--[[----------------< W I K I S O U R C E _ U R L _ M A K E >-------------------
Makes a Wikisource URL from Wikisource interwiki-link. Returns the URL and appropriate
label; nil else.
str is the value assigned to |chapter= (or aliases) or |title= or |title-link=
]]
local function wikisource_url_make (str)
local wl_type, D, L;
local ws_url, ws_label;
local wikisource_prefix = table.concat ({'https://', cfg.this_wiki_code, '.wikisource.org/wiki/'});
wl_type, D, L = utilities.is_wikilink (str); -- wl_type is 0 (not a wikilink), 1 (simple wikilink), 2 (complex wikilink)
if 0 == wl_type then -- not a wikilink; might be from |title-link=
str = D:match ('^[Ww]ikisource:(.+)') or D:match ('^[Ss]:(.+)'); -- article title from interwiki link with long-form or short-form namespace
if utilities.is_set (str) then
ws_url = table.concat ({ -- build a Wikisource URL
wikisource_prefix, -- prefix
str, -- article title
});
ws_label = str; -- label for the URL
end
elseif 1 == wl_type then -- simple wikilink: [[Wikisource:ws article]]
str = D:match ('^[Ww]ikisource:(.+)') or D:match ('^[Ss]:(.+)'); -- article title from interwiki link with long-form or short-form namespace
if utilities.is_set (str) then
ws_url = table.concat ({ -- build a Wikisource URL
wikisource_prefix, -- prefix
str, -- article title
});
ws_label = str; -- label for the URL
end
elseif 2 == wl_type then -- non-so-simple wikilink: [[Wikisource:ws article|displayed text]] ([[L|D]])
str = L:match ('^[Ww]ikisource:(.+)') or L:match ('^[Ss]:(.+)'); -- article title from interwiki link with long-form or short-form namespace
if utilities.is_set (str) then
ws_label = D; -- get ws article name from display portion of interwiki link
ws_url = table.concat ({ -- build a Wikisource URL
wikisource_prefix, -- prefix
str, -- article title without namespace from link portion of wikilink
});
end
end
if ws_url then
ws_url = mw.uri.encode (ws_url, 'WIKI'); -- make a usable URL
ws_url = ws_url:gsub ('%%23', '#'); -- undo percent-encoding of fragment marker
end
return ws_url, ws_label, L or D; -- return proper URL or nil and a label or nil
end
--[[----------------< F O R M A T _ P E R I O D I C A L >-----------------------
Format the three periodical parameters: |script-<periodical>=, |<periodical>=,
and |trans-<periodical>= into a single Periodical meta-parameter.
]]
local function format_periodical (script_periodical, script_periodical_source, periodical, trans_periodical)
if not utilities.is_set (periodical) then
periodical = ''; -- to be safe for concatenation
else
periodical = utilities.wrap_style ('italic-title', periodical); -- style
end
periodical = script_concatenate (periodical, script_periodical, script_periodical_source); -- <bdi> tags, lang attribute, categorization, etc.; must be done after title is wrapped
if utilities.is_set (trans_periodical) then
trans_periodical = utilities.wrap_style ('trans-italic-title', trans_periodical);
if utilities.is_set (periodical) then
periodical = periodical .. ' ' .. trans_periodical;
else -- here when trans-periodical without periodical or script-periodical
periodical = trans_periodical;
utilities.set_message ('err_trans_missing_title', {'periodical'});
end
end
return periodical;
end
--[[------------------< F O R M A T _ C H A P T E R _ T I T L E >---------------
Format the four chapter parameters: |script-chapter=, |chapter=, |trans-chapter=,
and |chapter-url= into a single chapter meta- parameter (chapter_url_source used
for error messages).
]]
local function format_chapter_title (script_chapter, script_chapter_source, chapter, trans_chapter, trans_chapter_source, chapter_url, chapter_url_source, no_quotes, access)
local ws_url, ws_label, L = wikisource_url_make (chapter); -- make a wikisource URL and label from a wikisource interwiki link
if ws_url then
ws_label = ws_label:gsub ('_', ' '); -- replace underscore separators with space characters
chapter = ws_label;
end
if not utilities.is_set (chapter) then
chapter = ''; -- to be safe for concatenation
else
if false == no_quotes then
chapter = kern_quotes (chapter); -- if necessary, separate chapter title's leading and trailing quote marks from module provided quote marks
chapter = utilities.wrap_style ('quoted-title', chapter);
end
end
chapter = script_concatenate (chapter, script_chapter, script_chapter_source); -- <bdi> tags, lang attribute, categorization, etc.; must be done after title is wrapped
if utilities.is_set (chapter_url) then
chapter = external_link (chapter_url, chapter, chapter_url_source, access); -- adds bare_url_missing_title error if appropriate
elseif ws_url then
chapter = external_link (ws_url, chapter .. ' ', 'ws link in chapter'); -- adds bare_url_missing_title error if appropriate; space char to move icon away from chap text; TODO: better way to do this?
chapter = utilities.substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], L, chapter});
end
if utilities.is_set (trans_chapter) then
trans_chapter = utilities.wrap_style ('trans-quoted-title', trans_chapter);
if utilities.is_set (chapter) then
chapter = chapter .. ' ' .. trans_chapter;
else -- here when trans_chapter without chapter or script-chapter
chapter = trans_chapter;
utilities.set_message ('err_trans_missing_title', {trans_chapter_source:match ('trans%-?(.+)')}); -- when no chapter, get matching name from trans-<param>
end
end
return chapter;
end
--[[----------------< H A S _ I N V I S I B L E _ C H A R S >-------------------
This function searches a parameter's value for non-printable or invisible characters.
The search stops at the first match.
This function will detect the visible replacement character when it is part of the Wikisource.
Detects but ignores nowiki and math stripmarkers. Also detects other named stripmarkers
(gallery, math, pre, ref) and identifies them with a slightly different error message.
See also coins_cleanup().
Output of this function is an error message that identifies the character or the
Unicode group, or the stripmarker that was detected along with its position (or,
for multi-byte characters, the position of its first byte) in the parameter value.
]]
local function has_invisible_chars (param, v)
local position; -- position of invisible char or starting position of stripmarker
local capture; -- used by stripmarker detection to hold name of the stripmarker
local stripmarker; -- boolean set true when a stripmarker is found
capture = string.match (v, '[%w%p ]*'); -- test for values that are simple ASCII text and bypass other tests if true
if capture == v then -- if same there are no Unicode characters
return;
end
for _, invisible_char in ipairs (cfg.invisible_chars) do
local char_name = invisible_char[1]; -- the character or group name
local pattern = invisible_char[2]; -- the pattern used to find it
position, _, capture = mw.ustring.find (v, pattern); -- see if the parameter value contains characters that match the pattern
if position and (cfg.invisible_defs.zwj == capture) then -- if we found a zero-width joiner character
if mw.ustring.find (v, cfg.indic_script) then -- it's ok if one of the Indic scripts
position = nil; -- unset position
elseif cfg.emoji_t[mw.ustring.codepoint (v, position+1)] then -- is zwj followed by a character listed in emoji{}?
position = nil; -- unset position
end
end
if position then
if 'nowiki' == capture or 'math' == capture or -- nowiki and math stripmarkers (not an error condition)
('templatestyles' == capture and utilities.in_array (param, {'id', 'quote'})) then -- templatestyles stripmarker allowed in these parameters
stripmarker = true; -- set a flag
elseif true == stripmarker and cfg.invisible_defs.del == capture then -- because stripmakers begin and end with the delete char, assume that we've found one end of a stripmarker
position = nil; -- unset; for now, ignore linter warning
else
local err_msg;
if capture and not (cfg.invisible_defs.del == capture or cfg.invisible_defs.zwj == capture) then
err_msg = utilities.substitute (cfg.messages.invisible_1, {capture, char_name}); -- <capture> here is stripmarker name
else
err_msg = utilities.substitute (cfg.messages.invisible_2, char_name);
end
utilities.set_message ('err_invisible_char', {err_msg, utilities.wrap_style ('parameter', param), position}); -- add error message
return; -- and done with this parameter
end
end
end
end
--[[-------------------< A R G U M E N T _ W R A P P E R >----------------------
Argument wrapper. This function provides support for argument mapping defined
in the configuration file so that multiple names can be transparently aliased to
single internal variable.
]]
local function argument_wrapper ( args )
local origin = {};
return setmetatable({
ORIGIN = function ( self, k )
local dummy = self[k]; -- force the variable to be loaded; for now, ignore linter warning
return origin[k];
end
},
{
__index = function ( tbl, k )
if origin[k] ~= nil then
return nil;
end
local v;
local list = cfg.aliases[k];
if type( list ) == 'table' then
v, origin[k] = utilities.select_one ( args, list, 'err_redundant_parameters' );
if origin[k] == nil then
origin[k] = ''; -- Empty string, not nil
end
elseif list ~= nil then
v, origin[k] = args[list], list;
else
-- maybe let through instead of raising an error?
-- v, origin[k] = args[k], k;
error( cfg.messages['unknown_argument_map'] .. ': ' .. k);
end
-- Empty strings, not nil;
if v == nil then
v = '';
origin[k] = '';
end
-- tbl = rawset( tbl, k, v );
rawset( tbl, k, v );
return v;
end,
});
end
--[[--------------------------< N O W R A P _ D A T E >-------------------------
When date is YYYY-MM-DD format wrap in nowrap span: <span ...>YYYY-MM-DD</span>.
When date is DD MMMM YYYY or is MMMM DD, YYYY then wrap in nowrap span:
<span ...>DD MMMM</span> YYYY or <span ...>MMMM DD,</span> YYYY
DOES NOT yet support MMMM YYYY or any of the date ranges.
]]
local function nowrap_date (date)
local cap;
local cap2;
if date:match("^%d%d%d%d%-%d%d%-%d%d$") then
date = utilities.substitute (cfg.presentation['nowrap1'], date);
elseif date:match("^%a+%s*%d%d?,%s+%d%d%d%d$") or date:match ("^%d%d?%s*%a+%s+%d%d%d%d$") then
cap, cap2 = string.match (date, "^(.*)%s+(%d%d%d%d)$");
date = utilities.substitute (cfg.presentation['nowrap2'], {cap, cap2});
end
return date;
end
--[[--------------------------< S E T _ T I T L E T Y P E >---------------------
This function sets default title types (equivalent to the citation including
|type=<default value>) for those templates that have defaults. Also handles the
special case where it is desirable to omit the title type from the rendered citation
(|type=none).
]]
local function set_titletype (cite_class, title_type)
if utilities.is_set (title_type) then
if 'none' == cfg.keywords_xlate[title_type] then
title_type = ''; -- if |type=none then type parameter not displayed
end
return title_type; -- if |type= has been set to any other value use that value
end
return cfg.title_types [cite_class] or ''; -- set template's default title type; else empty string for concatenation
end
--[[--------------------------< S A F E _ J O I N >-----------------------------
Joins a sequence of strings together while checking for duplicate separation characters.
]]
local function safe_join( tbl, duplicate_char )
local f = {}; -- create a function table appropriate to type of 'duplicate character'
if 1 == #duplicate_char then -- for single byte ASCII characters use the string library functions
f.gsub = string.gsub
f.match = string.match
f.sub = string.sub
else -- for multi-byte characters use the ustring library functions
f.gsub = mw.ustring.gsub
f.match = mw.ustring.match
f.sub = mw.ustring.sub
end
local str = ''; -- the output string
for _, value in ipairs( tbl ) do
if value == nil then value = ''; end
if str == '' then -- if output string is empty
str = value; -- assign value to it (first time through the loop)
elseif value ~= '' then
local comp; -- what does 'comp' mean?
if value:sub(1, 1) == '<' then -- special case of values enclosed in spans and other markup.
comp = value:gsub( "%b<>", "" ); -- remove HTML markup (<span>string</span> -> string)
else
comp = value;
end
-- typically duplicate_char is sepc
if f.sub(comp, 1, 1) == duplicate_char then -- is first character same as duplicate_char? why test first character?
-- Because individual string segments often (always?) begin with terminal punct for the
-- preceding segment: 'First element' .. 'sepc next element' .. etc.?
local trim = false;
local end_chr = f.sub(str, -1, -1); -- get the last character of the output string
-- str = str .. "<HERE(enchr=" .. end_chr .. ")" -- debug stuff?
if end_chr == duplicate_char then -- if same as separator
str = f.sub(str, 1, -2); -- remove it
elseif end_chr == "'" then -- if it might be wiki-markup
if f.sub(str, -3, -1) == duplicate_char .. "''" then -- if last three chars of str are sepc''
str = f.sub(str, 1, -4) .. "''"; -- remove them and add back ''
elseif f.sub(str, -5, -1) == duplicate_char .. "]]''" then -- if last five chars of str are sepc]]''
trim = true; -- why? why do this and next differently from previous?
elseif f.sub(str, -4, -1) == duplicate_char .. "]''" then -- if last four chars of str are sepc]''
trim = true; -- same question
end
elseif end_chr == "]" then -- if it might be wiki-markup
if f.sub(str, -3, -1) == duplicate_char .. "]]" then -- if last three chars of str are sepc]] wikilink
trim = true;
elseif f.sub(str, -3, -1) == duplicate_char .. '"]' then -- if last three chars of str are sepc"] quoted external link
trim = true;
elseif f.sub(str, -2, -1) == duplicate_char .. "]" then -- if last two chars of str are sepc] external link
trim = true;
elseif f.sub(str, -4, -1) == duplicate_char .. "'']" then -- normal case when |url=something & |title=Title.
trim = true;
end
elseif end_chr == " " then -- if last char of output string is a space
if f.sub(str, -2, -1) == duplicate_char .. " " then -- if last two chars of str are <sepc><space>
str = f.sub(str, 1, -3); -- remove them both
end
end
if trim then
if value ~= comp then -- value does not equal comp when value contains HTML markup
local dup2 = duplicate_char;
if f.match(dup2, "%A" ) then dup2 = "%" .. dup2; end -- if duplicate_char not a letter then escape it
value = f.gsub(value, "(%b<>)" .. dup2, "%1", 1 ) -- remove duplicate_char if it follows HTML markup
else
value = f.sub(value, 2, -1 ); -- remove duplicate_char when it is first character
end
end
end
str = str .. value; -- add it to the output string
end
end
return str;
end
--[[--------------------------< I S _ S U F F I X >-----------------------------
returns true if suffix is properly formed Jr, Sr, or ordinal in the range 1–9.
Puncutation not allowed.
]]
local function is_suffix (suffix)
if utilities.in_array (suffix, {'Jr', 'Sr', 'Jnr', 'Snr', '1st', '2nd', '3rd'}) or suffix:match ('^%dth$') then
return true;
end
return false;
end
--[[--------------------< I S _ G O O D _ V A N C _ N A M E >-------------------
For Vancouver style, author/editor names are supposed to be rendered in Latin
(read ASCII) characters. When a name uses characters that contain diacritical
marks, those characters are to be converted to the corresponding Latin
character. When a name is written using a non-Latin alphabet or logogram, that
name is to be transliterated into Latin characters. The module doesn't do this
so editors may/must.
This test allows |first= and |last= names to contain any of the letters defined
in the four Unicode Latin character sets
[http://www.unicode.org/charts/PDF/U0000.pdf C0 Controls and Basic Latin] 0041–005A, 0061–007A
[http://www.unicode.org/charts/PDF/U0080.pdf C1 Controls and Latin-1 Supplement] 00C0–00D6, 00D8–00F6, 00F8–00FF
[http://www.unicode.org/charts/PDF/U0100.pdf Latin Extended-A] 0100–017F
[http://www.unicode.org/charts/PDF/U0180.pdf Latin Extended-B] 0180–01BF, 01C4–024F
|lastn= also allowed to contain hyphens, spaces, and apostrophes.
(http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35029/)
|firstn= also allowed to contain hyphens, spaces, apostrophes, and periods
This original test:
if nil == mw.ustring.find (last, "^[A-Za-zÀ-ÖØ-öø-ƿDŽ-ɏ%-%s%']*$")
or nil == mw.ustring.find (first, "^[A-Za-zÀ-ÖØ-öø-ƿDŽ-ɏ%-%s%'%.]+[2-6%a]*$") then
was written outside of the code editor and pasted here because the code editor
gets confused between character insertion point and cursor position. The test has
been rewritten to use decimal character escape sequence for the individual bytes
of the Unicode characters so that it is not necessary to use an external editor
to maintain this code.
\195\128-\195\150 – À-Ö (U+00C0–U+00D6 – C0 controls)
\195\152-\195\182 – Ø-ö (U+00D8-U+00F6 – C0 controls)
\195\184-\198\191 – ø-ƿ (U+00F8-U+01BF – C0 controls, Latin extended A & B)
\199\132-\201\143 – DŽ-ɏ (U+01C4-U+024F – Latin extended B)
]]
local function is_good_vanc_name (last, first, suffix, position)
if not suffix then
if first:find ('[,%s]') then -- when there is a space or comma, might be first name/initials + generational suffix
first = first:match ('(.-)[,%s]+'); -- get name/initials
suffix = first:match ('[,%s]+(.+)$'); -- get generational suffix
end
end
if utilities.is_set (suffix) then
if not is_suffix (suffix) then
add_vanc_error (cfg.err_msg_supl.suffix, position);
return false; -- not a name with an appropriate suffix
end
end
if nil == mw.ustring.find (last, "^[A-Za-z\195\128-\195\150\195\152-\195\182\195\184-\198\191\199\132-\201\143\225\184\128-\225\187\191%-%s%']*$") or
nil == mw.ustring.find (first, "^[A-Za-z\195\128-\195\150\195\152-\195\182\195\184-\198\191\199\132-\201\143\225\184\128-\225\187\191%-%s%'%.]*$") then
add_vanc_error (cfg.err_msg_supl['non-Latin char'], position);
return false; -- not a string of Latin characters; Vancouver requires Romanization
end;
return true;
end
--[[--------------------------< R E D U C E _ T O _ I N I T I A L S >------------------------------------------
Attempts to convert names to initials in support of |name-list-style=vanc.
Names in |firstn= may be separated by spaces or hyphens, or for initials, a period.
See http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35062/.
Vancouver style requires family rank designations (Jr, II, III, etc.) to be rendered
as Jr, 2nd, 3rd, etc. See http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35085/.
This code only accepts and understands generational suffix in the Vancouver format
because Roman numerals look like, and can be mistaken for, initials.
This function uses ustring functions because firstname initials may be any of the
Unicode Latin characters accepted by is_good_vanc_name ().
]]
local function reduce_to_initials (first, position)
if first:find (',', 1, true) then
return first; -- commas not allowed; abandon
end
local name, suffix = mw.ustring.match (first, "^(%u+) ([%dJS][%drndth]+)$");
if not name then -- if not initials and a suffix
name = mw.ustring.match (first, "^(%u+)$"); -- is it just initials?
end
if name then -- if first is initials with or without suffix
if 3 > mw.ustring.len (name) then -- if one or two initials
if suffix then -- if there is a suffix
if is_suffix (suffix) then -- is it legitimate?
return first; -- one or two initials and a valid suffix so nothing to do
else
add_vanc_error (cfg.err_msg_supl.suffix, position); -- one or two initials with invalid suffix so error message
return first; -- and return first unmolested
end
else
return first; -- one or two initials without suffix; nothing to do
end
end
end -- if here then name has 3 or more uppercase letters so treat them as a word
local initials_t = {}; -- table to hold initials
local i = 1; -- counter for number of initials
local names_t = mw.text.split (first, '[%s%-]+'); -- split into a sequence of names and possible suffix
while names_t[i] do -- loop through the sequence
if 1 < i and names_t[i]:match ('[%dJS][%drndth]+%.?$') then -- if not the first name, and looks like a suffix (may have trailing dot)
names_t[i] = names_t[i]:gsub ('%.', ''); -- remove terminal dot if present
if is_suffix (names_t[i]) then -- if a legitimate suffix
table.insert (initials_t, ' ' .. names_t[i]); -- add a separator space, insert at end of initials sequence
break; -- and done because suffix must fall at the end of a name
end -- no error message if not a suffix; possibly because of Romanization
end
if 3 > i then
table.insert (initials_t, mw.ustring.sub (names_t[i], 1, 1)); -- insert the initial at end of initials sequence
end
i = i + 1; -- bump the counter
end
return table.concat (initials_t); -- Vancouver format does not include spaces.
end
--[[--------------------------< I N T E R W I K I _ P R E F I X E N _ G E T >----------------------------------
extract interwiki prefixen from <value>. Returns two one or two values:
false – no prefixen
nil – prefix exists but not recognized
project prefix, language prefix – when value has either of:
:<project>:<language>:<article>
:<language>:<project>:<article>
project prefix, nil – when <value> has only a known single-letter prefix
nil, language prefix – when <value> has only a known language prefix
accepts single-letter project prefixen: 'd' (wikidata), 's' (wikisource), and 'w' (wikipedia) prefixes; at this
writing, the other single-letter prefixen (b (wikibook), c (commons), m (meta), n (wikinews), q (wikiquote), and
v (wikiversity)) are not supported.
]]
local function interwiki_prefixen_get (value, is_link)
if not value:find (':%l+:') then -- if no prefix
return false; -- abandon; boolean here to distinguish from nil fail returns later
end
local prefix_patterns_linked_t = { -- sequence of valid interwiki and inter project prefixen
'^%[%[:([dsw]):(%l%l+):', -- wikilinked; project and language prefixes
'^%[%[:(%l%l+):([dsw]):', -- wikilinked; language and project prefixes
'^%[%[:([dsw]):', -- wikilinked; project prefix
'^%[%[:(%l%l+):', -- wikilinked; language prefix
}
local prefix_patterns_unlinked_t = { -- sequence of valid interwiki and inter project prefixen
'^:([dsw]):(%l%l+):', -- project and language prefixes
'^:(%l%l+):([dsw]):', -- language and project prefixes
'^:([dsw]):', -- project prefix
'^:(%l%l+):', -- language prefix
}
local cap1, cap2;
for _, pattern in ipairs ((is_link and prefix_patterns_linked_t) or prefix_patterns_unlinked_t) do
cap1, cap2 = value:match (pattern);
if cap1 then
break; -- found a match so stop looking
end
end
if cap1 and cap2 then -- when both then :project:language: or :language:project: (both forms allowed)
if 1 == #cap1 then -- length == 1 then :project:language:
if cfg.inter_wiki_map[cap2] then -- is language prefix in the interwiki map?
return cap1, cap2; -- return interwiki project and interwiki language
end
else -- here when :language:project:
if cfg.inter_wiki_map[cap1] then -- is language prefix in the interwiki map?
return cap2, cap1; -- return interwiki project and interwiki language
end
end
return nil; -- unknown interwiki language
elseif not (cap1 or cap2) then -- both are nil?
return nil; -- we got something that looks like a project prefix but isn't; return fail
elseif 1 == #cap1 then -- here when one capture
return cap1, nil; -- length is 1 so return project, nil language
else -- here when one capture and its length it more than 1
if cfg.inter_wiki_map[cap1] then -- is language prefix in the interwiki map?
return nil, cap1; -- return nil project, language
end
end
end
--[[--------------------------< L I S T _ P E O P L E >--------------------------
Formats a list of people (authors, contributors, editors, interviewers, translators)
names in the list will be linked when
|<name>-link= has a value
|<name>-mask- does NOT have a value; masked names are presumed to have been
rendered previously so should have been linked there
when |<name>-mask=0, the associated name is not rendered
]]
local function list_people (control, people, etal)
local sep;
local namesep;
local format = control.format;
local maximum = control.maximum;
local name_list = {};
if 'vanc' == format then -- Vancouver-like name styling?
sep = cfg.presentation['sep_nl_vanc']; -- name-list separator between names is a comma
namesep = cfg.presentation['sep_name_vanc']; -- last/first separator is a space
else
sep = cfg.presentation['sep_nl']; -- name-list separator between names is a semicolon
namesep = cfg.presentation['sep_name']; -- last/first separator is <comma><space>
end
if sep:sub (-1, -1) ~= " " then sep = sep .. " " end
if utilities.is_set (maximum) and maximum < 1 then return "", 0; end -- returned 0 is for EditorCount; not used for other names
for i, person in ipairs (people) do
if utilities.is_set (person.last) then
local mask = person.mask;
local one;
local sep_one = sep;
if utilities.is_set (maximum) and i > maximum then
etal = true;
break;
end
if mask then
local n = tonumber (mask); -- convert to a number if it can be converted; nil else
if n then
one = 0 ~= n and string.rep("—", n) or nil; -- make a string of (n > 0) mdashes, nil else, to replace name
person.link = nil; -- don't create link to name if name is replaces with mdash string or has been set nil
else
one = mask; -- replace name with mask text (must include name-list separator)
sep_one = " "; -- modify name-list separator
end
else
one = person.last; -- get surname
local first = person.first -- get given name
if utilities.is_set (first) then
if ("vanc" == format) then -- if Vancouver format
one = one:gsub ('%.', ''); -- remove periods from surnames (http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35029/)
if not person.corporate and is_good_vanc_name (one, first, nil, i) then -- and name is all Latin characters; corporate authors not tested
first = reduce_to_initials (first, i); -- attempt to convert first name(s) to initials
end
end
one = one .. namesep .. first;
end
end
if utilities.is_set (person.link) then
one = utilities.make_wikilink (person.link, one); -- link author/editor
end
if one then -- if <one> has a value (name, mdash replacement, or mask text replacement)
local proj, tag = interwiki_prefixen_get (one, true); -- get the interwiki prefixen if present
if 'w' == proj and ('Wikipedia' == mw.site.namespaces.Project['name']) then
proj = nil; -- for stuff like :w:de:<article>, :w is unnecessary TODO: maint cat?
end
if proj then
local proj_name = ({['d'] = cfg.messages.wikidata, ['s'] = cfg.messages.wikisource, ['w'] = cfg.messages.wikipedia})[proj]; -- :w (wikipedia) for linking from a non-wikipedia project
if proj_name then
one = one .. utilities.wrap_style ('interproj', proj_name); -- add resized leading space, brackets, static text, language name
utilities.add_prop_cat ('interproj-linked-name', proj); -- categorize it; <proj> is sort key
tag = nil; -- unset; don't do both project and language
end
end
if tag == cfg.this_wiki_code then
tag = nil; -- stuff like :en:<article> at en.wiki is pointless TODO: maint cat?
end
if tag then
local lang = cfg.lang_tag_remap[tag] or cfg.mw_languages_by_tag_t[tag];
if lang then -- error messaging done in extract_names() where we know parameter names
one = one .. utilities.wrap_style ('interwiki', lang); -- add resized leading space, brackets, static text, language name
utilities.add_prop_cat ('interwiki-linked-name', tag); -- categorize it; <tag> is sort key
end
end
table.insert (name_list, one); -- add it to the list of names
table.insert (name_list, sep_one); -- add the proper name-list separator
end
end
end
local count = #name_list / 2; -- (number of names + number of separators) divided by 2
if 0 < count then
if 1 < count and not etal then
if 'amp' == format then
name_list[#name_list-2] = " & "; -- replace last separator with ampersand text
elseif 'and' == format then
if 2 == count then
name_list[#name_list-2] = cfg.presentation.sep_nl_and; -- replace last separator with 'and' text
else
name_list[#name_list-2] = cfg.presentation.sep_nl_end; -- replace last separator with '(sep) and' text
end
end
end
name_list[#name_list] = nil; -- erase the last separator
end
local result = table.concat (name_list); -- construct list
if etal and utilities.is_set (result) then -- etal may be set by |display-authors=etal but we might not have a last-first list
result = result .. sep .. cfg.messages['et al']; -- we've got a last-first list and etal so add et al.
end
return result, count; -- return name-list string and count of number of names (count used for editor names only)
end
--[[--------------------< M A K E _ C I T E R E F _ I D >-----------------------
Generates a CITEREF anchor ID if we have at least one name or a date. Otherwise
returns an empty string.
namelist is one of the contributor-, author-, or editor-name lists chosen in that
order. year is Year or anchor_year.
]]
local function make_citeref_id (namelist, year)
local names={}; -- a table for the one to four names and year
for i,v in ipairs (namelist) do -- loop through the list and take up to the first four last names
names[i] = v.last
if i == 4 then break end -- if four then done
end
table.insert (names, year); -- add the year at the end
local id = table.concat(names); -- concatenate names and year for CITEREF id
if utilities.is_set (id) then -- if concatenation is not an empty string
return "CITEREF" .. id; -- add the CITEREF portion
else
return ''; -- return an empty string; no reason to include CITEREF id in this citation
end
end
--[[--------------------------< C I T E _ C L A S S _A T T R I B U T E _M A K E >------------------------------
construct <cite> tag class attribute for this citation.
<cite_class> – config.CitationClass from calling template
<mode> – value from |mode= parameter
]]
local function cite_class_attribute_make (cite_class, mode)
local class_t = {};
table.insert (class_t, 'citation'); -- required for blue highlight
if 'citation' ~= cite_class then
table.insert (class_t, cite_class); -- identify this template for user css
table.insert (class_t, utilities.is_set (mode) and mode or 'cs1'); -- identify the citation style for user css or javascript
else
table.insert (class_t, utilities.is_set (mode) and mode or 'cs2'); -- identify the citation style for user css or javascript
end
for _, prop_key in ipairs (z.prop_keys_t) do
table.insert (class_t, prop_key); -- identify various properties for user css or javascript
end
return table.concat (class_t, ' '); -- make a big string and done
end
--[[---------------------< N A M E _ H A S _ E T A L >--------------------------
Evaluates the content of name parameters (author, editor, etc.) for variations on
the theme of et al. If found, the et al. is removed, a flag is set to true and
the function returns the modified name and the flag.
This function never sets the flag to false but returns its previous state because
it may have been set by previous passes through this function or by the associated
|display-<names>=etal parameter
]]
local function name_has_etal (name, etal, nocat, param)
if utilities.is_set (name) then -- name can be nil in which case just return
local patterns = cfg.et_al_patterns; -- get patterns from configuration
for _, pattern in ipairs (patterns) do -- loop through all of the patterns
if name:match (pattern) then -- if this 'et al' pattern is found in name
name = name:gsub (pattern, ''); -- remove the offending text
etal = true; -- set flag (may have been set previously here or by |display-<names>=etal)
if not nocat then -- no categorization for |vauthors=
utilities.set_message ('err_etal', {param}); -- and set an error if not added
end
end
end
end
return name, etal;
end
--[[---------------------< N A M E _ I S _ N U M E R I C >----------------------
Add an error message and category when <name> parameter value does not contain letters.
Add a maintenance category when <name> parameter value has numeric characters mixed with characters that are
not numeric characters; could be letters and/or punctuation characters.
This function will only emit one error and one maint message for the current template. Does not emit both error
and maint messages/categories for the same parameter value.
returns nothing
]]
local function name_is_numeric (name, name_alias, list_name)
local patterns = {
'^%D+%d', -- <name> must have digits preceded by other characters
'^%D*%d+%D+', -- <name> must have digits followed by other characters
}
if not added_numeric_name_errs and mw.ustring.match (name, '^[%A]+$') then -- if we have not already set an error message and <name> does not have any alpha characters
utilities.set_message ('err_numeric_names', name_alias); -- add an error message
added_numeric_name_errs = true; -- set the flag so we emit only one error message
return; -- when here no point in further testing; abandon
end
if not added_numeric_name_maint then -- if we have already set a maint message
for _, pattern in ipairs (patterns) do -- spin through list of patterns
if mw.ustring.match (name, pattern) then -- digits preceded or followed by anything but digits; %D+ includes punctuation
utilities.set_message ('maint_numeric_names', cfg.special_case_translation [list_name]); -- add a maint cat for this template
added_numeric_name_maint = true; -- set the flag so we emit only one maint message
return; -- when here no point in further testing; abandon
end
end
end
end
--[[-----------------< N A M E _ H A S _ M U L T _ N A M E S >------------------
Evaluates the content of last/surname (authors etc.) parameters for multiple names.
Multiple names are indicated if there is more than one comma or any "unescaped"
semicolons. Escaped semicolons are ones used as part of selected HTML entities.
If the condition is met, the function adds the multiple name maintenance category.
Same test for first except that commas should not appear in given names (MOS:JR says
that the generational suffix does not take a separator character). Titles, degrees,
postnominals, affiliations, all normally comma separated don't belong in a citation.
<name> – name parameter value
<list_name> – AuthorList, EditorList, etc
<limit> – number of allowed commas; 1 (default) for surnames; 0 for given names
returns nothing
]]
local function name_has_mult_names (name, list_name, limit)
local _, commas, semicolons, nbsps;
limit = limit and limit or 1;
if utilities.is_set (name) then
_, commas = name:gsub (',', ''); -- count the number of commas
_, semicolons = name:gsub (';', ''); -- count the number of semicolons
-- nbsps probably should be its own separate count rather than merged in
-- some way with semicolons because Lua patterns do not support the
-- grouping operator that regex does, which means there is no way to add
-- more entities to escape except by adding more counts with the new
-- entities
_, nbsps = name:gsub (' ',''); -- count nbsps
-- There is exactly 1 semicolon per entity, so subtract nbsps
-- from semicolons to 'escape' them. If additional entities are added,
-- they also can be subtracted.
if limit < commas or 0 < (semicolons - nbsps) then
utilities.set_message ('maint_mult_names', cfg.special_case_translation [list_name]); -- add a maint message
end
end
end
--[=[-------------------------< I S _ G E N E R I C >----------------------------------------------------------
Compares values assigned to various parameters according to the string provided as <item> in the function call.
<item> can have on of two values:
'generic_names' – for name-holding parameters: |last=, |first=, |editor-last=, etc
'generic_titles' – for |title=
There are two types of generic tests. The 'accept' tests look for a pattern that should not be rejected by the
'reject' test. For example,
|author=[[John Smith (author)|Smith, John]]
would be rejected by the 'author' reject test. But piped wikilinks with 'author' disambiguation should not be
rejected so the 'accept' test prevents that from happening. Accept tests are always performed before reject
tests.
Each of the 'accept' and 'reject' sequence tables hold tables for en.wiki (['en']) and local.wiki (['local'])
that each can hold a test sequence table The sequence table holds, at index [1], a test pattern, and, at index
[2], a boolean control value. The control value tells string.find() or mw.ustring.find() to do plain-text search (true)
or a pattern search (false). The intent of all this complexity is to make these searches as fast as possible so
that we don't run out of processing time on very large articles.
Returns
true when a reject test finds the pattern or string
false when an accept test finds the pattern or string
nil else
]=]
local function is_generic (item, value)
local str_lower = { -- use string.lower() for en.wiki (['en']) and use mw.ustring.lower() or local.wiki (['local'])
['en'] = string.lower,
['local'] = mw.ustring.lower,
}
local str_find = { -- use string.find() for en.wiki (['en']) and use mw.ustring.find() or local.wiki (['local'])
['en'] = string.find,
['local'] = mw.ustring.find,
}
local function test (val, test_t, wiki) -- local function to do the testing; <wiki> selects lower() and find() functions
val = test_t[2] and str_lower[wiki](value) or val; -- when <test_t[2]> set to 'true', plaintext search using lowercase value
return str_find[wiki] (val, test_t[1], 1, test_t[2]); -- return nil when not found or matched
end
local test_types_t = {'accept', 'reject'}; -- test accept patterns first, then reject patterns
local wikis_t = {'en', 'local'}; -- do tests for each of these keys; en.wiki first, local.wiki second
for _, test_type in ipairs (test_types_t) do -- for each test type
for _, generic_value in pairs (cfg.special_case_translation[item][test_type]) do -- spin through the list of generic value fragments to accept or reject
for _, wiki in ipairs (wikis_t) do
if generic_value[wiki] then
if test (value, generic_value[wiki], wiki) then -- go do the test
return ('reject' == test_type); -- param value rejected, return true; false else
end
end
end
end
end
end
--[[--------------------------< N A M E _ I S _ G E N E R I C >------------------------------------------------
calls is_generic() to determine if <name> is a 'generic name' listed in cfg.generic_names; <name_alias> is the
parameter name used in error messaging
]]
local function name_is_generic (name, name_alias)
if not added_generic_name_errs and is_generic ('generic_names', name) then
utilities.set_message ('err_generic_name', name_alias); -- set an error message
added_generic_name_errs = true;
end
end
--[[--------------------------< N A M E _ C H E C K S >--------------------------------------------------------
This function calls various name checking functions used to validate the content of the various name-holding parameters.
]]
local function name_checks (last, first, list_name, last_alias, first_alias)
local accept_name;
if utilities.is_set (last) then
last, accept_name = utilities.has_accept_as_written (last); -- remove accept-this-as-written markup when it wraps all of <last>
if not accept_name then -- <last> not wrapped in accept-as-written markup
name_has_mult_names (last, list_name); -- check for multiple names in the parameter
name_is_numeric (last, last_alias, list_name); -- check for names that have no letters or are a mix of digits and other characters
name_is_generic (last, last_alias); -- check for names found in the generic names list
end
end
if utilities.is_set (first) then
first, accept_name = utilities.has_accept_as_written (first); -- remove accept-this-as-written markup when it wraps all of <first>
if not accept_name then -- <first> not wrapped in accept-as-written markup
name_has_mult_names (first, list_name, 0); -- check for multiple names in the parameter; 0 is number of allowed commas in a given name
name_is_numeric (first, first_alias, list_name); -- check for names that have no letters or are a mix of digits and other characters
name_is_generic (first, first_alias); -- check for names found in the generic names list
end
local wl_type, D = utilities.is_wikilink (first);
if 0 ~= wl_type then
first = D;
utilities.set_message ('err_bad_paramlink', first_alias);
end
end
return last, first; -- done
end
--[[----------------------< E X T R A C T _ N A M E S >-------------------------
Gets name list from the input arguments
Searches through args in sequential order to find |lastn= and |firstn= parameters
(or their aliases), and their matching link and mask parameters. Stops searching
when both |lastn= and |firstn= are not found in args after two sequential attempts:
found |last1=, |last2=, and |last3= but doesn't find |last4= and |last5= then the
search is done.
This function emits an error message when there is a |firstn= without a matching
|lastn=. When there are 'holes' in the list of last names, |last1= and |last3=
are present but |last2= is missing, an error message is emitted. |lastn= is not
required to have a matching |firstn=.
When an author or editor parameter contains some form of 'et al.', the 'et al.'
is stripped from the parameter and a flag (etal) returned that will cause list_people()
to add the static 'et al.' text from Module:Citation/CS1/Configuration. This keeps
'et al.' out of the template's metadata. When this occurs, an error is emitted.
]]
local function extract_names(args, list_name)
local names = {}; -- table of names
local last; -- individual name components
local first;
local link;
local mask;
local i = 1; -- loop counter/indexer
local n = 1; -- output table indexer
local count = 0; -- used to count the number of times we haven't found a |last= (or alias for authors, |editor-last or alias for editors)
local etal = false; -- return value set to true when we find some form of et al. in an author parameter
local last_alias, first_alias, link_alias; -- selected parameter aliases used in error messaging
while true do
last, last_alias = utilities.select_one ( args, cfg.aliases[list_name .. '-Last'], 'err_redundant_parameters', i ); -- search through args for name components beginning at 1
first, first_alias = utilities.select_one ( args, cfg.aliases[list_name .. '-First'], 'err_redundant_parameters', i );
link, link_alias = utilities.select_one ( args, cfg.aliases[list_name .. '-Link'], 'err_redundant_parameters', i );
mask = utilities.select_one ( args, cfg.aliases[list_name .. '-Mask'], 'err_redundant_parameters', i );
if last then -- error check |lastn= alias for unknown interwiki link prefix; done here because this is where we have the parameter name
local project, language = interwiki_prefixen_get (last, true); -- true because we expect interwiki links in |lastn= to be wikilinked
if nil == project and nil == language then -- when both are nil
utilities.set_message ('err_bad_paramlink', last_alias); -- not known, emit an error message -- TODO: err_bad_interwiki?
last = utilities.remove_wiki_link (last); -- remove wikilink markup; show display value only
end
end
if link then -- error check |linkn= alias for unknown interwiki link prefix
local project, language = interwiki_prefixen_get (link, false); -- false because wiki links in |author-linkn= is an error
if nil == project and nil == language then -- when both are nil
utilities.set_message ('err_bad_paramlink', link_alias); -- not known, emit an error message -- TODO: err_bad_interwiki?
link = nil; -- unset so we don't link
link_alias = nil;
end
end
last, etal = name_has_etal (last, etal, false, last_alias); -- find and remove variations on et al.
first, etal = name_has_etal (first, etal, false, first_alias); -- find and remove variations on et al.
last, first = name_checks (last, first, list_name, last_alias, first_alias); -- multiple names, extraneous annotation, etc. checks
if first and not last then -- if there is a firstn without a matching lastn
local alias = first_alias:find ('given', 1, true) and 'given' or 'first'; -- get first or given form of the alias
utilities.set_message ('err_first_missing_last', {
first_alias, -- param name of alias missing its mate
first_alias:gsub (alias, {['first'] = 'last', ['given'] = 'surname'}), -- make param name appropriate to the alias form
}); -- add this error message
elseif not first and not last then -- if both firstn and lastn aren't found, are we done?
count = count + 1; -- number of times we haven't found last and first
if 2 <= count then -- two missing names and we give up
break; -- normal exit or there is a two-name hole in the list; can't tell which
end
else -- we have last with or without a first
link = link_title_ok (link, link_alias, last, last_alias); -- check for improper wiki-markup
if first then
link = link_title_ok (link, link_alias, first, first_alias); -- check for improper wiki-markup
end
names[n] = {last = last, first = first, link = link, mask = mask, corporate = false}; -- add this name to our names list (corporate for |vauthors= only)
n = n + 1; -- point to next location in the names table
if 1 == count then -- if the previous name was missing
utilities.set_message ('err_missing_name', {list_name:match ("(%w+)List"):lower(), i - 1}); -- add this error message
end
count = 0; -- reset the counter, we're looking for two consecutive missing names
end
i = i + 1; -- point to next args location
end
return names, etal; -- all done, return our list of names and the etal flag
end
--[[--------------------------< N A M E _ T A G _ G E T >------------------------------------------------------
attempt to decode |language=<lang_param> and return language name and matching tag; nil else.
This function looks for:
<lang_param> as a tag in cfg.lang_tag_remap{}
<lang_param> as a name in cfg.lang_name_remap{}
<lang_param> as a name in cfg.mw_languages_by_name_t
<lang_param> as a tag in cfg.mw_languages_by_tag_t
when those fail, presume that <lang_param> is an IETF-like tag that MediaWiki does not recognize. Strip all
script, region, variant, whatever subtags from <lang_param> to leave just a two or three character language tag
and look for the new <lang_param> in cfg.mw_languages_by_tag_t{}
on success, returns name (in properly capitalized form) and matching tag (in lowercase); on failure returns nil
]]
local function name_tag_get (lang_param)
local lang_param_lc = mw.ustring.lower (lang_param); -- use lowercase as an index into the various tables
local name;
local tag;
name = cfg.lang_tag_remap[lang_param_lc]; -- assume <lang_param_lc> is a tag; attempt to get remapped language name
if name then -- when <name>, <lang_param> is a tag for a remapped language name
if cfg.lang_name_remap[name:lower()][2] ~= lang_param_lc then
utilities.set_message ('maint_unknown_lang'); -- add maint category if not already added
return name, cfg.lang_name_remap[name:lower()][2]; -- so return name and tag from lang_name_remap[name]; special case to xlate sr-ec and sr-el to sr-cyrl and sr-latn
end
return name, lang_param_lc; -- so return <name> from remap and <lang_param_lc>
end
tag = lang_param_lc:match ('^(%a%a%a?)%-.*'); -- still assuming that <lang_param_lc> is a tag; strip script, region, variant subtags
name = cfg.lang_tag_remap[tag]; -- attempt to get remapped language name with language subtag only
if name then -- when <name>, <tag> is a tag for a remapped language name
return name, tag; -- so return <name> from remap and <tag>
end
if cfg.lang_name_remap[lang_param_lc] then -- not a remapped tag, assume <lang_param_lc> is a name; attempt to get remapped language tag
return cfg.lang_name_remap[lang_param_lc][1], cfg.lang_name_remap[lang_param_lc][2]; -- for this <lang_param_lc>, return a (possibly) new name and appropriate tag
end
name = cfg.mw_languages_by_tag_t[lang_param_lc]; -- assume that <lang_param_lc> is a tag; attempt to get its matching language name
if name then
return name, lang_param_lc; -- <lang_param_lc> is a tag so return it and <name>
end
tag = cfg.mw_languages_by_name_t[lang_param_lc]; -- assume that <lang_param_lc> is a language name; attempt to get its matching tag
if tag then
return cfg.mw_languages_by_tag_t[tag], tag; -- <lang_param_lc> is a name so return the name from the table and <tag>
end
tag = lang_param_lc:match ('^(%a%a%a?)%-.*'); -- is <lang_param_lc> an IETF-like tag that MediaWiki doesn't recognize? <tag> gets the language subtag; nil else
if tag then
name = cfg.mw_languages_by_tag_t[tag]; -- attempt to get a language name using the shortened <tag>
if name then
return name, tag; -- <lang_param_lc> is an unrecognized IETF-like tag so return <name> and language subtag
end
end
end
--[[-------------------< L A N G U A G E _ P A R A M E T E R >------------------
Gets language name from a provided two- or three-character ISO 639 code. If a code
is recognized by MediaWiki, use the returned name; if not, then use the value that
was provided with the language parameter.
When |language= contains a recognized language (either code or name), the page is
assigned to the category for that code: Category:Norwegian-language sources (no).
For valid three-character code languages, the page is assigned to the single category
for '639-2' codes: Category:CS1 ISO 639-2 language sources.
Languages that are the same as the local wiki are not categorized. MediaWiki does
not recognize three-character equivalents of two-character codes: code 'ar' is
recognized but code 'ara' is not.
This function supports multiple languages in the form |language=nb, French, th
where the language names or codes are separated from each other by commas with
optional space characters.
]]
local function language_parameter (lang)
local tag; -- some form of IETF-like language tag; language subtag with optional region, sript, vatiant, etc subtags
local lang_subtag; -- ve populates |language= with mostly unecessary region subtags that MediaWiki does not recognize; this is the base language subtag
local name; -- the language name
local language_list = {}; -- table of language names to be rendered
local names_t = mw.text.split (lang, '%s*,%s*'); -- names should be a comma separated list
for _, lang in ipairs (names_t) do -- reuse lang here because we don't yet know if lang is a language name or a language tag; for now, ignore linter warning
name, tag = name_tag_get (lang); -- attempt to get name/tag pair for <lang>; <name> has proper capitalization; <tag> is lowercase
if utilities.is_set (tag) then
lang_subtag = tag:gsub ('^(%a%a%a?)%-.*', '%1'); -- for categorization, strip any IETF-like tags from language tag
if cfg.this_wiki_code ~= lang_subtag then -- when the language is not the same as this wiki's language
if 2 == lang_subtag:len() then -- and is a two-character tag
utilities.add_prop_cat ('foreign-lang-source', {name, tag}, lang_subtag); -- categorize it; tag appended to allow for multiple language categorization
else -- or is a recognized language (but has a three-character tag)
utilities.add_prop_cat ('foreign-lang-source-2', {lang_subtag}, lang_subtag); -- categorize it differently TODO: support multiple three-character tag categories per cs1|2 template?
end
elseif cfg.local_lang_cat_enable then -- when the language and this wiki's language are the same and categorization is enabled
utilities.add_prop_cat ('local-lang-source', {name, lang_subtag}); -- categorize it
end
else
name = lang; -- return whatever <lang> has so that we show something
utilities.set_message ('maint_unknown_lang'); -- add maint category if not already added
end
table.insert (language_list, name);
end
name = utilities.make_sep_list (#language_list, language_list);
if (1 == #language_list) and (lang_subtag == cfg.this_wiki_code) then -- when only one language, find lang name in this wiki lang name; for |language=en-us, 'English' in 'American English'
return ''; -- if one language and that language is this wiki's return an empty string (no annotation)
end
return (" " .. wrap_msg ('language', name)); -- otherwise wrap with '(in ...)'
--[[ TODO: should only return blank or name rather than full list
so we can clean up the bunched parenthetical elements Language, Type, Format
]]
end
--[[-----------------------< S E T _ C S _ S T Y L E >--------------------------
Gets the default CS style configuration for the given mode.
Returns default separator and either postscript as passed in or the default.
In CS1, the default postscript and separator are '.'.
In CS2, the default postscript is the empty string and the default separator is ','.
]]
local function set_cs_style (postscript, mode)
if utilities.is_set(postscript) then
-- emit a maintenance message if user postscript is the default cs1 postscript
-- we catch the opposite case for cs2 in set_style
if mode == 'cs1' and postscript == cfg.presentation['ps_' .. mode] then
utilities.set_message ('maint_postscript');
end
else
postscript = cfg.presentation['ps_' .. mode];
end
return cfg.presentation['sep_' .. mode], postscript;
end
--[[--------------------------< S E T _ S T Y L E >-----------------------------
Sets the separator and postscript styles. Checks the |mode= first and the
#invoke CitationClass second. Removes the postscript if postscript == none.
]]
local function set_style (mode, postscript, cite_class)
local sep;
if 'cs2' == mode then
sep, postscript = set_cs_style (postscript, 'cs2');
elseif 'cs1' == mode then
sep, postscript = set_cs_style (postscript, 'cs1');
elseif 'citation' == cite_class then
sep, postscript = set_cs_style (postscript, 'cs2');
else
sep, postscript = set_cs_style (postscript, 'cs1');
end
if cfg.keywords_xlate[postscript:lower()] == 'none' then
-- emit a maintenance message if user postscript is the default cs2 postscript
-- we catch the opposite case for cs1 in set_cs_style
if 'cs2' == mode or ('cs1' ~= mode and 'citation' == cite_class) then -- {{citation |title=Title |mode=cs1 |postscript=none}} should not emit maint message
utilities.set_message ('maint_postscript');
end
postscript = '';
end
return sep, postscript
end
--[=[-------------------------< I S _ P D F >-----------------------------------
Determines if a URL has the file extension that is one of the PDF file extensions
used by [[MediaWiki:Common.css]] when applying the PDF icon to external links.
returns true if file extension is one of the recognized extensions, else false
]=]
local function is_pdf (url)
return url:match ('%.pdf$') or url:match ('%.PDF$') or
url:match ('%.pdf[%?#]') or url:match ('%.PDF[%?#]') or
url:match ('%.PDF#') or url:match ('%.pdf#');
end
--[[--------------------------< S T Y L E _ F O R M A T >-----------------------
Applies CSS style to |format=, |chapter-format=, etc. Also emits an error message
if the format parameter does not have a matching URL parameter. If the format parameter
is not set and the URL contains a file extension that is recognized as a PDF document
by MediaWiki's commons.css, this code will set the format parameter to (PDF) with
the appropriate styling.
]]
local function style_format (format, url, fmt_param, url_param)
if utilities.is_set (format) then
format = utilities.wrap_style ('format', format); -- add leading space, parentheses, resize
if not utilities.is_set (url) then
utilities.set_message ('err_format_missing_url', {fmt_param, url_param}); -- add an error message
end
elseif is_pdf (url) then -- format is not set so if URL is a PDF file then
format = utilities.wrap_style ('format', 'PDF'); -- set format to PDF
else
format = ''; -- empty string for concatenation
end
return format;
end
--[[---------------------< G E T _ D I S P L A Y _ N A M E S >------------------
Returns a number that defines the number of names displayed for author and editor
name lists and a Boolean flag to indicate when et al. should be appended to the name list.
When the value assigned to |display-xxxxors= is a number greater than or equal to zero,
return the number and the previous state of the 'etal' flag (false by default
but may have been set to true if the name list contains some variant of the text 'et al.').
When the value assigned to |display-xxxxors= is the keyword 'etal', return a number
that is one greater than the number of authors in the list and set the 'etal' flag true.
This will cause the list_people() to display all of the names in the name list followed by 'et al.'
In all other cases, returns nil and the previous state of the 'etal' flag.
inputs:
max: A['DisplayAuthors'] or A['DisplayEditors'], etc; a number or some flavor of etal
count: #a or #e
list_name: 'authors' or 'editors'
etal: author_etal or editor_etal
This function sets an error message when |display-xxxxors= value greater than or equal to number of names but
not when <max> comes from {{cs1 config}} global settings. When using global settings, <param> is set to the
keyword 'cs1 config' which is used to supress the normal error. Error is suppressed because it is to be expected
that some citations in an article will have the same or fewer names that the limit specified in {{cs1 config}}.
]]
local function get_display_names (max, count, etal, param)
if utilities.is_set (max) then
if 'etal' == cfg.keywords_xlate[max:lower():gsub("[ '%.]", '')] then -- the :gsub() portion makes 'etal' from a variety of 'et al.' spellings and stylings
max = count + 1; -- number of authors + 1 so display all author name plus et al.
etal = true; -- overrides value set by extract_names()
elseif max:match ('^%d+$') then -- if is a string of numbers
max = tonumber (max); -- make it a number
if (max >= count) and ('cs1 config' ~= param) then -- error when local |display-xxxxors= value greater than or equal to number of names; not an error when using global setting
utilities.set_message ('err_disp_name', {param, max}); -- add error message
max = nil;
end
else -- not a valid keyword or number
utilities.set_message ('err_disp_name', {param, max}); -- add error message
max = nil; -- unset; as if |display-xxxxors= had not been set
end
end
return max, etal;
end
--[[----------< E X T R A _ T E X T _ I N _ P A G E _ C H E C K >---------------
Adds error if |page=, |pages=, |quote-page=, |quote-pages= has what appears to be
some form of p. or pp. abbreviation in the first characters of the parameter content.
check page for extraneous p, p., pp, pp., pg, pg. at start of parameter value:
good pattern: '^P[^%.P%l]' matches when page begins PX or P# but not Px
where x and X are letters and # is a digit
bad pattern: '^[Pp][PpGg]' matches when page begins pp, pP, Pp, PP, pg, pG, Pg, PG
]]
local function extra_text_in_page_check (val, name)
if not val:match (cfg.vol_iss_pg_patterns.good_ppattern) then
for _, pattern in ipairs (cfg.vol_iss_pg_patterns.bad_ppatterns) do -- spin through the selected sequence table of patterns
if val:match (pattern) then -- when a match, error so
utilities.set_message ('err_extra_text_pages', name); -- add error message
return; -- and done
end
end
end
end
--[[--------------------------< E X T R A _ T E X T _ I N _ V O L _ I S S _ C H E C K >------------------------
Adds error if |volume= or |issue= has what appears to be some form of redundant 'type' indicator. Applies to
both; this function looks for issue text in both |issue= and |volume= and looks for volume-like text in |voluem=
and |issue=.
For |volume=:
'V.', or 'Vol.' (with or without the dot) abbreviations or 'Volume' in the first characters of the parameter
content (all case insensitive). 'V' and 'v' (without the dot) are presumed to be roman numerals so
are allowed.
For |issue=:
'No.', 'I.', 'Iss.' (with or without the dot) abbreviations, or 'Issue' in the first characters of the
parameter content (all case insensitive); numero styling: 'n°' with degree sign U+00B0, and № precomposed
numero sign U+2116.
Single character values ('v', 'i', 'n') allowed when not followed by separator character ('.', ':', '=', or
whitespace character) – param values are trimmed of whitespace by MediaWiki before delivered to the module.
<val> is |volume= or |issue= parameter value
<name> is |volume= or |issue= parameter name for error message
<selector> is 'v' for |volume=, 'i' for |issue=
sets error message on failure; returns nothing
]]
local function extra_text_in_vol_iss_check (val, name, selector)
if not utilities.is_set (val) then
return;
end
local handler = 'v' == selector and 'err_extra_text_volume' or 'err_extra_text_issue';
local accept_val; -- for now, ignore linter warning
val, accept_val = utilities.has_accept_as_written (val);
--if accept_val then -- if uncomment this, |volume=((vol. 1)) suppresses error
-- return;
--end
--val = mw.text.unstripNoWiki (val); -- if uncomment this, |volume=<nowiki>vol. 1</nowiki> shows error
val = val:lower(); -- force parameter value to lower case
for _, pattern in ipairs (cfg.vol_iss_pg_patterns.vi_patterns_t) do -- spin through the sequence table of patterns
if val:match (pattern) then -- when a match, error so
utilities.set_message (handler, name); -- add error message
return; -- and done
end
end
end
--[=[-------------------------< G E T _ V _ N A M E _ T A B L E >----------------------------------------------
split apart a |vauthors= or |veditors= parameter. This function allows for corporate names, wrapped in doubled
parentheses to also have commas; in the old version of the code, the doubled parentheses were included in the
rendered citation and in the metadata. Individual author names may be wikilinked
|vauthors=Jones AB, [[E. B. White|White EB]], ((Black, Brown, and Co.))
]=]
local function get_v_name_table (vparam, output_table, output_link_table)
local _, accept = utilities.has_accept_as_written (vparam);
if accept then
utilities.add_prop_cat ('vanc-accept'); -- add properties category
end
local name_table = mw.text.split(vparam, "%s*,%s*"); -- names are separated by commas
local wl_type, label, link; -- wl_type not used here; just a placeholder
local i = 1;
while name_table[i] do
if name_table[i]:match ('^%(%(.*[^%)][^%)]$') then -- first segment of corporate with one or more commas; this segment has the opening doubled parentheses
local name = name_table[i];
i = i + 1; -- bump indexer to next segment
while name_table[i] do
name = name .. ', ' .. name_table[i]; -- concatenate with previous segments
if name_table[i]:match ('^.*%)%)$') then -- if this table member has the closing doubled parentheses
break; -- and done reassembling so
end
i = i + 1; -- bump indexer
end
table.insert (output_table, name); -- and add corporate name to the output table
table.insert (output_link_table, ''); -- no wikilink
else
wl_type, label, link = utilities.is_wikilink (name_table[i]); -- wl_type is: 0, no wl (text in label variable); 1, [[D]]; 2, [[L|D]]
table.insert (output_table, label); -- add this name
if 1 == wl_type then
table.insert (output_link_table, label); -- simple wikilink [[D]]
else
table.insert (output_link_table, link); -- no wikilink or [[L|D]]; add this link if there is one, else empty string
end
end
i = i + 1;
end
return output_table;
end
--[[--------------------------< P A R S E _ V A U T H O R S _ V E D I T O R S >--------------------------------
This function extracts author / editor names from |vauthors= or |veditors= and finds matching |xxxxor-maskn= and
|xxxxor-linkn= in args. It then returns a table of assembled names just as extract_names() does.
Author / editor names in |vauthors= or |veditors= must be in Vancouver system style. Corporate or institutional names
may sometimes be required and because such names will often fail the is_good_vanc_name() and other format compliance
tests, are wrapped in doubled parentheses ((corporate name)) to suppress the format tests.
Supports generational suffixes Jr, 2nd, 3rd, 4th–6th.
This function sets the Vancouver error when a required comma is missing and when there is a space between an author's initials.
]]
local function parse_vauthors_veditors (args, vparam, list_name)
local names = {}; -- table of names assembled from |vauthors=, |author-maskn=, |author-linkn=
local v_name_table = {};
local v_link_table = {}; -- when name is wikilinked, targets go in this table
local etal = false; -- return value set to true when we find some form of et al. vauthors parameter
local last, first, link, mask, suffix;
local corporate = false;
vparam, etal = name_has_etal (vparam, etal, true); -- find and remove variations on et al. do not categorize (do it here because et al. might have a period)
v_name_table = get_v_name_table (vparam, v_name_table, v_link_table); -- names are separated by commas
for i, v_name in ipairs(v_name_table) do
first = ''; -- set to empty string for concatenation and because it may have been set for previous author/editor
local accept_name;
v_name, accept_name = utilities.has_accept_as_written (v_name); -- remove accept-this-as-written markup when it wraps all of <v_name>
if accept_name then
last = v_name;
corporate = true; -- flag used in list_people()
elseif string.find(v_name, "%s") then
if v_name:find('[;%.]') then -- look for commonly occurring punctuation characters;
add_vanc_error (cfg.err_msg_supl.punctuation, i);
end
local lastfirst_t = mw.text.split(v_name, "%s+");
first = table.remove (lastfirst_t); -- removes and returns value of last element in table which should be initials or generational suffix
if not mw.ustring.match (first, '^%u+$') then -- mw.ustring here so that later we will catch non-Latin characters
suffix = first; -- not initials so assume that whatever we got is a generational suffix
first = table.remove (lastfirst_t); -- get what should be the initials from the table
end
last = table.concat (lastfirst_t, ' '); -- returns a string that is the concatenation of all other names that are not initials and generational suffix
if not utilities.is_set (last) then
first = ''; -- unset
last = v_name; -- last empty because something wrong with first
add_vanc_error (cfg.err_msg_supl.name, i);
end
if mw.ustring.match (last, '%a+%s+%u+%s+%a+') then
add_vanc_error (cfg.err_msg_supl['missing comma'], i); -- matches last II last; the case when a comma is missing
end
if mw.ustring.match (v_name, ' %u %u$') then -- this test is in the wrong place TODO: move or replace with a more appropriate test
add_vanc_error (cfg.err_msg_supl.initials, i); -- matches a space between two initials
end
else
last = v_name; -- last name or single corporate name? Doesn't support multiword corporate names? do we need this?
end
if utilities.is_set (first) then
if not mw.ustring.match (first, "^%u?%u$") then -- first shall contain one or two upper-case letters, nothing else
add_vanc_error (cfg.err_msg_supl.initials, i); -- too many initials; mixed case initials (which may be ok Romanization); hyphenated initials
end
is_good_vanc_name (last, first, suffix, i); -- check first and last before restoring the suffix which may have a non-Latin digit
if utilities.is_set (suffix) then
first = first .. ' ' .. suffix; -- if there was a suffix concatenate with the initials
suffix = ''; -- unset so we don't add this suffix to all subsequent names
end
else
if not corporate then
is_good_vanc_name (last, '', nil, i);
end
end
link = utilities.select_one ( args, cfg.aliases[list_name .. '-Link'], 'err_redundant_parameters', i ) or v_link_table[i];
mask = utilities.select_one ( args, cfg.aliases[list_name .. '-Mask'], 'err_redundant_parameters', i );
names[i] = {last = last, first = first, link = link, mask = mask, corporate = corporate}; -- add this assembled name to our names list
end
return names, etal; -- all done, return our list of names
end
--[[--------------------------< S E L E C T _ A U T H O R _ E D I T O R _ S O U R C E >------------------------
Select one of |authors=, |authorn= / |lastn / firstn=, or |vauthors= as the source of the author name list or
select one of |editorn= / editor-lastn= / |editor-firstn= or |veditors= as the source of the editor name list.
Only one of these appropriate three will be used. The hierarchy is: |authorn= (and aliases) highest and |authors= lowest;
|editorn= (and aliases) highest and |veditors= lowest (support for |editors= withdrawn)
When looking for |authorn= / |editorn= parameters, test |xxxxor1= and |xxxxor2= (and all of their aliases); stops after the second
test which mimicks the test used in extract_names() when looking for a hole in the author name list. There may be a better
way to do this, I just haven't discovered what that way is.
Emits an error message when more than one xxxxor name source is provided.
In this function, vxxxxors = vauthors or veditors; xxxxors = authors as appropriate.
]]
local function select_author_editor_source (vxxxxors, xxxxors, args, list_name)
local lastfirst = false;
if utilities.select_one ( args, cfg.aliases[list_name .. '-Last'], 'none', 1 ) or -- do this twice in case we have a |first1= without a |last1=; this ...
utilities.select_one ( args, cfg.aliases[list_name .. '-First'], 'none', 1 ) or -- ... also catches the case where |first= is used with |vauthors=
utilities.select_one ( args, cfg.aliases[list_name .. '-Last'], 'none', 2 ) or
utilities.select_one ( args, cfg.aliases[list_name .. '-First'], 'none', 2 ) then
lastfirst = true;
end
if (utilities.is_set (vxxxxors) and true == lastfirst) or -- these are the three error conditions
(utilities.is_set (vxxxxors) and utilities.is_set (xxxxors)) or
(true == lastfirst and utilities.is_set (xxxxors)) then
utilities.set_message ('err_redundant_parameters',
utilities.substitute (cfg.special_case_translation.name_list_params,
('AuthorList' == list_name) and cfg.special_case_translation.author or cfg.special_case_translation.editor)); -- add error message
end
if true == lastfirst then return 1 end; -- return a number indicating which author name source to use
if utilities.is_set (vxxxxors) then return 2 end;
if utilities.is_set (xxxxors) then return 3 end;
return 1; -- no authors so return 1; this allows missing author name test to run in case there is a first without last
end
--[[--------------------------< I S _ V A L I D _ P A R A M E T E R _ V A L U E >------------------------------
This function is used to validate a parameter's assigned value for those parameters that have only a limited number
of allowable values (yes, y, true, live, dead, etc.). When the parameter value has not been assigned a value (missing
or empty in the source template) the function returns the value specified by ret_val. If the parameter value is one
of the list of allowed values returns the translated value; else, emits an error message and returns the value
specified by ret_val.
TODO: explain <invert>
]]
local function is_valid_parameter_value (value, name, possible, ret_val, invert)
if not utilities.is_set (value) then
return ret_val; -- an empty parameter is ok
end
if (not invert and utilities.in_array (value, possible)) then -- normal; <value> is in <possible> table
return cfg.keywords_xlate[value]; -- return translation of parameter keyword
elseif invert and not utilities.in_array (value, possible) then -- invert; <value> is not in <possible> table
return value; -- return <value> as it is
else
utilities.set_message ('err_invalid_param_val', {name, value}); -- not an allowed value so add error message
return ret_val;
end
end
--[[--------------------------< T E R M I N A T E _ N A M E _ L I S T >----------------------------------------
This function terminates a name list (author, contributor, editor) with a separator character (sepc) and a space
when the last character is not a sepc character or when the last three characters are not sepc followed by two
closing square brackets (close of a wikilink). When either of these is true, the name_list is terminated with a
single space character.
]]
local function terminate_name_list (name_list, sepc)
if (string.sub (name_list, -3, -1) == sepc .. '. ') then -- if already properly terminated
return name_list; -- just return the name list
elseif (string.sub (name_list, -1, -1) == sepc) or (string.sub (name_list, -3, -1) == sepc .. ']]') then -- if last name in list ends with sepc char
return name_list .. " "; -- don't add another
else
return name_list .. sepc .. ' '; -- otherwise terminate the name list
end
end
--[[-------------------------< F O R M A T _ V O L U M E _ I S S U E >-----------------------------------------
returns the concatenation of the formatted volume and issue (or journal article number) parameters as a single
string; or formatted volume or formatted issue, or an empty string if neither are set.
]]
local function format_volume_issue (volume, issue, article, cite_class, origin, sepc, lower)
if not utilities.is_set (volume) and not utilities.is_set (issue) and not utilities.is_set (article) then
return '';
end
-- same condition as in format_pages_sheets()
local is_journal = 'journal' == cite_class or (utilities.in_array (cite_class, {'citation', 'map', 'interview'}) and 'journal' == origin);
local is_numeric_vol = volume and (volume:match ('^[MDCLXVI]+$') or volume:match ('^%d+$')); -- is only uppercase roman numerals or only digits?
local is_long_vol = volume and (4 < mw.ustring.len(volume)); -- is |volume= value longer than 4 characters?
if volume and (not is_numeric_vol and is_long_vol) then -- when not all digits or Roman numerals, is |volume= longer than 4 characters?
utilities.add_prop_cat ('long-vol'); -- yes, add properties cat
end
if is_journal then -- journal-style formatting
local vol = '';
if utilities.is_set (volume) then
if is_numeric_vol then -- |volume= value all digits or all uppercase Roman numerals?
vol = utilities.substitute (cfg.presentation['vol-bold'], {sepc, volume}); -- render in bold face
elseif is_long_vol then -- not all digits or Roman numerals; longer than 4 characters?
vol = utilities.substitute (cfg.messages['j-vol'], {sepc, utilities.hyphen_to_dash (volume)}); -- not bold
else -- four or fewer characters
vol = utilities.substitute (cfg.presentation['vol-bold'], {sepc, utilities.hyphen_to_dash (volume)}); -- bold
end
end
vol = vol .. (utilities.is_set (issue) and utilities.substitute (cfg.messages['j-issue'], issue) or '')
vol = vol .. (utilities.is_set (article) and utilities.substitute (cfg.messages['j-article-num'], article) or '')
return vol;
end
if 'podcast' == cite_class and utilities.is_set (issue) then
return wrap_msg ('issue', {sepc, issue}, lower);
end
if 'conference' == cite_class and utilities.is_set (article) then -- |article-number= supported only in journal and conference cites
if utilities.is_set (volume) and utilities.is_set (article) then -- both volume and article number
return wrap_msg ('vol-art', {sepc, utilities.hyphen_to_dash (volume), article}, lower);
elseif utilities.is_set (article) then -- article number alone; when volume alone, handled below
return wrap_msg ('art', {sepc, article}, lower);
end
end
-- all other types of citation
if utilities.is_set (volume) and utilities.is_set (issue) then
return wrap_msg ('vol-no', {sepc, utilities.hyphen_to_dash (volume), issue}, lower);
elseif utilities.is_set (volume) then
return wrap_msg ('vol', {sepc, utilities.hyphen_to_dash (volume)}, lower);
else
return wrap_msg ('issue', {sepc, issue}, lower);
end
end
--[[-------------------------< F O R M A T _ P A G E S _ S H E E T S >-----------------------------------------
adds static text to one of |page(s)= or |sheet(s)= values and returns it with all of the others set to empty strings.
The return order is:
page, pages, sheet, sheets
Singular has priority over plural when both are provided.
]]
local function format_pages_sheets (page, pages, sheet, sheets, cite_class, origin, sepc, nopp, lower)
if 'map' == cite_class then -- only cite map supports sheet(s) as in-source locators
if utilities.is_set (sheet) then
if 'journal' == origin then
return '', '', wrap_msg ('j-sheet', sheet, lower), '';
else
return '', '', wrap_msg ('sheet', {sepc, sheet}, lower), '';
end
elseif utilities.is_set (sheets) then
if 'journal' == origin then
return '', '', '', wrap_msg ('j-sheets', sheets, lower);
else
return '', '', '', wrap_msg ('sheets', {sepc, sheets}, lower);
end
end
end
local is_journal = 'journal' == cite_class or (utilities.in_array (cite_class, {'citation', 'map', 'interview'}) and 'journal' == origin);
if utilities.is_set (page) then
if is_journal then
return utilities.substitute (cfg.messages['j-page(s)'], page), '', '', '';
elseif not nopp then
return utilities.substitute (cfg.messages['p-prefix'], {sepc, page}), '', '', '';
else
return utilities.substitute (cfg.messages['nopp'], {sepc, page}), '', '', '';
end
elseif utilities.is_set (pages) then
if is_journal then
return utilities.substitute (cfg.messages['j-page(s)'], pages), '', '', '';
elseif tonumber(pages) ~= nil and not nopp then -- if pages is only digits, assume a single page number
return '', utilities.substitute (cfg.messages['p-prefix'], {sepc, pages}), '', '';
elseif not nopp then
return '', utilities.substitute (cfg.messages['pp-prefix'], {sepc, pages}), '', '';
else
return '', utilities.substitute (cfg.messages['nopp'], {sepc, pages}), '', '';
end
end
return '', '', '', ''; -- return empty strings
end
--[[--------------------------< I N S O U R C E _ L O C _ G E T >----------------------------------------------
returns one of the in-source locators: page, pages, or at.
If any of these are interwiki links to Wikisource, returns the label portion of the interwiki-link as plain text
for use in COinS. This COinS thing is done because here we convert an interwiki-link to an external link and
add an icon span around that; get_coins_pages() doesn't know about the span. TODO: should it?
TODO: add support for sheet and sheets?; streamline;
TODO: make it so that this function returns only one of the three as the single in-source (the return value assigned
to a new name)?
]]
local function insource_loc_get (page, page_orig, pages, pages_orig, at)
local ws_url, ws_label, coins_pages, L; -- for Wikisource interwiki-links; TODO: this corrupts page metadata (span remains in place after cleanup; fix there?)
if utilities.is_set (page) then
if utilities.is_set (pages) or utilities.is_set (at) then
pages = ''; -- unset the others
at = '';
end
extra_text_in_page_check (page, page_orig); -- emit error message when |page= value begins with what looks like p., pp., etc.
ws_url, ws_label, L = wikisource_url_make (page); -- make ws URL from |page= interwiki link; link portion L becomes tooltip label
if ws_url then
page = external_link (ws_url, ws_label .. ' ', 'ws link in page'); -- space char after label to move icon away from in-source text; TODO: a better way to do this?
page = utilities.substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], L, page});
coins_pages = ws_label;
end
elseif utilities.is_set (pages) then
if utilities.is_set (at) then
at = ''; -- unset
end
extra_text_in_page_check (pages, pages_orig); -- emit error message when |page= value begins with what looks like p., pp., etc.
ws_url, ws_label, L = wikisource_url_make (pages); -- make ws URL from |pages= interwiki link; link portion L becomes tooltip label
if ws_url then
pages = external_link (ws_url, ws_label .. ' ', 'ws link in pages'); -- space char after label to move icon away from in-source text; TODO: a better way to do this?
pages = utilities.substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], L, pages});
coins_pages = ws_label;
end
elseif utilities.is_set (at) then
ws_url, ws_label, L = wikisource_url_make (at); -- make ws URL from |at= interwiki link; link portion L becomes tooltip label
if ws_url then
at = external_link (ws_url, ws_label .. ' ', 'ws link in at'); -- space char after label to move icon away from in-source text; TODO: a better way to do this?
at = utilities.substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], L, at});
coins_pages = ws_label;
end
end
return page, pages, at, coins_pages;
end
--[[--------------------------< I S _ U N I Q U E _ A R C H I V E _ U R L >------------------------------------
add error message when |archive-url= value is same as |url= or chapter-url= (or alias...) value
]]
local function is_unique_archive_url (archive, url, c_url, source, date)
if utilities.is_set (archive) then
if archive == url or archive == c_url then
utilities.set_message ('err_bad_url', {utilities.wrap_style ('parameter', source)}); -- add error message
return '', ''; -- unset |archive-url= and |archive-date= because same as |url= or |chapter-url=
end
end
return archive, date;
end
--[=[-------------------------< A R C H I V E _ U R L _ C H E C K >--------------------------------------------
Check archive.org URLs to make sure they at least look like they are pointing at valid archives and not to the
save snapshot URL or to calendar pages. When the archive URL is 'https://web.archive.org/save/' (or http://...)
archive.org saves a snapshot of the target page in the URL. That is something that Wikipedia should not allow
unwitting readers to do.
When the archive.org URL does not have a complete timestamp, archive.org chooses a snapshot according to its own
algorithm or provides a calendar 'search' result. [[WP:ELNO]] discourages links to search results.
This function looks at the value assigned to |archive-url= and returns empty strings for |archive-url= and
|archive-date= and an error message when:
|archive-url= holds an archive.org save command URL
|archive-url= is an archive.org URL that does not have a complete timestamp (YYYYMMDDhhmmss 14 digits) in the
correct place
otherwise returns |archive-url= and |archive-date=
There are two mostly compatible archive.org URLs:
//web.archive.org/<timestamp>... -- the old form
//web.archive.org/web/<timestamp>... -- the new form
The old form does not support or map to the new form when it contains a display flag. There are four identified flags
('id_', 'js_', 'cs_', 'im_') but since archive.org ignores others following the same form (two letters and an underscore)
we don't check for these specific flags but we do check the form.
This function supports a preview mode. When the article is rendered in preview mode, this function may return a modified
archive URL:
for save command errors, return undated wildcard (/*/)
for timestamp errors when the timestamp has a wildcard, return the URL unmodified
for timestamp errors when the timestamp does not have a wildcard, return with timestamp limited to six digits plus wildcard (/yyyymm*/)
A secondary function is to return an archive-url timestamp from those archive urls that have them. The timestamp
is used by validation.archive_date_check() to see if the value in |archive-date= matches the timestamp in the
archive url.
]=]
local function archive_url_check (url, date)
local err_msg; -- start with the error message empty
local path, timestamp, flag; -- portions of the archive.org URL
local function is_ts_date_valid (ts, origin_date) -- local function to validate archive url taimestamp date; time ignored
local y, m, d = ts:match ('(%d%d%d%d)(%d%d)(%d%d)'); -- split into parts
if y and validation.is_valid_date (y, m, d) and (tonumber (y) >= 1996) then
return true, tonumber (y) >= origin_date; -- <origin_date> is 1996 for archive.org
end
end
if (not url:match('//web%.archive%.org/')) and (not url:match('//liveweb%.archive%.org/')) then -- also deprecated liveweb Wayback machine URL
return url, date; -- not an archive.org archive, return ArchiveURL and ArchiveDate
end
if url:match('//web%.archive%.org/save/') then -- if a save command URL, we don't want to allow saving of the target page
err_msg = cfg.err_msg_supl.save;
url = url:gsub ('(//web%.archive%.org)/save/', '%1/*/', 1); -- for preview mode: modify ArchiveURL
elseif url:match('//liveweb%.archive%.org/') then
err_msg = cfg.err_msg_supl.liveweb;
else
path, timestamp, flag = url:match('//web%.archive%.org/([^%d]*)(%d+)([^/]*)/'); -- split out some of the URL parts for evaluation
if not path then -- malformed in some way; pattern did not match
err_msg = cfg.err_msg_supl.timestamp;
elseif 14 ~= timestamp:len() then -- path and flag optional, must have 14-digit timestamp here
err_msg = cfg.err_msg_supl.timestamp;
if '*' ~= flag then
local replacement = timestamp:match ('^%d%d%d%d%d%d') or timestamp:match ('^%d%d%d%d'); -- get the first 6 (YYYYMM) or first 4 digits (YYYY)
if replacement then -- nil if there aren't at least 4 digits (year)
replacement = replacement .. string.rep ('0', 14 - replacement:len()); -- year or yearmo (4 or 6 digits) zero-fill to make 14-digit timestamp
url=url:gsub ('(//web%.archive%.org/[^%d]*)%d[^/]*', '%1' .. replacement .. '*', 1) -- for preview, modify ts to 14 digits plus splat for calendar display
end
end
elseif not is_ts_date_valid (timestamp, 1996) then -- is ymd portion of timestamp a valid date?
err_msg = cfg.err_msg_supl.timestamp;
timestamp = nil; -- unset because invalid
elseif utilities.is_set (path) and 'web/' ~= path then -- older archive URLs do not have the extra 'web/' path element
err_msg = cfg.err_msg_supl.path;
elseif utilities.is_set (flag) and not utilities.is_set (path) then -- flag not allowed with the old form URL (without the 'web/' path element)
err_msg = cfg.err_msg_supl.flag;
elseif utilities.is_set (flag) and not flag:match ('%a%a_') then -- flag if present must be two alpha characters and underscore (requires 'web/' path element)
err_msg = cfg.err_msg_supl.flag;
else
return url, date, timestamp; -- return ArchiveURL, ArchiveDate, and timestamp from |archive-url=
end
end
-- if here, something not right so
utilities.set_message ('err_archive_url', {err_msg}); -- add error message and
if is_preview_mode then
return url, date, timestamp; -- preview mode so return ArchiveURL, ArchiveDate, and timestamp from |archive-url=
else
return '', ''; -- return empty strings for ArchiveURL and ArchiveDate
end
end
--[[--------------------------< P L A C E _ C H E C K >--------------------------------------------------------
check |place=, |publication-place=, |location= to see if these params include digits. This function added because
many editors misuse location to specify the in-source location (|page(s)= and |at= are supposed to do that)
returns the original parameter value without modification; added maint cat when parameter value contains digits
]]
local function place_check (param_val)
if not utilities.is_set (param_val) then -- parameter empty or omitted
return param_val; -- return that empty state
end
if mw.ustring.find (param_val, '%d') then -- not empty, are there digits in the parameter value
utilities.set_message ('maint_location'); -- yep, add maint cat
end
return param_val; -- and done
end
--[[--------------------------< I S _ A R C H I V E D _ C O P Y >----------------------------------------------
compares |title= to 'Archived copy' (placeholder added by bots that can't find proper title); if matches, return true; nil else
]]
local function is_archived_copy (title)
title = mw.ustring.lower(title); -- switch title to lower case
if title:find (cfg.special_case_translation.archived_copy.en) then -- if title is 'Archived copy'
return true;
elseif cfg.special_case_translation.archived_copy['local'] then
if mw.ustring.find (title, cfg.special_case_translation.archived_copy['local']) then -- mw.ustring() because might not be Latin script
return true;
end
end
end
--[[--------------------------< D I S P L A Y _ N A M E S _ S E L E C T >--------------------------------------
for any of the |display-authors=, |display-editors=, etc parameters, select either the local or global setting.
When both are present, look at <local_display_names> value. When the value is some sort of 'et al.'string,
special handling is required.
When {{cs1 config}} has |display-<namelist>= AND this template has |display-<namelist>=etal AND:
the number of names specified by <number_of_names> is:
greater than the number specified in the global |display-<namelist>= parameter (<global_display_names>)
use global |display-<namelist>= parameter value
set overridden maint category
less than or equal to the number specified in the global |display-<namelist>= parameter
use local |display-<namelist>= parameter value
The purpose of this function is to prevent categorizing a template that has fewer names than the global setting
to keep the etal annotation specified by <local_display_names>.
]]
local function display_names_select (global_display_names, local_display_names, param_name, number_of_names)
if global_display_names and utilities.is_set (local_display_names) then -- when both
if 'etal' == local_display_names:lower():gsub("[ '%.]", '') then -- the :gsub() portion makes 'etal' from a variety of 'et al.' spellings and stylings
number_of_names = tonumber (number_of_names); -- convert these to numbers for comparison
local global_display_names_num = tonumber (global_display_names); -- <global_display_names> not set when parameter value is not digits
if number_of_names > global_display_names_num then -- template has more names than global config allows to be displayed?
utilities.set_message ('maint_overridden_setting'); -- set a maint message because global is overriding local |display-<namelist>=etal
return global_display_names, 'cs1 config'; -- return global with spoof parameter name (for get_display_names())
else
return local_display_names, param_name; -- return local because fewer names so let <local_display_names> control
end
end
-- here when <global_display_names> and <local_display_names> both numbers; <global_display_names> controls
utilities.set_message ('maint_overridden_setting'); -- set a maint message
return global_display_names, 'cs1 config'; -- return global with spoof parameter name (for get_display_names())
end
-- here when only one of <global_display_names> or <local_display_names> set
if global_display_names then
return global_display_names, 'cs1 config'; -- return global with spoof parameter name (for get_display_names())
else
return local_display_names, param_name; -- return local
end
end
--[[--------------------------< M O D E _ S E T >--------------------------------------------------------------
fetch global mode setting from {{cs1 config}} (if present) or from |mode= (if present); global setting overrides
local |mode= parameter value. When both are present, emit maintenance message
]]
local function mode_set (Mode, Mode_origin)
local mode;
if cfg.global_cs1_config_t['Mode'] then -- global setting in {{cs1 config}}; nil when empty or assigned value invalid
mode = is_valid_parameter_value (cfg.global_cs1_config_t['Mode'], 'cs1 config: mode', cfg.keywords_lists['mode'], ''); -- error messaging 'param' here is a hoax
else
mode = is_valid_parameter_value (Mode, Mode_origin, cfg.keywords_lists['mode'], '');
end
if cfg.global_cs1_config_t['Mode'] and utilities.is_set (Mode) then -- when template has |mode=<something> which global setting has overridden
utilities.set_message ('maint_overridden_setting'); -- set a maint message
end
return mode;
end
--[[--------------------------< Q U O T E _ M A K E >----------------------------------------------------------
create quotation from |quote=, |trans-quote=, and/or script-quote= with or without |quote-page= or |quote-pages=
when any of those three quote parameters are set, this function unsets <PostScript>. When none of those parameters
are set, |quote-page= and |quote-pages= are unset to nil so that they are not included in the template's metadata
]]
local function quote_make (quote, trans_quote, script_quote, quote_page, quote_pages, nopp, sepc, postscript)
if utilities.is_set (quote) or utilities.is_set (trans_quote) or utilities.is_set (script_quote) then
if utilities.is_set (quote) then
if quote:sub(1, 1) == '"' and quote:sub(-1, -1) == '"' then -- if first and last characters of quote are quote marks
quote = quote:sub(2, -2); -- strip them off
end
end
quote = kern_quotes (quote); -- kern if needed
quote = utilities.wrap_style ('quoted-text', quote ); -- wrap in <q>...</q> tags
if utilities.is_set (script_quote) then
quote = script_concatenate (quote, script_quote, 'script-quote'); -- <bdi> tags, lang attribute, categorization, etc.; must be done after quote is wrapped
end
if utilities.is_set (trans_quote) then
if trans_quote:sub(1, 1) == '"' and trans_quote:sub(-1, -1) == '"' then -- if first and last characters of |trans-quote are quote marks
trans_quote = trans_quote:sub(2, -2); -- strip them off
end
quote = quote .. " " .. utilities.wrap_style ('trans-quoted-title', trans_quote );
end
if utilities.is_set (quote_page) or utilities.is_set (quote_pages) then -- add page prefix
local quote_prefix = '';
if utilities.is_set (quote_page) then
extra_text_in_page_check (quote_page, 'quote-page'); -- add to maint cat if |quote-page= value begins with what looks like p., pp., etc.
if not nopp then
quote_prefix = utilities.substitute (cfg.messages['p-prefix'], {sepc, quote_page});
else
quote_prefix = utilities.substitute (cfg.messages['nopp'], {sepc, quote_page});
end
elseif utilities.is_set (quote_pages) then
extra_text_in_page_check (quote_pages, 'quote-pages'); -- add to maint cat if |quote-pages= value begins with what looks like p., pp., etc.
if tonumber(quote_pages) ~= nil and not nopp then -- if only digits, assume single page
quote_prefix = utilities.substitute (cfg.messages['p-prefix'], {sepc, quote_pages});
elseif not nopp then
quote_prefix = utilities.substitute (cfg.messages['pp-prefix'], {sepc, quote_pages});
else
quote_prefix = utilities.substitute (cfg.messages['nopp'], {sepc, quote_pages});
end
end
quote = quote_prefix .. ": " .. quote;
else
quote = sepc .. " " .. quote;
end
postscript = ""; -- cs1|2 does not supply terminal punctuation when |quote= is set
elseif utilities.is_set (quote_page) or utilities.is_set (quote_pages) then
quote_page = nil; -- unset; these require |quote=; TODO: error message?
quote_pages = nil;
end
return quote, quote_page, quote_pages, postscript;
end
--[[--------------------------< C H E C K _ P U B L I S H E R _ N A M E >--------------------------------------
look for variations of '<text>: <text>' that might be '<location>: <publisher>' in |publisher= parameter value.
when found, emit a maintenance message; return nil else
<publisher> is the value assigned to |publisher= or |institution=
]]
local function check_publisher_name (publisher)
local patterns_t = {
'^[%w%s]+%s*:%s*[%w%s]+$', -- plain text <location>: <publisher>
'^%[+[%w%s:|]+%]+%s*:%s*[%w%s]+$', -- partially wikilinked [[<location>]]: <publisher>
'^[%w%s]+%s*:%s*%[+[%w%s:|]+%]+$', -- partially wikilinked <location>: [[<publisher>]]
'^%[+[%w%s:|]+%]+%s*:%s*%[+[%w%s:|]+%]+$', -- wikilinked [[<location>]]: [[<publisher>]]
}
for _, pattern in ipairs (patterns_t) do -- spin through the patterns_t sequence
if mw.ustring.match (publisher, pattern) then -- does this pattern match?
utilities.set_message ('maint_publisher_location'); -- set a maint message
return; -- and done
end
end
end
--[[--------------------------< I S _ P A G E _ A R T _ N U M >------------------------------------------------
compare the trailing (rightmost) characters of the |doi= value against the whole value assigned to |page(s)=.
return boolean true when:
|page(s)= has exactly 8 digits and a dot between the fourth and fifth digits matches the trailing 9 characters
of the |doi= value: |page=12345678 → |page=1234.5678 matches |doi=10.xxxx/yyyy1234.5678
|page(s)= is 5 or more characters and matches |doi= values's trailing characters
|page(s)= begins with a lowercase 'e' and |page(s)= without the 'e' matches |doi= values's trailing
characters: |page=e12345 → |page=12345 matches |doi=10.xxxx/yyyy12345
|page(s)= begins with a uppercase 'CD' followed by (typically) six digits matches |doi= values that ends with
'CDxxxxxx.pubx' (where 'x' is any single digit)
return nil when |page(s)= values:
are ranges separated by underscore, hyphen, emdash, endash, figure dash, or minus character
are comma- or semicolon-separated lists of pages
have external urls (has text 'http')
are digit-only values less than 10000
do not match |doi= values's trailing characters
]]
local function is_page_art_num (page, doi)
if not (utilities.is_set (page) and utilities.is_set (doi)) then -- both required
return; -- abandon; nothing to do
end
if page:match ('[,;_−–—‒%-]') then -- when |page(s)= might be a page range or a separated list of pages
return; -- abandon
end
page = page:lower(); -- because doi names are case insensitive
doi = doi:lower(); -- force these to lowercase for testing
if page:match ('http') then -- when |page(s)= appears to hold a url
return; -- abandon
end
if tonumber (page) then -- is |page(s)= digits only
if 10000 > tonumber (page) then -- when |page(s)= less than 10000
return; -- abandon
end
if doi:match (page .. '$') then -- digits only page number match the last digits in |doi=?
return true;
end
if 8 == page:len() then -- special case when |page(s)= is exactly 8 digits
local dot_page = page:gsub ('(%d%d%d%d)(%d%d%d%d)', '%1.%2'); -- make a |page=xxxx.yyyy version commonly used in |doi=
if doi:match (dot_page .. '$') then -- 8-digit dotted page number match the last characters in |doi=?
return true;
end
end
else -- here when |page(s)= is alpha-numeric
if 4 < page:len() then -- when |page(s)= is five or more characters
if doi:match (page .. '$') then -- alpha-numeric page match the last characters in |doi=?
return true;
end
local epage = page:match ('^e([%w]+)$'); -- if first character of |page= is 'e', remove it
if epage and doi:match (epage .. '$') then -- page number match the last characters in |doi=?
return true;
end
local cdpage = page:match ('^cd%d+$'); -- if first characters of |page= are 'CD' and last characters are digits (typically 6 digits)
if cdpage and doi:match (cdpage .. '%.pub%d$') then -- page number matches doi 'CDxxxxxx.pubx' where 'x' is a digit
return true;
end
end
end
end
--[[--------------------------< C I T A T I O N 0 >------------------------------------------------------------
This is the main function doing the majority of the citation formatting.
]]
local function citation0( config, args )
--[[
Load Input Parameters
The argument_wrapper facilitates the mapping of multiple aliases to single internal variable.
]]
local A = argument_wrapper ( args );
-- Pick out the relevant fields from the arguments. Different citation templates
-- define different field names for the same underlying things.
local author_etal;
local a = {}; -- authors list from |lastn= / |firstn= pairs or |vauthors=
local Authors;
local NameListStyle;
if cfg.global_cs1_config_t['NameListStyle'] then -- global setting in {{cs1 config}} overrides local |name-list-style= parameter value; nil when empty or assigned value invalid
NameListStyle = is_valid_parameter_value (cfg.global_cs1_config_t['NameListStyle'], 'cs1 config: name-list-style', cfg.keywords_lists['name-list-style'], ''); -- error messaging 'param' here is a hoax
else
NameListStyle = is_valid_parameter_value (A['NameListStyle'], A:ORIGIN('NameListStyle'), cfg.keywords_lists['name-list-style'], '');
end
if cfg.global_cs1_config_t['NameListStyle'] and utilities.is_set (A['NameListStyle']) then -- when template has |name-list-style=<something> which global setting has overridden
utilities.set_message ('maint_overridden_setting'); -- set a maint message
end
local Collaboration = A['Collaboration'];
do -- to limit scope of selected
local selected = select_author_editor_source (A['Vauthors'], A['Authors'], args, 'AuthorList');
if 1 == selected then
a, author_etal = extract_names (args, 'AuthorList'); -- fetch author list from |authorn= / |lastn= / |firstn=, |author-linkn=, and |author-maskn=
elseif 2 == selected then
NameListStyle = 'vanc'; -- override whatever |name-list-style= might be
a, author_etal = parse_vauthors_veditors (args, A['Vauthors'], 'AuthorList'); -- fetch author list from |vauthors=, |author-linkn=, and |author-maskn=
elseif 3 == selected then
Authors = A['Authors']; -- use content of |people= or |credits=; |authors= is deprecated; TODO: constrain |people= and |credits= to cite av media, episode, serial?
end
if utilities.is_set (Collaboration) then
author_etal = true; -- so that |display-authors=etal not required
end
end
local editor_etal;
local e = {}; -- editors list from |editor-lastn= / |editor-firstn= pairs or |veditors=
do -- to limit scope of selected
local selected = select_author_editor_source (A['Veditors'], nil, args, 'EditorList'); -- support for |editors= withdrawn
if 1 == selected then
e, editor_etal = extract_names (args, 'EditorList'); -- fetch editor list from |editorn= / |editor-lastn= / |editor-firstn=, |editor-linkn=, and |editor-maskn=
elseif 2 == selected then
NameListStyle = 'vanc'; -- override whatever |name-list-style= might be
e, editor_etal = parse_vauthors_veditors (args, args.veditors, 'EditorList'); -- fetch editor list from |veditors=, |editor-linkn=, and |editor-maskn=
end
end
local Chapter = A['Chapter']; -- done here so that we have access to |contribution= from |chapter= and |script-chapter= aliases
local Chapter_origin = A:ORIGIN ('Chapter');
local ScriptChapter = A['ScriptChapter'];
local ScriptChapter_origin = A:ORIGIN ('ScriptChapter');
local Contribution; -- because contribution is required for contributor(s)
if 'contribution' == Chapter_origin then -- is there a |contribution= parameter?
Contribution = Chapter; -- get the name of the contribution
elseif 'script-contribution' == ScriptChapter_origin then -- no, so is there a |script-contribution= parameter?
Contribution = ScriptChapter; -- get the name of the contribution
end
local c = {}; -- contributors list from |contributor-lastn= / contributor-firstn= pairs
if utilities.in_array (config.CitationClass, {"book", "citation"}) and not utilities.is_set (A['Periodical']) then -- |contributor= and |contribution= only supported in book cites
c = extract_names (args, 'ContributorList'); -- fetch contributor list from |contributorn= / |contributor-lastn=, -firstn=, -linkn=, -maskn=
if 0 < #c then
if not utilities.is_set (Contribution) then -- |contributor= requires |contribution=
utilities.set_message ('err_contributor_missing_required_param', 'contribution'); -- add missing contribution error message
c = {}; -- blank the contributors' table; it is used as a flag later
end
if 0 == #a then -- |contributor= requires |author=
utilities.set_message ('err_contributor_missing_required_param', 'author'); -- add missing author error message
c = {}; -- blank the contributors' table; it is used as a flag later
end
end
else -- if not a book cite
if utilities.select_one (args, cfg.aliases['ContributorList-Last'], 'err_redundant_parameters', 1 ) then -- are there contributor name list parameters?
utilities.set_message ('err_contributor_ignored'); -- add contributor ignored error message
end
Contribution = nil; -- unset
end
local Title = A['Title'];
local TitleLink = A['TitleLink'];
local auto_select = ''; -- default is auto
local accept_link;
TitleLink, accept_link = utilities.has_accept_as_written (TitleLink, true); -- test for accept-this-as-written markup
if (not accept_link) and utilities.in_array (TitleLink, {'none', 'pmc', 'doi'}) then -- check for special keywords
auto_select = TitleLink; -- remember selection for later
TitleLink = ''; -- treat as if |title-link= would have been empty
end
TitleLink = link_title_ok (TitleLink, A:ORIGIN ('TitleLink'), Title, 'title'); -- check for wiki-markup in |title-link= or wiki-markup in |title= when |title-link= is set
local Section = ''; -- {{cite map}} only; preset to empty string for concatenation if not used
if 'map' == config.CitationClass and 'section' == Chapter_origin then
Section = A['Chapter']; -- get |section= from |chapter= alias list; |chapter= and the other aliases not supported in {{cite map}}
Chapter = ''; -- unset for now; will be reset later from |map= if present
end
local Periodical = A['Periodical'];
local Periodical_origin = A:ORIGIN('Periodical');
local ScriptPeriodical = A['ScriptPeriodical'];
local ScriptPeriodical_origin = A:ORIGIN('ScriptPeriodical');
local TransPeriodical = A['TransPeriodical'];
local TransPeriodical_origin = A:ORIGIN ('TransPeriodical');
if (utilities.in_array (config.CitationClass, {'book', 'encyclopaedia'}) and (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical) or utilities.is_set (TransPeriodical))) then
local param;
if utilities.is_set (Periodical) then -- get a parameter name from one of these periodical related meta-parameters
Periodical = ''; -- unset because not valid {{cite book}} or {{cite encyclopedia}} parameters
param = Periodical_origin -- get parameter name for error messaging
elseif utilities.is_set (TransPeriodical) then
TransPeriodical = ''; -- unset because not valid {{cite book}} or {{cite encyclopedia}} parameters
param = TransPeriodical_origin; -- get parameter name for error messaging
elseif utilities.is_set (ScriptPeriodical) then
ScriptPeriodical = ''; -- unset because not valid {{cite book}} or {{cite encyclopedia}} parameters
param = ScriptPeriodical_origin; -- get parameter name for error messaging
end
if utilities.is_set (param) then -- if we found one
utilities.set_message ('err_periodical_ignored', {param}); -- emit an error message
end
end
if utilities.is_set (Periodical) then
local i;
Periodical, i = utilities.strip_apostrophe_markup (Periodical); -- strip apostrophe markup so that metadata isn't contaminated
if i then -- non-zero when markup was stripped so emit an error message
utilities.set_message ('err_apostrophe_markup', {Periodical_origin});
end
end
if 'mailinglist' == config.CitationClass then -- special case for {{cite mailing list}}
if utilities.is_set (Periodical) and utilities.is_set (A ['MailingList']) then -- both set emit an error TODO: make a function for this and similar?
utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', Periodical_origin) .. cfg.presentation['sep_list_pair'] .. utilities.wrap_style ('parameter', 'mailinglist')});
end
Periodical = A ['MailingList']; -- error or no, set Periodical to |mailinglist= value because this template is {{cite mailing list}}
Periodical_origin = A:ORIGIN('MailingList');
end
-- web and news not tested for now because of
-- Wikipedia:Administrators%27_noticeboard#Is_there_a_semi-automated_tool_that_could_fix_these_annoying_"Cite_Web"_errors?
if not (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)) then -- 'periodical' templates require periodical parameter
-- local p = {['journal'] = 'journal', ['magazine'] = 'magazine', ['news'] = 'newspaper', ['web'] = 'website'}; -- for error message
local p = {['journal'] = 'journal', ['magazine'] = 'magazine'}; -- for error message
if p[config.CitationClass] then
utilities.set_message ('err_missing_periodical', {config.CitationClass, p[config.CitationClass]});
end
end
local Volume;
if 'citation' == config.CitationClass then
if utilities.is_set (Periodical) then
if not utilities.in_array (Periodical_origin, cfg.citation_no_volume_t) then -- {{citation}} does not render |volume= when these parameters are used
Volume = A['Volume']; -- but does for all other 'periodicals'
end
elseif utilities.is_set (ScriptPeriodical) then
if 'script-website' ~= ScriptPeriodical_origin then -- {{citation}} does not render volume for |script-website=
Volume = A['Volume']; -- but does for all other 'periodicals'
end
else
Volume = A['Volume']; -- and does for non-'periodical' cites
end
elseif utilities.in_array (config.CitationClass, cfg.templates_using_volume) then -- render |volume= for cs1 according to the configuration settings
Volume = A['Volume'];
end
extra_text_in_vol_iss_check (Volume, A:ORIGIN ('Volume'), 'v');
local Issue;
if 'citation' == config.CitationClass then
if utilities.is_set (Periodical) and utilities.in_array (Periodical_origin, cfg.citation_issue_t) then -- {{citation}} may render |issue= when these parameters are used
Issue = A['Issue'];
end
elseif utilities.in_array (config.CitationClass, cfg.templates_using_issue) then -- conference & map books do not support issue; {{citation}} listed here because included in settings table
if not (utilities.in_array (config.CitationClass, {'conference', 'map', 'citation'}) and not (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical))) then
Issue = A['Issue'];
end
end
local ArticleNumber;
if utilities.in_array (config.CitationClass, {'journal', 'conference'}) or ('citation' == config.CitationClass and utilities.is_set (Periodical) and 'journal' == Periodical_origin) then
ArticleNumber = A['ArticleNumber'];
end
extra_text_in_vol_iss_check (Issue, A:ORIGIN ('Issue'), 'i');
Issue = utilities.hyphen_to_dash (Issue);
local Page;
local Pages;
local At;
local QuotePage;
local QuotePages;
if not utilities.in_array (config.CitationClass, cfg.templates_not_using_page) then -- TODO: rewrite to emit ignored parameter error message?
Page = A['Page'];
Pages = utilities.hyphen_to_dash (A['Pages']);
At = A['At'];
QuotePage = A['QuotePage'];
QuotePages = utilities.hyphen_to_dash (A['QuotePages']);
end
local NoPP = is_valid_parameter_value (A['NoPP'], A:ORIGIN('NoPP'), cfg.keywords_lists['yes_true_y'], nil);
local Mode = mode_set (A['Mode'], A:ORIGIN('Mode'));
-- separator character and postscript
local sepc, PostScript = set_style (Mode:lower(), A['PostScript'], config.CitationClass);
local Quote;
Quote, QuotePage, QuotePages, PostScript = quote_make (A['Quote'], A['TransQuote'], A['ScriptQuote'], QuotePage, QuotePages, NoPP, sepc, PostScript);
local Edition = A['Edition'];
local PublicationPlace = place_check (A['PublicationPlace'], A:ORIGIN('PublicationPlace'));
local Place = place_check (A['Place'], A:ORIGIN('Place'));
local PublisherName = A['PublisherName'];
local PublisherName_origin = A:ORIGIN('PublisherName');
if utilities.is_set (PublisherName) and (cfg.keywords_xlate['none'] ~= PublisherName) then
local i;
PublisherName, i = utilities.strip_apostrophe_markup (PublisherName); -- strip apostrophe markup so that metadata isn't contaminated; publisher is never italicized
if i and (0 < i) then -- non-zero when markup was stripped so emit an error message
utilities.set_message ('err_apostrophe_markup', {PublisherName_origin});
end
end
if ('document' == config.CitationClass) and not utilities.is_set (PublisherName) then
utilities.set_message ('err_missing_publisher', {config.CitationClass, 'publisher'});
end
local Newsgroup = A['Newsgroup']; -- TODO: strip apostrophe markup?
local Newsgroup_origin = A:ORIGIN('Newsgroup');
if 'newsgroup' == config.CitationClass then
if utilities.is_set (PublisherName) and (cfg.keywords_xlate['none'] ~= PublisherName) then -- general use parameter |publisher= not allowed in cite newsgroup
utilities.set_message ('err_parameter_ignored', {PublisherName_origin});
end
PublisherName = nil; -- ensure that this parameter is unset for the time being; will be used again after COinS
end
if 'book' == config.CitationClass or 'encyclopaedia' == config.CitationClass or ('citation' == config.CitationClass and not utilities.is_set (Periodical)) then
local accept;
PublisherName, accept = utilities.has_accept_as_written (PublisherName); -- check for and remove accept-as-written markup from |publisher= wrapped
if not accept then -- when no accept-as-written markup
check_publisher_name (PublisherName); -- emit maint message when |publisher= might be prefixed with publisher's location
end
end
local URL = A['URL']; -- TODO: better way to do this for URL, ChapterURL, and MapURL?
local UrlAccess = is_valid_parameter_value (A['UrlAccess'], A:ORIGIN('UrlAccess'), cfg.keywords_lists['url-access'], nil);
if not utilities.is_set (URL) and utilities.is_set (UrlAccess) then
UrlAccess = nil;
utilities.set_message ('err_param_access_requires_param', 'url');
end
local ChapterURL = A['ChapterURL'];
local ChapterUrlAccess = is_valid_parameter_value (A['ChapterUrlAccess'], A:ORIGIN('ChapterUrlAccess'), cfg.keywords_lists['url-access'], nil);
if not utilities.is_set (ChapterURL) and utilities.is_set (ChapterUrlAccess) then
ChapterUrlAccess = nil;
utilities.set_message ('err_param_access_requires_param', {A:ORIGIN('ChapterUrlAccess'):gsub ('%-access', '')});
end
local MapUrlAccess = is_valid_parameter_value (A['MapUrlAccess'], A:ORIGIN('MapUrlAccess'), cfg.keywords_lists['url-access'], nil);
if not utilities.is_set (A['MapURL']) and utilities.is_set (MapUrlAccess) then
MapUrlAccess = nil;
utilities.set_message ('err_param_access_requires_param', {'map-url'});
end
local this_page = mw.title.getCurrentTitle(); -- also used for COinS and for language
local no_tracking_cats = is_valid_parameter_value (A['NoTracking'], A:ORIGIN('NoTracking'), cfg.keywords_lists['yes_true_y'], nil);
-- check this page to see if it is in one of the namespaces that cs1 is not supposed to add to the error categories
if not utilities.is_set (no_tracking_cats) then -- ignore if we are already not going to categorize this page
if cfg.uncategorized_namespaces[this_page.namespace] then -- is this page's namespace id one of the uncategorized namespace ids?
no_tracking_cats = "true"; -- set no_tracking_cats
end
for _, v in ipairs (cfg.uncategorized_subpages) do -- cycle through page name patterns
if this_page.text:match (v) then -- test page name against each pattern
no_tracking_cats = "true"; -- set no_tracking_cats
break; -- bail out if one is found
end
end
end
-- check for extra |page=, |pages= or |at= parameters. (also sheet and sheets while we're at it)
utilities.select_one (args, {'page', 'p', 'pp', 'pages', 'at', 'sheet', 'sheets'}, 'err_redundant_parameters'); -- this is a dummy call simply to get the error message and category
local coins_pages;
Page, Pages, At, coins_pages = insource_loc_get (Page, A:ORIGIN('Page'), Pages, A:ORIGIN('Pages'), At);
if utilities.is_set (PublicationPlace) and utilities.is_set (Place) then -- both |publication-place= and |place= (|location=) allowed if different
utilities.add_prop_cat ('location-test'); -- add property cat to evaluate how often PublicationPlace and Place are used together
if PublicationPlace == Place then
Place = ''; -- unset; don't need both if they are the same
end
elseif not utilities.is_set (PublicationPlace) and utilities.is_set (Place) then -- when only |place= (|location=) is set ...
PublicationPlace = Place; -- promote |place= (|location=) to |publication-place
end
if PublicationPlace == Place then Place = ''; end -- don't need both if they are the same
local URL_origin = A:ORIGIN('URL'); -- get name of parameter that holds URL
local ChapterURL_origin = A:ORIGIN('ChapterURL'); -- get name of parameter that holds ChapterURL
-- local ScriptChapter = A['ScriptChapter'];
-- local ScriptChapter_origin = A:ORIGIN ('ScriptChapter');
local Format = A['Format'];
local ChapterFormat = A['ChapterFormat'];
local TransChapter = A['TransChapter'];
local TransChapter_origin = A:ORIGIN ('TransChapter');
local TransTitle = A['TransTitle'];
local ScriptTitle = A['ScriptTitle'];
--[[
Parameter remapping for cite encyclopedia:
When the citation has these parameters:
|encyclopedia= and |title= then map |title= to |article= and |encyclopedia= to |title= for rendering
|encyclopedia= and |article= then map |encyclopedia= to |title= for rendering
|trans-title= maps to |trans-chapter= when |title= is re-mapped
|url= maps to |chapter-url= when |title= is remapped
All other combinations of |encyclopedia=, |title=, and |article= are not modified
]]
local Encyclopedia = A['Encyclopedia']; -- used as a flag by this module and by ~/COinS
local ScriptEncyclopedia = A['ScriptEncyclopedia'];
local TransEncyclopedia = A['TransEncyclopedia'];
if utilities.is_set (Encyclopedia) or utilities.is_set (ScriptEncyclopedia) then -- emit error message when Encyclopedia set but template is other than {{cite encyclopedia}} or {{citation}}
if 'encyclopaedia' ~= config.CitationClass and 'citation' ~= config.CitationClass then
if utilities.is_set (Encyclopedia) then
utilities.set_message ('err_parameter_ignored', {A:ORIGIN ('Encyclopedia')});
else
utilities.set_message ('err_parameter_ignored', {A:ORIGIN ('ScriptEncyclopedia')});
end
Encyclopedia = nil; -- unset these because not supported by this template
ScriptEncyclopedia = nil;
TransEncyclopedia = nil;
end
elseif utilities.is_set (TransEncyclopedia) then
utilities.set_message ('err_trans_missing_title', {'encyclopedia'});
end
if ('encyclopaedia' == config.CitationClass) or ('citation' == config.CitationClass and utilities.is_set (Encyclopedia)) then
if utilities.is_set (Periodical) and utilities.is_set (Encyclopedia) then -- when both parameters set emit an error message; {{citation}} only; Periodical not allowed in {{cite encyclopedia}}
utilities.set_message ('err_periodical_ignored', {Periodical_origin});
end
if utilities.is_set (Encyclopedia) or utilities.is_set (ScriptEncyclopedia) then
Periodical = Encyclopedia; -- error or no, set Periodical to Encyclopedia for rendering; {{citation}} could (not legitimately) have both; use Encyclopedia
Periodical_origin = A:ORIGIN ('Encyclopedia');
ScriptPeriodical = ScriptEncyclopedia;
ScriptPeriodical_origin = A:ORIGIN ('ScriptEncyclopedia');
if utilities.is_set (Title) or utilities.is_set (ScriptTitle) then
if not utilities.is_set (Chapter) then
Chapter = Title; -- |encyclopedia= and |title= are set so map |title= params to |article= params for rendering
ScriptChapter = ScriptTitle;
ScriptChapter_origin = A:ORIGIN('ScriptTitle')
TransChapter = TransTitle;
ChapterURL = URL;
ChapterURL_origin = URL_origin;
ChapterUrlAccess = UrlAccess;
ChapterFormat = Format;
if not utilities.is_set (ChapterURL) and utilities.is_set (TitleLink) then
Chapter = utilities.make_wikilink (TitleLink, Chapter);
end
Title = Periodical; -- now map |encyclopedia= params to |title= params for rendering
ScriptTitle = ScriptPeriodical or '';
TransTitle = TransEncyclopedia or '';
Periodical = ''; -- redundant so unset
ScriptPeriodical = '';
URL = '';
Format = '';
TitleLink = '';
end
elseif utilities.is_set (Chapter) or utilities.is_set (ScriptChapter) then -- |title= not set
Title = Periodical; -- |encyclopedia= set and |article= set so map |encyclopedia= to |title= for rendering
ScriptTitle = ScriptPeriodical or '';
TransTitle = TransEncyclopedia or '';
Periodical = ''; -- redundant so unset
ScriptPeriodical = '';
end
end
end
-- special case for cite techreport.
local ID = A['ID'];
if (config.CitationClass == "techreport") then -- special case for cite techreport
if utilities.is_set (A['Number']) then -- cite techreport uses 'number', which other citations alias to 'issue'
if not utilities.is_set (ID) then -- can we use ID for the "number"?
ID = A['Number']; -- yes, use it
else -- ID has a value so emit error message
utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', 'id') .. cfg.presentation['sep_list_pair'] .. utilities.wrap_style ('parameter', 'number')});
end
end
end
-- Account for the oddity that is {{cite conference}}, before generation of COinS data.
local ChapterLink -- = A['ChapterLink']; -- deprecated as a parameter but still used internally by cite episode
local Conference = A['Conference'];
local BookTitle = A['BookTitle'];
local TransTitle_origin = A:ORIGIN ('TransTitle');
if 'conference' == config.CitationClass then
if utilities.is_set (BookTitle) then
Chapter = Title;
Chapter_origin = 'title';
-- ChapterLink = TitleLink; -- |chapter-link= is deprecated
ChapterURL = URL;
ChapterUrlAccess = UrlAccess;
ChapterURL_origin = URL_origin;
URL_origin = '';
ChapterFormat = Format;
TransChapter = TransTitle;
TransChapter_origin = TransTitle_origin;
Title = BookTitle;
Format = '';
-- TitleLink = '';
TransTitle = '';
URL = '';
end
elseif 'speech' ~= config.CitationClass then
Conference = ''; -- not cite conference or cite speech so make sure this is empty string
end
local cs2_lower = function (text) -- if mode is cs2, use lower case
if (sepc ~= '.') then
return text:lower();
end
return text;
end
local use_lowercase = ( sepc == ',' ); -- controls capitalization of certain static text
-- cite map oddities
local Cartography = "";
local Scale = "";
local Sheet = A['Sheet'] or '';
local Sheets = A['Sheets'] or '';
if config.CitationClass == "map" then
if utilities.is_set (Chapter) then --TODO: make a function for this and similar?
utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', 'map') .. cfg.presentation['sep_list_pair'] .. utilities.wrap_style ('parameter', Chapter_origin)}); -- add error message
end
Chapter = A['Map'];
Chapter_origin = A:ORIGIN('Map'); -- for now, ignore linter warning
ChapterURL = A['MapURL'];
ChapterURL_origin = A:ORIGIN('MapURL');
TransChapter = A['TransMap'];
ScriptChapter = A['ScriptMap']
ScriptChapter_origin = A:ORIGIN('ScriptMap')
ChapterUrlAccess = MapUrlAccess;
ChapterFormat = A['MapFormat'];
Cartography = A['Cartography'];
if utilities.is_set ( Cartography ) then
Cartography = sepc .. " " .. wrap_msg ('cartography', Cartography, use_lowercase);
end
Scale = A['Scale'];
if utilities.is_set ( Scale ) then
Scale = sepc .. " " .. Scale;
end
end
-- Account for the oddities that are {{cite episode}} and {{cite serial}}, before generation of COinS data.
local Series = A['Series'];
if 'episode' == config.CitationClass or 'serial' == config.CitationClass then
local SeriesLink = A['SeriesLink'];
SeriesLink = link_title_ok (SeriesLink, A:ORIGIN ('SeriesLink'), Series, 'series'); -- check for wiki-markup in |series-link= or wiki-markup in |series= when |series-link= is set
local Network = A['Network'];
local Station = A['Station'];
local s, n = {}, {};
-- do common parameters first
if utilities.is_set (Network) then table.insert(n, Network); end
if utilities.is_set (Station) then table.insert(n, Station); end
ID = table.concat(n, sepc .. ' ');
if 'episode' == config.CitationClass then -- handle the oddities that are strictly {{cite episode}}
local Season = A['Season'];
local SeriesNumber = A['SeriesNumber'];
if utilities.is_set (Season) and utilities.is_set (SeriesNumber) then -- these are mutually exclusive so if both are set TODO: make a function for this and similar?
utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', 'season') .. cfg.presentation['sep_list_pair'] .. utilities.wrap_style ('parameter', 'seriesno')}); -- add error message
SeriesNumber = ''; -- unset; prefer |season= over |seriesno=
end
-- assemble a table of parts concatenated later into Series
if utilities.is_set (Season) then table.insert(s, wrap_msg ('season', Season, use_lowercase)); end
if utilities.is_set (SeriesNumber) then table.insert(s, wrap_msg ('seriesnum', SeriesNumber, use_lowercase)); end
if utilities.is_set (Issue) then table.insert(s, wrap_msg ('episode', Issue, use_lowercase)); end
Issue = ''; -- unset because this is not a unique parameter
Chapter = Title; -- promote title parameters to chapter
ScriptChapter = ScriptTitle;
ScriptChapter_origin = A:ORIGIN('ScriptTitle');
ChapterLink = TitleLink; -- alias |episode-link=
TransChapter = TransTitle;
ChapterURL = URL;
ChapterUrlAccess = UrlAccess;
ChapterURL_origin = URL_origin;
ChapterFormat = Format;
Title = Series; -- promote series to title
TitleLink = SeriesLink;
Series = table.concat(s, sepc .. ' '); -- this is concatenation of season, seriesno, episode number
if utilities.is_set (ChapterLink) and not utilities.is_set (ChapterURL) then -- link but not URL
Chapter = utilities.make_wikilink (ChapterLink, Chapter);
elseif utilities.is_set (ChapterLink) and utilities.is_set (ChapterURL) then -- if both are set, URL links episode;
Series = utilities.make_wikilink (ChapterLink, Series);
end
URL = ''; -- unset
TransTitle = '';
ScriptTitle = '';
Format = '';
else -- now oddities that are cite serial
Issue = ''; -- unset because this parameter no longer supported by the citation/core version of cite serial
Chapter = A['Episode']; -- TODO: make |episode= available to cite episode someday?
if utilities.is_set (Series) and utilities.is_set (SeriesLink) then
Series = utilities.make_wikilink (SeriesLink, Series);
end
Series = utilities.wrap_style ('italic-title', Series); -- series is italicized
end
end
-- end of {{cite episode}} stuff
-- handle type parameter for those CS1 citations that have default values
local TitleType = A['TitleType'];
local Degree = A['Degree'];
if utilities.in_array (config.CitationClass, {'AV-media-notes', 'document', 'interview', 'mailinglist', 'map', 'podcast', 'pressrelease', 'report', 'speech', 'techreport', 'thesis'}) then
TitleType = set_titletype (config.CitationClass, TitleType);
if utilities.is_set (Degree) and "Thesis" == TitleType then -- special case for cite thesis
TitleType = Degree .. ' ' .. cfg.title_types ['thesis']:lower();
end
end
if utilities.is_set (TitleType) then -- if type parameter is specified
TitleType = utilities.substitute ( cfg.messages['type'], TitleType); -- display it in parentheses
-- TODO: Hack on TitleType to fix bunched parentheses problem
end
-- legacy: promote PublicationDate to Date if neither Date nor Year are set.
local Date = A['Date'];
local Date_origin; -- to hold the name of parameter promoted to Date; required for date error messaging
local PublicationDate = A['PublicationDate'];
local Year = A['Year'];
if utilities.is_set (Year) then
validation.year_check (Year); -- returns nothing; emits maint message when |year= doesn't hold a 'year' value
end
if not utilities.is_set (Date) then
Date = Year; -- promote Year to Date
Year = nil; -- make nil so Year as empty string isn't used for CITEREF
if not utilities.is_set (Date) and utilities.is_set (PublicationDate) then -- use PublicationDate when |date= and |year= are not set
Date = PublicationDate; -- promote PublicationDate to Date
PublicationDate = ''; -- unset, no longer needed
Date_origin = A:ORIGIN('PublicationDate'); -- save the name of the promoted parameter
else
Date_origin = A:ORIGIN('Year'); -- save the name of the promoted parameter
end
else
Date_origin = A:ORIGIN('Date'); -- not a promotion; name required for error messaging
end
if PublicationDate == Date then PublicationDate = ''; end -- if PublicationDate is same as Date, don't display in rendered citation
--[[
Go test all of the date-holding parameters for valid MOS:DATE format and make sure that dates are real dates. This must be done before we do COinS because here is where
we get the date used in the metadata.
Date validation supporting code is in Module:Citation/CS1/Date_validation
]]
local DF = is_valid_parameter_value (A['DF'], A:ORIGIN('DF'), cfg.keywords_lists['df'], '');
if not utilities.is_set (DF) then
DF = cfg.global_df; -- local |df= if present overrides global df set by {{use xxx date}} template
end
local ArchiveURL;
local ArchiveDate;
local ArchiveFormat = A['ArchiveFormat'];
local archive_url_timestamp; -- timestamp from wayback machine url
ArchiveURL, ArchiveDate, archive_url_timestamp = archive_url_check (A['ArchiveURL'], A['ArchiveDate'])
ArchiveFormat = style_format (ArchiveFormat, ArchiveURL, 'archive-format', 'archive-url');
ArchiveURL, ArchiveDate = is_unique_archive_url (ArchiveURL, URL, ChapterURL, A:ORIGIN('ArchiveURL'), ArchiveDate); -- add error message when URL or ChapterURL == ArchiveURL
local AccessDate = A['AccessDate'];
local COinS_date = {}; -- holds date info extracted from |date= for the COinS metadata by Module:Date verification
local DoiBroken = A['DoiBroken'];
local Embargo = A['Embargo'];
local anchor_year; -- used in the CITEREF identifier
do -- create defined block to contain local variables date_parameters_list, mismatch
local date_parameters_list = { -- AirDate has been promoted to Date so not necessary to check it
['access-date'] = {val = AccessDate, name = A:ORIGIN ('AccessDate')},
['archive-date'] = {val = ArchiveDate, name = A:ORIGIN ('ArchiveDate')},
['date'] = {val = Date, name = Date_origin},
['doi-broken-date'] = {val = DoiBroken, name = A:ORIGIN ('DoiBroken')},
['pmc-embargo-date'] = {val = Embargo, name = A:ORIGIN ('Embargo')},
['publication-date'] = {val = PublicationDate, name = A:ORIGIN ('PublicationDate')},
['year'] = {val = Year, name = A:ORIGIN ('Year')},
};
local error_list = {};
anchor_year, Embargo = validation.dates(date_parameters_list, COinS_date, error_list);
if utilities.is_set (Year) and utilities.is_set (Date) then -- both |date= and |year= not normally needed;
validation.year_date_check (Year, A:ORIGIN ('Year'), Date, A:ORIGIN ('Date'), error_list);
end
if 0 == #error_list then -- error free dates only; 0 when error_list is empty
local modified = false; -- flag
if utilities.is_set (DF) then -- if we need to reformat dates
modified = validation.reformat_dates (date_parameters_list, DF); -- reformat to DF format, use long month names if appropriate
end
if true == validation.date_hyphen_to_dash (date_parameters_list) then -- convert hyphens to dashes where appropriate
modified = true;
utilities.set_message ('maint_date_format'); -- hyphens were converted so add maint category
end
-- for those wikis that can and want to have English date names translated to the local language; not supported at en.wiki
if cfg.date_name_auto_xlate_enable and validation.date_name_xlate (date_parameters_list, cfg.date_digit_auto_xlate_enable ) then
utilities.set_message ('maint_date_auto_xlated'); -- add maint cat
modified = true;
end
if modified then -- if the date_parameters_list values were modified
AccessDate = date_parameters_list['access-date'].val; -- overwrite date holding parameters with modified values
ArchiveDate = date_parameters_list['archive-date'].val;
Date = date_parameters_list['date'].val;
DoiBroken = date_parameters_list['doi-broken-date'].val;
PublicationDate = date_parameters_list['publication-date'].val;
end
if archive_url_timestamp and utilities.is_set (ArchiveDate) then
validation.archive_date_check (ArchiveDate, archive_url_timestamp, DF); -- does YYYYMMDD in archive_url_timestamp match date in ArchiveDate
end
else
utilities.set_message ('err_bad_date', {utilities.make_sep_list (#error_list, error_list)}); -- add this error message
end
end -- end of do
if utilities.in_array (config.CitationClass, {'book', 'encyclopaedia'}) or -- {{cite book}}, {{cite encyclopedia}}; TODO: {{cite conference}} and others?
('citation' == config.CitationClass and utilities.is_set (Encyclopedia)) or -- {{citation}} as an encylopedia citation
('citation' == config.CitationClass and not utilities.is_set (Periodical)) then -- {{citation}} as a book citation
if utilities.is_set (PublicationPlace) then
if not utilities.is_set (PublisherName) then
local date = COinS_date.rftdate and tonumber (COinS_date.rftdate:match ('%d%d%d%d')); -- get year portion of COinS date (because in Arabic numerals); convert string to number
if date and (1850 <= date) then -- location has no publisher; if date is 1850 or later
utilities.set_message ('maint_location_no_publisher'); -- add maint cat
end
else -- PublisherName has a value
if cfg.keywords_xlate['none'] == PublisherName then -- if that value is 'none' (only for book and encyclopedia citations)
PublisherName = ''; -- unset
end
end
end
end
local ID_list_t; -- sequence table of rendered identifiers
local ID_list_coins_t; -- table of identifiers and their values from args; key is same as cfg.id_handlers's key
local Class = A['Class']; -- arxiv class identifier
local ID_support = {
{A['ASINTLD'], 'ASIN', 'err_asintld_missing_asin', A:ORIGIN ('ASINTLD')},
{DoiBroken, 'DOI', 'err_doibroken_missing_doi', A:ORIGIN ('DoiBroken')},
{Embargo, 'PMC', 'err_embargo_missing_pmc', A:ORIGIN ('Embargo')},
}
ID_list_t, ID_list_coins_t = identifiers.identifier_lists_get (args, {
DoiBroken = DoiBroken, -- for |doi=
ASINTLD = A['ASINTLD'], -- for |asin=
Embargo = Embargo, -- for |pmc=
Class = Class, -- for |arxiv=
CitationClass = config.CitationClass, -- for |arxiv=
Year=anchor_year, -- for |isbn=
}, ID_support);
for _, id in ipairs (ID_list_t) do -- search through the ID_list_t sequence looking for an ISBN identifier string
if id:find ('ISBN', 3, true) then -- plain find, start at index 3 ([[ISBN)
if 'citation' == config.CitationClass then -- catch ve/citoid malformed book cites in {{citation}} templates
if utilities.is_set (Periodical) then -- when a |work= alias is set
utilities.set_message ('maint_work_isbn'); -- add maint cat
break; -- and abandon
end
elseif ({['journal'] = true, ['magazine'] = true, ['news'] = true, ['web'] = true})[config.CitationClass] then
utilities.set_message ('maint_periodical_has_isbn'); -- add maint cat
break; -- and abandon
end
end
end
-- if 'citation' == config.CitationClass then -- catch ve/citoid malformed book cites in {{citation}} templates
-- for _, id in ipairs (ID_list_t) do -- search through the ID_list_t sequence looking for an ISBN identifier string
-- if id:find ('ISBN', 3, true) then -- plain find, start at index 3 ([[ISBN)
-- if utilities.is_set (Periodical) then -- when a |work= alias is set
-- utilities.set_message ('maint_work_isbn'); -- add maint cat
-- end
-- end
-- end
-- end
-- Account for the oddities that are {{cite arxiv}}, {{cite biorxiv}}, {{cite citeseerx}}, {{cite medrxiv}}, {{cite ssrn}}, before generation of COinS data.
if utilities.in_array (config.CitationClass, whitelist.preprint_template_list_t) then -- |arxiv= or |eprint= required for cite arxiv; |biorxiv=, |citeseerx=, |medrxiv=, |ssrn= required for their templates
if not (args[cfg.id_handlers[config.CitationClass:upper()].parameters[1]] or -- can't use ID_list_coins_t k/v table here because invalid parameters omitted
args[cfg.id_handlers[config.CitationClass:upper()].parameters[2]]) then -- which causes unexpected parameter missing error message
utilities.set_message ('err_' .. config.CitationClass .. '_missing'); -- add error message
end
Periodical = ({['arxiv'] = 'arXiv', ['biorxiv'] = 'bioRxiv', ['citeseerx'] = 'CiteSeerX', ['medrxiv'] = 'medRxiv', ['ssrn'] = 'Social Science Research Network'})[config.CitationClass];
end
-- Link the title of the work if no |url= was provided, but we have a |pmc= or a |doi= with |doi-access=free
if config.CitationClass == "journal" and not utilities.is_set (URL) and not utilities.is_set (TitleLink) and not utilities.in_array (cfg.keywords_xlate[Title], {'off', 'none'}) then -- TODO: remove 'none' once existing citations have been switched to 'off', so 'none' can be used as token for "no title" instead
if 'none' ~= cfg.keywords_xlate[auto_select] then -- if auto-linking not disabled
if identifiers.auto_link_urls[auto_select] then -- manual selection
URL = identifiers.auto_link_urls[auto_select]; -- set URL to be the same as identifier's external link
URL_origin = cfg.id_handlers[auto_select:upper()].parameters[1]; -- set URL_origin to parameter name for use in error message if citation is missing a |title=
elseif identifiers.auto_link_urls['pmc'] then -- auto-select PMC
URL = identifiers.auto_link_urls['pmc']; -- set URL to be the same as the PMC external link if not embargoed
URL_origin = cfg.id_handlers['PMC'].parameters[1]; -- set URL_origin to parameter name for use in error message if citation is missing a |title=
elseif identifiers.auto_link_urls['doi'] then -- auto-select DOI
URL = identifiers.auto_link_urls['doi'];
URL_origin = cfg.id_handlers['DOI'].parameters[1];
end
end
if utilities.is_set (URL) then -- set when using an identifier-created URL
if utilities.is_set (AccessDate) then -- |access-date= requires |url=; identifier-created URL is not |url=
utilities.set_message ('err_accessdate_missing_url'); -- add an error message
AccessDate = ''; -- unset
end
if utilities.is_set (ArchiveURL) then -- |archive-url= requires |url=; identifier-created URL is not |url=
utilities.set_message ('err_archive_missing_url'); -- add an error message
ArchiveURL = ''; -- unset
end
end
end
-- At this point fields may be nil if they weren't specified in the template use. We can use that fact.
-- Test if citation has no title
if not utilities.is_set (Title) and not utilities.is_set (TransTitle) and not utilities.is_set (ScriptTitle) then -- has special case for cite episode
utilities.set_message ('err_citation_missing_title', {'episode' == config.CitationClass and 'series' or 'title'});
end
if utilities.in_array (cfg.keywords_xlate[Title], {'off', 'none'}) and
utilities.in_array (config.CitationClass, {'journal', 'citation'}) and
(utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)) and
('journal' == Periodical_origin or 'script-journal' == ScriptPeriodical_origin) then -- special case for journal cites
Title = ''; -- set title to empty string
utilities.set_message ('maint_untitled'); -- add maint cat
end
if 'journal' == config.CitationClass or ('citation' == config.CitationClass and utilities.is_set (Periodical) and 'journal' == Periodical_origin) then
if is_page_art_num (((utilities.is_set (Page) and Page) or (utilities.is_set (Pages) and Pages)) or nil, ID_list_coins_t['DOI']) then -- does |page(s)= look like it holds an article number
utilities.set_message ('maint_page_art_num'); -- add maint cat
end
end
-- COinS metadata (see <http://ocoins.info/>) for automated parsing of citation information.
-- handle the oddity that is cite encyclopedia and {{citation |encyclopedia=something}}. Here we presume that
-- when Periodical, Title, and Chapter are all set, then Periodical is the book (encyclopedia) title, Title
-- is the article title, and Chapter is a section within the article. So, we remap
local coins_chapter = Chapter; -- default assuming that remapping not required
local coins_title = Title; -- et tu
if 'encyclopaedia' == config.CitationClass or ('citation' == config.CitationClass and utilities.is_set (Encyclopedia)) then
if utilities.is_set (Chapter) and utilities.is_set (Title) and utilities.is_set (Periodical) then -- if all are used then
coins_chapter = Title; -- remap
coins_title = Periodical;
end
end
local coins_author = a; -- default for coins rft.au
if 0 < #c then -- but if contributor list
coins_author = c; -- use that instead
end
-- this is the function call to COinS()
local OCinSoutput = metadata.COinS({
['Periodical'] = utilities.strip_apostrophe_markup (Periodical), -- no markup in the metadata
['Encyclopedia'] = Encyclopedia, -- just a flag; content ignored by ~/COinS
['Chapter'] = metadata.make_coins_title (coins_chapter, ScriptChapter), -- Chapter and ScriptChapter stripped of bold / italic / accept-as-written markup
['Degree'] = Degree; -- cite thesis only
['Title'] = metadata.make_coins_title (coins_title, ScriptTitle), -- Title and ScriptTitle stripped of bold / italic / accept-as-written markup
['PublicationPlace'] = PublicationPlace,
['Date'] = COinS_date.rftdate, -- COinS_date.* has correctly formatted date values if Date is valid;
['Season'] = COinS_date.rftssn,
['Quarter'] = COinS_date.rftquarter,
['Chron'] = COinS_date.rftchron,
['Series'] = Series,
['Volume'] = Volume,
['Issue'] = Issue,
['ArticleNumber'] = ArticleNumber,
['Pages'] = coins_pages or metadata.get_coins_pages (first_set ({Sheet, Sheets, Page, Pages, At, QuotePage, QuotePages}, 7)), -- pages stripped of external links
['Edition'] = Edition,
['PublisherName'] = PublisherName or Newsgroup, -- any apostrophe markup already removed from PublisherName
['URL'] = first_set ({ChapterURL, URL}, 2),
['Authors'] = coins_author,
['ID_list'] = ID_list_coins_t,
['RawPage'] = this_page.prefixedText,
}, config.CitationClass);
-- Account for the oddities that are {{cite arxiv}}, {{cite biorxiv}}, {{cite citeseerx}}, {{cite medrxiv}}, and {{cite ssrn}} AFTER generation of COinS data.
if utilities.in_array (config.CitationClass, whitelist.preprint_template_list_t) then -- we have set rft.jtitle in COinS to arXiv, bioRxiv, CiteSeerX, medRxiv, or ssrn now unset so it isn't displayed
Periodical = ''; -- periodical not allowed in these templates; if article has been published, use cite journal
end
-- special case for cite newsgroup. Do this after COinS because we are modifying Publishername to include some static text
if 'newsgroup' == config.CitationClass and utilities.is_set (Newsgroup) then
PublisherName = utilities.substitute (cfg.messages['newsgroup'], external_link( 'news:' .. Newsgroup, Newsgroup, Newsgroup_origin, nil ));
end
local Editors;
local EditorCount; -- used only for choosing {ed.) or (eds.) annotation at end of editor name-list
local Contributors; -- assembled contributors name list
local contributor_etal = nil; -- must be intentionally initialized
local Translators; -- assembled translators name list
local translator_etal = nil; -- must be intentionally initialized
local t = extract_names (args, 'TranslatorList'); -- fetch translator list from |translatorn= / |translator-lastn=, -firstn=, -linkn=, -maskn=
local Interviewers;
local interviewers_list = extract_names (args, 'InterviewerList'); -- process preferred interviewers parameters
local interviewer_etal = nil; -- must be intentionally initialized
-- Now perform various field substitutions.
-- We also add leading spaces and surrounding markup and punctuation to the
-- various parts of the citation, but only when they are non-nil.
do
local last_first_list;
local control = {
format = NameListStyle, -- empty string, '&', 'amp', 'and', or 'vanc'
maximum = nil, -- as if display-authors or display-editors not set
mode = Mode
};
do -- do editor name list first because the now unsupported coauthors used to modify control table
local display_names, param = display_names_select (cfg.global_cs1_config_t['DisplayEditors'], A['DisplayEditors'], A:ORIGIN ('DisplayEditors'), #e);
control.maximum, editor_etal = get_display_names (display_names, #e, editor_etal, param);
Editors, EditorCount = list_people (control, e, editor_etal);
if 1 == EditorCount and (true == editor_etal or 1 < #e) then -- only one editor displayed but includes etal then
EditorCount = 2; -- spoof to display (eds.) annotation
end
end
do -- now do interviewers
local display_names, param = display_names_select (cfg.global_cs1_config_t['DisplayInterviewers'], A['DisplayInterviewers'], A:ORIGIN ('DisplayInterviewers'), #interviewers_list);
control.maximum, interviewer_etal = get_display_names (display_names, #interviewers_list, interviewer_etal, param);
Interviewers = list_people (control, interviewers_list, interviewer_etal);
end
do -- now do translators
local display_names, param = display_names_select (cfg.global_cs1_config_t['DisplayTranslators'], A['DisplayTranslators'], A:ORIGIN ('DisplayTranslators'), #t);
control.maximum, translator_etal = get_display_names (display_names, #t, translator_etal, param);
Translators = list_people (control, t, translator_etal);
end
do -- now do contributors
local display_names, param = display_names_select (cfg.global_cs1_config_t['DisplayContributors'], A['DisplayContributors'], A:ORIGIN ('DisplayContributors'), #c);
control.maximum, contributor_etal = get_display_names (display_names, #c, contributor_etal, param);
Contributors = list_people (control, c, contributor_etal);
end
do -- now do authors
local display_names, param = display_names_select (cfg.global_cs1_config_t['DisplayAuthors'], A['DisplayAuthors'], A:ORIGIN ('DisplayAuthors'), #a, author_etal);
control.maximum, author_etal = get_display_names (display_names, #a, author_etal, param);
last_first_list = list_people (control, a, author_etal);
if utilities.is_set (Authors) then
Authors, author_etal = name_has_etal (Authors, author_etal, false, 'authors'); -- find and remove variations on et al.
if author_etal then
Authors = Authors .. ' ' .. cfg.messages['et al']; -- add et al. to authors parameter
end
else
Authors = last_first_list; -- either an author name list or an empty string
end
end -- end of do
if utilities.is_set (Authors) and utilities.is_set (Collaboration) then
Authors = utilities.substitute (cfg.messages['collaboration'], {Authors, Collaboration}); -- add collaboration after et al.
end
end
local ConferenceFormat = A['ConferenceFormat'];
local ConferenceURL = A['ConferenceURL'];
ConferenceFormat = style_format (ConferenceFormat, ConferenceURL, 'conference-format', 'conference-url');
Format = style_format (Format, URL, 'format', 'url');
-- special case for chapter format so no error message or cat when chapter not supported
if not (utilities.in_array (config.CitationClass, {'web', 'news', 'journal', 'magazine', 'pressrelease', 'podcast', 'newsgroup', 'arxiv', 'biorxiv', 'citeseerx', 'medrxiv', 'ssrn'}) or
('citation' == config.CitationClass and (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)) and not utilities.is_set (Encyclopedia))) then
ChapterFormat = style_format (ChapterFormat, ChapterURL, 'chapter-format', 'chapter-url');
end
if not utilities.is_set (URL) then
if utilities.in_array (config.CitationClass, {"web", "podcast", "mailinglist"}) or -- |url= required for {{cite web}}, {{cite podcast}}, and {{cite mailing list}}
('citation' == config.CitationClass and ('website' == Periodical_origin or 'script-website' == ScriptPeriodical_origin)) then -- and required for {{citation}} with |website= or |script-website=
if not url_was_archive_today then -- is <URL> empty/missing because it was a deprecated archive.today url
utilities.set_message ('err_cite_web_url'); -- no; emit an error message for an empty/ missing |url=
end
end
-- do we have |accessdate= without either |url= or |chapter-url=?
if utilities.is_set (AccessDate) and not utilities.is_set (ChapterURL) then -- ChapterURL may be set when URL is not set;
utilities.set_message ('err_accessdate_missing_url');
AccessDate = '';
end
end
local UrlStatus = is_valid_parameter_value (A['UrlStatus'], A:ORIGIN('UrlStatus'), cfg.keywords_lists['url-status'], '');
local OriginalURL
local OriginalURL_origin
local OriginalFormat
local OriginalAccess;
UrlStatus = UrlStatus:lower(); -- used later when assembling archived text
if utilities.is_set ( ArchiveURL ) then
if utilities.is_set (ChapterURL) then -- if chapter-url= is set apply archive url to it
OriginalURL = ChapterURL; -- save copy of source chapter's url for archive text
OriginalURL_origin = ChapterURL_origin; -- name of |chapter-url= parameter for error messages
OriginalFormat = ChapterFormat; -- and original |chapter-format=
if 'live' ~= UrlStatus then
ChapterURL = ArchiveURL -- swap-in the archive's URL
ChapterURL_origin = A:ORIGIN('ArchiveURL') -- name of |archive-url= parameter for error messages
ChapterFormat = ArchiveFormat or ''; -- swap in archive's format
ChapterUrlAccess = nil; -- restricted access levels do not make sense for archived URLs
end
elseif utilities.is_set (URL) then
OriginalURL = URL; -- save copy of original source URL
OriginalURL_origin = URL_origin; -- name of URL parameter for error messages
OriginalFormat = Format; -- and original |format=
OriginalAccess = UrlAccess;
if 'live' ~= UrlStatus then -- if URL set then |archive-url= applies to it
URL = ArchiveURL -- swap-in the archive's URL
URL_origin = A:ORIGIN('ArchiveURL') -- name of archive URL parameter for error messages
Format = ArchiveFormat or ''; -- swap in archive's format
UrlAccess = nil; -- restricted access levels do not make sense for archived URLs
end
end
elseif utilities.is_set (UrlStatus) then -- if |url-status= is set when |archive-url= is not set
utilities.set_message ('maint_url_status'); -- add maint cat
end
if utilities.in_array (config.CitationClass, {'web', 'news', 'journal', 'magazine', 'pressrelease', 'podcast', 'newsgroup', 'arxiv', 'biorxiv', 'citeseerx', 'medrxiv', 'ssrn'}) or -- if any of the 'periodical' cites except encyclopedia
('citation' == config.CitationClass and (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)) and not utilities.is_set (Encyclopedia)) then
local chap_param;
if utilities.is_set (Chapter) then -- get a parameter name from one of these chapter related meta-parameters
chap_param = A:ORIGIN ('Chapter')
elseif utilities.is_set (TransChapter) then
chap_param = A:ORIGIN ('TransChapter')
elseif utilities.is_set (ChapterURL) then
chap_param = A:ORIGIN ('ChapterURL')
elseif utilities.is_set (ScriptChapter) then
chap_param = ScriptChapter_origin;
else utilities.is_set (ChapterFormat)
chap_param = A:ORIGIN ('ChapterFormat')
end
if utilities.is_set (chap_param) then -- if we found one
utilities.set_message ('err_chapter_ignored', {chap_param}); -- add error message
Chapter = ''; -- and set them to empty string to be safe with concatenation
TransChapter = ''; -- for now, ignore linter warnings
ChapterURL = '';
ScriptChapter = '';
ChapterFormat = '';
end
else -- otherwise, format chapter / article title
local no_quotes = false; -- default assume that we will be quoting the chapter parameter value
if utilities.is_set (Contribution) and 0 < #c then -- if this is a contribution with contributor(s)
if utilities.in_array (Contribution:lower(), cfg.keywords_lists.contribution) then -- and a generic contribution title
no_quotes = true; -- then render it unquoted
end
end
Chapter = format_chapter_title (ScriptChapter, ScriptChapter_origin, Chapter, TransChapter, TransChapter_origin, ChapterURL, ChapterURL_origin, no_quotes, ChapterUrlAccess); -- Contribution is also in Chapter
if utilities.is_set (Chapter) then
Chapter = Chapter .. ChapterFormat ;
if 'map' == config.CitationClass and utilities.is_set (TitleType) then
Chapter = Chapter .. ' ' .. TitleType; -- map annotation here; not after title
end
Chapter = Chapter .. sepc .. ' ';
elseif utilities.is_set (ChapterFormat) then -- |chapter= not set but |chapter-format= is so ...
Chapter = ChapterFormat .. sepc .. ' '; -- ... ChapterFormat has error message, we want to see it
end
end
-- Format main title
local plain_title = false;
local accept_title;
Title, accept_title = utilities.has_accept_as_written (Title, true); -- remove accept-this-as-written markup when it wraps all of <Title>
if accept_title and ('' == Title) then -- only support forced empty for now "(())"
Title = cfg.messages['notitle']; -- replace by predefined "No title" message
-- TODO: utilities.set_message ( 'err_redundant_parameters', ...); -- issue proper error message instead of muting
ScriptTitle = ''; -- just mute for now
TransTitle = ''; -- just mute for now
plain_title = true; -- suppress text decoration for descriptive title
utilities.set_message ('maint_untitled'); -- add maint cat
end
if not accept_title then -- <Title> not wrapped in accept-as-written markup
if '...' == Title:sub (-3) then -- if ellipsis is the last three characters of |title=
Title = Title:gsub ('(%.%.%.)%.+$', '%1'); -- limit the number of dots to three
elseif not mw.ustring.find (Title, '%.%s*%a%.$') and -- end of title is not a 'dot-(optional space-)letter-dot' initialism ...
not mw.ustring.find (Title, '%s+%a%.$') then -- ...and not a 'space-letter-dot' initial (''Allium canadense'' L.)
Title = mw.ustring.gsub(Title, '%' .. sepc .. '$', ''); -- remove any trailing separator character; sepc and ms.ustring() here for languages that use multibyte separator characters
end
if utilities.is_set (ArchiveURL) and is_archived_copy (Title) then
utilities.set_message ('maint_archived_copy'); -- add maintenance category before we modify the content of Title
end
if is_generic ('generic_titles', Title) then
utilities.set_message ('err_generic_title'); -- set an error message
end
end
if (not plain_title) and (utilities.in_array (config.CitationClass, {'web', 'news', 'journal', 'magazine', 'document', 'pressrelease', 'podcast', 'newsgroup', 'mailinglist', 'interview', 'arxiv', 'biorxiv', 'citeseerx', 'medrxiv', 'ssrn'}) or
('citation' == config.CitationClass and (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)) and not utilities.is_set (Encyclopedia)) or
('map' == config.CitationClass and (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)))) then -- special case for cite map when the map is in a periodical treat as an article
Title = kern_quotes (Title); -- if necessary, separate title's leading and trailing quote marks from module provided quote marks
Title = utilities.wrap_style ('quoted-title', Title);
Title = script_concatenate (Title, ScriptTitle, 'script-title'); -- <bdi> tags, lang attribute, categorization, etc.; must be done after title is wrapped
TransTitle = utilities.wrap_style ('trans-quoted-title', TransTitle );
elseif plain_title or ('report' == config.CitationClass) then -- no styling for cite report and descriptive titles (otherwise same as above)
Title = script_concatenate (Title, ScriptTitle, 'script-title'); -- <bdi> tags, lang attribute, categorization, etc.; must be done after title is wrapped
TransTitle = utilities.wrap_style ('trans-quoted-title', TransTitle ); -- for cite report, use this form for trans-title
else
Title = utilities.wrap_style ('italic-title', Title);
Title = script_concatenate (Title, ScriptTitle, 'script-title'); -- <bdi> tags, lang attribute, categorization, etc.; must be done after title is wrapped
TransTitle = utilities.wrap_style ('trans-italic-title', TransTitle);
end
if utilities.is_set (TransTitle) then
if utilities.is_set (Title) then
TransTitle = " " .. TransTitle;
else
utilities.set_message ('err_trans_missing_title', {'title'});
end
end
if utilities.is_set (Title) then -- TODO: is this the right place to be making Wikisource URLs?
if utilities.is_set (TitleLink) and utilities.is_set (URL) then
utilities.set_message ('err_wikilink_in_url'); -- set an error message because we can't have both
TitleLink = ''; -- unset
end
if not utilities.is_set (TitleLink) and utilities.is_set (URL) then
Title = external_link (URL, Title, URL_origin, UrlAccess) .. TransTitle .. Format;
URL = ''; -- unset these because no longer needed
Format = ''; -- for now, ignore linter warning
elseif utilities.is_set (TitleLink) and not utilities.is_set (URL) then
local ws_url;
ws_url = wikisource_url_make (TitleLink); -- ignore ws_label return; not used here
if ws_url then
Title = external_link (ws_url, Title .. ' ', 'ws link in title-link'); -- space char after Title to move icon away from italic text; TODO: a better way to do this?
Title = utilities.substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], TitleLink, Title});
Title = Title .. TransTitle;
else
Title = utilities.make_wikilink (TitleLink, Title) .. TransTitle;
end
else
local ws_url, ws_label, L; -- Title has italic or quote markup by the time we get here which causes is_wikilink() to return 0 (not a wikilink)
ws_url, ws_label, L = wikisource_url_make (Title:gsub('^[\'"]*(.-)[\'"]*$', '%1')); -- make ws URL from |title= interwiki link (strip italic or quote markup); link portion L becomes tooltip label
if ws_url then
Title = Title:gsub ('%b[]', ws_label); -- replace interwiki link with ws_label to retain markup
Title = external_link (ws_url, Title .. ' ', 'ws link in title'); -- space char after Title to move icon away from italic text; TODO: a better way to do this?
Title = utilities.substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], L, Title});
Title = Title .. TransTitle;
else
Title = Title .. TransTitle;
end
end
else
Title = TransTitle;
end
if utilities.is_set (Place) then
Place = " " .. wrap_msg ('written', Place, use_lowercase) .. sepc .. " ";
end
local ConferenceURL_origin = A:ORIGIN('ConferenceURL'); -- get name of parameter that holds ConferenceURL
if utilities.is_set (Conference) then
if utilities.is_set (ConferenceURL) then
Conference = external_link( ConferenceURL, Conference, ConferenceURL_origin, nil );
end
Conference = sepc .. " " .. Conference .. ConferenceFormat;
elseif utilities.is_set (ConferenceURL) then
Conference = sepc .. " " .. external_link( ConferenceURL, nil, ConferenceURL_origin, nil );
end
local Position = '';
if not utilities.is_set (Position) then
local Minutes = A['Minutes'];
local Time = A['Time'];
if utilities.is_set (Minutes) then
if utilities.is_set (Time) then --TODO: make a function for this and similar?
utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', 'minutes') .. cfg.presentation['sep_list_pair'] .. utilities.wrap_style ('parameter', 'time')});
end
Position = utilities.substitute (cfg.messages.minutes, Minutes);
else
if utilities.is_set (Time) then
local TimeCaption = A['TimeCaption']
if not utilities.is_set (TimeCaption) then
TimeCaption = cs2_lower(cfg.messages['event']);
end
Position = " " .. TimeCaption .. " " .. Time;
end
end
else
Position = " " .. Position;
At = '';
end
Page, Pages, Sheet, Sheets = format_pages_sheets (Page, Pages, Sheet, Sheets, config.CitationClass, Periodical_origin, sepc, NoPP, use_lowercase);
At = utilities.is_set (At) and (sepc .. " " .. At) or "";
Position = utilities.is_set (Position) and (sepc .. " " .. Position) or "";
if config.CitationClass == 'map' then
local Sections = A['Sections']; -- Section (singular) is an alias of Chapter so set earlier
local Inset = A['Inset'];
if utilities.is_set ( Inset ) then
Inset = sepc .. " " .. wrap_msg ('inset', Inset, use_lowercase);
end
if utilities.is_set ( Sections ) then
Section = sepc .. " " .. wrap_msg ('sections', Sections, use_lowercase);
elseif utilities.is_set ( Section ) then
Section = sepc .. " " .. wrap_msg ('section', Section, use_lowercase);
end
At = At .. Inset .. Section;
end
local Others = A['Others'];
if utilities.is_set (Others) and 0 == #a and 0 == #e then -- add maint cat when |others= has value and used without |author=, |editor=
if config.CitationClass == "AV-media-notes"
or config.CitationClass == "audio-visual" then -- special maint for AV/M which has a lot of 'false' positives right now
utilities.set_message ('maint_others_avm')
else
utilities.set_message ('maint_others');
end
end
Others = utilities.is_set (Others) and (sepc .. " " .. Others) or "";
if utilities.is_set (Translators) then
Others = safe_join ({sepc .. ' ', wrap_msg ('translated', Translators, use_lowercase), Others}, sepc);
end
if utilities.is_set (Interviewers) then
Others = safe_join ({sepc .. ' ', wrap_msg ('interview', Interviewers, use_lowercase), Others}, sepc);
end
local TitleNote = A['TitleNote'];
TitleNote = utilities.is_set (TitleNote) and (sepc .. " " .. TitleNote) or "";
if utilities.is_set (Edition) then
if Edition:match ('%f[%a][Ee]d%n?%.?$') or Edition:match ('%f[%a][Ee]dition$') then -- Ed, ed, Ed., ed., Edn, edn, Edn., edn.
utilities.set_message ('err_extra_text_edition'); -- add error message
end
Edition = " " .. wrap_msg ('edition', Edition);
else
Edition = '';
end
Series = utilities.is_set (Series) and wrap_msg ('series', {sepc, Series}) or ""; -- not the same as SeriesNum
local Agency = A['Agency'] or ''; -- |agency= is supported by {{cite magazine}}, {{cite news}}, {{cite press release}}, {{cite web}}, and certain {{citation}} templates
if utilities.is_set (Agency) then -- this testing done here because {{citation}} supports 'news' citations
if utilities.in_array (config.CitationClass, {'magazine', 'news', 'pressrelease', 'web'}) or ('citation' == config.CitationClass and utilities.in_array (Periodical_origin, {"magazine", "newspaper", "work"})) then
Agency = wrap_msg ('agency', {sepc, Agency}); -- format for rendering
else
Agency = ''; -- unset; not supported
utilities.set_message ('err_parameter_ignored', {'agency'}); -- add error message
end
end
Volume = format_volume_issue (Volume, Issue, ArticleNumber, config.CitationClass, Periodical_origin, sepc, use_lowercase);
if utilities.is_set (AccessDate) then
local retrv_text = " " .. cfg.messages['retrieved'];
AccessDate = nowrap_date (AccessDate); -- wrap in nowrap span if date in appropriate format
retrv_text = cs2_lower(retrv_text); -- if mode is cs2, lower case
AccessDate = utilities.substitute (retrv_text, AccessDate); -- add retrieved text
AccessDate = utilities.substitute (cfg.presentation['accessdate'], {sepc, AccessDate}); -- allow editors to hide accessdates
end
if utilities.is_set (ID) then ID = sepc .. " " .. ID; end
local Docket = A['Docket'];
if "thesis" == config.CitationClass and utilities.is_set (Docket) then
ID = sepc .. " Docket " .. Docket .. ID;
end
if "report" == config.CitationClass and utilities.is_set (Docket) then -- for cite report when |docket= is set
ID = sepc .. ' ' .. Docket; -- overwrite ID even if |id= is set
end
if utilities.is_set (URL) then
URL = " " .. external_link( URL, nil, URL_origin, UrlAccess );
end
-- We check length of PostScript here because it will have been nuked by
-- the quote parameters. We'd otherwise emit a message even if there wasn't
-- a displayed postscript.
-- TODO: Should the max size (1) be configurable?
-- TODO: Should we check a specific pattern?
if utilities.is_set(PostScript) and mw.ustring.len(PostScript) > 1 then
utilities.set_message ('maint_postscript')
end
local Archived;
if utilities.is_set (ArchiveURL) then
if not utilities.is_set (ArchiveDate) then -- ArchiveURL set but ArchiveDate not set
utilities.set_message ('err_archive_missing_date'); -- emit an error message
ArchiveURL = ''; -- empty string for concatenation
ArchiveDate = ''; -- empty string for concatenation
end
else
if utilities.is_set (ArchiveDate) then -- ArchiveURL not set but ArchiveDate is set
utilities.set_message ('err_archive_date_missing_url'); -- emit an error message
ArchiveURL = ''; -- empty string for concatenation
ArchiveDate = ''; -- empty string for concatenation
end
end
if utilities.is_set (ArchiveURL) then
if "live" == UrlStatus then
if utilities.is_set (ArchiveDate) then
Archived = sepc .. ' ' .. utilities.substitute ( cfg.messages['archived-live'],
{ external_link( ArchiveURL, cs2_lower(cfg.messages['archived']), A:ORIGIN('ArchiveURL'), nil) .. ArchiveFormat, ArchiveDate } );
else
Archived = '';
end
if not utilities.is_set (OriginalURL) then
utilities.set_message ('err_archive_missing_url');
Archived = ''; -- empty string for concatenation
end
elseif utilities.is_set (OriginalURL) then -- UrlStatus is empty, 'dead', 'unfit', 'usurped', 'bot: unknown'
if utilities.in_array (UrlStatus, {'unfit', 'usurped', 'bot: unknown'}) then
Archived = sepc .. ' ' .. utilities.substitute ( cs2_lower(cfg.messages['archived-unfit']), ArchiveDate);
if 'bot: unknown' == UrlStatus then
utilities.set_message ('maint_bot_unknown'); -- and add a category if not already added
else
utilities.add_prop_cat ('unfit'); -- and add a category if not already added
end
else -- UrlStatus is empty, 'dead'
if utilities.is_set (ArchiveDate) then
Archived = sepc .. ' ' .. utilities.substitute ( cs2_lower(cfg.messages['archived-dead']),
{ external_link( OriginalURL, cfg.messages['original'], OriginalURL_origin, OriginalAccess ) .. OriginalFormat, ArchiveDate } );
else
Archived = ''; -- unset for concatenation
end
end
else -- OriginalUrl not set
utilities.set_message ('err_archive_missing_url');
Archived = ''; -- empty string for concatenation
end
elseif utilities.is_set (ArchiveFormat) then
Archived = ArchiveFormat; -- if set and ArchiveURL not set ArchiveFormat has error message
else
Archived = '';
end
local TranscriptURL = A['TranscriptURL']
local TranscriptFormat = A['TranscriptFormat'];
TranscriptFormat = style_format (TranscriptFormat, TranscriptURL, 'transcript-format', 'transcripturl');
local Transcript = A['Transcript'];
local TranscriptURL_origin = A:ORIGIN('TranscriptURL'); -- get name of parameter that holds TranscriptURL
if utilities.is_set (Transcript) then
if utilities.is_set (TranscriptURL) then
Transcript = external_link( TranscriptURL, Transcript, TranscriptURL_origin, nil );
end
Transcript = sepc .. ' ' .. Transcript .. TranscriptFormat;
elseif utilities.is_set (TranscriptURL) then
Transcript = external_link( TranscriptURL, nil, TranscriptURL_origin, nil );
end
local Publisher;
if utilities.is_set (PublicationDate) then
PublicationDate = wrap_msg ('published', PublicationDate);
end
if utilities.is_set (PublisherName) then
if utilities.is_set (PublicationPlace) then
Publisher = sepc .. " " .. PublicationPlace .. ": " .. PublisherName .. PublicationDate;
else
Publisher = sepc .. " " .. PublisherName .. PublicationDate;
end
elseif utilities.is_set (PublicationPlace) then
Publisher= sepc .. " " .. PublicationPlace .. PublicationDate;
else
Publisher = PublicationDate;
end
-- Several of the above rely upon detecting this as nil, so do it last.
if (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical) or utilities.is_set (TransPeriodical)) then
if utilities.is_set (Title) or utilities.is_set (TitleNote) then
Periodical = sepc .. " " .. format_periodical (ScriptPeriodical, ScriptPeriodical_origin, Periodical, TransPeriodical, TransPeriodical_origin);
else
Periodical = format_periodical (ScriptPeriodical, ScriptPeriodical_origin, Periodical, TransPeriodical, TransPeriodical_origin);
end
end
local Language = A['Language'];
if utilities.is_set (Language) then
Language = language_parameter (Language); -- format, categories, name from ISO639-1, etc.
else
Language=''; -- language not specified so make sure this is an empty string;
--[[ TODO: need to extract the wrap_msg from language_parameter
so that we can solve parentheses bunching problem with Format/Language/TitleType
]]
end
--[[
Handle the oddity that is cite speech. This code overrides whatever may be the value assigned to TitleNote (through |department=) and forces it to be " (Speech)" so that
the annotation directly follows the |title= parameter value in the citation rather than the |event= parameter value (if provided).
]]
if "speech" == config.CitationClass then -- cite speech only
TitleNote = TitleType; -- move TitleType to TitleNote so that it renders ahead of |event=
TitleType = ''; -- and unset
if utilities.is_set (Periodical) then -- if Periodical, perhaps because of an included |website= or |journal= parameter
if utilities.is_set (Conference) then -- and if |event= is set
Conference = Conference .. sepc .. " "; -- then add appropriate punctuation to the end of the Conference variable before rendering
end
end
end
-- Piece all bits together at last. Here, all should be non-nil.
-- We build things this way because it is more efficient in LUA
-- not to keep reassigning to the same string variable over and over.
local tcommon;
local tcommon2; -- used for book cite when |contributor= is set
local pgtext = Position .. Sheet .. Sheets .. Page .. Pages .. At;
if utilities.in_array (config.CitationClass, {"book", "citation"}) and not utilities.is_set (Periodical) then -- special cases for book cites
if utilities.is_set (Contributors) then -- when we are citing foreword, preface, introduction, etc.
tcommon = safe_join ({Title, TitleNote}, sepc); -- author and other stuff will come after this and before tcommon2
tcommon2 = safe_join ({TitleType, Series, Language, Volume, Others, Edition, Publisher}, sepc);
else
tcommon = safe_join ({Title, TitleNote, TitleType, Series, Language, Volume, Others, Edition, Publisher}, sepc);
end
elseif 'map' == config.CitationClass then -- special cases for cite map
if utilities.is_set (Chapter) then -- map in a book; TitleType is part of Chapter
tcommon = safe_join ({Title, Edition, Scale, Series, Language, Cartography, Others, Publisher, Volume}, sepc);
elseif utilities.is_set (Periodical) then -- map in a periodical
tcommon = safe_join ({Title, TitleType, Periodical, Scale, Series, Language, Cartography, Others, Publisher, Volume}, sepc);
else -- a sheet or stand-alone map
tcommon = safe_join ({Title, TitleType, Edition, Scale, Series, Language, Cartography, Others, Publisher}, sepc);
end
elseif 'episode' == config.CitationClass then -- special case for cite episode
tcommon = safe_join ({Title, TitleNote, TitleType, Series, Language, Edition, Publisher}, sepc);
else -- all other CS1 templates
tcommon = safe_join ({Title, TitleNote, Conference, Periodical, TitleType, Series, Language, Volume, Others, Edition, Publisher, Agency}, sepc);
end
if #ID_list_t > 0 then
ID_list_t = safe_join( { sepc .. " ", table.concat( ID_list_t, sepc .. " " ), ID }, sepc );
else
ID_list_t = ID;
end
local Via = A['Via'];
Via = utilities.is_set (Via) and wrap_msg ('via', Via) or '';
local idcommon;
if 'audio-visual' == config.CitationClass or 'episode' == config.CitationClass then -- special case for cite AV media & cite episode position transcript
idcommon = safe_join( { ID_list_t, URL, Archived, Transcript, AccessDate, Via, Quote }, sepc );
else
idcommon = safe_join( { ID_list_t, URL, Archived, AccessDate, Via, Quote }, sepc );
end
local text;
-- local pgtext = Position .. Sheet .. Sheets .. Page .. Pages .. At;
local OrigDate = A['OrigDate'];
OrigDate = utilities.is_set (OrigDate) and wrap_msg ('origdate', OrigDate) or '';
if utilities.is_set (Date) then
if utilities.is_set (Authors) or utilities.is_set (Editors) then -- date follows authors or editors when authors not set
Date = " (" .. Date .. ")" .. OrigDate .. sepc .. " "; -- in parentheses
else -- neither of authors and editors set
if (string.sub(tcommon, -1, -1) == sepc) then -- if the last character of tcommon is sepc
Date = " " .. Date .. OrigDate; -- Date does not begin with sepc
else
Date = sepc .. " " .. Date .. OrigDate; -- Date begins with sepc
end
end
end
if utilities.is_set (Authors) then
if (not utilities.is_set (Date)) then -- when date is set it's in parentheses; no Authors termination
Authors = terminate_name_list (Authors, sepc); -- when no date, terminate with 0 or 1 sepc and a space
end
if utilities.is_set (Editors) then
local in_text = '';
local post_text;
if utilities.is_set (Chapter) and 0 == #c then
in_text = cfg.messages['in'] .. ' ';
in_text = cs2_lower(in_text); -- lowercase for cs2
end
if EditorCount <= 1 then
post_text = ' (' .. cfg.messages['editor'] .. ')'; -- be consistent with no-author, no-date case
else
post_text = ' (' .. cfg.messages['editors'] .. ')';
end
Editors = terminate_name_list (in_text .. Editors .. post_text, sepc); -- terminate with 0 or 1 sepc and a space
end
if utilities.is_set (Contributors) then -- book cite and we're citing the intro, preface, etc.
local by_text = sepc .. ' ' .. cfg.messages['by'] .. ' ';
by_text = cs2_lower(by_text); -- lowercase for cs2
Authors = by_text .. Authors; -- author follows title so tweak it here
if utilities.is_set (Editors) and utilities.is_set (Date) then -- when Editors make sure that Authors gets terminated
Authors = terminate_name_list (Authors, sepc); -- terminate with 0 or 1 sepc and a space
end
if (not utilities.is_set (Date)) then -- when date is set it's in parentheses; no Contributors termination
Contributors = terminate_name_list (Contributors, sepc); -- terminate with 0 or 1 sepc and a space
end
text = safe_join( {Contributors, Date, Chapter, tcommon, Authors, Place, Editors, tcommon2, pgtext, idcommon }, sepc );
else
text = safe_join( {Authors, Date, Chapter, Place, Editors, tcommon, pgtext, idcommon }, sepc );
end
elseif utilities.is_set (Editors) then
if utilities.is_set (Date) then
if EditorCount <= 1 then
Editors = Editors .. cfg.presentation['sep_name'] .. cfg.messages['editor'];
else
Editors = Editors .. cfg.presentation['sep_name'] .. cfg.messages['editors'];
end
else
if EditorCount <= 1 then
Editors = Editors .. " (" .. cfg.messages['editor'] .. ")" .. sepc .. " "
else
Editors = Editors .. " (" .. cfg.messages['editors'] .. ")" .. sepc .. " "
end
end
text = safe_join( {Editors, Date, Chapter, Place, tcommon, pgtext, idcommon}, sepc );
else
if utilities.in_array (config.CitationClass, {"journal", "citation"}) and utilities.is_set (Periodical) then
text = safe_join( {Chapter, Place, tcommon, pgtext, Date, idcommon}, sepc );
else
text = safe_join( {Chapter, Place, tcommon, Date, pgtext, idcommon}, sepc );
end
end
if utilities.is_set (PostScript) and PostScript ~= sepc then
text = safe_join( {text, sepc}, sepc ); -- Deals with italics, spaces, etc.
if '.' == sepc then -- remove final seperator if present
text = text:gsub ('%' .. sepc .. '$', ''); -- dot must be escaped here
else
text = mw.ustring.gsub (text, sepc .. '$', ''); -- using ustring for non-dot sepc (likely a non-Latin character)
end
end
text = safe_join( {text, PostScript}, sepc );
-- Now enclose the whole thing in a <cite> element
local options_t = {};
options_t.class = cite_class_attribute_make (config.CitationClass, Mode);
local Ref = is_valid_parameter_value (A['Ref'], A:ORIGIN('Ref'), cfg.keywords_lists['ref'], nil, true); -- nil when |ref=harv; A['Ref'] else
if 'none' ~= cfg.keywords_xlate[(Ref and Ref:lower()) or ''] then
local namelist_t = {}; -- holds selected contributor, author, editor name list
local year = first_set ({Year, anchor_year}, 2); -- Year first for legacy citations and for YMD dates that require disambiguation
if #c > 0 then -- if there is a contributor list
namelist_t = c; -- select it
elseif #a > 0 then -- or an author list
namelist_t = a;
elseif #e > 0 then -- or an editor list
namelist_t = e;
end
local citeref_id;
if #namelist_t > 0 then -- if there are names in namelist_t
citeref_id = make_citeref_id (namelist_t, year); -- go make the CITEREF anchor
if mw.uri.anchorEncode (citeref_id) == ((Ref and mw.uri.anchorEncode (Ref)) or '') then -- Ref may already be encoded (by {{sfnref}}) so citeref_id must be encoded before comparison
utilities.set_message ('maint_ref_duplicates_default');
end
else
citeref_id = ''; -- unset
end
options_t.id = Ref or citeref_id;
end
if string.len (text:gsub('%b<>', '')) <= 2 then -- remove html and html-like tags; then get length of what remains;
z.error_cats_t = {}; -- blank the categories list
z.error_msgs_t = {}; -- blank the error messages list
OCinSoutput = nil; -- blank the metadata string
text = ''; -- blank the the citation
utilities.set_message ('err_empty_citation'); -- set empty citation message and category
end
local render_t = {}; -- here we collect the final bits for concatenation into the rendered citation
if is_preview_mode and (0 ~= #z.error_msgs_t or 0 ~= #z.maint_cats_t) then -- when in preview mode
if not utilities.is_set (options_t.id) then -- and when this template has not created an anchor ID
math.randomseed (tonumber (mw.getLanguage ('en'):formatDate ('U')) * 10000 + os.clock() * 10000);
options_t.id = 'CITEREF_temp_preview_id_' .. math.random (100000, 999999); -- create temporary anchor ID for preview mode
end
end
if utilities.is_set (options_t.id) then -- here we wrap the rendered citation in <cite ...>...</cite> tags
options_t.id = mw.uri.anchorEncode(options_t.id); -- do this here; also used when adding preview warning messages
table.insert (render_t, utilities.substitute (cfg.presentation['cite-id'], {options_t.id, mw.text.nowiki(options_t.class), text})); -- when |ref= is set or when there is a namelist
else
table.insert (render_t, utilities.substitute (cfg.presentation['cite'], {mw.text.nowiki(options_t.class), text})); -- when |ref=none or when namelist_t empty and |ref= is missing or is empty
end
if OCinSoutput then -- blanked when citation is 'empty' so don't bother to add boilerplate metadata span
table.insert (render_t, utilities.substitute (cfg.presentation['ocins'], OCinSoutput)); -- format and append metadata to the citation
end
local template_name = cfg.citation_class_map_t[config.CitationClass];
local template_link = '[[Template:' .. template_name .. '|' .. template_name .. ']]';
local msg_prefix = '<code class="cs1-code">{{' .. template_link .. '}}</code>: ';
if 0 ~= #z.error_msgs_t then
mw.addWarning (utilities.substitute (cfg.messages.warning_msg_e, {template_link, options_t.id}));
table.insert (render_t, ' '); -- insert a space between citation and its error messages
table.sort (z.error_msgs_t); -- sort the error messages list; sorting includes wrapping <span> and <code> tags; hidden-error sorts ahead of visible-error
local hidden = true; -- presume that the only error messages emited by this template are hidden
for _, v in ipairs (z.error_msgs_t) do -- spin through the list of error messages
if v:find ('cs1-visible-error', 1, true) then -- look for the visible error class name
hidden = false; -- found one; so don't hide the error message prefix
break; -- and done because no need to look further
end
end
z.error_msgs_t[1] = table.concat ({utilities.error_comment (msg_prefix, hidden), z.error_msgs_t[1]}); -- add error message prefix to first error message to prevent extraneous punctuation
table.insert (render_t, table.concat (z.error_msgs_t, '; ')); -- make a big string of error messages and add it to the rendering
end
if 0 ~= #z.maint_cats_t then
mw.addWarning (utilities.substitute (cfg.messages.warning_msg_m, {template_link, options_t.id}));
table.sort (z.maint_cats_t); -- sort the maintenance messages list
local maint_msgs_t = {}; -- here we collect all of the maint messages
if 0 == #z.error_msgs_t then -- if no error messages
table.insert (maint_msgs_t, msg_prefix); -- insert message prefix in maint message livery
end
for _, v in ipairs( z.maint_cats_t ) do -- append maintenance categories
table.insert (maint_msgs_t, -- assemble new maint message and add it to the maint_msgs_t table
table.concat ({v, ' (', utilities.substitute (cfg.messages[':cat wikilink'], v), ')'})
);
end
table.insert (render_t, utilities.substitute (cfg.presentation['hidden-maint'], table.concat (maint_msgs_t, ' '))); -- wrap the group of maint messages with proper presentation and save
end
if not no_tracking_cats then
local sort_key;
local cat_wikilink = 'cat wikilink';
if cfg.enable_sort_keys then -- when namespace sort keys enabled
local namespace_number = mw.title.getCurrentTitle().namespace; -- get namespace number for this wikitext
sort_key = (0 ~= namespace_number and (cfg.name_space_sort_keys[namespace_number] or cfg.name_space_sort_keys.other)) or nil; -- get sort key character; nil for mainspace
cat_wikilink = (not sort_key and 'cat wikilink') or 'cat wikilink sk'; -- make <cfg.messages> key
end
for _, v in ipairs (z.error_cats_t) do -- append error categories
table.insert (render_t, utilities.substitute (cfg.messages[cat_wikilink], {v, sort_key}));
end
if cfg.id_limits_data_load_fail then -- boolean true when load failed
utilities.set_message ('maint_id_limit_load_fail'); -- done here because this maint cat emits no message
end
for _, v in ipairs (z.maint_cats_t) do -- append maintenance categories
table.insert (render_t, utilities.substitute (cfg.messages[cat_wikilink], {v, sort_key}));
end
for _, v in ipairs (z.prop_cats_t) do -- append properties categories
table.insert (render_t, utilities.substitute (cfg.messages['cat wikilink'], v)); -- no sort keys
end
end
return table.concat (render_t); -- make a big string and done
end
--[[--------------------------< V A L I D A T E >--------------------------------------------------------------
Looks for a parameter's name in one of several whitelists.
Parameters in the whitelist can have three values:
true - active, supported parameters
false - deprecated, supported parameters
nil - unsupported parameters
]]
local function validate (name, cite_class, empty)
local enum_name; -- parameter name with enumerator (if any) replaced with '#'
local state;
local function state_test () -- local function to do testing of state values; operates on the upvalues <state> and <name>
if true == state then return true; end -- valid actively supported parameter
if false == state then
if empty then return nil; end -- empty deprecated parameters are treated as unknowns
deprecated_parameter (name); -- parameter is deprecated but still supported
return true;
end
if 'tracked' == state then
local base_name = name:gsub ('%d', ''); -- strip enumerators from parameter names that have them to get the base name
utilities.add_prop_cat ('tracked-param', {base_name}, base_name); -- add a properties category; <base_name> modifies <key>
return true;
end
return nil;
end
if 'string' ~= type (name) or name:find ('#') then -- # is a cs1|2 reserved character so parameters with # not permitted; positional params are 'number' type; not supported
return nil;
end
-- replace enumerator digit(s) with # (|last25= becomes |last#=) (mw.ustring because non-Western 'local' digits)
enum_name = mw.ustring.gsub (name, '%d+$', '#'); -- where enumerator is last charaters in parameter name (these to protect |s2cid=)
enum_name = mw.ustring.gsub (enum_name, '%d+([%-l])', '#%1'); -- where enumerator is in the middle of the parameter name; |author#link= is the oddity
if 'document' == cite_class then -- special case for {{cite document}}
state = whitelist.document_parameters_t[enum_name]; -- this list holds enumerated and nonenumerated parameters
if true == state_test () then return true; end
return false;
end
if utilities.in_array (cite_class, whitelist.preprint_template_list_t) then -- limited parameter sets allowed for these templates
state = whitelist.limited_parameters_t[enum_name]; -- this list holds enumerated and nonenumerated parameters
if true == state_test () then return true; end
state = whitelist.preprint_arguments_t[cite_class][name]; -- look in the parameter-list for the template identified by cite_class
if true == state_test () then return true; end
return false; -- not supported because not found or name is set to nil
end -- end limited parameter-set templates
if utilities.in_array (cite_class, whitelist.unique_param_template_list_t) then -- template-specific parameters for templates that accept parameters from the basic argument list
state = whitelist.unique_arguments_t[cite_class][name]; -- look in the template-specific parameter-lists for the template identified by cite_class
if true == state_test () then return true; end
end -- if here, fall into general validation
state = whitelist.common_parameters_t[enum_name]; -- all other templates; all normal parameters allowed; this list holds enumerated and nonenumerated parameters
if true == state_test () then return true; end
return false; -- not supported because not found or name is set to nil
end
--[=[-------------------------< I N T E R _ W I K I _ C H E C K >----------------------------------------------
check <value> for inter-language interwiki-link markup. <prefix> must be a MediaWiki-recognized language
code. when these values have the form (without leading colon):
[[<prefix>:link|label]] return label as plain-text
[[<prefix>:link]] return <prefix>:link as plain-text
return value as is else
]=]
local function inter_wiki_check (parameter, value)
local prefix = value:match ('%[%[(%a+):'); -- get an interwiki prefix if one exists
local _;
if prefix and cfg.inter_wiki_map[prefix:lower()] then -- if prefix is in the map, needs preceding colon so
utilities.set_message ('err_bad_paramlink', parameter); -- emit an error message
_, value, _ = utilities.is_wikilink (value); -- extract label portion from wikilink
end
return value;
end
--[[--------------------------< M I S S I N G _ P I P E _ C H E C K >------------------------------------------
Look at the contents of a parameter. If the content has a string of characters and digits followed by an equal
sign, compare the alphanumeric string to the list of cs1|2 parameters. If found, then the string is possibly a
parameter that is missing its pipe. There are two tests made:
{{cite ... |title=Title access-date=2016-03-17}} -- the first parameter has a value and whitespace separates that value from the missing pipe parameter name
{{cite ... |title=access-date=2016-03-17}} -- the first parameter has no value (whitespace after the first = is trimmed by MediaWiki)
cs1|2 shares some parameter names with XML/HTML attributes: class=, title=, etc. To prevent false positives XML/HTML
tags are removed before the search.
If a missing pipe is detected, this function adds the missing pipe maintenance category.
]]
local function missing_pipe_check (parameter, value)
local capture;
value = value:gsub ('%b<>', ''); -- remove XML/HTML tags because attributes: class=, title=, etc.
capture = value:match ('%s+(%a[%w%-]+)%s*=') or value:match ('^(%a[%w%-]+)%s*='); -- find and categorize parameters with possible missing pipes
if capture and validate (capture) then -- if the capture is a valid parameter name
utilities.set_message ('err_missing_pipe', parameter);
end
end
--[[--------------------------< H A S _ E X T R A N E O U S _ P U N C T >--------------------------------------
look for extraneous terminal punctuation in most parameter values; parameters listed in skip table are not checked
]]
local function has_extraneous_punc (param, value)
if 'number' == type (param) then
return;
end
param = param:gsub ('%d+', '#'); -- enumerated name-list mask params allow terminal punct; normalize
if cfg.punct_skip[param] then
return; -- parameter name found in the skip table so done
end
if value:match ('[,;:]$') then
utilities.set_message ('maint_extra_punct'); -- has extraneous punctuation; add maint cat
end
if value:match ('^=') then -- sometimes an extraneous '=' character appears ...
utilities.set_message ('maint_extra_punct'); -- has extraneous punctuation; add maint cat
end
end
--[[--------------------------< H A S _ T W L _ U R L >--------------------------------------------------------
look for The Wikipedia Library urls in url-holding parameters. TWL urls are accessible only for readers who are
active extended confirmed Wikipedia editors. This function sets an error message when such urls are discovered
and when appropriate, sets the |<param>-url-access=subscription. returns nothing.
looks for: '.wikipedialibrary.idm.oclc.org'
]]
local function has_twl_url (url_params_t, cite_args_t)
local url_error_t = {}; -- sequence of url-holding parameters that have a TWL url
for param, value in pairs (url_params_t) do
if value:find ('%.wikipedialibrary%.idm%.oclc%.org') then -- has the TWL base url?
table.insert (url_error_t, param); -- add parameter name to the error list
end
end
if 0 ~= #url_error_t then -- non-zero when there are errors
table.sort (url_error_t); -- sor for error messaging
for i, param in ipairs (url_error_t) do
if cfg.url_access_map_t[param] then -- if <param> has a matching -access parameter
cite_args_t[cfg.url_access_map_t[param]] = cfg.keywords_xlate.subscription; -- set |<param>-url-access=subscription
end
url_error_t[i] = utilities.wrap_style ('parameter', param); -- make the parameter pretty for error message
end
utilities.set_message ('err_param_has_twl_url', {utilities.make_sep_list (#url_error_t, url_error_t)}); -- add this error message
end
end
--[[--------------------------< M I S C _ U R L _ D E T E C T >------------------------------------------------
look for miscellaneous urls that might be converted to, or are duplicates of, identifiers.
]]
local misc_url_patterns_t = {
'idm%.oclc%.org', -- but not wikipedialibrary.idm.oclc.org; has_twl_url()
'https?://citeseerx?%.ist%.psu%.edu/', -- simplified so we catch all citeseer and citeseerx urls except ~/document/... (the 'new' form)
}
local ignore_patterns_t = {
'wikipedialibrary.idm.oclc.org', -- handled by has_twl_url()
'https?://citeseerx?%.ist%.psu%.edu/document', -- 'new'-form citeseerx urls that mostly work; ignore for now
}
local function misc_url_detect (url_params_t)
local ignore_flag = false;
for _, value in pairs (url_params_t) do
for _, ignore_pattern in ipairs (ignore_patterns_t) do
if value:find (ignore_pattern) then -- matches this ignore_pattern?
ignore_flag = true; -- found a pattern to ignore
break; --
end
end
if not ignore_flag then -- if ignore_flag is set then don't bother to test this url <value>
for _, misc_pattern in ipairs (misc_url_patterns_t) do
if value:find (misc_pattern) then -- matches this misc_pattern?
utilities.set_message ('maint_misc_url'); -- found one; set the message
return; -- and done; no need to test other <value>s
end
end
end
ignore_flag = false; -- reset
end
end
--[[--------------------------< H A S _ E X T R A N E O U S _ U R L >------------------------------------------
look for extraneous url parameter values; parameters listed in skip table are not checked
]]
local function has_extraneous_url (non_url_param_t)
local url_error_t = {};
check_for_url (non_url_param_t, url_error_t); -- extraneous url check
if 0 ~= #url_error_t then -- non-zero when there are errors
table.sort (url_error_t);
utilities.set_message ('err_param_has_ext_link', {utilities.make_sep_list (#url_error_t, url_error_t)}); -- add this error message
end
end
--[[--------------------------< _ C I T A T I O N >------------------------------------------------------------
Module entry point
frame – from template call (citation()); may be nil when called from another module
args_t – table of all cs1|2 parameters in the template (the parent frame)
config_t – table of template-supplied parameter (the #invoke frame)
]]
local function _citation (frame, args_t, config_t) -- save a copy in case we need to display an error message in preview mode
if not frame then
frame = mw.getCurrentFrame(); -- if called from another module, get a frame for frame-provided functions
end
-- i18n: set the name that your wiki uses to identify sandbox subpages from sandbox template invoke (or can be set here)
local sandbox = ((config_t.SandboxPath and '' ~= config_t.SandboxPath) and config_t.SandboxPath) or '/sandbox'; -- sandbox path from {{#invoke:Citation/CS1/sandbox|citation|SandboxPath=/...}}
is_sandbox = nil ~= string.find (frame:getTitle(), sandbox, 1, true); -- is this invoke the sandbox module?
sandbox = is_sandbox and sandbox or ''; -- use i18n sandbox to load sandbox modules when this module is the sandox; live modules else
cfg = mw.loadData ('Module:Citation/CS1/Configuration' .. sandbox); -- load sandbox versions of support modules when {{#invoke:Citation/CS1/sandbox|...}}; live modules else
whitelist = mw.loadData ('Module:Citation/CS1/Whitelist' .. sandbox);
utilities = require ('Module:Citation/CS1/Utilities' .. sandbox);
validation = require ('Module:Citation/CS1/Date_validation' .. sandbox);
identifiers = require ('Module:Citation/CS1/Identifiers' .. sandbox);
metadata = require ('Module:Citation/CS1/COinS' .. sandbox);
utilities.set_selected_modules (cfg); -- so that functions in Utilities can see the selected cfg tables
identifiers.set_selected_modules (cfg, utilities); -- so that functions in Identifiers can see the selected cfg tables and selected Utilities module
validation.set_selected_modules (cfg, utilities); -- so that functions in Date validataion can see selected cfg tables and the selected Utilities module
metadata.set_selected_modules (cfg, utilities); -- so that functions in COinS can see the selected cfg tables and selected Utilities module
z = utilities.z; -- table of error and category tables in Module:Citation/CS1/Utilities
local cite_args_t = {}; -- because args_t is the parent (template) frame args (which cannot be modified); params and their values will be placed here
is_preview_mode = not utilities.is_set (frame:preprocess ('{{REVISIONID}}'));
local suggestions = {}; -- table where we store suggestions if we need to loadData them
local error_text; -- used as a flag
local capture; -- the single supported capture when matching unknown parameters using patterns
local empty_unknowns = {}; -- sequence table to hold empty unknown params for error message listing
for k, v in pairs (args_t) do -- get parameters from the parent (template) frame
v = mw.ustring.gsub (v, '^%s*(.-)%s*$', '%1'); -- trim leading/trailing whitespace; when v is only whitespace, becomes empty string
if v ~= '' then
if cfg.enum_needs_xlation and 'string' == type (k) then -- for wikis that set date_names['local_digits'] to non-western digits
k = mw.ustring.gsub (k, '%d', cfg.date_names.local_digits); -- translate 'local' digits enumerators to Western 0-9
end
if not validate( k, config_t.CitationClass ) then
if type (k) ~= 'string' then -- exclude empty numbered parameters
if v:match("%S+") ~= nil then
error_text = utilities.set_message ('err_text_ignored', {v});
end
elseif validate (k:lower(), config_t.CitationClass) then
error_text = utilities.set_message ('err_parameter_ignored_suggest', {k, k:lower()}); -- suggest the lowercase version of the parameter
else
if nil == suggestions.suggestions then -- if this table is nil then we need to load it
suggestions = mw.loadData ('Module:Citation/CS1/Suggestions' .. sandbox); --load sandbox version of suggestion module when {{#invoke:Citation/CS1/sandbox|...}}; live module else
end
for pattern, param in pairs (suggestions.patterns) do -- loop through the patterns to see if we can suggest a proper parameter
capture = k:match (pattern); -- the whole match if no capture in pattern else the capture if a match
if capture then -- if the pattern matches
param = utilities.substitute (param, capture); -- add the capture to the suggested parameter (typically the enumerator)
if validate (param, config_t.CitationClass) then -- validate the suggestion to make sure that the suggestion is supported by this template (necessary for limited parameter lists)
error_text = utilities.set_message ('err_parameter_ignored_suggest', {k, param}); -- set the suggestion error message
else
error_text = utilities.set_message ('err_parameter_ignored', {k}); -- suggested param not supported by this template
v = ''; -- unset
end
end
end
if not utilities.is_set (error_text) then -- couldn't match with a pattern, is there an explicit suggestion?
if (suggestions.suggestions[ k:lower() ] ~= nil) and validate (suggestions.suggestions[ k:lower() ], config_t.CitationClass) then
utilities.set_message ('err_parameter_ignored_suggest', {k, suggestions.suggestions[ k:lower() ]});
else
utilities.set_message ('err_parameter_ignored', {k});
v = ''; -- unset value assigned to unrecognized parameters (this for the limited parameter lists)
end
end
end
end
cite_args_t[k] = v; -- save this parameter and its value
elseif not utilities.is_set (v) then -- for empty parameters
if not validate (k, config_t.CitationClass, true) then -- is this empty parameter a valid parameter
k = ('' == k) and cfg.err_msg_supl.empty_str or k; -- when k is empty string (or was space(s) trimmed to empty string), replace with descriptive text
table.insert (empty_unknowns, utilities.wrap_style ('parameter', k)); -- format for error message and add to the list
end
end
end
if 0 ~= #empty_unknowns then -- create empty unknown error message
utilities.set_message ('err_param_unknown_empty', {
1 == #empty_unknowns and '' or 's',
utilities.make_sep_list (#empty_unknowns, empty_unknowns)
});
end
local non_url_param_t = {}; -- table of parameters and values that are not url-holding parameters
local url_param_t = {}; -- table of url-holding paramters and their values
for k, v in pairs (cite_args_t) do
if 'string' == type (k) then -- don't evaluate positional parameters
has_invisible_chars (k, v); -- look for invisible characters
end
has_extraneous_punc (k, v); -- look for extraneous terminal punctuation in parameter values
missing_pipe_check (k, v); -- do we think that there is a parameter that is missing a pipe?
cite_args_t[k] = inter_wiki_check (k, v); -- when language interwiki-linked parameter missing leading colon replace with wiki-link label
if 'string' == type (k) then -- when parameter k is not positional
if not cfg.url_skip[k] then -- and not in url skip table
non_url_param_t[k] = v; -- make a parameter/value list for extraneous url check
else -- and is in url skip table (a url-holding parameter)
url_param_t[k] = v; -- make a parameter/value list to check for values that are The Wikipedia Library url
end
end
end
if cfg.suppress_archive_today_urls then
has_archive_today_url (cite_args_t); -- look for and suppress archive.today urls and dependent parameters for all templates
end
has_extraneous_url (non_url_param_t); -- look for url in parameter values where a url does not belong
has_twl_url (url_param_t, cite_args_t); -- look for url-holding parameters that hold a The Wikipedia Library url
misc_url_detect (url_param_t); -- look for url-holding parameters that hold certain miscellaneous urls
return table.concat ({
frame:extensionTag ('templatestyles', '', {src='Module:Citation/CS1' .. sandbox .. '/styles.css'}),
citation0 (config_t, cite_args_t)
});
end
--[[--------------------------< C I T A T I O N >--------------------------------------------------------------
Template entry point
]]
local function citation (frame)
local config_t = {}; -- table to store parameters from the module {{#invoke:}}
local args_t = frame:getParent().args; -- get template's preset parameters
for k, v in pairs (frame.args) do -- get parameters from the {{#invoke}} frame
config_t[k] = v;
-- args_t[k] = v; -- crude debug support that allows us to render a citation from module {{#invoke:}}; skips parameter validation; TODO: keep?
end
return _citation (frame, args_t, config_t)
end
--[[--------------------------< E X P O R T E D F U N C T I O N S >------------------------------------------
]]
return {
citation = citation, -- template entry point
_citation = _citation, -- module entry point
}
mzkfjlhyhvftcb93lj0ik7zu7t1myhw
731562
731560
2026-11-07T11:27:33Z
Rafka Aditia
29183
Ngabolaykeun révisi [[Special:Diff/731560|731560]] ku [[Special:Contributions/Rafka Aditia|Rafka Aditia]] ([[User talk:Rafka Aditia|Obrolan]]): Mangaruhan seueur kaca kalayan kasalahan
731562
Scribunto
text/plain
require ('strict');
--[[--------------------------< F O R W A R D D E C L A R A T I O N S >--------------------------------------
each of these counts against the Lua upvalue limit
]]
local validation; -- functions in Module:Citation/CS1/Date_validation
local utilities; -- functions in Module:Citation/CS1/Utilities
local z = {}; -- table of tables in Module:Citation/CS1/Utilities
local identifiers; -- functions and tables in Module:Citation/CS1/Identifiers
local metadata; -- functions in Module:Citation/CS1/COinS
local cfg = {}; -- table of configuration tables that are defined in Module:Citation/CS1/Configuration
local whitelist = {}; -- table of tables listing valid template parameter names; defined in Module:Citation/CS1/Whitelist
--[[------------------< P A G E S C O P E V A R I A B L E S >---------------
declare variables here that have page-wide scope that are not brought in from
other modules; that are created here and used here
]]
local added_deprecated_cat; -- Boolean flag so that the category is added only once
local added_vanc_errs; -- Boolean flag so we only emit one Vancouver error / category
local added_generic_name_errs; -- Boolean flag so we only emit one generic name error / category and stop testing names once an error is encountered
local added_numeric_name_errs; -- Boolean flag so we only emit one numeric name error / category and stop testing names once an error is encountered
local added_numeric_name_maint; -- Boolean flag so we only emit one numeric name maint category and stop testing names once a category has been emitted
local is_preview_mode; -- true when article is in preview mode; false when using 'Preview page with this template' (previewing the module)
local is_sandbox; -- true when using sandbox modules to render citation
--[[--------------------------< F I R S T _ S E T >------------------------------------------------------------
Locates and returns the first set value in a table of values where the order established in the table,
left-to-right (or top-to-bottom), is the order in which the values are evaluated. Returns nil if none are set.
This version replaces the original 'for _, val in pairs do' and a similar version that used ipairs. With the pairs
version the order of evaluation could not be guaranteed. With the ipairs version, a nil value would terminate
the for-loop before it reached the actual end of the list.
]]
local function first_set (list, count)
local i = 1;
while i <= count do -- loop through all items in list
if utilities.is_set( list[i] ) then
return list[i]; -- return the first set list member
end
i = i + 1; -- point to next
end
end
--[[--------------------------< A D D _ V A N C _ E R R O R >----------------------------------------------------
Adds a single Vancouver system error message to the template's output regardless of how many error actually exist.
To prevent duplication, added_vanc_errs is nil until an error message is emitted.
added_vanc_errs is a Boolean declared in page scope variables above
]]
local function add_vanc_error (source, position)
if added_vanc_errs then return end
added_vanc_errs = true; -- note that we've added this category
utilities.set_message ('err_vancouver', {source, position});
end
--[[--------------------------< I S _ S C H E M E >------------------------------------------------------------
does this thing that purports to be a URI scheme seem to be a valid scheme? The scheme is checked to see if it
is in agreement with http://tools.ietf.org/html/std66#section-3.1 which says:
Scheme names consist of a sequence of characters beginning with a
letter and followed by any combination of letters, digits, plus
("+"), period ("."), or hyphen ("-").
returns true if it does, else false
]]
local function is_scheme (scheme)
return scheme and scheme:match ('^%a[%a%d%+%.%-]*:'); -- true if scheme is set and matches the pattern
end
--[=[-------------------------< I S _ D O M A I N _ N A M E >--------------------------------------------------
Does this thing that purports to be a domain name seem to be a valid domain name?
Syntax defined here: http://tools.ietf.org/html/rfc1034#section-3.5
BNF defined here: https://tools.ietf.org/html/rfc4234
Single character names are generally reserved; see https://tools.ietf.org/html/draft-ietf-dnsind-iana-dns-01#page-15;
see also [[Single-letter second-level domain]]
list of TLDs: https://www.iana.org/domains/root/db
RFC 952 (modified by RFC 1123) requires the first and last character of a hostname to be a letter or a digit. Between
the first and last characters the name may use letters, digits, and the hyphen.
Also allowed are IPv4 addresses. IPv6 not supported
domain is expected to be stripped of any path so that the last character in the last character of the TLD. tld
is two or more alpha characters. Any preceding '//' (from splitting a URL with a scheme) will be stripped
here. Perhaps not necessary but retained in case it is necessary for IPv4 dot decimal.
There are several tests:
the first character of the whole domain name including subdomains must be a letter or a digit
internationalized domain name (ASCII characters with .xn-- ASCII Compatible Encoding (ACE) prefix xn-- in the TLD) see https://tools.ietf.org/html/rfc3490
single-letter/digit second-level domains in the .org, .cash, and .today TLDs
q, x, and z SL domains in the .com TLD
i and q SL domains in the .net TLD
single-letter SL domains in the ccTLDs (where the ccTLD is two letters)
two-character SL domains in gTLDs (where the gTLD is two or more letters)
three-plus-character SL domains in gTLDs (where the gTLD is two or more letters)
IPv4 dot-decimal address format; TLD not allowed
returns true if domain appears to be a proper name and TLD or IPv4 address, else false
]=]
local function is_domain_name (domain)
if not domain then
return false; -- if not set, abandon
end
domain = domain:gsub ('^//', ''); -- strip '//' from domain name if present; done here so we only have to do it once
if not domain:match ('^[%w]') then -- first character must be letter or digit
return false;
end
if domain:match ('^%a+:') then -- hack to detect things that look like s:Page:Title where Page: is namespace at Wikisource
return false;
end
local patterns = { -- patterns that look like URLs
'%f[%w][%w][%w%-]+[%w]%.%a%a+$', -- three or more character hostname.hostname or hostname.tld
'%f[%w][%w][%w%-]+[%w]%.xn%-%-[%w]+$', -- internationalized domain name with ACE prefix
'%f[%a][qxz]%.com$', -- assigned one character .com hostname (x.com times out 2015-12-10)
'%f[%a][iq]%.net$', -- assigned one character .net hostname (q.net registered but not active 2015-12-10)
'%f[%w][%w]%.%a%a$', -- one character hostname and ccTLD (2 chars)
'%f[%w][%w][%w]%.%a%a+$', -- two character hostname and TLD
'^%d%d?%d?%.%d%d?%d?%.%d%d?%d?%.%d%d?%d?', -- IPv4 address
'[%a%d]+%:?' -- IPv6 address
}
for _, pattern in ipairs (patterns) do -- loop through the patterns list
if domain:match (pattern) then
return true; -- if a match then we think that this thing that purports to be a URL is a URL
end
end
for _, d in ipairs (cfg.single_letter_2nd_lvl_domains_t) do -- look for single letter second level domain names for these top level domains
if domain:match ('%f[%w][%w]%.' .. d) then
return true
end
end
return false; -- no matches, we don't know what this thing is
end
--[[--------------------------< I S _ U R L >------------------------------------------------------------------
returns true if the scheme and domain parts of a URL appear to be a valid URL; else false.
This function is the last step in the validation process. This function is separate because there are cases that
are not covered by split_url(), for example is_parameter_ext_wikilink() which is looking for bracketted external
wikilinks.
]]
local function is_url (scheme, domain)
if utilities.is_set (scheme) then -- if scheme is set check it and domain
return is_scheme (scheme) and is_domain_name (domain);
else
return is_domain_name (domain); -- scheme not set when URL is protocol-relative
end
end
--[[--------------------------< S P L I T _ U R L >------------------------------------------------------------
Split a URL into a scheme, authority indicator, and domain.
First remove Fully Qualified Domain Name terminator (a dot following TLD) (if any) and any path(/), query(?) or fragment(#).
If protocol-relative URL, return nil scheme and domain else return nil for both scheme and domain.
When not protocol-relative, get scheme, authority indicator, and domain. If there is an authority indicator (one
or more '/' characters immediately following the scheme's colon), make sure that there are only 2.
Any URL that does not have news: scheme must have authority indicator (//). TODO: are there other common schemes
like news: that don't use authority indicator?
Strip off any port and path;
]]
local function split_url (url_str)
local scheme, authority, domain;
url_str = url_str:gsub ('([%a%d])%.?[/%?#].*$', '%1'); -- strip FQDN terminator and path(/), query(?), fragment (#) (the capture prevents false replacement of '//')
if url_str:match ('^//%S*') then -- if there is what appears to be a protocol-relative URL
domain = url_str:match ('^//(%S*)')
elseif url_str:match ('%S-:/*%S+') then -- if there is what appears to be a scheme, optional authority indicator, and domain name
scheme, authority, domain = url_str:match ('(%S-:)(/*)(%S+)'); -- extract the scheme, authority indicator, and domain portions
if utilities.is_set (authority) then
authority = authority:gsub ('//', '', 1); -- replace place 1 pair of '/' with nothing;
if utilities.is_set(authority) then -- if anything left (1 or 3+ '/' where authority should be) then
return scheme; -- return scheme only making domain nil which will cause an error message
end
else
if not scheme:match ('^news:') then -- except for news:..., MediaWiki won't link URLs that do not have authority indicator; TODO: a better way to do this test?
return scheme; -- return scheme only making domain nil which will cause an error message
end
end
domain = domain:gsub ('(%a):%d+', '%1'); -- strip port number if present
end
return scheme, domain;
end
--[[--------------------------< L I N K _ P A R A M _ O K >---------------------------------------------------
checks the content of |title-link=, |series-link=, |author-link=, etc. for properly formatted content: no wikilinks, no URLs
Link parameters are to hold the title of a Wikipedia article, so none of the WP:TITLESPECIALCHARACTERS are allowed:
# < > [ ] | { } _
except the underscore which is used as a space in wiki URLs and # which is used for section links
returns false when the value contains any of these characters.
When there are no illegal characters, this function returns TRUE if value DOES NOT appear to be a valid URL (the
|<param>-link= parameter is ok); else false when value appears to be a valid URL (the |<param>-link= parameter is NOT ok).
]]
local function link_param_ok (value)
local scheme, domain;
if value:find ('[<>%[%]|{}]') then -- if any prohibited characters
return false;
end
scheme, domain = split_url (value); -- get scheme or nil and domain or nil from URL;
return not is_url (scheme, domain); -- return true if value DOES NOT appear to be a valid URL
end
--[[--------------------------< L I N K _ T I T L E _ O K >---------------------------------------------------
Use link_param_ok() to validate |<param>-link= value and its matching |<title>= value.
|<title>= may be wiki-linked but not when |<param>-link= has a value. This function emits an error message when
that condition exists
check <link> for inter-language interwiki-link prefix. prefix must be a MediaWiki-recognized language
code and must begin with a colon.
]]
local function link_title_ok (link, lorig, title, torig)
local orig;
if utilities.is_set (link) then -- don't bother if <param>-link doesn't have a value
if not link_param_ok (link) then -- check |<param>-link= markup
orig = lorig; -- identify the failing link parameter
elseif title:find ('%[%[') then -- check |title= for wikilink markup
orig = torig; -- identify the failing |title= parameter
elseif link:match ('^%a+:') then -- if the link is what looks like an interwiki
local prefix = link:match ('^(%a+):'):lower(); -- get the interwiki prefix
if cfg.inter_wiki_map[prefix] then -- if prefix is in the map, must have preceding colon
orig = lorig; -- flag as error
end
end
end
if utilities.is_set (orig) then
link = ''; -- unset
utilities.set_message ('err_bad_paramlink', orig); -- URL or wikilink in |title= with |title-link=;
end
return link; -- link if ok, empty string else
end
--[[--------------------------< C H E C K _ U R L >------------------------------------------------------------
Determines whether a URL string appears to be valid.
First we test for space characters. If any are found, return false. Then split the URL into scheme and domain
portions, or for protocol-relative (//example.com) URLs, just the domain. Use is_url() to validate the two
portions of the URL. If both are valid, or for protocol-relative if domain is valid, return true, else false.
Because it is different from a standard URL, and because this module used external_link() to make external links
that work for standard and news: links, we validate newsgroup names here. The specification for a newsgroup name
is at https://tools.ietf.org/html/rfc5536#section-3.1.4
]]
local function check_url( url_str )
if nil == url_str:match ("^%S+$") then -- if there are any spaces in |url=value it can't be a proper URL
return false;
end
local scheme, domain;
scheme, domain = split_url (url_str); -- get scheme or nil and domain or nil from URL;
if 'news:' == scheme then -- special case for newsgroups
return domain:match('^[%a%d%+%-_]+%.[%a%d%+%-_%.]*[%a%d%+%-_]$');
end
return is_url (scheme, domain); -- return true if value appears to be a valid URL
end
--[=[-------------------------< I S _ P A R A M E T E R _ E X T _ W I K I L I N K >----------------------------
Return true if a parameter value has a string that begins and ends with square brackets [ and ] and the first
non-space characters following the opening bracket appear to be a URL. The test will also find external wikilinks
that use protocol-relative URLs. Also finds bare URLs.
The frontier pattern prevents a match on interwiki-links which are similar to scheme:path URLs. The tests that
find bracketed URLs are required because the parameters that call this test (currently |title=, |chapter=, |work=,
and |publisher=) may have wikilinks and there are articles or redirects like '//Hus' so, while uncommon, |title=[[//Hus]]
is possible as might be [[en://Hus]].
]=]
local function is_parameter_ext_wikilink (value)
local scheme, domain;
if value:match ('%f[%[]%[%a%S*:%S+.*%]') then -- if ext. wikilink with scheme and domain: [xxxx://yyyyy.zzz]
scheme, domain = split_url (value:match ('%f[%[]%[(%a%S*:%S+).*%]'));
elseif value:match ('%f[%[]%[//%S+.*%]') then -- if protocol-relative ext. wikilink: [//yyyyy.zzz]
scheme, domain = split_url (value:match ('%f[%[]%[(//%S+).*%]'));
elseif value:match ('%a%S*:%S+') then -- if bare URL with scheme; may have leading or trailing plain text
scheme, domain = split_url (value:match ('(%a%S*:%S+)'));
elseif value:match ('^//%S+') or value:match ('%s//%S+') then -- if protocol-relative bare URL: //yyyyy.zzz; authority indicator (//) must be be preceded nothing or by whitespace
scheme, domain = split_url (value:match ('(//%S+)')); -- what is left should be the domain
else
return false; -- didn't find anything that is obviously a URL
end
return is_url (scheme, domain); -- return true if value appears to be a valid URL
end
--[[-------------------------< C H E C K _ F O R _ U R L >-----------------------------------------------------
loop through a list of parameters and their values. Look at the value and if it has an external link, emit an error message.
]]
local function check_for_url (parameter_list, error_list)
for k, v in pairs (parameter_list) do -- for each parameter in the list
if is_parameter_ext_wikilink (v) then -- look at the value; if there is a URL add an error message
table.insert (error_list, utilities.wrap_style ('parameter', k));
end
end
end
--[[--------------------------< S A F E _ F O R _ U R L >------------------------------------------------------
Escape sequences for content that will be used for URL descriptions
]]
local function safe_for_url( str )
if str:match( "%[%[.-%]%]" ) ~= nil then
utilities.set_message ('err_wikilink_in_url', {});
end
return str:gsub( '[%[%]\n]', {
['['] = '[',
[']'] = ']',
['\n'] = ' ' } );
end
--[[--------------------------< E X T E R N A L _ L I N K >----------------------------------------------------
Format an external link with error checking
]]
local function external_link (URL, label, source, access)
local err_msg = '';
local domain;
local path;
local base_url;
if not utilities.is_set (label) then
label = URL;
if utilities.is_set (source) then
utilities.set_message ('err_bare_url_missing_title', {utilities.wrap_style ('parameter', source)});
else
error (cfg.messages["bare_url_no_origin"]); -- programmer error; valid parameter name does not have matching meta-parameter
end
end
if not check_url (URL) then
utilities.set_message ('err_bad_url', {utilities.wrap_style ('parameter', source)});
end
domain, path = URL:match ('^([/%.%-%+:%a%d]+)([/%?#].*)$'); -- split the URL into scheme plus domain and path
if path then -- if there is a path portion
path = path:gsub ('[%[%]]', {['['] = '%5b', [']'] = '%5d'}); -- replace '[' and ']' with their percent-encoded values
URL = table.concat ({domain, path}); -- and reassemble
end
base_url = table.concat ({ "[", URL, " ", safe_for_url (label), "]" }); -- assemble a wiki-markup URL
if utilities.is_set (access) then -- access level (subscription, registration, limited)
base_url = utilities.substitute (cfg.presentation['ext-link-access-signal'], {cfg.presentation[access].class, cfg.presentation[access].title, base_url}); -- add the appropriate icon
end
return base_url;
end
--[[--------------------------< D E P R E C A T E D _ P A R A M E T E R >--------------------------------------
Categorize and emit an error message when the citation contains one or more deprecated parameters. The function includes the
offending parameter name to the error message. Only one error message is emitted regardless of the number of deprecated
parameters in the citation.
added_deprecated_cat is a Boolean declared in page scope variables above
]]
local function deprecated_parameter(name)
if not added_deprecated_cat then
added_deprecated_cat = true; -- note that we've added this category
utilities.set_message ('err_deprecated_params', {name}); -- add error message
end
end
--[=[-------------------------< K E R N _ Q U O T E S >--------------------------------------------------------
Apply kerning to open the space between the quote mark provided by the module and a leading or trailing quote
mark contained in a |title= or |chapter= parameter's value.
This function will positive kern either single or double quotes:
"'Unkerned title with leading and trailing single quote marks'"
" 'Kerned title with leading and trailing single quote marks' " (in real life the kerning isn't as wide as this example)
Double single quotes (italic or bold wiki-markup) are not kerned.
Replaces Unicode quote marks in plain text or in the label portion of a [[L|D]] style wikilink with typewriter
quote marks regardless of the need for kerning. Unicode quote marks are not replaced in simple [[D]] wikilinks.
Call this function for chapter titles, for website titles, etc.; not for book titles.
]=]
local function kern_quotes (str)
local cap = '';
local wl_type, label, link;
wl_type, label, link = utilities.is_wikilink (str); -- wl_type is: 0, no wl (text in label variable); 1, [[D]]; 2, [[L|D]]
if 1 == wl_type then -- [[D]] simple wikilink with or without quote marks
if mw.ustring.match (str, '%[%[[\"“”\'‘’].+[\"“”\'‘’]%]%]') then -- leading and trailing quote marks
str = utilities.substitute (cfg.presentation['kern-left'], str);
str = utilities.substitute (cfg.presentation['kern-right'], str);
elseif mw.ustring.match (str, '%[%[[\"“”\'‘’].+%]%]') then -- leading quote marks
str = utilities.substitute (cfg.presentation['kern-left'], str);
elseif mw.ustring.match (str, '%[%[.+[\"“”\'‘’]%]%]') then -- trailing quote marks
str = utilities.substitute (cfg.presentation['kern-right'], str);
end
else -- plain text or [[L|D]]; text in label variable
label = mw.ustring.gsub (label, '[“”]', '\"'); -- replace “” (U+201C & U+201D) with " (typewriter double quote mark)
label = mw.ustring.gsub (label, '[‘’]', '\''); -- replace ‘’ (U+2018 & U+2019) with ' (typewriter single quote mark)
cap = mw.ustring.match (label, "^([\"\'][^\'].+)"); -- match leading double or single quote but not doubled single quotes (italic markup)
if utilities.is_set (cap) then
label = utilities.substitute (cfg.presentation['kern-left'], cap);
end
cap = mw.ustring.match (label, "^(.+[^\'][\"\'])$") -- match trailing double or single quote but not doubled single quotes (italic markup)
if utilities.is_set (cap) then
label = utilities.substitute (cfg.presentation['kern-right'], cap);
end
if 2 == wl_type then
str = utilities.make_wikilink (link, label); -- reassemble the wikilink
else
str = label;
end
end
return str;
end
--[[--------------------------< F O R M A T _ S C R I P T _ V A L U E >----------------------------------------
|script-title= holds title parameters that are not written in Latin-based scripts: Chinese, Japanese, Arabic, Hebrew, etc. These scripts should
not be italicized and may be written right-to-left. The value supplied by |script-title= is concatenated onto Title after Title has been wrapped
in italic markup.
Regardless of language, all values provided by |script-title= are wrapped in <bdi>...</bdi> tags to isolate RTL languages from the English left to right.
|script-title= provides a unique feature. The value in |script-title= may be prefixed with a two-character ISO 639-1 language code and a colon:
|script-title=ja:*** *** (where * represents a Japanese character)
Spaces between the two-character code and the colon and the colon and the first script character are allowed:
|script-title=ja : *** ***
|script-title=ja: *** ***
|script-title=ja :*** ***
Spaces preceding the prefix are allowed: |script-title = ja:*** ***
The prefix is checked for validity. If it is a valid ISO 639-1 language code, the lang attribute (lang="ja") is added to the <bdi> tag so that browsers can
know the language the tag contains. This may help the browser render the script more correctly. If the prefix is invalid, the lang attribute
is not added. At this time there is no error message for this condition.
Supports |script-title=, |script-chapter=, |script-<periodical>=
]]
local function format_script_value (script_value, script_param)
local lang=''; -- initialize to empty string
local name;
if script_value:match('^%l%l%l?%s*:') then -- if first 3 or 4 non-space characters are script language prefix
lang = script_value:match('^(%l%l%l?)%s*:%s*%S.*'); -- get the language prefix or nil if there is no script
if not utilities.is_set (lang) then
utilities.set_message ('err_script_parameter', {script_param, cfg.err_msg_supl['missing title part']}); -- prefix without 'title'; add error message
return ''; -- script_value was just the prefix so return empty string
end
-- if we get this far we have prefix and script
name = cfg.lang_tag_remap[lang] or mw.language.fetchLanguageName( lang, cfg.this_wiki_code ); -- get language name so that we can use it to categorize
if utilities.is_set (name) then -- is prefix a proper ISO 639-1 language code?
script_value = script_value:gsub ('^%l+%s*:%s*', ''); -- strip prefix from script
-- is prefix one of these language codes?
if utilities.in_array (lang, cfg.script_lang_codes) then
utilities.add_prop_cat ('script', {name, lang})
else
utilities.set_message ('err_script_parameter', {script_param, cfg.err_msg_supl['unknown language code']}); -- unknown script-language; add error message
end
lang = ' lang="' .. lang .. '" '; -- convert prefix into a lang attribute
else
utilities.set_message ('err_script_parameter', {script_param, cfg.err_msg_supl['invalid language code']}); -- invalid language code; add error message
lang = ''; -- invalid so set lang to empty string
end
else
utilities.set_message ('err_script_parameter', {script_param, cfg.err_msg_supl['missing prefix']}); -- no language code prefix; add error message
end
script_value = utilities.substitute (cfg.presentation['bdi'], {lang, script_value}); -- isolate in case script is RTL
return script_value;
end
--[[--------------------------< S C R I P T _ C O N C A T E N A T E >------------------------------------------
Initially for |title= and |script-title=, this function concatenates those two parameter values after the script
value has been wrapped in <bdi> tags.
]]
local function script_concatenate (title, script, script_param)
if utilities.is_set (script) then
script = format_script_value (script, script_param); -- <bdi> tags, lang attribute, categorization, etc.; returns empty string on error
if utilities.is_set (script) then
title = title .. ' ' .. script; -- concatenate title and script title
end
end
return title;
end
--[[--------------------------< W R A P _ M S G >--------------------------------------------------------------
Applies additional message text to various parameter values. Supplied string is wrapped using a message_list
configuration taking one argument. Supports lower case text for {{citation}} templates. Additional text taken
from citation_config.messages - the reason this function is similar to but separate from wrap_style().
]]
local function wrap_msg (key, str, lower)
if not utilities.is_set ( str ) then
return "";
end
if true == lower then
local msg;
msg = cfg.messages[key]:lower(); -- set the message to lower case before
return utilities.substitute ( msg, str ); -- including template text
else
return utilities.substitute ( cfg.messages[key], str );
end
end
--[[----------------< W I K I S O U R C E _ U R L _ M A K E >-------------------
Makes a Wikisource URL from Wikisource interwiki-link. Returns the URL and appropriate
label; nil else.
str is the value assigned to |chapter= (or aliases) or |title= or |title-link=
]]
local function wikisource_url_make (str)
local wl_type, D, L;
local ws_url, ws_label;
local wikisource_prefix = table.concat ({'https://', cfg.this_wiki_code, '.wikisource.org/wiki/'});
wl_type, D, L = utilities.is_wikilink (str); -- wl_type is 0 (not a wikilink), 1 (simple wikilink), 2 (complex wikilink)
if 0 == wl_type then -- not a wikilink; might be from |title-link=
str = D:match ('^[Ww]ikisource:(.+)') or D:match ('^[Ss]:(.+)'); -- article title from interwiki link with long-form or short-form namespace
if utilities.is_set (str) then
ws_url = table.concat ({ -- build a Wikisource URL
wikisource_prefix, -- prefix
str, -- article title
});
ws_label = str; -- label for the URL
end
elseif 1 == wl_type then -- simple wikilink: [[Wikisource:ws article]]
str = D:match ('^[Ww]ikisource:(.+)') or D:match ('^[Ss]:(.+)'); -- article title from interwiki link with long-form or short-form namespace
if utilities.is_set (str) then
ws_url = table.concat ({ -- build a Wikisource URL
wikisource_prefix, -- prefix
str, -- article title
});
ws_label = str; -- label for the URL
end
elseif 2 == wl_type then -- non-so-simple wikilink: [[Wikisource:ws article|displayed text]] ([[L|D]])
str = L:match ('^[Ww]ikisource:(.+)') or L:match ('^[Ss]:(.+)'); -- article title from interwiki link with long-form or short-form namespace
if utilities.is_set (str) then
ws_label = D; -- get ws article name from display portion of interwiki link
ws_url = table.concat ({ -- build a Wikisource URL
wikisource_prefix, -- prefix
str, -- article title without namespace from link portion of wikilink
});
end
end
if ws_url then
ws_url = mw.uri.encode (ws_url, 'WIKI'); -- make a usable URL
ws_url = ws_url:gsub ('%%23', '#'); -- undo percent-encoding of fragment marker
end
return ws_url, ws_label, L or D; -- return proper URL or nil and a label or nil
end
--[[----------------< F O R M A T _ P E R I O D I C A L >-----------------------
Format the three periodical parameters: |script-<periodical>=, |<periodical>=,
and |trans-<periodical>= into a single Periodical meta-parameter.
]]
local function format_periodical (script_periodical, script_periodical_source, periodical, trans_periodical)
if not utilities.is_set (periodical) then
periodical = ''; -- to be safe for concatenation
else
periodical = utilities.wrap_style ('italic-title', periodical); -- style
end
periodical = script_concatenate (periodical, script_periodical, script_periodical_source); -- <bdi> tags, lang attribute, categorization, etc.; must be done after title is wrapped
if utilities.is_set (trans_periodical) then
trans_periodical = utilities.wrap_style ('trans-italic-title', trans_periodical);
if utilities.is_set (periodical) then
periodical = periodical .. ' ' .. trans_periodical;
else -- here when trans-periodical without periodical or script-periodical
periodical = trans_periodical;
utilities.set_message ('err_trans_missing_title', {'periodical'});
end
end
return periodical;
end
--[[------------------< F O R M A T _ C H A P T E R _ T I T L E >---------------
Format the four chapter parameters: |script-chapter=, |chapter=, |trans-chapter=,
and |chapter-url= into a single chapter meta- parameter (chapter_url_source used
for error messages).
]]
local function format_chapter_title (script_chapter, script_chapter_source, chapter, chapter_source, trans_chapter, trans_chapter_source, chapter_url, chapter_url_source, no_quotes, access)
local ws_url, ws_label, L = wikisource_url_make (chapter); -- make a wikisource URL and label from a wikisource interwiki link
if ws_url then
ws_label = ws_label:gsub ('_', ' '); -- replace underscore separators with space characters
chapter = ws_label;
end
if not utilities.is_set (chapter) then
chapter = ''; -- to be safe for concatenation
else
if false == no_quotes then
chapter = kern_quotes (chapter); -- if necessary, separate chapter title's leading and trailing quote marks from module provided quote marks
chapter = utilities.wrap_style ('quoted-title', chapter);
end
end
chapter = script_concatenate (chapter, script_chapter, script_chapter_source); -- <bdi> tags, lang attribute, categorization, etc.; must be done after title is wrapped
if utilities.is_set (chapter_url) then
chapter = external_link (chapter_url, chapter, chapter_url_source, access); -- adds bare_url_missing_title error if appropriate
elseif ws_url then
chapter = external_link (ws_url, chapter .. ' ', 'ws link in chapter'); -- adds bare_url_missing_title error if appropriate; space char to move icon away from chap text; TODO: better way to do this?
chapter = utilities.substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], L, chapter});
end
if utilities.is_set (trans_chapter) then
trans_chapter = utilities.wrap_style ('trans-quoted-title', trans_chapter);
if utilities.is_set (chapter) then
chapter = chapter .. ' ' .. trans_chapter;
else -- here when trans_chapter without chapter or script-chapter
chapter = trans_chapter;
chapter_source = trans_chapter_source:match ('trans%-?(.+)'); -- when no chapter, get matching name from trans-<param>
utilities.set_message ('err_trans_missing_title', {chapter_source});
end
end
return chapter;
end
--[[----------------< H A S _ I N V I S I B L E _ C H A R S >-------------------
This function searches a parameter's value for non-printable or invisible characters.
The search stops at the first match.
This function will detect the visible replacement character when it is part of the Wikisource.
Detects but ignores nowiki and math stripmarkers. Also detects other named stripmarkers
(gallery, math, pre, ref) and identifies them with a slightly different error message.
See also coins_cleanup().
Output of this function is an error message that identifies the character or the
Unicode group, or the stripmarker that was detected along with its position (or,
for multi-byte characters, the position of its first byte) in the parameter value.
]]
local function has_invisible_chars (param, v)
local position = ''; -- position of invisible char or starting position of stripmarker
local capture; -- used by stripmarker detection to hold name of the stripmarker
local stripmarker; -- boolean set true when a stripmarker is found
capture = string.match (v, '[%w%p ]*'); -- test for values that are simple ASCII text and bypass other tests if true
if capture == v then -- if same there are no Unicode characters
return;
end
for _, invisible_char in ipairs (cfg.invisible_chars) do
local char_name = invisible_char[1]; -- the character or group name
local pattern = invisible_char[2]; -- the pattern used to find it
position, _, capture = mw.ustring.find (v, pattern); -- see if the parameter value contains characters that match the pattern
if position and (cfg.invisible_defs.zwj == capture) then -- if we found a zero-width joiner character
if mw.ustring.find (v, cfg.indic_script) then -- it's ok if one of the Indic scripts
position = nil; -- unset position
elseif cfg.emoji_t[mw.ustring.codepoint (v, position+1)] then -- is zwj followed by a character listed in emoji{}?
position = nil; -- unset position
end
end
if position then
if 'nowiki' == capture or 'math' == capture or -- nowiki and math stripmarkers (not an error condition)
('templatestyles' == capture and utilities.in_array (param, {'id', 'quote'})) then -- templatestyles stripmarker allowed in these parameters
stripmarker = true; -- set a flag
elseif true == stripmarker and cfg.invisible_defs.del == capture then -- because stripmakers begin and end with the delete char, assume that we've found one end of a stripmarker
position = nil; -- unset
else
local err_msg;
if capture and not (cfg.invisible_defs.del == capture or cfg.invisible_defs.zwj == capture) then
err_msg = capture .. ' ' .. char_name;
else
err_msg = char_name .. ' ' .. 'character';
end
utilities.set_message ('err_invisible_char', {err_msg, utilities.wrap_style ('parameter', param), position}); -- add error message
return; -- and done with this parameter
end
end
end
end
--[[-------------------< A R G U M E N T _ W R A P P E R >----------------------
Argument wrapper. This function provides support for argument mapping defined
in the configuration file so that multiple names can be transparently aliased to
single internal variable.
]]
local function argument_wrapper ( args )
local origin = {};
return setmetatable({
ORIGIN = function ( self, k )
local dummy = self[k]; -- force the variable to be loaded.
return origin[k];
end
},
{
__index = function ( tbl, k )
if origin[k] ~= nil then
return nil;
end
local args, list, v = args, cfg.aliases[k];
if type( list ) == 'table' then
v, origin[k] = utilities.select_one ( args, list, 'err_redundant_parameters' );
if origin[k] == nil then
origin[k] = ''; -- Empty string, not nil
end
elseif list ~= nil then
v, origin[k] = args[list], list;
else
-- maybe let through instead of raising an error?
-- v, origin[k] = args[k], k;
error( cfg.messages['unknown_argument_map'] .. ': ' .. k);
end
-- Empty strings, not nil;
if v == nil then
v = '';
origin[k] = '';
end
tbl = rawset( tbl, k, v );
return v;
end,
});
end
--[[--------------------------< N O W R A P _ D A T E >-------------------------
When date is YYYY-MM-DD format wrap in nowrap span: <span ...>YYYY-MM-DD</span>.
When date is DD MMMM YYYY or is MMMM DD, YYYY then wrap in nowrap span:
<span ...>DD MMMM</span> YYYY or <span ...>MMMM DD,</span> YYYY
DOES NOT yet support MMMM YYYY or any of the date ranges.
]]
local function nowrap_date (date)
local cap = '';
local cap2 = '';
if date:match("^%d%d%d%d%-%d%d%-%d%d$") then
date = utilities.substitute (cfg.presentation['nowrap1'], date);
elseif date:match("^%a+%s*%d%d?,%s+%d%d%d%d$") or date:match ("^%d%d?%s*%a+%s+%d%d%d%d$") then
cap, cap2 = string.match (date, "^(.*)%s+(%d%d%d%d)$");
date = utilities.substitute (cfg.presentation['nowrap2'], {cap, cap2});
end
return date;
end
--[[--------------------------< S E T _ T I T L E T Y P E >---------------------
This function sets default title types (equivalent to the citation including
|type=<default value>) for those templates that have defaults. Also handles the
special case where it is desirable to omit the title type from the rendered citation
(|type=none).
]]
local function set_titletype (cite_class, title_type)
if utilities.is_set (title_type) then
if 'none' == cfg.keywords_xlate[title_type] then
title_type = ''; -- if |type=none then type parameter not displayed
end
return title_type; -- if |type= has been set to any other value use that value
end
return cfg.title_types [cite_class] or ''; -- set template's default title type; else empty string for concatenation
end
--[[--------------------------< S A F E _ J O I N >-----------------------------
Joins a sequence of strings together while checking for duplicate separation characters.
]]
local function safe_join( tbl, duplicate_char )
local f = {}; -- create a function table appropriate to type of 'duplicate character'
if 1 == #duplicate_char then -- for single byte ASCII characters use the string library functions
f.gsub = string.gsub
f.match = string.match
f.sub = string.sub
else -- for multi-byte characters use the ustring library functions
f.gsub = mw.ustring.gsub
f.match = mw.ustring.match
f.sub = mw.ustring.sub
end
local str = ''; -- the output string
local comp = ''; -- what does 'comp' mean?
local end_chr = '';
local trim;
for _, value in ipairs( tbl ) do
if value == nil then value = ''; end
if str == '' then -- if output string is empty
str = value; -- assign value to it (first time through the loop)
elseif value ~= '' then
if value:sub(1, 1) == '<' then -- special case of values enclosed in spans and other markup.
comp = value:gsub( "%b<>", "" ); -- remove HTML markup (<span>string</span> -> string)
else
comp = value;
end
-- typically duplicate_char is sepc
if f.sub(comp, 1, 1) == duplicate_char then -- is first character same as duplicate_char? why test first character?
-- Because individual string segments often (always?) begin with terminal punct for the
-- preceding segment: 'First element' .. 'sepc next element' .. etc.?
trim = false;
end_chr = f.sub(str, -1, -1); -- get the last character of the output string
-- str = str .. "<HERE(enchr=" .. end_chr .. ")" -- debug stuff?
if end_chr == duplicate_char then -- if same as separator
str = f.sub(str, 1, -2); -- remove it
elseif end_chr == "'" then -- if it might be wiki-markup
if f.sub(str, -3, -1) == duplicate_char .. "''" then -- if last three chars of str are sepc''
str = f.sub(str, 1, -4) .. "''"; -- remove them and add back ''
elseif f.sub(str, -5, -1) == duplicate_char .. "]]''" then -- if last five chars of str are sepc]]''
trim = true; -- why? why do this and next differently from previous?
elseif f.sub(str, -4, -1) == duplicate_char .. "]''" then -- if last four chars of str are sepc]''
trim = true; -- same question
end
elseif end_chr == "]" then -- if it might be wiki-markup
if f.sub(str, -3, -1) == duplicate_char .. "]]" then -- if last three chars of str are sepc]] wikilink
trim = true;
elseif f.sub(str, -3, -1) == duplicate_char .. '"]' then -- if last three chars of str are sepc"] quoted external link
trim = true;
elseif f.sub(str, -2, -1) == duplicate_char .. "]" then -- if last two chars of str are sepc] external link
trim = true;
elseif f.sub(str, -4, -1) == duplicate_char .. "'']" then -- normal case when |url=something & |title=Title.
trim = true;
end
elseif end_chr == " " then -- if last char of output string is a space
if f.sub(str, -2, -1) == duplicate_char .. " " then -- if last two chars of str are <sepc><space>
str = f.sub(str, 1, -3); -- remove them both
end
end
if trim then
if value ~= comp then -- value does not equal comp when value contains HTML markup
local dup2 = duplicate_char;
if f.match(dup2, "%A" ) then dup2 = "%" .. dup2; end -- if duplicate_char not a letter then escape it
value = f.gsub(value, "(%b<>)" .. dup2, "%1", 1 ) -- remove duplicate_char if it follows HTML markup
else
value = f.sub(value, 2, -1 ); -- remove duplicate_char when it is first character
end
end
end
str = str .. value; -- add it to the output string
end
end
return str;
end
--[[--------------------------< I S _ S U F F I X >-----------------------------
returns true if suffix is properly formed Jr, Sr, or ordinal in the range 1–9.
Puncutation not allowed.
]]
local function is_suffix (suffix)
if utilities.in_array (suffix, {'Jr', 'Sr', 'Jnr', 'Snr', '1st', '2nd', '3rd'}) or suffix:match ('^%dth$') then
return true;
end
return false;
end
--[[--------------------< I S _ G O O D _ V A N C _ N A M E >-------------------
For Vancouver style, author/editor names are supposed to be rendered in Latin
(read ASCII) characters. When a name uses characters that contain diacritical
marks, those characters are to be converted to the corresponding Latin
character. When a name is written using a non-Latin alphabet or logogram, that
name is to be transliterated into Latin characters. The module doesn't do this
so editors may/must.
This test allows |first= and |last= names to contain any of the letters defined
in the four Unicode Latin character sets
[http://www.unicode.org/charts/PDF/U0000.pdf C0 Controls and Basic Latin] 0041–005A, 0061–007A
[http://www.unicode.org/charts/PDF/U0080.pdf C1 Controls and Latin-1 Supplement] 00C0–00D6, 00D8–00F6, 00F8–00FF
[http://www.unicode.org/charts/PDF/U0100.pdf Latin Extended-A] 0100–017F
[http://www.unicode.org/charts/PDF/U0180.pdf Latin Extended-B] 0180–01BF, 01C4–024F
|lastn= also allowed to contain hyphens, spaces, and apostrophes.
(http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35029/)
|firstn= also allowed to contain hyphens, spaces, apostrophes, and periods
This original test:
if nil == mw.ustring.find (last, "^[A-Za-zÀ-ÖØ-öø-ƿDŽ-ɏ%-%s%']*$")
or nil == mw.ustring.find (first, "^[A-Za-zÀ-ÖØ-öø-ƿDŽ-ɏ%-%s%'%.]+[2-6%a]*$") then
was written outside of the code editor and pasted here because the code editor
gets confused between character insertion point and cursor position. The test has
been rewritten to use decimal character escape sequence for the individual bytes
of the Unicode characters so that it is not necessary to use an external editor
to maintain this code.
\195\128-\195\150 – À-Ö (U+00C0–U+00D6 – C0 controls)
\195\152-\195\182 – Ø-ö (U+00D8-U+00F6 – C0 controls)
\195\184-\198\191 – ø-ƿ (U+00F8-U+01BF – C0 controls, Latin extended A & B)
\199\132-\201\143 – DŽ-ɏ (U+01C4-U+024F – Latin extended B)
]]
local function is_good_vanc_name (last, first, suffix, position)
if not suffix then
if first:find ('[,%s]') then -- when there is a space or comma, might be first name/initials + generational suffix
first = first:match ('(.-)[,%s]+'); -- get name/initials
suffix = first:match ('[,%s]+(.+)$'); -- get generational suffix
end
end
if utilities.is_set (suffix) then
if not is_suffix (suffix) then
add_vanc_error (cfg.err_msg_supl.suffix, position);
return false; -- not a name with an appropriate suffix
end
end
if nil == mw.ustring.find (last, "^[A-Za-z\195\128-\195\150\195\152-\195\182\195\184-\198\191\199\132-\201\143\225\184\128-\225\187\191%-%s%']*$") or
nil == mw.ustring.find (first, "^[A-Za-z\195\128-\195\150\195\152-\195\182\195\184-\198\191\199\132-\201\143\225\184\128-\225\187\191%-%s%'%.]*$") then
add_vanc_error (cfg.err_msg_supl['non-Latin char'], position);
return false; -- not a string of Latin characters; Vancouver requires Romanization
end;
return true;
end
--[[--------------------------< R E D U C E _ T O _ I N I T I A L S >------------------------------------------
Attempts to convert names to initials in support of |name-list-style=vanc.
Names in |firstn= may be separated by spaces or hyphens, or for initials, a period.
See http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35062/.
Vancouver style requires family rank designations (Jr, II, III, etc.) to be rendered
as Jr, 2nd, 3rd, etc. See http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35085/.
This code only accepts and understands generational suffix in the Vancouver format
because Roman numerals look like, and can be mistaken for, initials.
This function uses ustring functions because firstname initials may be any of the
Unicode Latin characters accepted by is_good_vanc_name ().
]]
local function reduce_to_initials (first, position)
if first:find (',', 1, true) then
return first; -- commas not allowed; abandon
end
local name, suffix = mw.ustring.match (first, "^(%u+) ([%dJS][%drndth]+)$");
if not name then -- if not initials and a suffix
name = mw.ustring.match (first, "^(%u+)$"); -- is it just initials?
end
if name then -- if first is initials with or without suffix
if 3 > mw.ustring.len (name) then -- if one or two initials
if suffix then -- if there is a suffix
if is_suffix (suffix) then -- is it legitimate?
return first; -- one or two initials and a valid suffix so nothing to do
else
add_vanc_error (cfg.err_msg_supl.suffix, position); -- one or two initials with invalid suffix so error message
return first; -- and return first unmolested
end
else
return first; -- one or two initials without suffix; nothing to do
end
end
end -- if here then name has 3 or more uppercase letters so treat them as a word
local initials_t, names_t = {}, {}; -- tables to hold name parts and initials
local i = 1; -- counter for number of initials
names_t = mw.text.split (first, '[%s%-]+'); -- split into a sequence of names and possible suffix
while names_t[i] do -- loop through the sequence
if 1 < i and names_t[i]:match ('[%dJS][%drndth]+%.?$') then -- if not the first name, and looks like a suffix (may have trailing dot)
names_t[i] = names_t[i]:gsub ('%.', ''); -- remove terminal dot if present
if is_suffix (names_t[i]) then -- if a legitimate suffix
table.insert (initials_t, ' ' .. names_t[i]); -- add a separator space, insert at end of initials sequence
break; -- and done because suffix must fall at the end of a name
end -- no error message if not a suffix; possibly because of Romanization
end
if 3 > i then
table.insert (initials_t, mw.ustring.sub (names_t[i], 1, 1)); -- insert the initial at end of initials sequence
end
i = i + 1; -- bump the counter
end
return table.concat (initials_t); -- Vancouver format does not include spaces.
end
--[[--------------------------< I N T E R W I K I _ P R E F I X E N _ G E T >----------------------------------
extract interwiki prefixen from <value>. Returns two one or two values:
false – no prefixen
nil – prefix exists but not recognized
project prefix, language prefix – when value has either of:
:<project>:<language>:<article>
:<language>:<project>:<article>
project prefix, nil – when <value> has only a known single-letter prefix
nil, language prefix – when <value> has only a known language prefix
accepts single-letter project prefixen: 'd' (wikidata), 's' (wikisource), and 'w' (wikipedia) prefixes; at this
writing, the other single-letter prefixen (b (wikibook), c (commons), m (meta), n (wikinews), q (wikiquote), and
v (wikiversity)) are not supported.
]]
local function interwiki_prefixen_get (value, is_link)
if not value:find (':%l+:') then -- if no prefix
return false; -- abandon; boolean here to distinguish from nil fail returns later
end
local prefix_patterns_linked_t = { -- sequence of valid interwiki and inter project prefixen
'^%[%[:([dsw]):(%l%l+):', -- wikilinked; project and language prefixes
'^%[%[:(%l%l+):([dsw]):', -- wikilinked; language and project prefixes
'^%[%[:([dsw]):', -- wikilinked; project prefix
'^%[%[:(%l%l+):', -- wikilinked; language prefix
}
local prefix_patterns_unlinked_t = { -- sequence of valid interwiki and inter project prefixen
'^:([dsw]):(%l%l+):', -- project and language prefixes
'^:(%l%l+):([dsw]):', -- language and project prefixes
'^:([dsw]):', -- project prefix
'^:(%l%l+):', -- language prefix
}
local cap1, cap2;
for _, pattern in ipairs ((is_link and prefix_patterns_linked_t) or prefix_patterns_unlinked_t) do
cap1, cap2 = value:match (pattern);
if cap1 then
break; -- found a match so stop looking
end
end
if cap1 and cap2 then -- when both then :project:language: or :language:project: (both forms allowed)
if 1 == #cap1 then -- length == 1 then :project:language:
if cfg.inter_wiki_map[cap2] then -- is language prefix in the interwiki map?
return cap1, cap2; -- return interwiki project and interwiki language
end
else -- here when :language:project:
if cfg.inter_wiki_map[cap1] then -- is language prefix in the interwiki map?
return cap2, cap1; -- return interwiki project and interwiki language
end
end
return nil; -- unknown interwiki language
elseif not (cap1 or cap2) then -- both are nil?
return nil; -- we got something that looks like a project prefix but isn't; return fail
elseif 1 == #cap1 then -- here when one capture
return cap1, nil; -- length is 1 so return project, nil language
else -- here when one capture and its length it more than 1
if cfg.inter_wiki_map[cap1] then -- is language prefix in the interwiki map?
return nil, cap1; -- return nil project, language
end
end
end
--[[--------------------------< L I S T _ P E O P L E >--------------------------
Formats a list of people (authors, contributors, editors, interviewers, translators)
names in the list will be linked when
|<name>-link= has a value
|<name>-mask- does NOT have a value; masked names are presumed to have been
rendered previously so should have been linked there
when |<name>-mask=0, the associated name is not rendered
]]
local function list_people (control, people, etal)
local sep;
local namesep;
local format = control.format;
local maximum = control.maximum;
local name_list = {};
if 'vanc' == format then -- Vancouver-like name styling?
sep = cfg.presentation['sep_nl_vanc']; -- name-list separator between names is a comma
namesep = cfg.presentation['sep_name_vanc']; -- last/first separator is a space
else
sep = cfg.presentation['sep_nl']; -- name-list separator between names is a semicolon
namesep = cfg.presentation['sep_name']; -- last/first separator is <comma><space>
end
if sep:sub (-1, -1) ~= " " then sep = sep .. " " end
if utilities.is_set (maximum) and maximum < 1 then return "", 0; end -- returned 0 is for EditorCount; not used for other names
for i, person in ipairs (people) do
if utilities.is_set (person.last) then
local mask = person.mask;
local one;
local sep_one = sep;
if utilities.is_set (maximum) and i > maximum then
etal = true;
break;
end
if mask then
local n = tonumber (mask); -- convert to a number if it can be converted; nil else
if n then
one = 0 ~= n and string.rep("—", n) or nil; -- make a string of (n > 0) mdashes, nil else, to replace name
person.link = nil; -- don't create link to name if name is replaces with mdash string or has been set nil
else
one = mask; -- replace name with mask text (must include name-list separator)
sep_one = " "; -- modify name-list separator
end
else
one = person.last; -- get surname
local first = person.first -- get given name
if utilities.is_set (first) then
if ("vanc" == format) then -- if Vancouver format
one = one:gsub ('%.', ''); -- remove periods from surnames (http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35029/)
if not person.corporate and is_good_vanc_name (one, first, nil, i) then -- and name is all Latin characters; corporate authors not tested
first = reduce_to_initials (first, i); -- attempt to convert first name(s) to initials
end
end
one = one .. namesep .. first;
end
end
if utilities.is_set (person.link) then
one = utilities.make_wikilink (person.link, one); -- link author/editor
end
if one then -- if <one> has a value (name, mdash replacement, or mask text replacement)
local proj, tag = interwiki_prefixen_get (one, true); -- get the interwiki prefixen if present
if 'w' == proj and ('Wikipedia' == mw.site.namespaces.Project['name']) then
proj = nil; -- for stuff like :w:de:<article>, :w is unnecessary TODO: maint cat?
end
if proj then
local proj_name = ({['d'] = 'Wikidata', ['s'] = 'Wikisource', ['w'] = 'Wikipedia'})[proj]; -- :w (wikipedia) for linking from a non-wikipedia project
if proj_name then
one = one .. utilities.wrap_style ('interproj', proj_name); -- add resized leading space, brackets, static text, language name
utilities.add_prop_cat ('interproj-linked-name', proj); -- categorize it; <proj> is sort key
tag = nil; -- unset; don't do both project and language
end
end
if tag == cfg.this_wiki_code then
tag = nil; -- stuff like :en:<article> at en.wiki is pointless TODO: maint cat?
end
if tag then
local lang = cfg.lang_tag_remap[tag] or cfg.mw_languages_by_tag_t[tag];
if lang then -- error messaging done in extract_names() where we know parameter names
one = one .. utilities.wrap_style ('interwiki', lang); -- add resized leading space, brackets, static text, language name
utilities.add_prop_cat ('interwiki-linked-name', tag); -- categorize it; <tag> is sort key
end
end
table.insert (name_list, one); -- add it to the list of names
table.insert (name_list, sep_one); -- add the proper name-list separator
end
end
end
local count = #name_list / 2; -- (number of names + number of separators) divided by 2
if 0 < count then
if 1 < count and not etal then
if 'amp' == format then
name_list[#name_list-2] = " & "; -- replace last separator with ampersand text
elseif 'and' == format then
if 2 == count then
name_list[#name_list-2] = cfg.presentation.sep_nl_and; -- replace last separator with 'and' text
else
name_list[#name_list-2] = cfg.presentation.sep_nl_end; -- replace last separator with '(sep) and' text
end
end
end
name_list[#name_list] = nil; -- erase the last separator
end
local result = table.concat (name_list); -- construct list
if etal and utilities.is_set (result) then -- etal may be set by |display-authors=etal but we might not have a last-first list
result = result .. sep .. cfg.messages['et al']; -- we've got a last-first list and etal so add et al.
end
return result, count; -- return name-list string and count of number of names (count used for editor names only)
end
--[[--------------------< M A K E _ C I T E R E F _ I D >-----------------------
Generates a CITEREF anchor ID if we have at least one name or a date. Otherwise
returns an empty string.
namelist is one of the contributor-, author-, or editor-name lists chosen in that
order. year is Year or anchor_year.
]]
local function make_citeref_id (namelist, year)
local names={}; -- a table for the one to four names and year
for i,v in ipairs (namelist) do -- loop through the list and take up to the first four last names
names[i] = v.last
if i == 4 then break end -- if four then done
end
table.insert (names, year); -- add the year at the end
local id = table.concat(names); -- concatenate names and year for CITEREF id
if utilities.is_set (id) then -- if concatenation is not an empty string
return "CITEREF" .. id; -- add the CITEREF portion
else
return ''; -- return an empty string; no reason to include CITEREF id in this citation
end
end
--[[--------------------------< C I T E _ C L A S S _A T T R I B U T E _M A K E >------------------------------
construct <cite> tag class attribute for this citation.
<cite_class> – config.CitationClass from calling template
<mode> – value from |mode= parameter
]]
local function cite_class_attribute_make (cite_class, mode)
local class_t = {};
table.insert (class_t, 'citation'); -- required for blue highlight
if 'citation' ~= cite_class then
table.insert (class_t, cite_class); -- identify this template for user css
table.insert (class_t, utilities.is_set (mode) and mode or 'cs1'); -- identify the citation style for user css or javascript
else
table.insert (class_t, utilities.is_set (mode) and mode or 'cs2'); -- identify the citation style for user css or javascript
end
for _, prop_key in ipairs (z.prop_keys_t) do
table.insert (class_t, prop_key); -- identify various properties for user css or javascript
end
return table.concat (class_t, ' '); -- make a big string and done
end
--[[---------------------< N A M E _ H A S _ E T A L >--------------------------
Evaluates the content of name parameters (author, editor, etc.) for variations on
the theme of et al. If found, the et al. is removed, a flag is set to true and
the function returns the modified name and the flag.
This function never sets the flag to false but returns its previous state because
it may have been set by previous passes through this function or by the associated
|display-<names>=etal parameter
]]
local function name_has_etal (name, etal, nocat, param)
if utilities.is_set (name) then -- name can be nil in which case just return
local patterns = cfg.et_al_patterns; -- get patterns from configuration
for _, pattern in ipairs (patterns) do -- loop through all of the patterns
if name:match (pattern) then -- if this 'et al' pattern is found in name
name = name:gsub (pattern, ''); -- remove the offending text
etal = true; -- set flag (may have been set previously here or by |display-<names>=etal)
if not nocat then -- no categorization for |vauthors=
utilities.set_message ('err_etal', {param}); -- and set an error if not added
end
end
end
end
return name, etal;
end
--[[---------------------< N A M E _ I S _ N U M E R I C >----------------------
Add an error message and category when <name> parameter value does not contain letters.
Add a maintenance category when <name> parameter value has numeric characters mixed with characters that are
not numeric characters; could be letters and/or punctuation characters.
This function will only emit one error and one maint message for the current template. Does not emit both error
and maint messages/categories for the same parameter value.
returns nothing
]]
local function name_is_numeric (name, name_alias, list_name)
local patterns = {
'^%D+%d', -- <name> must have digits preceded by other characters
'^%D*%d+%D+', -- <name> must have digits followed by other characters
}
if not added_numeric_name_errs and mw.ustring.match (name, '^[%A]+$') then -- if we have not already set an error message and <name> does not have any alpha characters
utilities.set_message ('err_numeric_names', name_alias); -- add an error message
added_numeric_name_errs = true; -- set the flag so we emit only one error message
return; -- when here no point in further testing; abandon
end
if not added_numeric_name_maint then -- if we have already set a maint message
for _, pattern in ipairs (patterns) do -- spin through list of patterns
if mw.ustring.match (name, pattern) then -- digits preceded or followed by anything but digits; %D+ includes punctuation
utilities.set_message ('maint_numeric_names', cfg.special_case_translation [list_name]); -- add a maint cat for this template
added_numeric_name_maint = true; -- set the flag so we emit only one maint message
return; -- when here no point in further testing; abandon
end
end
end
end
--[[-----------------< N A M E _ H A S _ M U L T _ N A M E S >------------------
Evaluates the content of last/surname (authors etc.) parameters for multiple names.
Multiple names are indicated if there is more than one comma or any "unescaped"
semicolons. Escaped semicolons are ones used as part of selected HTML entities.
If the condition is met, the function adds the multiple name maintenance category.
Same test for first except that commas should not appear in given names (MOS:JR says
that the generational suffix does not take a separator character). Titles, degrees,
postnominals, affiliations, all normally comma separated don't belong in a citation.
<name> – name parameter value
<list_name> – AuthorList, EditorList, etc
<limit> – number of allowed commas; 1 (default) for surnames; 0 for given names
returns nothing
]]
local function name_has_mult_names (name, list_name, limit)
local _, commas, semicolons, nbsps;
limit = limit and limit or 1;
if utilities.is_set (name) then
_, commas = name:gsub (',', ''); -- count the number of commas
_, semicolons = name:gsub (';', ''); -- count the number of semicolons
-- nbsps probably should be its own separate count rather than merged in
-- some way with semicolons because Lua patterns do not support the
-- grouping operator that regex does, which means there is no way to add
-- more entities to escape except by adding more counts with the new
-- entities
_, nbsps = name:gsub (' ',''); -- count nbsps
-- There is exactly 1 semicolon per entity, so subtract nbsps
-- from semicolons to 'escape' them. If additional entities are added,
-- they also can be subtracted.
if limit < commas or 0 < (semicolons - nbsps) then
utilities.set_message ('maint_mult_names', cfg.special_case_translation [list_name]); -- add a maint message
end
end
end
--[=[-------------------------< I S _ G E N E R I C >----------------------------------------------------------
Compares values assigned to various parameters according to the string provided as <item> in the function call.
<item> can have on of two values:
'generic_names' – for name-holding parameters: |last=, |first=, |editor-last=, etc
'generic_titles' – for |title=
There are two types of generic tests. The 'accept' tests look for a pattern that should not be rejected by the
'reject' test. For example,
|author=[[John Smith (author)|Smith, John]]
would be rejected by the 'author' reject test. But piped wikilinks with 'author' disambiguation should not be
rejected so the 'accept' test prevents that from happening. Accept tests are always performed before reject
tests.
Each of the 'accept' and 'reject' sequence tables hold tables for en.wiki (['en']) and local.wiki (['local'])
that each can hold a test sequence table The sequence table holds, at index [1], a test pattern, and, at index
[2], a boolean control value. The control value tells string.find() or mw.ustring.find() to do plain-text search (true)
or a pattern search (false). The intent of all this complexity is to make these searches as fast as possible so
that we don't run out of processing time on very large articles.
Returns
true when a reject test finds the pattern or string
false when an accept test finds the pattern or string
nil else
]=]
local function is_generic (item, value, wiki)
local test_val;
local str_lower = { -- use string.lower() for en.wiki (['en']) and use mw.ustring.lower() or local.wiki (['local'])
['en'] = string.lower,
['local'] = mw.ustring.lower,
}
local str_find = { -- use string.find() for en.wiki (['en']) and use mw.ustring.find() or local.wiki (['local'])
['en'] = string.find,
['local'] = mw.ustring.find,
}
local function test (val, test_t, wiki) -- local function to do the testing; <wiki> selects lower() and find() functions
val = test_t[2] and str_lower[wiki](value) or val; -- when <test_t[2]> set to 'true', plaintext search using lowercase value
return str_find[wiki] (val, test_t[1], 1, test_t[2]); -- return nil when not found or matched
end
local test_types_t = {'accept', 'reject'}; -- test accept patterns first, then reject patterns
local wikis_t = {'en', 'local'}; -- do tests for each of these keys; en.wiki first, local.wiki second
for _, test_type in ipairs (test_types_t) do -- for each test type
for _, generic_value in pairs (cfg.special_case_translation[item][test_type]) do -- spin through the list of generic value fragments to accept or reject
for _, wiki in ipairs (wikis_t) do
if generic_value[wiki] then
if test (value, generic_value[wiki], wiki) then -- go do the test
return ('reject' == test_type); -- param value rejected, return true; false else
end
end
end
end
end
end
--[[--------------------------< N A M E _ I S _ G E N E R I C >------------------------------------------------
calls is_generic() to determine if <name> is a 'generic name' listed in cfg.generic_names; <name_alias> is the
parameter name used in error messaging
]]
local function name_is_generic (name, name_alias)
if not added_generic_name_errs and is_generic ('generic_names', name) then
utilities.set_message ('err_generic_name', name_alias); -- set an error message
added_generic_name_errs = true;
end
end
--[[--------------------------< N A M E _ C H E C K S >--------------------------------------------------------
This function calls various name checking functions used to validate the content of the various name-holding parameters.
]]
local function name_checks (last, first, list_name, last_alias, first_alias)
local accept_name;
if utilities.is_set (last) then
last, accept_name = utilities.has_accept_as_written (last); -- remove accept-this-as-written markup when it wraps all of <last>
if not accept_name then -- <last> not wrapped in accept-as-written markup
name_has_mult_names (last, list_name); -- check for multiple names in the parameter
name_is_numeric (last, last_alias, list_name); -- check for names that have no letters or are a mix of digits and other characters
name_is_generic (last, last_alias); -- check for names found in the generic names list
end
end
if utilities.is_set (first) then
first, accept_name = utilities.has_accept_as_written (first); -- remove accept-this-as-written markup when it wraps all of <first>
if not accept_name then -- <first> not wrapped in accept-as-written markup
name_has_mult_names (first, list_name, 0); -- check for multiple names in the parameter; 0 is number of allowed commas in a given name
name_is_numeric (first, first_alias, list_name); -- check for names that have no letters or are a mix of digits and other characters
name_is_generic (first, first_alias); -- check for names found in the generic names list
end
local wl_type, D = utilities.is_wikilink (first);
if 0 ~= wl_type then
first = D;
utilities.set_message ('err_bad_paramlink', first_alias);
end
end
return last, first; -- done
end
--[[----------------------< E X T R A C T _ N A M E S >-------------------------
Gets name list from the input arguments
Searches through args in sequential order to find |lastn= and |firstn= parameters
(or their aliases), and their matching link and mask parameters. Stops searching
when both |lastn= and |firstn= are not found in args after two sequential attempts:
found |last1=, |last2=, and |last3= but doesn't find |last4= and |last5= then the
search is done.
This function emits an error message when there is a |firstn= without a matching
|lastn=. When there are 'holes' in the list of last names, |last1= and |last3=
are present but |last2= is missing, an error message is emitted. |lastn= is not
required to have a matching |firstn=.
When an author or editor parameter contains some form of 'et al.', the 'et al.'
is stripped from the parameter and a flag (etal) returned that will cause list_people()
to add the static 'et al.' text from Module:Citation/CS1/Configuration. This keeps
'et al.' out of the template's metadata. When this occurs, an error is emitted.
]]
local function extract_names(args, list_name)
local names = {}; -- table of names
local last; -- individual name components
local first;
local link;
local mask;
local i = 1; -- loop counter/indexer
local n = 1; -- output table indexer
local count = 0; -- used to count the number of times we haven't found a |last= (or alias for authors, |editor-last or alias for editors)
local etal = false; -- return value set to true when we find some form of et al. in an author parameter
local last_alias, first_alias, link_alias; -- selected parameter aliases used in error messaging
while true do
last, last_alias = utilities.select_one ( args, cfg.aliases[list_name .. '-Last'], 'err_redundant_parameters', i ); -- search through args for name components beginning at 1
first, first_alias = utilities.select_one ( args, cfg.aliases[list_name .. '-First'], 'err_redundant_parameters', i );
link, link_alias = utilities.select_one ( args, cfg.aliases[list_name .. '-Link'], 'err_redundant_parameters', i );
mask = utilities.select_one ( args, cfg.aliases[list_name .. '-Mask'], 'err_redundant_parameters', i );
if last then -- error check |lastn= alias for unknown interwiki link prefix; done here because this is where we have the parameter name
local project, language = interwiki_prefixen_get (last, true); -- true because we expect interwiki links in |lastn= to be wikilinked
if nil == project and nil == language then -- when both are nil
utilities.set_message ('err_bad_paramlink', last_alias); -- not known, emit an error message -- TODO: err_bad_interwiki?
last = utilities.remove_wiki_link (last); -- remove wikilink markup; show display value only
end
end
if link then -- error check |linkn= alias for unknown interwiki link prefix
local project, language = interwiki_prefixen_get (link, false); -- false because wiki links in |author-linkn= is an error
if nil == project and nil == language then -- when both are nil
utilities.set_message ('err_bad_paramlink', link_alias); -- not known, emit an error message -- TODO: err_bad_interwiki?
link = nil; -- unset so we don't link
link_alias = nil;
end
end
last, etal = name_has_etal (last, etal, false, last_alias); -- find and remove variations on et al.
first, etal = name_has_etal (first, etal, false, first_alias); -- find and remove variations on et al.
last, first = name_checks (last, first, list_name, last_alias, first_alias); -- multiple names, extraneous annotation, etc. checks
if first and not last then -- if there is a firstn without a matching lastn
local alias = first_alias:find ('given', 1, true) and 'given' or 'first'; -- get first or given form of the alias
utilities.set_message ('err_first_missing_last', {
first_alias, -- param name of alias missing its mate
first_alias:gsub (alias, {['first'] = 'last', ['given'] = 'surname'}), -- make param name appropriate to the alias form
}); -- add this error message
elseif not first and not last then -- if both firstn and lastn aren't found, are we done?
count = count + 1; -- number of times we haven't found last and first
if 2 <= count then -- two missing names and we give up
break; -- normal exit or there is a two-name hole in the list; can't tell which
end
else -- we have last with or without a first
local result;
link = link_title_ok (link, link_alias, last, last_alias); -- check for improper wiki-markup
if first then
link = link_title_ok (link, link_alias, first, first_alias); -- check for improper wiki-markup
end
names[n] = {last = last, first = first, link = link, mask = mask, corporate = false}; -- add this name to our names list (corporate for |vauthors= only)
n = n + 1; -- point to next location in the names table
if 1 == count then -- if the previous name was missing
utilities.set_message ('err_missing_name', {list_name:match ("(%w+)List"):lower(), i - 1}); -- add this error message
end
count = 0; -- reset the counter, we're looking for two consecutive missing names
end
i = i + 1; -- point to next args location
end
return names, etal; -- all done, return our list of names and the etal flag
end
--[[--------------------------< N A M E _ T A G _ G E T >------------------------------------------------------
attempt to decode |language=<lang_param> and return language name and matching tag; nil else.
This function looks for:
<lang_param> as a tag in cfg.lang_tag_remap{}
<lang_param> as a name in cfg.lang_name_remap{}
<lang_param> as a name in cfg.mw_languages_by_name_t
<lang_param> as a tag in cfg.mw_languages_by_tag_t
when those fail, presume that <lang_param> is an IETF-like tag that MediaWiki does not recognize. Strip all
script, region, variant, whatever subtags from <lang_param> to leave just a two or three character language tag
and look for the new <lang_param> in cfg.mw_languages_by_tag_t{}
on success, returns name (in properly capitalized form) and matching tag (in lowercase); on failure returns nil
]]
local function name_tag_get (lang_param)
local lang_param_lc = mw.ustring.lower (lang_param); -- use lowercase as an index into the various tables
local name;
local tag;
name = cfg.lang_tag_remap[lang_param_lc]; -- assume <lang_param_lc> is a tag; attempt to get remapped language name
if name then -- when <name>, <lang_param> is a tag for a remapped language name
if cfg.lang_name_remap[name:lower()][2] ~= lang_param_lc then
utilities.set_message ('maint_unknown_lang'); -- add maint category if not already added
return name, cfg.lang_name_remap[name:lower()][2]; -- so return name and tag from lang_name_remap[name]; special case to xlate sr-ec and sr-el to sr-cyrl and sr-latn
end
return name, lang_param_lc; -- so return <name> from remap and <lang_param_lc>
end
tag = lang_param_lc:match ('^(%a%a%a?)%-.*'); -- still assuming that <lang_param_lc> is a tag; strip script, region, variant subtags
name = cfg.lang_tag_remap[tag]; -- attempt to get remapped language name with language subtag only
if name then -- when <name>, <tag> is a tag for a remapped language name
return name, tag; -- so return <name> from remap and <tag>
end
if cfg.lang_name_remap[lang_param_lc] then -- not a remapped tag, assume <lang_param_lc> is a name; attempt to get remapped language tag
return cfg.lang_name_remap[lang_param_lc][1], cfg.lang_name_remap[lang_param_lc][2]; -- for this <lang_param_lc>, return a (possibly) new name and appropriate tag
end
name = cfg.mw_languages_by_tag_t[lang_param_lc]; -- assume that <lang_param_lc> is a tag; attempt to get its matching language name
if name then
return name, lang_param_lc; -- <lang_param_lc> is a tag so return it and <name>
end
tag = cfg.mw_languages_by_name_t[lang_param_lc]; -- assume that <lang_param_lc> is a language name; attempt to get its matching tag
if tag then
return cfg.mw_languages_by_tag_t[tag], tag; -- <lang_param_lc> is a name so return the name from the table and <tag>
end
tag = lang_param_lc:match ('^(%a%a%a?)%-.*'); -- is <lang_param_lc> an IETF-like tag that MediaWiki doesn't recognize? <tag> gets the language subtag; nil else
if tag then
name = cfg.mw_languages_by_tag_t[tag]; -- attempt to get a language name using the shortened <tag>
if name then
return name, tag; -- <lang_param_lc> is an unrecognized IETF-like tag so return <name> and language subtag
end
end
end
--[[-------------------< L A N G U A G E _ P A R A M E T E R >------------------
Gets language name from a provided two- or three-character ISO 639 code. If a code
is recognized by MediaWiki, use the returned name; if not, then use the value that
was provided with the language parameter.
When |language= contains a recognized language (either code or name), the page is
assigned to the category for that code: Category:Norwegian-language sources (no).
For valid three-character code languages, the page is assigned to the single category
for '639-2' codes: Category:CS1 ISO 639-2 language sources.
Languages that are the same as the local wiki are not categorized. MediaWiki does
not recognize three-character equivalents of two-character codes: code 'ar' is
recognized but code 'ara' is not.
This function supports multiple languages in the form |language=nb, French, th
where the language names or codes are separated from each other by commas with
optional space characters.
]]
local function language_parameter (lang)
local tag; -- some form of IETF-like language tag; language subtag with optional region, sript, vatiant, etc subtags
local lang_subtag; -- ve populates |language= with mostly unecessary region subtags the MediaWiki does not recognize; this is the base language subtag
local name; -- the language name
local language_list = {}; -- table of language names to be rendered
local names_t = {}; -- table made from the value assigned to |language=
local this_wiki_name = mw.language.fetchLanguageName (cfg.this_wiki_code, cfg.this_wiki_code); -- get this wiki's language name
names_t = mw.text.split (lang, '%s*,%s*'); -- names should be a comma separated list
for _, lang in ipairs (names_t) do -- reuse lang here because we don't yet know if lang is a language name or a language tag
name, tag = name_tag_get (lang); -- attempt to get name/tag pair for <lang>; <name> has proper capitalization; <tag> is lowercase
if utilities.is_set (tag) then
lang_subtag = tag:gsub ('^(%a%a%a?)%-.*', '%1'); -- for categorization, strip any IETF-like tags from language tag
if cfg.this_wiki_code ~= lang_subtag then -- when the language is not the same as this wiki's language
if 2 == lang_subtag:len() then -- and is a two-character tag
utilities.add_prop_cat ('foreign-lang-source', {name, tag}, lang_subtag); -- categorize it; tag appended to allow for multiple language categorization
else -- or is a recognized language (but has a three-character tag)
utilities.add_prop_cat ('foreign-lang-source-2', {lang_subtag}, lang_subtag); -- categorize it differently TODO: support multiple three-character tag categories per cs1|2 template?
end
elseif cfg.local_lang_cat_enable then -- when the language and this wiki's language are the same and categorization is enabled
utilities.add_prop_cat ('local-lang-source', {name, lang_subtag}); -- categorize it
end
else
name = lang; -- return whatever <lang> has so that we show something
utilities.set_message ('maint_unknown_lang'); -- add maint category if not already added
end
table.insert (language_list, name);
name = ''; -- so we can reuse it
end
name = utilities.make_sep_list (#language_list, language_list);
if (1 == #language_list) and (lang_subtag == cfg.this_wiki_code) then -- when only one language, find lang name in this wiki lang name; for |language=en-us, 'English' in 'American English'
return ''; -- if one language and that language is this wiki's return an empty string (no annotation)
end
return (" " .. wrap_msg ('language', name)); -- otherwise wrap with '(in ...)'
--[[ TODO: should only return blank or name rather than full list
so we can clean up the bunched parenthetical elements Language, Type, Format
]]
end
--[[-----------------------< S E T _ C S _ S T Y L E >--------------------------
Gets the default CS style configuration for the given mode.
Returns default separator and either postscript as passed in or the default.
In CS1, the default postscript and separator are '.'.
In CS2, the default postscript is the empty string and the default separator is ','.
]]
local function set_cs_style (postscript, mode)
if utilities.is_set(postscript) then
-- emit a maintenance message if user postscript is the default cs1 postscript
-- we catch the opposite case for cs2 in set_style
if mode == 'cs1' and postscript == cfg.presentation['ps_' .. mode] then
utilities.set_message ('maint_postscript');
end
else
postscript = cfg.presentation['ps_' .. mode];
end
return cfg.presentation['sep_' .. mode], postscript;
end
--[[--------------------------< S E T _ S T Y L E >-----------------------------
Sets the separator and postscript styles. Checks the |mode= first and the
#invoke CitationClass second. Removes the postscript if postscript == none.
]]
local function set_style (mode, postscript, cite_class)
local sep;
if 'cs2' == mode then
sep, postscript = set_cs_style (postscript, 'cs2');
elseif 'cs1' == mode then
sep, postscript = set_cs_style (postscript, 'cs1');
elseif 'citation' == cite_class then
sep, postscript = set_cs_style (postscript, 'cs2');
else
sep, postscript = set_cs_style (postscript, 'cs1');
end
if cfg.keywords_xlate[postscript:lower()] == 'none' then
-- emit a maintenance message if user postscript is the default cs2 postscript
-- we catch the opposite case for cs1 in set_cs_style
if 'cs2' == mode or ('cs1' ~= mode and 'citation' == cite_class) then -- {{citation |title=Title |mode=cs1 |postscript=none}} should not emit maint message
utilities.set_message ('maint_postscript');
end
postscript = '';
end
return sep, postscript
end
--[=[-------------------------< I S _ P D F >-----------------------------------
Determines if a URL has the file extension that is one of the PDF file extensions
used by [[MediaWiki:Common.css]] when applying the PDF icon to external links.
returns true if file extension is one of the recognized extensions, else false
]=]
local function is_pdf (url)
return url:match ('%.pdf$') or url:match ('%.PDF$') or
url:match ('%.pdf[%?#]') or url:match ('%.PDF[%?#]') or
url:match ('%.PDF#') or url:match ('%.pdf#');
end
--[[--------------------------< S T Y L E _ F O R M A T >-----------------------
Applies CSS style to |format=, |chapter-format=, etc. Also emits an error message
if the format parameter does not have a matching URL parameter. If the format parameter
is not set and the URL contains a file extension that is recognized as a PDF document
by MediaWiki's commons.css, this code will set the format parameter to (PDF) with
the appropriate styling.
]]
local function style_format (format, url, fmt_param, url_param)
if utilities.is_set (format) then
format = utilities.wrap_style ('format', format); -- add leading space, parentheses, resize
if not utilities.is_set (url) then
utilities.set_message ('err_format_missing_url', {fmt_param, url_param}); -- add an error message
end
elseif is_pdf (url) then -- format is not set so if URL is a PDF file then
format = utilities.wrap_style ('format', 'PDF'); -- set format to PDF
else
format = ''; -- empty string for concatenation
end
return format;
end
--[[---------------------< G E T _ D I S P L A Y _ N A M E S >------------------
Returns a number that defines the number of names displayed for author and editor
name lists and a Boolean flag to indicate when et al. should be appended to the name list.
When the value assigned to |display-xxxxors= is a number greater than or equal to zero,
return the number and the previous state of the 'etal' flag (false by default
but may have been set to true if the name list contains some variant of the text 'et al.').
When the value assigned to |display-xxxxors= is the keyword 'etal', return a number
that is one greater than the number of authors in the list and set the 'etal' flag true.
This will cause the list_people() to display all of the names in the name list followed by 'et al.'
In all other cases, returns nil and the previous state of the 'etal' flag.
inputs:
max: A['DisplayAuthors'] or A['DisplayEditors'], etc; a number or some flavor of etal
count: #a or #e
list_name: 'authors' or 'editors'
etal: author_etal or editor_etal
This function sets an error message when |display-xxxxors= value greater than or equal to number of names but
not when <max> comes from {{cs1 config}} global settings. When using global settings, <param> is set to the
keyword 'cs1 config' which is used to supress the normal error. Error is suppressed because it is to be expected
that some citations in an article will have the same or fewer names that the limit specified in {{cs1 config}}.
]]
local function get_display_names (max, count, list_name, etal, param)
if utilities.is_set (max) then
if 'etal' == max:lower():gsub("[ '%.]", '') then -- the :gsub() portion makes 'etal' from a variety of 'et al.' spellings and stylings
max = count + 1; -- number of authors + 1 so display all author name plus et al.
etal = true; -- overrides value set by extract_names()
elseif max:match ('^%d+$') then -- if is a string of numbers
max = tonumber (max); -- make it a number
if (max >= count) and ('cs1 config' ~= param) then -- error when local |display-xxxxors= value greater than or equal to number of names; not an error when using global setting
utilities.set_message ('err_disp_name', {param, max}); -- add error message
max = nil;
end
else -- not a valid keyword or number
utilities.set_message ('err_disp_name', {param, max}); -- add error message
max = nil; -- unset; as if |display-xxxxors= had not been set
end
end
return max, etal;
end
--[[----------< E X T R A _ T E X T _ I N _ P A G E _ C H E C K >---------------
Adds error if |page=, |pages=, |quote-page=, |quote-pages= has what appears to be
some form of p. or pp. abbreviation in the first characters of the parameter content.
check page for extraneous p, p., pp, pp., pg, pg. at start of parameter value:
good pattern: '^P[^%.P%l]' matches when page begins PX or P# but not Px
where x and X are letters and # is a digit
bad pattern: '^[Pp][PpGg]' matches when page begins pp, pP, Pp, PP, pg, pG, Pg, PG
]]
local function extra_text_in_page_check (val, name)
if not val:match (cfg.vol_iss_pg_patterns.good_ppattern) then
for _, pattern in ipairs (cfg.vol_iss_pg_patterns.bad_ppatterns) do -- spin through the selected sequence table of patterns
if val:match (pattern) then -- when a match, error so
utilities.set_message ('err_extra_text_pages', name); -- add error message
return; -- and done
end
end
end
end
--[[--------------------------< E X T R A _ T E X T _ I N _ V O L _ I S S _ C H E C K >------------------------
Adds error if |volume= or |issue= has what appears to be some form of redundant 'type' indicator. Applies to
both; this function looks for issue text in both |issue= and |volume= and looks for volume-like text in |voluem=
and |issue=.
For |volume=:
'V.', or 'Vol.' (with or without the dot) abbreviations or 'Volume' in the first characters of the parameter
content (all case insensitive). 'V' and 'v' (without the dot) are presumed to be roman numerals so
are allowed.
For |issue=:
'No.', 'I.', 'Iss.' (with or without the dot) abbreviations, or 'Issue' in the first characters of the
parameter content (all case insensitive); numero styling: 'n°' with degree sign U+00B0, and № precomposed
numero sign U+2116.
Single character values ('v', 'i', 'n') allowed when not followed by separator character ('.', ':', '=', or
whitespace character) – param values are trimmed of whitespace by MediaWiki before delivered to the module.
<val> is |volume= or |issue= parameter value
<name> is |volume= or |issue= parameter name for error message
<selector> is 'v' for |volume=, 'i' for |issue=
sets error message on failure; returns nothing
]]
local function extra_text_in_vol_iss_check (val, name, selector)
if not utilities.is_set (val) then
return;
end
local handler = 'v' == selector and 'err_extra_text_volume' or 'err_extra_text_issue';
val = val:lower(); -- force parameter value to lower case
for _, pattern in ipairs (cfg.vol_iss_pg_patterns.vi_patterns_t) do -- spin through the sequence table of patterns
if val:match (pattern) then -- when a match, error so
utilities.set_message (handler, name); -- add error message
return; -- and done
end
end
end
--[=[-------------------------< G E T _ V _ N A M E _ T A B L E >----------------------------------------------
split apart a |vauthors= or |veditors= parameter. This function allows for corporate names, wrapped in doubled
parentheses to also have commas; in the old version of the code, the doubled parentheses were included in the
rendered citation and in the metadata. Individual author names may be wikilinked
|vauthors=Jones AB, [[E. B. White|White EB]], ((Black, Brown, and Co.))
]=]
local function get_v_name_table (vparam, output_table, output_link_table)
local _, accept = utilities.has_accept_as_written (vparam);
if accept then
utilities.add_prop_cat ('vanc-accept'); -- add properties category
end
local name_table = mw.text.split(vparam, "%s*,%s*"); -- names are separated by commas
local wl_type, label, link; -- wl_type not used here; just a placeholder
local i = 1;
while name_table[i] do
if name_table[i]:match ('^%(%(.*[^%)][^%)]$') then -- first segment of corporate with one or more commas; this segment has the opening doubled parentheses
local name = name_table[i];
i = i + 1; -- bump indexer to next segment
while name_table[i] do
name = name .. ', ' .. name_table[i]; -- concatenate with previous segments
if name_table[i]:match ('^.*%)%)$') then -- if this table member has the closing doubled parentheses
break; -- and done reassembling so
end
i = i + 1; -- bump indexer
end
table.insert (output_table, name); -- and add corporate name to the output table
table.insert (output_link_table, ''); -- no wikilink
else
wl_type, label, link = utilities.is_wikilink (name_table[i]); -- wl_type is: 0, no wl (text in label variable); 1, [[D]]; 2, [[L|D]]
table.insert (output_table, label); -- add this name
if 1 == wl_type then
table.insert (output_link_table, label); -- simple wikilink [[D]]
else
table.insert (output_link_table, link); -- no wikilink or [[L|D]]; add this link if there is one, else empty string
end
end
i = i + 1;
end
return output_table;
end
--[[--------------------------< P A R S E _ V A U T H O R S _ V E D I T O R S >--------------------------------
This function extracts author / editor names from |vauthors= or |veditors= and finds matching |xxxxor-maskn= and
|xxxxor-linkn= in args. It then returns a table of assembled names just as extract_names() does.
Author / editor names in |vauthors= or |veditors= must be in Vancouver system style. Corporate or institutional names
may sometimes be required and because such names will often fail the is_good_vanc_name() and other format compliance
tests, are wrapped in doubled parentheses ((corporate name)) to suppress the format tests.
Supports generational suffixes Jr, 2nd, 3rd, 4th–6th.
This function sets the Vancouver error when a required comma is missing and when there is a space between an author's initials.
]]
local function parse_vauthors_veditors (args, vparam, list_name)
local names = {}; -- table of names assembled from |vauthors=, |author-maskn=, |author-linkn=
local v_name_table = {};
local v_link_table = {}; -- when name is wikilinked, targets go in this table
local etal = false; -- return value set to true when we find some form of et al. vauthors parameter
local last, first, link, mask, suffix;
local corporate = false;
vparam, etal = name_has_etal (vparam, etal, true); -- find and remove variations on et al. do not categorize (do it here because et al. might have a period)
v_name_table = get_v_name_table (vparam, v_name_table, v_link_table); -- names are separated by commas
for i, v_name in ipairs(v_name_table) do
first = ''; -- set to empty string for concatenation and because it may have been set for previous author/editor
local accept_name;
v_name, accept_name = utilities.has_accept_as_written (v_name); -- remove accept-this-as-written markup when it wraps all of <v_name>
if accept_name then
last = v_name;
corporate = true; -- flag used in list_people()
elseif string.find(v_name, "%s") then
if v_name:find('[;%.]') then -- look for commonly occurring punctuation characters;
add_vanc_error (cfg.err_msg_supl.punctuation, i);
end
local lastfirstTable = {}
lastfirstTable = mw.text.split(v_name, "%s+")
first = table.remove(lastfirstTable); -- removes and returns value of last element in table which should be initials or generational suffix
if not mw.ustring.match (first, '^%u+$') then -- mw.ustring here so that later we will catch non-Latin characters
suffix = first; -- not initials so assume that whatever we got is a generational suffix
first = table.remove(lastfirstTable); -- get what should be the initials from the table
end
last = table.concat(lastfirstTable, ' ') -- returns a string that is the concatenation of all other names that are not initials and generational suffix
if not utilities.is_set (last) then
first = ''; -- unset
last = v_name; -- last empty because something wrong with first
add_vanc_error (cfg.err_msg_supl.name, i);
end
if mw.ustring.match (last, '%a+%s+%u+%s+%a+') then
add_vanc_error (cfg.err_msg_supl['missing comma'], i); -- matches last II last; the case when a comma is missing
end
if mw.ustring.match (v_name, ' %u %u$') then -- this test is in the wrong place TODO: move or replace with a more appropriate test
add_vanc_error (cfg.err_msg_supl.initials, i); -- matches a space between two initials
end
else
last = v_name; -- last name or single corporate name? Doesn't support multiword corporate names? do we need this?
end
if utilities.is_set (first) then
if not mw.ustring.match (first, "^%u?%u$") then -- first shall contain one or two upper-case letters, nothing else
add_vanc_error (cfg.err_msg_supl.initials, i); -- too many initials; mixed case initials (which may be ok Romanization); hyphenated initials
end
is_good_vanc_name (last, first, suffix, i); -- check first and last before restoring the suffix which may have a non-Latin digit
if utilities.is_set (suffix) then
first = first .. ' ' .. suffix; -- if there was a suffix concatenate with the initials
suffix = ''; -- unset so we don't add this suffix to all subsequent names
end
else
if not corporate then
is_good_vanc_name (last, '', nil, i);
end
end
link = utilities.select_one ( args, cfg.aliases[list_name .. '-Link'], 'err_redundant_parameters', i ) or v_link_table[i];
mask = utilities.select_one ( args, cfg.aliases[list_name .. '-Mask'], 'err_redundant_parameters', i );
names[i] = {last = last, first = first, link = link, mask = mask, corporate = corporate}; -- add this assembled name to our names list
end
return names, etal; -- all done, return our list of names
end
--[[--------------------------< S E L E C T _ A U T H O R _ E D I T O R _ S O U R C E >------------------------
Select one of |authors=, |authorn= / |lastn / firstn=, or |vauthors= as the source of the author name list or
select one of |editorn= / editor-lastn= / |editor-firstn= or |veditors= as the source of the editor name list.
Only one of these appropriate three will be used. The hierarchy is: |authorn= (and aliases) highest and |authors= lowest;
|editorn= (and aliases) highest and |veditors= lowest (support for |editors= withdrawn)
When looking for |authorn= / |editorn= parameters, test |xxxxor1= and |xxxxor2= (and all of their aliases); stops after the second
test which mimicks the test used in extract_names() when looking for a hole in the author name list. There may be a better
way to do this, I just haven't discovered what that way is.
Emits an error message when more than one xxxxor name source is provided.
In this function, vxxxxors = vauthors or veditors; xxxxors = authors as appropriate.
]]
local function select_author_editor_source (vxxxxors, xxxxors, args, list_name)
local lastfirst = false;
if utilities.select_one ( args, cfg.aliases[list_name .. '-Last'], 'none', 1 ) or -- do this twice in case we have a |first1= without a |last1=; this ...
utilities.select_one ( args, cfg.aliases[list_name .. '-First'], 'none', 1 ) or -- ... also catches the case where |first= is used with |vauthors=
utilities.select_one ( args, cfg.aliases[list_name .. '-Last'], 'none', 2 ) or
utilities.select_one ( args, cfg.aliases[list_name .. '-First'], 'none', 2 ) then
lastfirst = true;
end
if (utilities.is_set (vxxxxors) and true == lastfirst) or -- these are the three error conditions
(utilities.is_set (vxxxxors) and utilities.is_set (xxxxors)) or
(true == lastfirst and utilities.is_set (xxxxors)) then
local err_name;
if 'AuthorList' == list_name then -- figure out which name should be used in error message
err_name = 'author';
else
err_name = 'editor';
end
utilities.set_message ('err_redundant_parameters', err_name .. '-name-list parameters'); -- add error message
end
if true == lastfirst then return 1 end; -- return a number indicating which author name source to use
if utilities.is_set (vxxxxors) then return 2 end;
if utilities.is_set (xxxxors) then return 3 end;
return 1; -- no authors so return 1; this allows missing author name test to run in case there is a first without last
end
--[[--------------------------< I S _ V A L I D _ P A R A M E T E R _ V A L U E >------------------------------
This function is used to validate a parameter's assigned value for those parameters that have only a limited number
of allowable values (yes, y, true, live, dead, etc.). When the parameter value has not been assigned a value (missing
or empty in the source template) the function returns the value specified by ret_val. If the parameter value is one
of the list of allowed values returns the translated value; else, emits an error message and returns the value
specified by ret_val.
TODO: explain <invert>
]]
local function is_valid_parameter_value (value, name, possible, ret_val, invert)
if not utilities.is_set (value) then
return ret_val; -- an empty parameter is ok
end
if (not invert and utilities.in_array (value, possible)) then -- normal; <value> is in <possible> table
return cfg.keywords_xlate[value]; -- return translation of parameter keyword
elseif invert and not utilities.in_array (value, possible) then -- invert; <value> is not in <possible> table
return value; -- return <value> as it is
else
utilities.set_message ('err_invalid_param_val', {name, value}); -- not an allowed value so add error message
return ret_val;
end
end
--[[--------------------------< T E R M I N A T E _ N A M E _ L I S T >----------------------------------------
This function terminates a name list (author, contributor, editor) with a separator character (sepc) and a space
when the last character is not a sepc character or when the last three characters are not sepc followed by two
closing square brackets (close of a wikilink). When either of these is true, the name_list is terminated with a
single space character.
]]
local function terminate_name_list (name_list, sepc)
if (string.sub (name_list, -3, -1) == sepc .. '. ') then -- if already properly terminated
return name_list; -- just return the name list
elseif (string.sub (name_list, -1, -1) == sepc) or (string.sub (name_list, -3, -1) == sepc .. ']]') then -- if last name in list ends with sepc char
return name_list .. " "; -- don't add another
else
return name_list .. sepc .. ' '; -- otherwise terminate the name list
end
end
--[[-------------------------< F O R M A T _ V O L U M E _ I S S U E >-----------------------------------------
returns the concatenation of the formatted volume and issue (or journal article number) parameters as a single
string; or formatted volume or formatted issue, or an empty string if neither are set.
]]
local function format_volume_issue (volume, issue, article, cite_class, origin, sepc, lower)
if not utilities.is_set (volume) and not utilities.is_set (issue) and not utilities.is_set (article) then
return '';
end
-- same condition as in format_pages_sheets()
local is_journal = 'journal' == cite_class or (utilities.in_array (cite_class, {'citation', 'map', 'interview'}) and 'journal' == origin);
local is_numeric_vol = volume and (volume:match ('^[MDCLXVI]+$') or volume:match ('^%d+$')); -- is only uppercase roman numerals or only digits?
local is_long_vol = volume and (4 < mw.ustring.len(volume)); -- is |volume= value longer than 4 characters?
if volume and (not is_numeric_vol and is_long_vol) then -- when not all digits or Roman numerals, is |volume= longer than 4 characters?
utilities.add_prop_cat ('long-vol'); -- yes, add properties cat
end
if is_journal then -- journal-style formatting
local vol = '';
if utilities.is_set (volume) then
if is_numeric_vol then -- |volume= value all digits or all uppercase Roman numerals?
vol = utilities.substitute (cfg.presentation['vol-bold'], {sepc, volume}); -- render in bold face
elseif is_long_vol then -- not all digits or Roman numerals; longer than 4 characters?
vol = utilities.substitute (cfg.messages['j-vol'], {sepc, utilities.hyphen_to_dash (volume)}); -- not bold
else -- four or fewer characters
vol = utilities.substitute (cfg.presentation['vol-bold'], {sepc, utilities.hyphen_to_dash (volume)}); -- bold
end
end
vol = vol .. (utilities.is_set (issue) and utilities.substitute (cfg.messages['j-issue'], issue) or '')
vol = vol .. (utilities.is_set (article) and utilities.substitute (cfg.messages['j-article-num'], article) or '')
return vol;
end
if 'podcast' == cite_class and utilities.is_set (issue) then
return wrap_msg ('issue', {sepc, issue}, lower);
end
if 'conference' == cite_class and utilities.is_set (article) then -- |article-number= supported only in journal and conference cites
if utilities.is_set (volume) and utilities.is_set (article) then -- both volume and article number
return wrap_msg ('vol-art', {sepc, utilities.hyphen_to_dash (volume), article}, lower);
elseif utilities.is_set (article) then -- article number alone; when volume alone, handled below
return wrap_msg ('art', {sepc, article}, lower);
end
end
-- all other types of citation
if utilities.is_set (volume) and utilities.is_set (issue) then
return wrap_msg ('vol-no', {sepc, utilities.hyphen_to_dash (volume), issue}, lower);
elseif utilities.is_set (volume) then
return wrap_msg ('vol', {sepc, utilities.hyphen_to_dash (volume)}, lower);
else
return wrap_msg ('issue', {sepc, issue}, lower);
end
end
--[[-------------------------< F O R M A T _ P A G E S _ S H E E T S >-----------------------------------------
adds static text to one of |page(s)= or |sheet(s)= values and returns it with all of the others set to empty strings.
The return order is:
page, pages, sheet, sheets
Singular has priority over plural when both are provided.
]]
local function format_pages_sheets (page, pages, sheet, sheets, cite_class, origin, sepc, nopp, lower)
if 'map' == cite_class then -- only cite map supports sheet(s) as in-source locators
if utilities.is_set (sheet) then
if 'journal' == origin then
return '', '', wrap_msg ('j-sheet', sheet, lower), '';
else
return '', '', wrap_msg ('sheet', {sepc, sheet}, lower), '';
end
elseif utilities.is_set (sheets) then
if 'journal' == origin then
return '', '', '', wrap_msg ('j-sheets', sheets, lower);
else
return '', '', '', wrap_msg ('sheets', {sepc, sheets}, lower);
end
end
end
local is_journal = 'journal' == cite_class or (utilities.in_array (cite_class, {'citation', 'map', 'interview'}) and 'journal' == origin);
if utilities.is_set (page) then
if is_journal then
return utilities.substitute (cfg.messages['j-page(s)'], page), '', '', '';
elseif not nopp then
return utilities.substitute (cfg.messages['p-prefix'], {sepc, page}), '', '', '';
else
return utilities.substitute (cfg.messages['nopp'], {sepc, page}), '', '', '';
end
elseif utilities.is_set (pages) then
if is_journal then
return utilities.substitute (cfg.messages['j-page(s)'], pages), '', '', '';
elseif tonumber(pages) ~= nil and not nopp then -- if pages is only digits, assume a single page number
return '', utilities.substitute (cfg.messages['p-prefix'], {sepc, pages}), '', '';
elseif not nopp then
return '', utilities.substitute (cfg.messages['pp-prefix'], {sepc, pages}), '', '';
else
return '', utilities.substitute (cfg.messages['nopp'], {sepc, pages}), '', '';
end
end
return '', '', '', ''; -- return empty strings
end
--[[--------------------------< I N S O U R C E _ L O C _ G E T >----------------------------------------------
returns one of the in-source locators: page, pages, or at.
If any of these are interwiki links to Wikisource, returns the label portion of the interwiki-link as plain text
for use in COinS. This COinS thing is done because here we convert an interwiki-link to an external link and
add an icon span around that; get_coins_pages() doesn't know about the span. TODO: should it?
TODO: add support for sheet and sheets?; streamline;
TODO: make it so that this function returns only one of the three as the single in-source (the return value assigned
to a new name)?
]]
local function insource_loc_get (page, page_orig, pages, pages_orig, at)
local ws_url, ws_label, coins_pages, L; -- for Wikisource interwiki-links; TODO: this corrupts page metadata (span remains in place after cleanup; fix there?)
if utilities.is_set (page) then
if utilities.is_set (pages) or utilities.is_set (at) then
pages = ''; -- unset the others
at = '';
end
extra_text_in_page_check (page, page_orig); -- emit error message when |page= value begins with what looks like p., pp., etc.
ws_url, ws_label, L = wikisource_url_make (page); -- make ws URL from |page= interwiki link; link portion L becomes tooltip label
if ws_url then
page = external_link (ws_url, ws_label .. ' ', 'ws link in page'); -- space char after label to move icon away from in-source text; TODO: a better way to do this?
page = utilities.substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], L, page});
coins_pages = ws_label;
end
elseif utilities.is_set (pages) then
if utilities.is_set (at) then
at = ''; -- unset
end
extra_text_in_page_check (pages, pages_orig); -- emit error message when |page= value begins with what looks like p., pp., etc.
ws_url, ws_label, L = wikisource_url_make (pages); -- make ws URL from |pages= interwiki link; link portion L becomes tooltip label
if ws_url then
pages = external_link (ws_url, ws_label .. ' ', 'ws link in pages'); -- space char after label to move icon away from in-source text; TODO: a better way to do this?
pages = utilities.substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], L, pages});
coins_pages = ws_label;
end
elseif utilities.is_set (at) then
ws_url, ws_label, L = wikisource_url_make (at); -- make ws URL from |at= interwiki link; link portion L becomes tooltip label
if ws_url then
at = external_link (ws_url, ws_label .. ' ', 'ws link in at'); -- space char after label to move icon away from in-source text; TODO: a better way to do this?
at = utilities.substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], L, at});
coins_pages = ws_label;
end
end
return page, pages, at, coins_pages;
end
--[[--------------------------< I S _ U N I Q U E _ A R C H I V E _ U R L >------------------------------------
add error message when |archive-url= value is same as |url= or chapter-url= (or alias...) value
]]
local function is_unique_archive_url (archive, url, c_url, source, date)
if utilities.is_set (archive) then
if archive == url or archive == c_url then
utilities.set_message ('err_bad_url', {utilities.wrap_style ('parameter', source)}); -- add error message
return '', ''; -- unset |archive-url= and |archive-date= because same as |url= or |chapter-url=
end
end
return archive, date;
end
--[=[-------------------------< A R C H I V E _ U R L _ C H E C K >--------------------------------------------
Check archive.org URLs to make sure they at least look like they are pointing at valid archives and not to the
save snapshot URL or to calendar pages. When the archive URL is 'https://web.archive.org/save/' (or http://...)
archive.org saves a snapshot of the target page in the URL. That is something that Wikipedia should not allow
unwitting readers to do.
When the archive.org URL does not have a complete timestamp, archive.org chooses a snapshot according to its own
algorithm or provides a calendar 'search' result. [[WP:ELNO]] discourages links to search results.
This function looks at the value assigned to |archive-url= and returns empty strings for |archive-url= and
|archive-date= and an error message when:
|archive-url= holds an archive.org save command URL
|archive-url= is an archive.org URL that does not have a complete timestamp (YYYYMMDDhhmmss 14 digits) in the
correct place
otherwise returns |archive-url= and |archive-date=
There are two mostly compatible archive.org URLs:
//web.archive.org/<timestamp>... -- the old form
//web.archive.org/web/<timestamp>... -- the new form
The old form does not support or map to the new form when it contains a display flag. There are four identified flags
('id_', 'js_', 'cs_', 'im_') but since archive.org ignores others following the same form (two letters and an underscore)
we don't check for these specific flags but we do check the form.
This function supports a preview mode. When the article is rendered in preview mode, this function may return a modified
archive URL:
for save command errors, return undated wildcard (/*/)
for timestamp errors when the timestamp has a wildcard, return the URL unmodified
for timestamp errors when the timestamp does not have a wildcard, return with timestamp limited to six digits plus wildcard (/yyyymm*/)
A secondary function is to return an archive-url timestamp from those urls that have them (archive.org and
archive.today). The timestamp is used by validation.archive_date_check() to see if the value in |archive-date=
matches the timestamp in the archive url.
]=]
local function archive_url_check (url, date)
local err_msg = ''; -- start with the error message empty
local path, timestamp, flag; -- portions of the archive.org URL
timestamp = url:match ('//archive.today/(%d%d%d%d%d%d%d%d%d%d%d%d%d%d)/') or -- get timestamp from archive.today urls
url:match ('//archive.today/(%d%d%d%d%.%d%d%.%d%d%-%d%d%d%d%d%d)/'); -- this timestamp needs cleanup
if timestamp then -- if this was an archive.today url ...
return url, date, timestamp:gsub ('[%.%-]', ''); -- return ArchiveURL, ArchiveDate, and timestamp (dots and dashes removed) from |archive-url=, and done
end
-- here for archive.org urls
if (not url:match('//web%.archive%.org/')) and (not url:match('//liveweb%.archive%.org/')) then -- also deprecated liveweb Wayback machine URL
return url, date; -- not an archive.org archive, return ArchiveURL and ArchiveDate
end
if url:match('//web%.archive%.org/save/') then -- if a save command URL, we don't want to allow saving of the target page
err_msg = cfg.err_msg_supl.save;
url = url:gsub ('(//web%.archive%.org)/save/', '%1/*/', 1); -- for preview mode: modify ArchiveURL
elseif url:match('//liveweb%.archive%.org/') then
err_msg = cfg.err_msg_supl.liveweb;
else
path, timestamp, flag = url:match('//web%.archive%.org/([^%d]*)(%d+)([^/]*)/'); -- split out some of the URL parts for evaluation
if not path then -- malformed in some way; pattern did not match
err_msg = cfg.err_msg_supl.timestamp;
elseif 14 ~= timestamp:len() then -- path and flag optional, must have 14-digit timestamp here
err_msg = cfg.err_msg_supl.timestamp;
if '*' ~= flag then
local replacement = timestamp:match ('^%d%d%d%d%d%d') or timestamp:match ('^%d%d%d%d'); -- get the first 6 (YYYYMM) or first 4 digits (YYYY)
if replacement then -- nil if there aren't at least 4 digits (year)
replacement = replacement .. string.rep ('0', 14 - replacement:len()); -- year or yearmo (4 or 6 digits) zero-fill to make 14-digit timestamp
url=url:gsub ('(//web%.archive%.org/[^%d]*)%d[^/]*', '%1' .. replacement .. '*', 1) -- for preview, modify ts to 14 digits plus splat for calendar display
end
end
elseif utilities.is_set (path) and 'web/' ~= path then -- older archive URLs do not have the extra 'web/' path element
err_msg = cfg.err_msg_supl.path;
elseif utilities.is_set (flag) and not utilities.is_set (path) then -- flag not allowed with the old form URL (without the 'web/' path element)
err_msg = cfg.err_msg_supl.flag;
elseif utilities.is_set (flag) and not flag:match ('%a%a_') then -- flag if present must be two alpha characters and underscore (requires 'web/' path element)
err_msg = cfg.err_msg_supl.flag;
else
return url, date, timestamp; -- return ArchiveURL, ArchiveDate, and timestamp from |archive-url=
end
end
-- if here, something not right so
utilities.set_message ('err_archive_url', {err_msg}); -- add error message and
if is_preview_mode then
return url, date, timestamp; -- preview mode so return ArchiveURL, ArchiveDate, and timestamp from |archive-url=
else
return '', ''; -- return empty strings for ArchiveURL and ArchiveDate
end
end
--[[--------------------------< P L A C E _ C H E C K >--------------------------------------------------------
check |place=, |publication-place=, |location= to see if these params include digits. This function added because
many editors misuse location to specify the in-source location (|page(s)= and |at= are supposed to do that)
returns the original parameter value without modification; added maint cat when parameter value contains digits
]]
local function place_check (param_val)
if not utilities.is_set (param_val) then -- parameter empty or omitted
return param_val; -- return that empty state
end
if mw.ustring.find (param_val, '%d') then -- not empty, are there digits in the parameter value
utilities.set_message ('maint_location'); -- yep, add maint cat
end
return param_val; -- and done
end
--[[--------------------------< I S _ A R C H I V E D _ C O P Y >----------------------------------------------
compares |title= to 'Archived copy' (placeholder added by bots that can't find proper title); if matches, return true; nil else
]]
local function is_archived_copy (title)
title = mw.ustring.lower(title); -- switch title to lower case
if title:find (cfg.special_case_translation.archived_copy.en) then -- if title is 'Archived copy'
return true;
elseif cfg.special_case_translation.archived_copy['local'] then
if mw.ustring.find (title, cfg.special_case_translation.archived_copy['local']) then -- mw.ustring() because might not be Latin script
return true;
end
end
end
--[[--------------------------< D I S P L A Y _ N A M E S _ S E L E C T >--------------------------------------
for any of the |display-authors=, |display-editors=, etc parameters, select either the local or global setting.
When both are present, look at <local_display_names> value. When the value is some sort of 'et al.'string,
special handling is required.
When {{cs1 config}} has |display-<namelist>= AND this template has |display-<namelist>=etal AND:
the number of names specified by <number_of_names> is:
greater than the number specified in the global |display-<namelist>= parameter (<global_display_names>)
use global |display-<namelist>= parameter value
set overridden maint category
less than or equal to the number specified in the global |display-<namelist>= parameter
use local |display-<namelist>= parameter value
The purpose of this function is to prevent categorizing a template that has fewer names than the global setting
to keep the etal annotation specified by <local_display_names>.
]]
local function display_names_select (global_display_names, local_display_names, param_name, number_of_names, test)
if global_display_names and utilities.is_set (local_display_names) then -- when both
if 'etal' == local_display_names:lower():gsub("[ '%.]", '') then -- the :gsub() portion makes 'etal' from a variety of 'et al.' spellings and stylings
number_of_names = tonumber (number_of_names); -- convert these to numbers for comparison
local global_display_names_num = tonumber (global_display_names); -- <global_display_names> not set when parameter value is not digits
if number_of_names > global_display_names_num then -- template has more names than global config allows to be displayed?
utilities.set_message ('maint_overridden_setting'); -- set a maint message because global is overriding local |display-<namelist>=etal
return global_display_names, 'cs1 config'; -- return global with spoof parameter name (for get_display_names())
else
return local_display_names, param_name; -- return local because fewer names so let <local_display_names> control
end
end
-- here when <global_display_names> and <local_display_names> both numbers; <global_display_names> controls
utilities.set_message ('maint_overridden_setting'); -- set a maint message
return global_display_names, 'cs1 config'; -- return global with spoof parameter name (for get_display_names())
end
-- here when only one of <global_display_names> or <local_display_names> set
if global_display_names then
return global_display_names, 'cs1 config'; -- return global with spoof parameter name (for get_display_names())
else
return local_display_names, param_name; -- return local
end
end
--[[--------------------------< M O D E _ S E T >--------------------------------------------------------------
fetch global mode setting from {{cs1 config}} (if present) or from |mode= (if present); global setting overrides
local |mode= parameter value. When both are present, emit maintenance message
]]
local function mode_set (Mode, Mode_origin)
local mode;
if cfg.global_cs1_config_t['Mode'] then -- global setting in {{cs1 config}}; nil when empty or assigned value invalid
mode = is_valid_parameter_value (cfg.global_cs1_config_t['Mode'], 'cs1 config: mode', cfg.keywords_lists['mode'], ''); -- error messaging 'param' here is a hoax
else
mode = is_valid_parameter_value (Mode, Mode_origin, cfg.keywords_lists['mode'], '');
end
if cfg.global_cs1_config_t['Mode'] and utilities.is_set (Mode) then -- when template has |mode=<something> which global setting has overridden
utilities.set_message ('maint_overridden_setting'); -- set a maint message
end
return mode;
end
--[[--------------------------< Q U O T E _ M A K E >----------------------------------------------------------
create quotation from |quote=, |trans-quote=, and/or script-quote= with or without |quote-page= or |quote-pages=
when any of those three quote parameters are set, this function unsets <PostScript>. When none of those parameters
are set, |quote-page= and |quote-pages= are unset to nil so that they are not included in the template's metadata
]]
local function quote_make (quote, trans_quote, script_quote, quote_page, quote_pages, nopp, sepc, postscript)
if utilities.is_set (quote) or utilities.is_set (trans_quote) or utilities.is_set (script_quote) then
if utilities.is_set (quote) then
if quote:sub(1, 1) == '"' and quote:sub(-1, -1) == '"' then -- if first and last characters of quote are quote marks
quote = quote:sub(2, -2); -- strip them off
end
end
quote = kern_quotes (quote); -- kern if needed
quote = utilities.wrap_style ('quoted-text', quote ); -- wrap in <q>...</q> tags
if utilities.is_set (script_quote) then
quote = script_concatenate (quote, script_quote, 'script-quote'); -- <bdi> tags, lang attribute, categorization, etc.; must be done after quote is wrapped
end
if utilities.is_set (trans_quote) then
if trans_quote:sub(1, 1) == '"' and trans_quote:sub(-1, -1) == '"' then -- if first and last characters of |trans-quote are quote marks
trans_quote = trans_quote:sub(2, -2); -- strip them off
end
quote = quote .. " " .. utilities.wrap_style ('trans-quoted-title', trans_quote );
end
if utilities.is_set (quote_page) or utilities.is_set (quote_pages) then -- add page prefix
local quote_prefix = '';
if utilities.is_set (quote_page) then
extra_text_in_page_check (quote_page, 'quote-page'); -- add to maint cat if |quote-page= value begins with what looks like p., pp., etc.
if not nopp then
quote_prefix = utilities.substitute (cfg.messages['p-prefix'], {sepc, quote_page}), '', '', '';
else
quote_prefix = utilities.substitute (cfg.messages['nopp'], {sepc, quote_page}), '', '', '';
end
elseif utilities.is_set (quote_pages) then
extra_text_in_page_check (quote_pages, 'quote-pages'); -- add to maint cat if |quote-pages= value begins with what looks like p., pp., etc.
if tonumber(quote_pages) ~= nil and not nopp then -- if only digits, assume single page
quote_prefix = utilities.substitute (cfg.messages['p-prefix'], {sepc, quote_pages}), '', '';
elseif not nopp then
quote_prefix = utilities.substitute (cfg.messages['pp-prefix'], {sepc, quote_pages}), '', '';
else
quote_prefix = utilities.substitute (cfg.messages['nopp'], {sepc, quote_pages}), '', '';
end
end
quote = quote_prefix .. ": " .. quote;
else
quote = sepc .. " " .. quote;
end
postscript = ""; -- cs1|2 does not supply terminal punctuation when |quote= is set
elseif utilities.is_set (quote_page) or utilities.is_set (quote_pages) then
quote_page = nil; -- unset; these require |quote=; TODO: error message?
quote_pages = nil;
end
return quote, quote_page, quote_pages, postscript;
end
--[[--------------------------< C H E C K _ P U B L I S H E R _ N A M E >--------------------------------------
look for variations of '<text>: <text>' that might be '<location>: <publisher>' in |publisher= parameter value.
when found, emit a maintenance message; return nil else
<publisher> is the value assigned to |publisher= or |institution=
]]
local function check_publisher_name (publisher)
local patterns_t = {
'^[%w%s]+%s*:%s*[%w%s]+$', -- plain text <location>: <publisher>
'^%[+[%w%s:|]+%]+%s*:%s*[%w%s]+$', -- partially wikilinked [[<location>]]: <publisher>
'^[%w%s]+%s*:%s*%[+[%w%s:|]+%]+$', -- partially wikilinked <location>: [[<publisher>]]
'^%[+[%w%s:|]+%]+%s*:%s*%[+[%w%s:|]+%]+$', -- wikilinked [[<location>]]: [[<publisher>]]
}
for _, pattern in ipairs (patterns_t) do -- spin through the patterns_t sequence
if mw.ustring.match (publisher, pattern) then -- does this pattern match?
utilities.set_message ('maint_publisher_location'); -- set a maint message
return; -- and done
end
end
end
--[[--------------------------< C I T A T I O N 0 >------------------------------------------------------------
This is the main function doing the majority of the citation formatting.
]]
local function citation0( config, args )
--[[
Load Input Parameters
The argument_wrapper facilitates the mapping of multiple aliases to single internal variable.
]]
local A = argument_wrapper ( args );
local i
-- Pick out the relevant fields from the arguments. Different citation templates
-- define different field names for the same underlying things.
local author_etal;
local a = {}; -- authors list from |lastn= / |firstn= pairs or |vauthors=
local Authors;
local NameListStyle;
if cfg.global_cs1_config_t['NameListStyle'] then -- global setting in {{cs1 config}} overrides local |name-list-style= parameter value; nil when empty or assigned value invalid
NameListStyle = is_valid_parameter_value (cfg.global_cs1_config_t['NameListStyle'], 'cs1 config: name-list-style', cfg.keywords_lists['name-list-style'], ''); -- error messaging 'param' here is a hoax
else
NameListStyle = is_valid_parameter_value (A['NameListStyle'], A:ORIGIN('NameListStyle'), cfg.keywords_lists['name-list-style'], '');
end
if cfg.global_cs1_config_t['NameListStyle'] and utilities.is_set (A['NameListStyle']) then -- when template has |name-list-style=<something> which global setting has overridden
utilities.set_message ('maint_overridden_setting'); -- set a maint message
end
local Collaboration = A['Collaboration'];
do -- to limit scope of selected
local selected = select_author_editor_source (A['Vauthors'], A['Authors'], args, 'AuthorList');
if 1 == selected then
a, author_etal = extract_names (args, 'AuthorList'); -- fetch author list from |authorn= / |lastn= / |firstn=, |author-linkn=, and |author-maskn=
elseif 2 == selected then
NameListStyle = 'vanc'; -- override whatever |name-list-style= might be
a, author_etal = parse_vauthors_veditors (args, A['Vauthors'], 'AuthorList'); -- fetch author list from |vauthors=, |author-linkn=, and |author-maskn=
elseif 3 == selected then
Authors = A['Authors']; -- use content of |people= or |credits=; |authors= is deprecated; TODO: constrain |people= and |credits= to cite av media, episode, serial?
end
if utilities.is_set (Collaboration) then
author_etal = true; -- so that |display-authors=etal not required
end
end
local editor_etal;
local e = {}; -- editors list from |editor-lastn= / |editor-firstn= pairs or |veditors=
do -- to limit scope of selected
local selected = select_author_editor_source (A['Veditors'], nil, args, 'EditorList'); -- support for |editors= withdrawn
if 1 == selected then
e, editor_etal = extract_names (args, 'EditorList'); -- fetch editor list from |editorn= / |editor-lastn= / |editor-firstn=, |editor-linkn=, and |editor-maskn=
elseif 2 == selected then
NameListStyle = 'vanc'; -- override whatever |name-list-style= might be
e, editor_etal = parse_vauthors_veditors (args, args.veditors, 'EditorList'); -- fetch editor list from |veditors=, |editor-linkn=, and |editor-maskn=
end
end
local Chapter = A['Chapter']; -- done here so that we have access to |contribution= from |chapter= aliases
local Chapter_origin = A:ORIGIN ('Chapter');
local Contribution; -- because contribution is required for contributor(s)
if 'contribution' == Chapter_origin then
Contribution = Chapter; -- get the name of the contribution
end
local c = {}; -- contributors list from |contributor-lastn= / contributor-firstn= pairs
if utilities.in_array (config.CitationClass, {"book", "citation"}) and not utilities.is_set (A['Periodical']) then -- |contributor= and |contribution= only supported in book cites
c = extract_names (args, 'ContributorList'); -- fetch contributor list from |contributorn= / |contributor-lastn=, -firstn=, -linkn=, -maskn=
if 0 < #c then
if not utilities.is_set (Contribution) then -- |contributor= requires |contribution=
utilities.set_message ('err_contributor_missing_required_param', 'contribution'); -- add missing contribution error message
c = {}; -- blank the contributors' table; it is used as a flag later
end
if 0 == #a then -- |contributor= requires |author=
utilities.set_message ('err_contributor_missing_required_param', 'author'); -- add missing author error message
c = {}; -- blank the contributors' table; it is used as a flag later
end
end
else -- if not a book cite
if utilities.select_one (args, cfg.aliases['ContributorList-Last'], 'err_redundant_parameters', 1 ) then -- are there contributor name list parameters?
utilities.set_message ('err_contributor_ignored'); -- add contributor ignored error message
end
Contribution = nil; -- unset
end
local Title = A['Title'];
local TitleLink = A['TitleLink'];
local auto_select = ''; -- default is auto
local accept_link;
TitleLink, accept_link = utilities.has_accept_as_written (TitleLink, true); -- test for accept-this-as-written markup
if (not accept_link) and utilities.in_array (TitleLink, {'none', 'pmc', 'doi'}) then -- check for special keywords
auto_select = TitleLink; -- remember selection for later
TitleLink = ''; -- treat as if |title-link= would have been empty
end
TitleLink = link_title_ok (TitleLink, A:ORIGIN ('TitleLink'), Title, 'title'); -- check for wiki-markup in |title-link= or wiki-markup in |title= when |title-link= is set
local Section = ''; -- {{cite map}} only; preset to empty string for concatenation if not used
if 'map' == config.CitationClass and 'section' == Chapter_origin then
Section = A['Chapter']; -- get |section= from |chapter= alias list; |chapter= and the other aliases not supported in {{cite map}}
Chapter = ''; -- unset for now; will be reset later from |map= if present
end
local Periodical = A['Periodical'];
local Periodical_origin = A:ORIGIN('Periodical');
local ScriptPeriodical = A['ScriptPeriodical'];
local ScriptPeriodical_origin = A:ORIGIN('ScriptPeriodical');
local TransPeriodical = A['TransPeriodical'];
local TransPeriodical_origin = A:ORIGIN ('TransPeriodical');
if (utilities.in_array (config.CitationClass, {'book', 'encyclopaedia'}) and (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical) or utilities.is_set (TransPeriodical))) then
local param;
if utilities.is_set (Periodical) then -- get a parameter name from one of these periodical related meta-parameters
Periodical = ''; -- unset because not valid {{cite book}} or {{cite encyclopedia}} parameters
param = Periodical_origin -- get parameter name for error messaging
elseif utilities.is_set (TransPeriodical) then
TransPeriodical = ''; -- unset because not valid {{cite book}} or {{cite encyclopedia}} parameters
param = TransPeriodical_origin; -- get parameter name for error messaging
elseif utilities.is_set (ScriptPeriodical) then
ScriptPeriodical = ''; -- unset because not valid {{cite book}} or {{cite encyclopedia}} parameters
param = ScriptPeriodical_origin; -- get parameter name for error messaging
end
if utilities.is_set (param) then -- if we found one
utilities.set_message ('err_periodical_ignored', {param}); -- emit an error message
end
end
if utilities.is_set (Periodical) then
local i;
Periodical, i = utilities.strip_apostrophe_markup (Periodical); -- strip apostrophe markup so that metadata isn't contaminated
if i then -- non-zero when markup was stripped so emit an error message
utilities.set_message ('err_apostrophe_markup', {Periodical_origin});
end
end
if 'mailinglist' == config.CitationClass then -- special case for {{cite mailing list}}
if utilities.is_set (Periodical) and utilities.is_set (A ['MailingList']) then -- both set emit an error TODO: make a function for this and similar?
utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', Periodical_origin) .. cfg.presentation['sep_list_pair'] .. utilities.wrap_style ('parameter', 'mailinglist')});
end
Periodical = A ['MailingList']; -- error or no, set Periodical to |mailinglist= value because this template is {{cite mailing list}}
Periodical_origin = A:ORIGIN('MailingList');
end
-- web and news not tested for now because of
-- Wikipedia:Administrators%27_noticeboard#Is_there_a_semi-automated_tool_that_could_fix_these_annoying_"Cite_Web"_errors?
if not (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)) then -- 'periodical' templates require periodical parameter
-- local p = {['journal'] = 'journal', ['magazine'] = 'magazine', ['news'] = 'newspaper', ['web'] = 'website'}; -- for error message
local p = {['journal'] = 'journal', ['magazine'] = 'magazine'}; -- for error message
if p[config.CitationClass] then
utilities.set_message ('err_missing_periodical', {config.CitationClass, p[config.CitationClass]});
end
end
local Volume;
if 'citation' == config.CitationClass then
if utilities.is_set (Periodical) then
if not utilities.in_array (Periodical_origin, cfg.citation_no_volume_t) then -- {{citation}} does not render |volume= when these parameters are used
Volume = A['Volume']; -- but does for all other 'periodicals'
end
elseif utilities.is_set (ScriptPeriodical) then
if 'script-website' ~= ScriptPeriodical_origin then -- {{citation}} does not render volume for |script-website=
Volume = A['Volume']; -- but does for all other 'periodicals'
end
else
Volume = A['Volume']; -- and does for non-'periodical' cites
end
elseif utilities.in_array (config.CitationClass, cfg.templates_using_volume) then -- render |volume= for cs1 according to the configuration settings
Volume = A['Volume'];
end
extra_text_in_vol_iss_check (Volume, A:ORIGIN ('Volume'), 'v');
local Issue;
if 'citation' == config.CitationClass then
if utilities.is_set (Periodical) and utilities.in_array (Periodical_origin, cfg.citation_issue_t) then -- {{citation}} may render |issue= when these parameters are used
Issue = utilities.hyphen_to_dash (A['Issue']);
end
elseif utilities.in_array (config.CitationClass, cfg.templates_using_issue) then -- conference & map books do not support issue; {{citation}} listed here because included in settings table
if not (utilities.in_array (config.CitationClass, {'conference', 'map', 'citation'}) and not (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical))) then
Issue = utilities.hyphen_to_dash (A['Issue']);
end
end
local ArticleNumber;
if utilities.in_array (config.CitationClass, {'journal', 'conference'}) or ('citation' == config.CitationClass and utilities.is_set (Periodical) and 'journal' == Periodical_origin) then
ArticleNumber = A['ArticleNumber'];
end
extra_text_in_vol_iss_check (Issue, A:ORIGIN ('Issue'), 'i');
local Page;
local Pages;
local At;
local QuotePage;
local QuotePages;
if not utilities.in_array (config.CitationClass, cfg.templates_not_using_page) then -- TODO: rewrite to emit ignored parameter error message?
Page = A['Page'];
Pages = utilities.hyphen_to_dash (A['Pages']);
At = A['At'];
QuotePage = A['QuotePage'];
QuotePages = utilities.hyphen_to_dash (A['QuotePages']);
end
local NoPP = is_valid_parameter_value (A['NoPP'], A:ORIGIN('NoPP'), cfg.keywords_lists['yes_true_y'], nil);
local Mode = mode_set (A['Mode'], A:ORIGIN('Mode'));
-- separator character and postscript
local sepc, PostScript = set_style (Mode:lower(), A['PostScript'], config.CitationClass);
local Quote;
Quote, QuotePage, QuotePages, PostScript = quote_make (A['Quote'], A['TransQuote'], A['ScriptQuote'], QuotePage, QuotePages, NoPP, sepc, PostScript);
local Edition = A['Edition'];
local PublicationPlace = place_check (A['PublicationPlace'], A:ORIGIN('PublicationPlace'));
local Place = place_check (A['Place'], A:ORIGIN('Place'));
local PublisherName = A['PublisherName'];
local PublisherName_origin = A:ORIGIN('PublisherName');
if utilities.is_set (PublisherName) and (cfg.keywords_xlate['none'] ~= PublisherName) then
local i = 0;
PublisherName, i = utilities.strip_apostrophe_markup (PublisherName); -- strip apostrophe markup so that metadata isn't contaminated; publisher is never italicized
if i and (0 < i) then -- non-zero when markup was stripped so emit an error message
utilities.set_message ('err_apostrophe_markup', {PublisherName_origin});
end
end
if ('document' == config.CitationClass) and not utilities.is_set (PublisherName) then
utilities.set_message ('err_missing_publisher', {config.CitationClass, 'publisher'});
end
local Newsgroup = A['Newsgroup']; -- TODO: strip apostrophe markup?
local Newsgroup_origin = A:ORIGIN('Newsgroup');
if 'newsgroup' == config.CitationClass then
if utilities.is_set (PublisherName) and (cfg.keywords_xlate['none'] ~= PublisherName) then -- general use parameter |publisher= not allowed in cite newsgroup
utilities.set_message ('err_parameter_ignored', {PublisherName_origin});
end
PublisherName = nil; -- ensure that this parameter is unset for the time being; will be used again after COinS
end
if 'book' == config.CitationClass or 'encyclopaedia' == config.CitationClass or ('citation' == config.CitationClass and not utilities.is_set (Periodical)) then
local accept;
PublisherName, accept = utilities.has_accept_as_written (PublisherName); -- check for and remove accept-as-written markup from |publisher= wrapped
if not accept then -- when no accept-as-written markup
check_publisher_name (PublisherName); -- emit maint message when |publisher= might be prefixed with publisher's location
end
end
local URL = A['URL']; -- TODO: better way to do this for URL, ChapterURL, and MapURL?
local UrlAccess = is_valid_parameter_value (A['UrlAccess'], A:ORIGIN('UrlAccess'), cfg.keywords_lists['url-access'], nil);
if not utilities.is_set (URL) and utilities.is_set (UrlAccess) then
UrlAccess = nil;
utilities.set_message ('err_param_access_requires_param', 'url');
end
local ChapterURL = A['ChapterURL'];
local ChapterUrlAccess = is_valid_parameter_value (A['ChapterUrlAccess'], A:ORIGIN('ChapterUrlAccess'), cfg.keywords_lists['url-access'], nil);
if not utilities.is_set (ChapterURL) and utilities.is_set (ChapterUrlAccess) then
ChapterUrlAccess = nil;
utilities.set_message ('err_param_access_requires_param', {A:ORIGIN('ChapterUrlAccess'):gsub ('%-access', '')});
end
local MapUrlAccess = is_valid_parameter_value (A['MapUrlAccess'], A:ORIGIN('MapUrlAccess'), cfg.keywords_lists['url-access'], nil);
if not utilities.is_set (A['MapURL']) and utilities.is_set (MapUrlAccess) then
MapUrlAccess = nil;
utilities.set_message ('err_param_access_requires_param', {'map-url'});
end
local this_page = mw.title.getCurrentTitle(); -- also used for COinS and for language
local no_tracking_cats = is_valid_parameter_value (A['NoTracking'], A:ORIGIN('NoTracking'), cfg.keywords_lists['yes_true_y'], nil);
-- check this page to see if it is in one of the namespaces that cs1 is not supposed to add to the error categories
if not utilities.is_set (no_tracking_cats) then -- ignore if we are already not going to categorize this page
if cfg.uncategorized_namespaces[this_page.namespace] then -- is this page's namespace id one of the uncategorized namespace ids?
no_tracking_cats = "true"; -- set no_tracking_cats
end
for _, v in ipairs (cfg.uncategorized_subpages) do -- cycle through page name patterns
if this_page.text:match (v) then -- test page name against each pattern
no_tracking_cats = "true"; -- set no_tracking_cats
break; -- bail out if one is found
end
end
end
-- check for extra |page=, |pages= or |at= parameters. (also sheet and sheets while we're at it)
utilities.select_one (args, {'page', 'p', 'pp', 'pages', 'at', 'sheet', 'sheets'}, 'err_redundant_parameters'); -- this is a dummy call simply to get the error message and category
local coins_pages;
Page, Pages, At, coins_pages = insource_loc_get (Page, A:ORIGIN('Page'), Pages, A:ORIGIN('Pages'), At);
if utilities.is_set (PublicationPlace) and utilities.is_set (Place) then -- both |publication-place= and |place= (|location=) allowed if different
utilities.add_prop_cat ('location-test'); -- add property cat to evaluate how often PublicationPlace and Place are used together
if PublicationPlace == Place then
Place = ''; -- unset; don't need both if they are the same
end
elseif not utilities.is_set (PublicationPlace) and utilities.is_set (Place) then -- when only |place= (|location=) is set ...
PublicationPlace = Place; -- promote |place= (|location=) to |publication-place
end
if PublicationPlace == Place then Place = ''; end -- don't need both if they are the same
local URL_origin = A:ORIGIN('URL'); -- get name of parameter that holds URL
local ChapterURL_origin = A:ORIGIN('ChapterURL'); -- get name of parameter that holds ChapterURL
local ScriptChapter = A['ScriptChapter'];
local ScriptChapter_origin = A:ORIGIN ('ScriptChapter');
local Format = A['Format'];
local ChapterFormat = A['ChapterFormat'];
local TransChapter = A['TransChapter'];
local TransChapter_origin = A:ORIGIN ('TransChapter');
local TransTitle = A['TransTitle'];
local ScriptTitle = A['ScriptTitle'];
--[[
Parameter remapping for cite encyclopedia:
When the citation has these parameters:
|encyclopedia= and |title= then map |title= to |article= and |encyclopedia= to |title= for rendering
|encyclopedia= and |article= then map |encyclopedia= to |title= for rendering
|trans-title= maps to |trans-chapter= when |title= is re-mapped
|url= maps to |chapter-url= when |title= is remapped
All other combinations of |encyclopedia=, |title=, and |article= are not modified
]]
local Encyclopedia = A['Encyclopedia']; -- used as a flag by this module and by ~/COinS
local ScriptEncyclopedia = A['ScriptEncyclopedia'];
local TransEncyclopedia = A['TransEncyclopedia'];
if utilities.is_set (Encyclopedia) or utilities.is_set (ScriptEncyclopedia) then -- emit error message when Encyclopedia set but template is other than {{cite encyclopedia}} or {{citation}}
if 'encyclopaedia' ~= config.CitationClass and 'citation' ~= config.CitationClass then
if utilities.is_set (Encyclopedia) then
utilities.set_message ('err_parameter_ignored', {A:ORIGIN ('Encyclopedia')});
else
utilities.set_message ('err_parameter_ignored', {A:ORIGIN ('ScriptEncyclopedia')});
end
Encyclopedia = nil; -- unset these because not supported by this template
ScriptEncyclopedia = nil;
TransEncyclopedia = nil;
end
elseif utilities.is_set (TransEncyclopedia) then
utilities.set_message ('err_trans_missing_title', {'encyclopedia'});
end
if ('encyclopaedia' == config.CitationClass) or ('citation' == config.CitationClass and utilities.is_set (Encyclopedia)) then
if utilities.is_set (Periodical) and utilities.is_set (Encyclopedia) then -- when both parameters set emit an error message; {{citation}} only; Periodical not allowed in {{cite encyclopedia}}
utilities.set_message ('err_periodical_ignored', {Periodical_origin});
end
if utilities.is_set (Encyclopedia) or utilities.is_set (ScriptEncyclopedia) then
Periodical = Encyclopedia; -- error or no, set Periodical to Encyclopedia for rendering; {{citation}} could (not legitimately) have both; use Encyclopedia
Periodical_origin = A:ORIGIN ('Encyclopedia');
ScriptPeriodical = ScriptEncyclopedia;
ScriptPeriodical_origin = A:ORIGIN ('ScriptEncyclopedia');
if utilities.is_set (Title) or utilities.is_set (ScriptTitle) then
if not utilities.is_set (Chapter) then
Chapter = Title; -- |encyclopedia= and |title= are set so map |title= params to |article= params for rendering
ScriptChapter = ScriptTitle;
ScriptChapter_origin = A:ORIGIN('ScriptTitle')
TransChapter = TransTitle;
ChapterURL = URL;
ChapterURL_origin = URL_origin;
ChapterUrlAccess = UrlAccess;
ChapterFormat = Format;
if not utilities.is_set (ChapterURL) and utilities.is_set (TitleLink) then
Chapter = utilities.make_wikilink (TitleLink, Chapter);
end
Title = Periodical; -- now map |encyclopedia= params to |title= params for rendering
ScriptTitle = ScriptPeriodical or '';
TransTitle = TransEncyclopedia or '';
Periodical = ''; -- redundant so unset
ScriptPeriodical = '';
URL = '';
Format = '';
TitleLink = '';
end
elseif utilities.is_set (Chapter) or utilities.is_set (ScriptChapter) then -- |title= not set
Title = Periodical; -- |encyclopedia= set and |article= set so map |encyclopedia= to |title= for rendering
ScriptTitle = ScriptPeriodical or '';
TransTitle = TransEncyclopedia or '';
Periodical = ''; -- redundant so unset
ScriptPeriodical = '';
end
end
end
-- special case for cite techreport.
local ID = A['ID'];
if (config.CitationClass == "techreport") then -- special case for cite techreport
if utilities.is_set (A['Number']) then -- cite techreport uses 'number', which other citations alias to 'issue'
if not utilities.is_set (ID) then -- can we use ID for the "number"?
ID = A['Number']; -- yes, use it
else -- ID has a value so emit error message
utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', 'id') .. cfg.presentation['sep_list_pair'] .. utilities.wrap_style ('parameter', 'number')});
end
end
end
-- Account for the oddity that is {{cite conference}}, before generation of COinS data.
local ChapterLink -- = A['ChapterLink']; -- deprecated as a parameter but still used internally by cite episode
local Conference = A['Conference'];
local BookTitle = A['BookTitle'];
local TransTitle_origin = A:ORIGIN ('TransTitle');
if 'conference' == config.CitationClass then
if utilities.is_set (BookTitle) then
Chapter = Title;
Chapter_origin = 'title';
-- ChapterLink = TitleLink; -- |chapter-link= is deprecated
ChapterURL = URL;
ChapterUrlAccess = UrlAccess;
ChapterURL_origin = URL_origin;
URL_origin = '';
ChapterFormat = Format;
TransChapter = TransTitle;
TransChapter_origin = TransTitle_origin;
Title = BookTitle;
Format = '';
-- TitleLink = '';
TransTitle = '';
URL = '';
end
elseif 'speech' ~= config.CitationClass then
Conference = ''; -- not cite conference or cite speech so make sure this is empty string
end
local use_lowercase = ( sepc == ',' ); -- controls capitalization of certain static text
-- cite map oddities
local Cartography = "";
local Scale = "";
local Sheet = A['Sheet'] or '';
local Sheets = A['Sheets'] or '';
if config.CitationClass == "map" then
if utilities.is_set (Chapter) then --TODO: make a function for this and similar?
utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', 'map') .. cfg.presentation['sep_list_pair'] .. utilities.wrap_style ('parameter', Chapter_origin)}); -- add error message
end
Chapter = A['Map'];
Chapter_origin = A:ORIGIN('Map');
ChapterURL = A['MapURL'];
ChapterURL_origin = A:ORIGIN('MapURL');
TransChapter = A['TransMap'];
ScriptChapter = A['ScriptMap']
ScriptChapter_origin = A:ORIGIN('ScriptMap')
ChapterUrlAccess = MapUrlAccess;
ChapterFormat = A['MapFormat'];
Cartography = A['Cartography'];
if utilities.is_set ( Cartography ) then
Cartography = sepc .. " " .. wrap_msg ('cartography', Cartography, use_lowercase);
end
Scale = A['Scale'];
if utilities.is_set ( Scale ) then
Scale = sepc .. " " .. Scale;
end
end
-- Account for the oddities that are {{cite episode}} and {{cite serial}}, before generation of COinS data.
local Series = A['Series'];
if 'episode' == config.CitationClass or 'serial' == config.CitationClass then
local SeriesLink = A['SeriesLink'];
SeriesLink = link_title_ok (SeriesLink, A:ORIGIN ('SeriesLink'), Series, 'series'); -- check for wiki-markup in |series-link= or wiki-markup in |series= when |series-link= is set
local Network = A['Network'];
local Station = A['Station'];
local s, n = {}, {};
-- do common parameters first
if utilities.is_set (Network) then table.insert(n, Network); end
if utilities.is_set (Station) then table.insert(n, Station); end
ID = table.concat(n, sepc .. ' ');
if 'episode' == config.CitationClass then -- handle the oddities that are strictly {{cite episode}}
local Season = A['Season'];
local SeriesNumber = A['SeriesNumber'];
if utilities.is_set (Season) and utilities.is_set (SeriesNumber) then -- these are mutually exclusive so if both are set TODO: make a function for this and similar?
utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', 'season') .. cfg.presentation['sep_list_pair'] .. utilities.wrap_style ('parameter', 'seriesno')}); -- add error message
SeriesNumber = ''; -- unset; prefer |season= over |seriesno=
end
-- assemble a table of parts concatenated later into Series
if utilities.is_set (Season) then table.insert(s, wrap_msg ('season', Season, use_lowercase)); end
if utilities.is_set (SeriesNumber) then table.insert(s, wrap_msg ('seriesnum', SeriesNumber, use_lowercase)); end
if utilities.is_set (Issue) then table.insert(s, wrap_msg ('episode', Issue, use_lowercase)); end
Issue = ''; -- unset because this is not a unique parameter
Chapter = Title; -- promote title parameters to chapter
ScriptChapter = ScriptTitle;
ScriptChapter_origin = A:ORIGIN('ScriptTitle');
ChapterLink = TitleLink; -- alias |episode-link=
TransChapter = TransTitle;
ChapterURL = URL;
ChapterUrlAccess = UrlAccess;
ChapterURL_origin = URL_origin;
ChapterFormat = Format;
Title = Series; -- promote series to title
TitleLink = SeriesLink;
Series = table.concat(s, sepc .. ' '); -- this is concatenation of season, seriesno, episode number
if utilities.is_set (ChapterLink) and not utilities.is_set (ChapterURL) then -- link but not URL
Chapter = utilities.make_wikilink (ChapterLink, Chapter);
elseif utilities.is_set (ChapterLink) and utilities.is_set (ChapterURL) then -- if both are set, URL links episode;
Series = utilities.make_wikilink (ChapterLink, Series);
end
URL = ''; -- unset
TransTitle = '';
ScriptTitle = '';
Format = '';
else -- now oddities that are cite serial
Issue = ''; -- unset because this parameter no longer supported by the citation/core version of cite serial
Chapter = A['Episode']; -- TODO: make |episode= available to cite episode someday?
if utilities.is_set (Series) and utilities.is_set (SeriesLink) then
Series = utilities.make_wikilink (SeriesLink, Series);
end
Series = utilities.wrap_style ('italic-title', Series); -- series is italicized
end
end
-- end of {{cite episode}} stuff
-- handle type parameter for those CS1 citations that have default values
local TitleType = A['TitleType'];
local Degree = A['Degree'];
if utilities.in_array (config.CitationClass, {'AV-media-notes', 'document', 'interview', 'mailinglist', 'map', 'podcast', 'pressrelease', 'report', 'speech', 'techreport', 'thesis'}) then
TitleType = set_titletype (config.CitationClass, TitleType);
if utilities.is_set (Degree) and "Thesis" == TitleType then -- special case for cite thesis
TitleType = Degree .. ' ' .. cfg.title_types ['thesis']:lower();
end
end
if utilities.is_set (TitleType) then -- if type parameter is specified
TitleType = utilities.substitute ( cfg.messages['type'], TitleType); -- display it in parentheses
-- TODO: Hack on TitleType to fix bunched parentheses problem
end
-- legacy: promote PublicationDate to Date if neither Date nor Year are set.
local Date = A['Date'];
local Date_origin; -- to hold the name of parameter promoted to Date; required for date error messaging
local PublicationDate = A['PublicationDate'];
local Year = A['Year'];
if utilities.is_set (Year) then
validation.year_check (Year); -- returns nothing; emits maint message when |year= doesn't hold a 'year' value
end
if not utilities.is_set (Date) then
Date = Year; -- promote Year to Date
Year = nil; -- make nil so Year as empty string isn't used for CITEREF
if not utilities.is_set (Date) and utilities.is_set (PublicationDate) then -- use PublicationDate when |date= and |year= are not set
Date = PublicationDate; -- promote PublicationDate to Date
PublicationDate = ''; -- unset, no longer needed
Date_origin = A:ORIGIN('PublicationDate'); -- save the name of the promoted parameter
else
Date_origin = A:ORIGIN('Year'); -- save the name of the promoted parameter
end
else
Date_origin = A:ORIGIN('Date'); -- not a promotion; name required for error messaging
end
if PublicationDate == Date then PublicationDate = ''; end -- if PublicationDate is same as Date, don't display in rendered citation
--[[
Go test all of the date-holding parameters for valid MOS:DATE format and make sure that dates are real dates. This must be done before we do COinS because here is where
we get the date used in the metadata.
Date validation supporting code is in Module:Citation/CS1/Date_validation
]]
local DF = is_valid_parameter_value (A['DF'], A:ORIGIN('DF'), cfg.keywords_lists['df'], '');
if not utilities.is_set (DF) then
DF = cfg.global_df; -- local |df= if present overrides global df set by {{use xxx date}} template
end
local ArchiveURL;
local ArchiveDate;
local ArchiveFormat = A['ArchiveFormat'];
local archive_url_timestamp; -- timestamp from wayback machine url
ArchiveURL, ArchiveDate, archive_url_timestamp = archive_url_check (A['ArchiveURL'], A['ArchiveDate'])
ArchiveFormat = style_format (ArchiveFormat, ArchiveURL, 'archive-format', 'archive-url');
ArchiveURL, ArchiveDate = is_unique_archive_url (ArchiveURL, URL, ChapterURL, A:ORIGIN('ArchiveURL'), ArchiveDate); -- add error message when URL or ChapterURL == ArchiveURL
local AccessDate = A['AccessDate'];
local COinS_date = {}; -- holds date info extracted from |date= for the COinS metadata by Module:Date verification
local DoiBroken = A['DoiBroken'];
local Embargo = A['Embargo'];
local anchor_year; -- used in the CITEREF identifier
do -- create defined block to contain local variables error_message, date_parameters_list, mismatch
local error_message = '';
-- AirDate has been promoted to Date so not necessary to check it
local date_parameters_list = {
['access-date'] = {val = AccessDate, name = A:ORIGIN ('AccessDate')},
['archive-date'] = {val = ArchiveDate, name = A:ORIGIN ('ArchiveDate')},
['date'] = {val = Date, name = Date_origin},
['doi-broken-date'] = {val = DoiBroken, name = A:ORIGIN ('DoiBroken')},
['pmc-embargo-date'] = {val = Embargo, name = A:ORIGIN ('Embargo')},
['publication-date'] = {val = PublicationDate, name = A:ORIGIN ('PublicationDate')},
['year'] = {val = Year, name = A:ORIGIN ('Year')},
};
local error_list = {};
anchor_year, Embargo = validation.dates(date_parameters_list, COinS_date, error_list);
if utilities.is_set (Year) and utilities.is_set (Date) then -- both |date= and |year= not normally needed;
validation.year_date_check (Year, A:ORIGIN ('Year'), Date, A:ORIGIN ('Date'), error_list);
end
if 0 == #error_list then -- error free dates only; 0 when error_list is empty
local modified = false; -- flag
if utilities.is_set (DF) then -- if we need to reformat dates
modified = validation.reformat_dates (date_parameters_list, DF); -- reformat to DF format, use long month names if appropriate
end
if true == validation.date_hyphen_to_dash (date_parameters_list) then -- convert hyphens to dashes where appropriate
modified = true;
utilities.set_message ('maint_date_format'); -- hyphens were converted so add maint category
end
-- for those wikis that can and want to have English date names translated to the local language; not supported at en.wiki
if cfg.date_name_auto_xlate_enable and validation.date_name_xlate (date_parameters_list, cfg.date_digit_auto_xlate_enable ) then
utilities.set_message ('maint_date_auto_xlated'); -- add maint cat
modified = true;
end
if modified then -- if the date_parameters_list values were modified
AccessDate = date_parameters_list['access-date'].val; -- overwrite date holding parameters with modified values
ArchiveDate = date_parameters_list['archive-date'].val;
Date = date_parameters_list['date'].val;
DoiBroken = date_parameters_list['doi-broken-date'].val;
PublicationDate = date_parameters_list['publication-date'].val;
end
if archive_url_timestamp and utilities.is_set (ArchiveDate) then
validation.archive_date_check (ArchiveDate, archive_url_timestamp, DF); -- does YYYYMMDD in archive_url_timestamp match date in ArchiveDate
end
else
utilities.set_message ('err_bad_date', {utilities.make_sep_list (#error_list, error_list)}); -- add this error message
end
end -- end of do
if utilities.in_array (config.CitationClass, {'book', 'encyclopaedia'}) or -- {{cite book}}, {{cite encyclopedia}}; TODO: {{cite conference}} and others?
('citation' == config.CitationClass and utilities.is_set (Encyclopedia)) or -- {{citation}} as an encylopedia citation
('citation' == config.CitationClass and not utilities.is_set (Periodical)) then -- {{citation}} as a book citation
if utilities.is_set (PublicationPlace) then
if not utilities.is_set (PublisherName) then
local date = COinS_date.rftdate and tonumber (COinS_date.rftdate:match ('%d%d%d%d')); -- get year portion of COinS date (because in Arabic numerals); convert string to number
if date and (1850 <= date) then -- location has no publisher; if date is 1850 or later
utilities.set_message ('maint_location_no_publisher'); -- add maint cat
end
else -- PublisherName has a value
if cfg.keywords_xlate['none'] == PublisherName then -- if that value is 'none' (only for book and encyclopedia citations)
PublisherName = ''; -- unset
end
end
end
end
local ID_list = {}; -- sequence table of rendered identifiers
local ID_list_coins = {}; -- table of identifiers and their values from args; key is same as cfg.id_handlers's key
local Class = A['Class']; -- arxiv class identifier
local ID_support = {
{A['ASINTLD'], 'ASIN', 'err_asintld_missing_asin', A:ORIGIN ('ASINTLD')},
{DoiBroken, 'DOI', 'err_doibroken_missing_doi', A:ORIGIN ('DoiBroken')},
{Embargo, 'PMC', 'err_embargo_missing_pmc', A:ORIGIN ('Embargo')},
}
ID_list, ID_list_coins = identifiers.identifier_lists_get (args, {DoiBroken = DoiBroken, ASINTLD = A['ASINTLD'], Embargo = Embargo, Class = Class, Year=anchor_year}, ID_support);
-- Account for the oddities that are {{cite arxiv}}, {{cite biorxiv}}, {{cite citeseerx}}, {{cite medrxiv}}, {{cite ssrn}}, before generation of COinS data.
if utilities.in_array (config.CitationClass, whitelist.preprint_template_list_t) then -- |arxiv= or |eprint= required for cite arxiv; |biorxiv=, |citeseerx=, |medrxiv=, |ssrn= required for their templates
if not (args[cfg.id_handlers[config.CitationClass:upper()].parameters[1]] or -- can't use ID_list_coins k/v table here because invalid parameters omitted
args[cfg.id_handlers[config.CitationClass:upper()].parameters[2]]) then -- which causes unexpected parameter missing error message
utilities.set_message ('err_' .. config.CitationClass .. '_missing'); -- add error message
end
Periodical = ({['arxiv'] = 'arXiv', ['biorxiv'] = 'bioRxiv', ['citeseerx'] = 'CiteSeerX', ['medrxiv'] = 'medRxiv', ['ssrn'] = 'Social Science Research Network'})[config.CitationClass];
end
-- Link the title of the work if no |url= was provided, but we have a |pmc= or a |doi= with |doi-access=free
if config.CitationClass == "journal" and not utilities.is_set (URL) and not utilities.is_set (TitleLink) and not utilities.in_array (cfg.keywords_xlate[Title], {'off', 'none'}) then -- TODO: remove 'none' once existing citations have been switched to 'off', so 'none' can be used as token for "no title" instead
if 'none' ~= cfg.keywords_xlate[auto_select] then -- if auto-linking not disabled
if identifiers.auto_link_urls[auto_select] then -- manual selection
URL = identifiers.auto_link_urls[auto_select]; -- set URL to be the same as identifier's external link
URL_origin = cfg.id_handlers[auto_select:upper()].parameters[1]; -- set URL_origin to parameter name for use in error message if citation is missing a |title=
elseif identifiers.auto_link_urls['pmc'] then -- auto-select PMC
URL = identifiers.auto_link_urls['pmc']; -- set URL to be the same as the PMC external link if not embargoed
URL_origin = cfg.id_handlers['PMC'].parameters[1]; -- set URL_origin to parameter name for use in error message if citation is missing a |title=
elseif identifiers.auto_link_urls['doi'] then -- auto-select DOI
URL = identifiers.auto_link_urls['doi'];
URL_origin = cfg.id_handlers['DOI'].parameters[1];
end
end
if utilities.is_set (URL) then -- set when using an identifier-created URL
if utilities.is_set (AccessDate) then -- |access-date= requires |url=; identifier-created URL is not |url=
utilities.set_message ('err_accessdate_missing_url'); -- add an error message
AccessDate = ''; -- unset
end
if utilities.is_set (ArchiveURL) then -- |archive-url= requires |url=; identifier-created URL is not |url=
utilities.set_message ('err_archive_missing_url'); -- add an error message
ArchiveURL = ''; -- unset
end
end
end
-- At this point fields may be nil if they weren't specified in the template use. We can use that fact.
-- Test if citation has no title
if not utilities.is_set (Title) and not utilities.is_set (TransTitle) and not utilities.is_set (ScriptTitle) then -- has special case for cite episode
utilities.set_message ('err_citation_missing_title', {'episode' == config.CitationClass and 'series' or 'title'});
end
if utilities.in_array (cfg.keywords_xlate[Title], {'off', 'none'}) and
utilities.in_array (config.CitationClass, {'journal', 'citation'}) and
(utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)) and
('journal' == Periodical_origin or 'script-journal' == ScriptPeriodical_origin) then -- special case for journal cites
Title = ''; -- set title to empty string
utilities.set_message ('maint_untitled'); -- add maint cat
end
-- COinS metadata (see <http://ocoins.info/>) for automated parsing of citation information.
-- handle the oddity that is cite encyclopedia and {{citation |encyclopedia=something}}. Here we presume that
-- when Periodical, Title, and Chapter are all set, then Periodical is the book (encyclopedia) title, Title
-- is the article title, and Chapter is a section within the article. So, we remap
local coins_chapter = Chapter; -- default assuming that remapping not required
local coins_title = Title; -- et tu
if 'encyclopaedia' == config.CitationClass or ('citation' == config.CitationClass and utilities.is_set (Encyclopedia)) then
if utilities.is_set (Chapter) and utilities.is_set (Title) and utilities.is_set (Periodical) then -- if all are used then
coins_chapter = Title; -- remap
coins_title = Periodical;
end
end
local coins_author = a; -- default for coins rft.au
if 0 < #c then -- but if contributor list
coins_author = c; -- use that instead
end
-- this is the function call to COinS()
local OCinSoutput = metadata.COinS({
['Periodical'] = utilities.strip_apostrophe_markup (Periodical), -- no markup in the metadata
['Encyclopedia'] = Encyclopedia, -- just a flag; content ignored by ~/COinS
['Chapter'] = metadata.make_coins_title (coins_chapter, ScriptChapter), -- Chapter and ScriptChapter stripped of bold / italic / accept-as-written markup
['Degree'] = Degree; -- cite thesis only
['Title'] = metadata.make_coins_title (coins_title, ScriptTitle), -- Title and ScriptTitle stripped of bold / italic / accept-as-written markup
['PublicationPlace'] = PublicationPlace,
['Date'] = COinS_date.rftdate, -- COinS_date.* has correctly formatted date values if Date is valid;
['Season'] = COinS_date.rftssn,
['Quarter'] = COinS_date.rftquarter,
['Chron'] = COinS_date.rftchron,
['Series'] = Series,
['Volume'] = Volume,
['Issue'] = Issue,
['ArticleNumber'] = ArticleNumber,
['Pages'] = coins_pages or metadata.get_coins_pages (first_set ({Sheet, Sheets, Page, Pages, At, QuotePage, QuotePages}, 7)), -- pages stripped of external links
['Edition'] = Edition,
['PublisherName'] = PublisherName or Newsgroup, -- any apostrophe markup already removed from PublisherName
['URL'] = first_set ({ChapterURL, URL}, 2),
['Authors'] = coins_author,
['ID_list'] = ID_list_coins,
['RawPage'] = this_page.prefixedText,
}, config.CitationClass);
-- Account for the oddities that are {{cite arxiv}}, {{cite biorxiv}}, {{cite citeseerx}}, {{cite medrxiv}}, and {{cite ssrn}} AFTER generation of COinS data.
if utilities.in_array (config.CitationClass, whitelist.preprint_template_list_t) then -- we have set rft.jtitle in COinS to arXiv, bioRxiv, CiteSeerX, medRxiv, or ssrn now unset so it isn't displayed
Periodical = ''; -- periodical not allowed in these templates; if article has been published, use cite journal
end
-- special case for cite newsgroup. Do this after COinS because we are modifying Publishername to include some static text
if 'newsgroup' == config.CitationClass and utilities.is_set (Newsgroup) then
PublisherName = utilities.substitute (cfg.messages['newsgroup'], external_link( 'news:' .. Newsgroup, Newsgroup, Newsgroup_origin, nil ));
end
local Editors;
local EditorCount; -- used only for choosing {ed.) or (eds.) annotation at end of editor name-list
local Contributors; -- assembled contributors name list
local contributor_etal;
local Translators; -- assembled translators name list
local translator_etal;
local t = {}; -- translators list from |translator-lastn= / translator-firstn= pairs
t = extract_names (args, 'TranslatorList'); -- fetch translator list from |translatorn= / |translator-lastn=, -firstn=, -linkn=, -maskn=
local Interviewers;
local interviewers_list = {};
interviewers_list = extract_names (args, 'InterviewerList'); -- process preferred interviewers parameters
local interviewer_etal;
-- Now perform various field substitutions.
-- We also add leading spaces and surrounding markup and punctuation to the
-- various parts of the citation, but only when they are non-nil.
do
local last_first_list;
local control = {
format = NameListStyle, -- empty string, '&', 'amp', 'and', or 'vanc'
maximum = nil, -- as if display-authors or display-editors not set
mode = Mode
};
do -- do editor name list first because the now unsupported coauthors used to modify control table
local display_names, param = display_names_select (cfg.global_cs1_config_t['DisplayEditors'], A['DisplayEditors'], A:ORIGIN ('DisplayEditors'), #e);
control.maximum, editor_etal = get_display_names (display_names, #e, 'editors', editor_etal, param);
Editors, EditorCount = list_people (control, e, editor_etal);
if 1 == EditorCount and (true == editor_etal or 1 < #e) then -- only one editor displayed but includes etal then
EditorCount = 2; -- spoof to display (eds.) annotation
end
end
do -- now do interviewers
local display_names, param = display_names_select (cfg.global_cs1_config_t['DisplayInterviewers'], A['DisplayInterviewers'], A:ORIGIN ('DisplayInterviewers'), #interviewers_list);
control.maximum, interviewer_etal = get_display_names (display_names, #interviewers_list, 'interviewers', interviewer_etal, param);
Interviewers = list_people (control, interviewers_list, interviewer_etal);
end
do -- now do translators
local display_names, param = display_names_select (cfg.global_cs1_config_t['DisplayTranslators'], A['DisplayTranslators'], A:ORIGIN ('DisplayTranslators'), #t);
control.maximum, translator_etal = get_display_names (display_names, #t, 'translators', translator_etal, param);
Translators = list_people (control, t, translator_etal);
end
do -- now do contributors
local display_names, param = display_names_select (cfg.global_cs1_config_t['DisplayContributors'], A['DisplayContributors'], A:ORIGIN ('DisplayContributors'), #c);
control.maximum, contributor_etal = get_display_names (display_names, #c, 'contributors', contributor_etal, param);
Contributors = list_people (control, c, contributor_etal);
end
do -- now do authors
local display_names, param = display_names_select (cfg.global_cs1_config_t['DisplayAuthors'], A['DisplayAuthors'], A:ORIGIN ('DisplayAuthors'), #a, author_etal);
control.maximum, author_etal = get_display_names (display_names, #a, 'authors', author_etal, param);
last_first_list = list_people (control, a, author_etal);
if utilities.is_set (Authors) then
Authors, author_etal = name_has_etal (Authors, author_etal, false, 'authors'); -- find and remove variations on et al.
if author_etal then
Authors = Authors .. ' ' .. cfg.messages['et al']; -- add et al. to authors parameter
end
else
Authors = last_first_list; -- either an author name list or an empty string
end
end -- end of do
if utilities.is_set (Authors) and utilities.is_set (Collaboration) then
Authors = Authors .. ' (' .. Collaboration .. ')'; -- add collaboration after et al.
end
end
local ConferenceFormat = A['ConferenceFormat'];
local ConferenceURL = A['ConferenceURL'];
ConferenceFormat = style_format (ConferenceFormat, ConferenceURL, 'conference-format', 'conference-url');
Format = style_format (Format, URL, 'format', 'url');
-- special case for chapter format so no error message or cat when chapter not supported
if not (utilities.in_array (config.CitationClass, {'web', 'news', 'journal', 'magazine', 'pressrelease', 'podcast', 'newsgroup', 'arxiv', 'biorxiv', 'citeseerx', 'medrxiv', 'ssrn'}) or
('citation' == config.CitationClass and (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)) and not utilities.is_set (Encyclopedia))) then
ChapterFormat = style_format (ChapterFormat, ChapterURL, 'chapter-format', 'chapter-url');
end
if not utilities.is_set (URL) then
if utilities.in_array (config.CitationClass, {"web", "podcast", "mailinglist"}) or -- |url= required for cite web, cite podcast, and cite mailinglist
('citation' == config.CitationClass and ('website' == Periodical_origin or 'script-website' == ScriptPeriodical_origin)) then -- and required for {{citation}} with |website= or |script-website=
utilities.set_message ('err_cite_web_url');
end
-- do we have |accessdate= without either |url= or |chapter-url=?
if utilities.is_set (AccessDate) and not utilities.is_set (ChapterURL) then -- ChapterURL may be set when URL is not set;
utilities.set_message ('err_accessdate_missing_url');
AccessDate = '';
end
end
local UrlStatus = is_valid_parameter_value (A['UrlStatus'], A:ORIGIN('UrlStatus'), cfg.keywords_lists['url-status'], '');
local OriginalURL
local OriginalURL_origin
local OriginalFormat
local OriginalAccess;
UrlStatus = UrlStatus:lower(); -- used later when assembling archived text
if utilities.is_set ( ArchiveURL ) then
if utilities.is_set (ChapterURL) then -- if chapter-url= is set apply archive url to it
OriginalURL = ChapterURL; -- save copy of source chapter's url for archive text
OriginalURL_origin = ChapterURL_origin; -- name of |chapter-url= parameter for error messages
OriginalFormat = ChapterFormat; -- and original |chapter-format=
if 'live' ~= UrlStatus then
ChapterURL = ArchiveURL -- swap-in the archive's URL
ChapterURL_origin = A:ORIGIN('ArchiveURL') -- name of |archive-url= parameter for error messages
ChapterFormat = ArchiveFormat or ''; -- swap in archive's format
ChapterUrlAccess = nil; -- restricted access levels do not make sense for archived URLs
end
elseif utilities.is_set (URL) then
OriginalURL = URL; -- save copy of original source URL
OriginalURL_origin = URL_origin; -- name of URL parameter for error messages
OriginalFormat = Format; -- and original |format=
OriginalAccess = UrlAccess;
if 'live' ~= UrlStatus then -- if URL set then |archive-url= applies to it
URL = ArchiveURL -- swap-in the archive's URL
URL_origin = A:ORIGIN('ArchiveURL') -- name of archive URL parameter for error messages
Format = ArchiveFormat or ''; -- swap in archive's format
UrlAccess = nil; -- restricted access levels do not make sense for archived URLs
end
end
elseif utilities.is_set (UrlStatus) then -- if |url-status= is set when |archive-url= is not set
utilities.set_message ('maint_url_status'); -- add maint cat
end
if utilities.in_array (config.CitationClass, {'web', 'news', 'journal', 'magazine', 'pressrelease', 'podcast', 'newsgroup', 'arxiv', 'biorxiv', 'citeseerx', 'medrxiv', 'ssrn'}) or -- if any of the 'periodical' cites except encyclopedia
('citation' == config.CitationClass and (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)) and not utilities.is_set (Encyclopedia)) then
local chap_param;
if utilities.is_set (Chapter) then -- get a parameter name from one of these chapter related meta-parameters
chap_param = A:ORIGIN ('Chapter')
elseif utilities.is_set (TransChapter) then
chap_param = A:ORIGIN ('TransChapter')
elseif utilities.is_set (ChapterURL) then
chap_param = A:ORIGIN ('ChapterURL')
elseif utilities.is_set (ScriptChapter) then
chap_param = ScriptChapter_origin;
else utilities.is_set (ChapterFormat)
chap_param = A:ORIGIN ('ChapterFormat')
end
if utilities.is_set (chap_param) then -- if we found one
utilities.set_message ('err_chapter_ignored', {chap_param}); -- add error message
Chapter = ''; -- and set them to empty string to be safe with concatenation
TransChapter = '';
ChapterURL = '';
ScriptChapter = '';
ChapterFormat = '';
end
else -- otherwise, format chapter / article title
local no_quotes = false; -- default assume that we will be quoting the chapter parameter value
if utilities.is_set (Contribution) and 0 < #c then -- if this is a contribution with contributor(s)
if utilities.in_array (Contribution:lower(), cfg.keywords_lists.contribution) then -- and a generic contribution title
no_quotes = true; -- then render it unquoted
end
end
Chapter = format_chapter_title (ScriptChapter, ScriptChapter_origin, Chapter, Chapter_origin, TransChapter, TransChapter_origin, ChapterURL, ChapterURL_origin, no_quotes, ChapterUrlAccess); -- Contribution is also in Chapter
if utilities.is_set (Chapter) then
Chapter = Chapter .. ChapterFormat ;
if 'map' == config.CitationClass and utilities.is_set (TitleType) then
Chapter = Chapter .. ' ' .. TitleType; -- map annotation here; not after title
end
Chapter = Chapter .. sepc .. ' ';
elseif utilities.is_set (ChapterFormat) then -- |chapter= not set but |chapter-format= is so ...
Chapter = ChapterFormat .. sepc .. ' '; -- ... ChapterFormat has error message, we want to see it
end
end
-- Format main title
local plain_title = false;
local accept_title;
Title, accept_title = utilities.has_accept_as_written (Title, true); -- remove accept-this-as-written markup when it wraps all of <Title>
if accept_title and ('' == Title) then -- only support forced empty for now "(())"
Title = cfg.messages['notitle']; -- replace by predefined "No title" message
-- TODO: utilities.set_message ( 'err_redundant_parameters', ...); -- issue proper error message instead of muting
ScriptTitle = ''; -- just mute for now
TransTitle = ''; -- just mute for now
plain_title = true; -- suppress text decoration for descriptive title
utilities.set_message ('maint_untitled'); -- add maint cat
end
if not accept_title then -- <Title> not wrapped in accept-as-written markup
if '...' == Title:sub (-3) then -- if ellipsis is the last three characters of |title=
Title = Title:gsub ('(%.%.%.)%.+$', '%1'); -- limit the number of dots to three
elseif not mw.ustring.find (Title, '%.%s*%a%.$') and -- end of title is not a 'dot-(optional space-)letter-dot' initialism ...
not mw.ustring.find (Title, '%s+%a%.$') then -- ...and not a 'space-letter-dot' initial (''Allium canadense'' L.)
Title = mw.ustring.gsub(Title, '%' .. sepc .. '$', ''); -- remove any trailing separator character; sepc and ms.ustring() here for languages that use multibyte separator characters
end
if utilities.is_set (ArchiveURL) and is_archived_copy (Title) then
utilities.set_message ('maint_archived_copy'); -- add maintenance category before we modify the content of Title
end
if is_generic ('generic_titles', Title) then
utilities.set_message ('err_generic_title'); -- set an error message
end
end
if (not plain_title) and (utilities.in_array (config.CitationClass, {'web', 'news', 'journal', 'magazine', 'document', 'pressrelease', 'podcast', 'newsgroup', 'mailinglist', 'interview', 'arxiv', 'biorxiv', 'citeseerx', 'medrxiv', 'ssrn'}) or
('citation' == config.CitationClass and (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)) and not utilities.is_set (Encyclopedia)) or
('map' == config.CitationClass and (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)))) then -- special case for cite map when the map is in a periodical treat as an article
Title = kern_quotes (Title); -- if necessary, separate title's leading and trailing quote marks from module provided quote marks
Title = utilities.wrap_style ('quoted-title', Title);
Title = script_concatenate (Title, ScriptTitle, 'script-title'); -- <bdi> tags, lang attribute, categorization, etc.; must be done after title is wrapped
TransTitle = utilities.wrap_style ('trans-quoted-title', TransTitle );
elseif plain_title or ('report' == config.CitationClass) then -- no styling for cite report and descriptive titles (otherwise same as above)
Title = script_concatenate (Title, ScriptTitle, 'script-title'); -- <bdi> tags, lang attribute, categorization, etc.; must be done after title is wrapped
TransTitle = utilities.wrap_style ('trans-quoted-title', TransTitle ); -- for cite report, use this form for trans-title
else
Title = utilities.wrap_style ('italic-title', Title);
Title = script_concatenate (Title, ScriptTitle, 'script-title'); -- <bdi> tags, lang attribute, categorization, etc.; must be done after title is wrapped
TransTitle = utilities.wrap_style ('trans-italic-title', TransTitle);
end
if utilities.is_set (TransTitle) then
if utilities.is_set (Title) then
TransTitle = " " .. TransTitle;
else
utilities.set_message ('err_trans_missing_title', {'title'});
end
end
if utilities.is_set (Title) then -- TODO: is this the right place to be making Wikisource URLs?
if utilities.is_set (TitleLink) and utilities.is_set (URL) then
utilities.set_message ('err_wikilink_in_url'); -- set an error message because we can't have both
TitleLink = ''; -- unset
end
if not utilities.is_set (TitleLink) and utilities.is_set (URL) then
Title = external_link (URL, Title, URL_origin, UrlAccess) .. TransTitle .. Format;
URL = ''; -- unset these because no longer needed
Format = "";
elseif utilities.is_set (TitleLink) and not utilities.is_set (URL) then
local ws_url;
ws_url = wikisource_url_make (TitleLink); -- ignore ws_label return; not used here
if ws_url then
Title = external_link (ws_url, Title .. ' ', 'ws link in title-link'); -- space char after Title to move icon away from italic text; TODO: a better way to do this?
Title = utilities.substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], TitleLink, Title});
Title = Title .. TransTitle;
else
Title = utilities.make_wikilink (TitleLink, Title) .. TransTitle;
end
else
local ws_url, ws_label, L; -- Title has italic or quote markup by the time we get here which causes is_wikilink() to return 0 (not a wikilink)
ws_url, ws_label, L = wikisource_url_make (Title:gsub('^[\'"]*(.-)[\'"]*$', '%1')); -- make ws URL from |title= interwiki link (strip italic or quote markup); link portion L becomes tooltip label
if ws_url then
Title = Title:gsub ('%b[]', ws_label); -- replace interwiki link with ws_label to retain markup
Title = external_link (ws_url, Title .. ' ', 'ws link in title'); -- space char after Title to move icon away from italic text; TODO: a better way to do this?
Title = utilities.substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], L, Title});
Title = Title .. TransTitle;
else
Title = Title .. TransTitle;
end
end
else
Title = TransTitle;
end
if utilities.is_set (Place) then
Place = " " .. wrap_msg ('written', Place, use_lowercase) .. sepc .. " ";
end
local ConferenceURL_origin = A:ORIGIN('ConferenceURL'); -- get name of parameter that holds ConferenceURL
if utilities.is_set (Conference) then
if utilities.is_set (ConferenceURL) then
Conference = external_link( ConferenceURL, Conference, ConferenceURL_origin, nil );
end
Conference = sepc .. " " .. Conference .. ConferenceFormat;
elseif utilities.is_set (ConferenceURL) then
Conference = sepc .. " " .. external_link( ConferenceURL, nil, ConferenceURL_origin, nil );
end
local Position = '';
if not utilities.is_set (Position) then
local Minutes = A['Minutes'];
local Time = A['Time'];
if utilities.is_set (Minutes) then
if utilities.is_set (Time) then --TODO: make a function for this and similar?
utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', 'minutes') .. cfg.presentation['sep_list_pair'] .. utilities.wrap_style ('parameter', 'time')});
end
Position = " " .. Minutes .. " " .. cfg.messages['minutes'];
else
if utilities.is_set (Time) then
local TimeCaption = A['TimeCaption']
if not utilities.is_set (TimeCaption) then
TimeCaption = cfg.messages['event'];
if sepc ~= '.' then
TimeCaption = TimeCaption:lower();
end
end
Position = " " .. TimeCaption .. " " .. Time;
end
end
else
Position = " " .. Position;
At = '';
end
Page, Pages, Sheet, Sheets = format_pages_sheets (Page, Pages, Sheet, Sheets, config.CitationClass, Periodical_origin, sepc, NoPP, use_lowercase);
At = utilities.is_set (At) and (sepc .. " " .. At) or "";
Position = utilities.is_set (Position) and (sepc .. " " .. Position) or "";
if config.CitationClass == 'map' then
local Sections = A['Sections']; -- Section (singular) is an alias of Chapter so set earlier
local Inset = A['Inset'];
if utilities.is_set ( Inset ) then
Inset = sepc .. " " .. wrap_msg ('inset', Inset, use_lowercase);
end
if utilities.is_set ( Sections ) then
Section = sepc .. " " .. wrap_msg ('sections', Sections, use_lowercase);
elseif utilities.is_set ( Section ) then
Section = sepc .. " " .. wrap_msg ('section', Section, use_lowercase);
end
At = At .. Inset .. Section;
end
local Others = A['Others'];
if utilities.is_set (Others) and 0 == #a and 0 == #e then -- add maint cat when |others= has value and used without |author=, |editor=
if config.CitationClass == "AV-media-notes"
or config.CitationClass == "audio-visual" then -- special maint for AV/M which has a lot of 'false' positives right now
utilities.set_message ('maint_others_avm')
else
utilities.set_message ('maint_others');
end
end
Others = utilities.is_set (Others) and (sepc .. " " .. Others) or "";
if utilities.is_set (Translators) then
Others = safe_join ({sepc .. ' ', wrap_msg ('translated', Translators, use_lowercase), Others}, sepc);
end
if utilities.is_set (Interviewers) then
Others = safe_join ({sepc .. ' ', wrap_msg ('interview', Interviewers, use_lowercase), Others}, sepc);
end
local TitleNote = A['TitleNote'];
TitleNote = utilities.is_set (TitleNote) and (sepc .. " " .. TitleNote) or "";
if utilities.is_set (Edition) then
if Edition:match ('%f[%a][Ee]d%n?%.?$') or Edition:match ('%f[%a][Ee]dition$') then -- Ed, ed, Ed., ed., Edn, edn, Edn., edn.
utilities.set_message ('err_extra_text_edition'); -- add error message
end
Edition = " " .. wrap_msg ('edition', Edition);
else
Edition = '';
end
Series = utilities.is_set (Series) and wrap_msg ('series', {sepc, Series}) or ""; -- not the same as SeriesNum
local Agency = A['Agency'] or ''; -- |agency= is supported by {{cite magazine}}, {{cite news}}, {{cite press release}}, {{cite web}}, and certain {{citation}} templates
if utilities.is_set (Agency) then -- this testing done here because {{citation}} supports 'news' citations
if utilities.in_array (config.CitationClass, {'magazine', 'news', 'pressrelease', 'web'}) or ('citation' == config.CitationClass and utilities.in_array (Periodical_origin, {"magazine", "newspaper", "work"})) then
Agency = wrap_msg ('agency', {sepc, Agency}); -- format for rendering
else
Agency = ''; -- unset; not supported
utilities.set_message ('err_parameter_ignored', {'agency'}); -- add error message
end
end
Volume = format_volume_issue (Volume, Issue, ArticleNumber, config.CitationClass, Periodical_origin, sepc, use_lowercase);
if utilities.is_set (AccessDate) then
local retrv_text = " " .. cfg.messages['retrieved']
AccessDate = nowrap_date (AccessDate); -- wrap in nowrap span if date in appropriate format
if (sepc ~= ".") then retrv_text = retrv_text:lower() end -- if mode is cs2, lower case
AccessDate = utilities.substitute (retrv_text, AccessDate); -- add retrieved text
AccessDate = utilities.substitute (cfg.presentation['accessdate'], {sepc, AccessDate}); -- allow editors to hide accessdates
end
if utilities.is_set (ID) then ID = sepc .. " " .. ID; end
local Docket = A['Docket'];
if "thesis" == config.CitationClass and utilities.is_set (Docket) then
ID = sepc .. " Docket " .. Docket .. ID;
end
if "report" == config.CitationClass and utilities.is_set (Docket) then -- for cite report when |docket= is set
ID = sepc .. ' ' .. Docket; -- overwrite ID even if |id= is set
end
if utilities.is_set (URL) then
URL = " " .. external_link( URL, nil, URL_origin, UrlAccess );
end
-- We check length of PostScript here because it will have been nuked by
-- the quote parameters. We'd otherwise emit a message even if there wasn't
-- a displayed postscript.
-- TODO: Should the max size (1) be configurable?
-- TODO: Should we check a specific pattern?
if utilities.is_set(PostScript) and mw.ustring.len(PostScript) > 1 then
utilities.set_message ('maint_postscript')
end
local Archived;
if utilities.is_set (ArchiveURL) then
if not utilities.is_set (ArchiveDate) then -- ArchiveURL set but ArchiveDate not set
utilities.set_message ('err_archive_missing_date'); -- emit an error message
ArchiveURL = ''; -- empty string for concatenation
ArchiveDate = ''; -- empty string for concatenation
end
else
if utilities.is_set (ArchiveDate) then -- ArchiveURL not set but ArchiveDate is set
utilities.set_message ('err_archive_date_missing_url'); -- emit an error message
ArchiveURL = ''; -- empty string for concatenation
ArchiveDate = ''; -- empty string for concatenation
end
end
if utilities.is_set (ArchiveURL) then
local arch_text;
if "live" == UrlStatus then
arch_text = cfg.messages['archived'];
if sepc ~= "." then arch_text = arch_text:lower() end
if utilities.is_set (ArchiveDate) then
Archived = sepc .. ' ' .. utilities.substitute ( cfg.messages['archived-live'],
{external_link( ArchiveURL, arch_text, A:ORIGIN('ArchiveURL'), nil) .. ArchiveFormat, ArchiveDate } );
else
Archived = '';
end
if not utilities.is_set (OriginalURL) then
utilities.set_message ('err_archive_missing_url');
Archived = ''; -- empty string for concatenation
end
elseif utilities.is_set (OriginalURL) then -- UrlStatus is empty, 'dead', 'unfit', 'usurped', 'bot: unknown'
if utilities.in_array (UrlStatus, {'unfit', 'usurped', 'bot: unknown'}) then
arch_text = cfg.messages['archived-unfit'];
if sepc ~= "." then arch_text = arch_text:lower() end
Archived = sepc .. ' ' .. arch_text .. ArchiveDate; -- format already styled
if 'bot: unknown' == UrlStatus then
utilities.set_message ('maint_bot_unknown'); -- and add a category if not already added
else
utilities.add_prop_cat ('unfit'); -- and add a category if not already added
end
else -- UrlStatus is empty, 'dead'
arch_text = cfg.messages['archived-dead'];
if sepc ~= "." then arch_text = arch_text:lower() end
if utilities.is_set (ArchiveDate) then
Archived = sepc .. " " .. utilities.substitute ( arch_text,
{ external_link( OriginalURL, cfg.messages['original'], OriginalURL_origin, OriginalAccess ) .. OriginalFormat, ArchiveDate } ); -- format already styled
else
Archived = ''; -- unset for concatenation
end
end
else -- OriginalUrl not set
utilities.set_message ('err_archive_missing_url');
Archived = ''; -- empty string for concatenation
end
elseif utilities.is_set (ArchiveFormat) then
Archived = ArchiveFormat; -- if set and ArchiveURL not set ArchiveFormat has error message
else
Archived = '';
end
local TranscriptURL = A['TranscriptURL']
local TranscriptFormat = A['TranscriptFormat'];
TranscriptFormat = style_format (TranscriptFormat, TranscriptURL, 'transcript-format', 'transcripturl');
local Transcript = A['Transcript'];
local TranscriptURL_origin = A:ORIGIN('TranscriptURL'); -- get name of parameter that holds TranscriptURL
if utilities.is_set (Transcript) then
if utilities.is_set (TranscriptURL) then
Transcript = external_link( TranscriptURL, Transcript, TranscriptURL_origin, nil );
end
Transcript = sepc .. ' ' .. Transcript .. TranscriptFormat;
elseif utilities.is_set (TranscriptURL) then
Transcript = external_link( TranscriptURL, nil, TranscriptURL_origin, nil );
end
local Publisher;
if utilities.is_set (PublicationDate) then
PublicationDate = wrap_msg ('published', PublicationDate);
end
if utilities.is_set (PublisherName) then
if utilities.is_set (PublicationPlace) then
Publisher = sepc .. " " .. PublicationPlace .. ": " .. PublisherName .. PublicationDate;
else
Publisher = sepc .. " " .. PublisherName .. PublicationDate;
end
elseif utilities.is_set (PublicationPlace) then
Publisher= sepc .. " " .. PublicationPlace .. PublicationDate;
else
Publisher = PublicationDate;
end
-- Several of the above rely upon detecting this as nil, so do it last.
if (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical) or utilities.is_set (TransPeriodical)) then
if utilities.is_set (Title) or utilities.is_set (TitleNote) then
Periodical = sepc .. " " .. format_periodical (ScriptPeriodical, ScriptPeriodical_origin, Periodical, TransPeriodical, TransPeriodical_origin);
else
Periodical = format_periodical (ScriptPeriodical, ScriptPeriodical_origin, Periodical, TransPeriodical, TransPeriodical_origin);
end
end
local Language = A['Language'];
if utilities.is_set (Language) then
Language = language_parameter (Language); -- format, categories, name from ISO639-1, etc.
else
Language=''; -- language not specified so make sure this is an empty string;
--[[ TODO: need to extract the wrap_msg from language_parameter
so that we can solve parentheses bunching problem with Format/Language/TitleType
]]
end
--[[
Handle the oddity that is cite speech. This code overrides whatever may be the value assigned to TitleNote (through |department=) and forces it to be " (Speech)" so that
the annotation directly follows the |title= parameter value in the citation rather than the |event= parameter value (if provided).
]]
if "speech" == config.CitationClass then -- cite speech only
TitleNote = TitleType; -- move TitleType to TitleNote so that it renders ahead of |event=
TitleType = ''; -- and unset
if utilities.is_set (Periodical) then -- if Periodical, perhaps because of an included |website= or |journal= parameter
if utilities.is_set (Conference) then -- and if |event= is set
Conference = Conference .. sepc .. " "; -- then add appropriate punctuation to the end of the Conference variable before rendering
end
end
end
-- Piece all bits together at last. Here, all should be non-nil.
-- We build things this way because it is more efficient in LUA
-- not to keep reassigning to the same string variable over and over.
local tcommon;
local tcommon2; -- used for book cite when |contributor= is set
if utilities.in_array (config.CitationClass, {"book", "citation"}) and not utilities.is_set (Periodical) then -- special cases for book cites
if utilities.is_set (Contributors) then -- when we are citing foreword, preface, introduction, etc.
tcommon = safe_join ({Title, TitleNote}, sepc); -- author and other stuff will come after this and before tcommon2
tcommon2 = safe_join ({TitleType, Series, Language, Volume, Others, Edition, Publisher}, sepc);
else
tcommon = safe_join ({Title, TitleNote, TitleType, Series, Language, Volume, Others, Edition, Publisher}, sepc);
end
elseif 'map' == config.CitationClass then -- special cases for cite map
if utilities.is_set (Chapter) then -- map in a book; TitleType is part of Chapter
tcommon = safe_join ({Title, Edition, Scale, Series, Language, Cartography, Others, Publisher, Volume}, sepc);
elseif utilities.is_set (Periodical) then -- map in a periodical
tcommon = safe_join ({Title, TitleType, Periodical, Scale, Series, Language, Cartography, Others, Publisher, Volume}, sepc);
else -- a sheet or stand-alone map
tcommon = safe_join ({Title, TitleType, Edition, Scale, Series, Language, Cartography, Others, Publisher}, sepc);
end
elseif 'episode' == config.CitationClass then -- special case for cite episode
tcommon = safe_join ({Title, TitleNote, TitleType, Series, Language, Edition, Publisher}, sepc);
else -- all other CS1 templates
tcommon = safe_join ({Title, TitleNote, Conference, Periodical, TitleType, Series, Language, Volume, Others, Edition, Publisher, Agency}, sepc);
end
if #ID_list > 0 then
ID_list = safe_join( { sepc .. " ", table.concat( ID_list, sepc .. " " ), ID }, sepc );
else
ID_list = ID;
end
local Via = A['Via'];
Via = utilities.is_set (Via) and wrap_msg ('via', Via) or '';
local idcommon;
if 'audio-visual' == config.CitationClass or 'episode' == config.CitationClass then -- special case for cite AV media & cite episode position transcript
idcommon = safe_join( { ID_list, URL, Archived, Transcript, AccessDate, Via, Quote }, sepc );
else
idcommon = safe_join( { ID_list, URL, Archived, AccessDate, Via, Quote }, sepc );
end
local text;
local pgtext = Position .. Sheet .. Sheets .. Page .. Pages .. At;
local OrigDate = A['OrigDate'];
OrigDate = utilities.is_set (OrigDate) and wrap_msg ('origdate', OrigDate) or '';
if utilities.is_set (Date) then
if utilities.is_set (Authors) or utilities.is_set (Editors) then -- date follows authors or editors when authors not set
Date = " (" .. Date .. ")" .. OrigDate .. sepc .. " "; -- in parentheses
else -- neither of authors and editors set
if (string.sub(tcommon, -1, -1) == sepc) then -- if the last character of tcommon is sepc
Date = " " .. Date .. OrigDate; -- Date does not begin with sepc
else
Date = sepc .. " " .. Date .. OrigDate; -- Date begins with sepc
end
end
end
if utilities.is_set (Authors) then
if (not utilities.is_set (Date)) then -- when date is set it's in parentheses; no Authors termination
Authors = terminate_name_list (Authors, sepc); -- when no date, terminate with 0 or 1 sepc and a space
end
if utilities.is_set (Editors) then
local in_text = '';
local post_text = '';
if utilities.is_set (Chapter) and 0 == #c then
in_text = cfg.messages['in'] .. ' ';
if (sepc ~= '.') then
in_text = in_text:lower(); -- lowercase for cs2
end
end
if EditorCount <= 1 then
post_text = ' (' .. cfg.messages['editor'] .. ')'; -- be consistent with no-author, no-date case
else
post_text = ' (' .. cfg.messages['editors'] .. ')';
end
Editors = terminate_name_list (in_text .. Editors .. post_text, sepc); -- terminate with 0 or 1 sepc and a space
end
if utilities.is_set (Contributors) then -- book cite and we're citing the intro, preface, etc.
local by_text = sepc .. ' ' .. cfg.messages['by'] .. ' ';
if (sepc ~= '.') then by_text = by_text:lower() end -- lowercase for cs2
Authors = by_text .. Authors; -- author follows title so tweak it here
if utilities.is_set (Editors) and utilities.is_set (Date) then -- when Editors make sure that Authors gets terminated
Authors = terminate_name_list (Authors, sepc); -- terminate with 0 or 1 sepc and a space
end
if (not utilities.is_set (Date)) then -- when date is set it's in parentheses; no Contributors termination
Contributors = terminate_name_list (Contributors, sepc); -- terminate with 0 or 1 sepc and a space
end
text = safe_join( {Contributors, Date, Chapter, tcommon, Authors, Place, Editors, tcommon2, pgtext, idcommon }, sepc );
else
text = safe_join( {Authors, Date, Chapter, Place, Editors, tcommon, pgtext, idcommon }, sepc );
end
elseif utilities.is_set (Editors) then
if utilities.is_set (Date) then
if EditorCount <= 1 then
Editors = Editors .. cfg.presentation['sep_name'] .. cfg.messages['editor'];
else
Editors = Editors .. cfg.presentation['sep_name'] .. cfg.messages['editors'];
end
else
if EditorCount <= 1 then
Editors = Editors .. " (" .. cfg.messages['editor'] .. ")" .. sepc .. " "
else
Editors = Editors .. " (" .. cfg.messages['editors'] .. ")" .. sepc .. " "
end
end
text = safe_join( {Editors, Date, Chapter, Place, tcommon, pgtext, idcommon}, sepc );
else
if utilities.in_array (config.CitationClass, {"journal", "citation"}) and utilities.is_set (Periodical) then
text = safe_join( {Chapter, Place, tcommon, pgtext, Date, idcommon}, sepc );
else
text = safe_join( {Chapter, Place, tcommon, Date, pgtext, idcommon}, sepc );
end
end
if utilities.is_set (PostScript) and PostScript ~= sepc then
text = safe_join( {text, sepc}, sepc ); -- Deals with italics, spaces, etc.
if '.' == sepc then -- remove final seperator if present
text = text:gsub ('%' .. sepc .. '$', ''); -- dot must be escaped here
else
text = mw.ustring.gsub (text, sepc .. '$', ''); -- using ustring for non-dot sepc (likely a non-Latin character)
end
end
text = safe_join( {text, PostScript}, sepc );
-- Now enclose the whole thing in a <cite> element
local options_t = {};
options_t.class = cite_class_attribute_make (config.CitationClass, Mode);
local Ref = is_valid_parameter_value (A['Ref'], A:ORIGIN('Ref'), cfg.keywords_lists['ref'], nil, true); -- nil when |ref=harv; A['Ref'] else
if 'none' ~= cfg.keywords_xlate[(Ref and Ref:lower()) or ''] then
local namelist_t = {}; -- holds selected contributor, author, editor name list
local year = first_set ({Year, anchor_year}, 2); -- Year first for legacy citations and for YMD dates that require disambiguation
if #c > 0 then -- if there is a contributor list
namelist_t = c; -- select it
elseif #a > 0 then -- or an author list
namelist_t = a;
elseif #e > 0 then -- or an editor list
namelist_t = e;
end
local citeref_id;
if #namelist_t > 0 then -- if there are names in namelist_t
citeref_id = make_citeref_id (namelist_t, year); -- go make the CITEREF anchor
if mw.uri.anchorEncode (citeref_id) == ((Ref and mw.uri.anchorEncode (Ref)) or '') then -- Ref may already be encoded (by {{sfnref}}) so citeref_id must be encoded before comparison
utilities.set_message ('maint_ref_duplicates_default');
end
else
citeref_id = ''; -- unset
end
options_t.id = Ref or citeref_id;
end
if string.len (text:gsub('%b<>', '')) <= 2 then -- remove html and html-like tags; then get length of what remains;
z.error_cats_t = {}; -- blank the categories list
z.error_msgs_t = {}; -- blank the error messages list
OCinSoutput = nil; -- blank the metadata string
text = ''; -- blank the the citation
utilities.set_message ('err_empty_citation'); -- set empty citation message and category
end
local render_t = {}; -- here we collect the final bits for concatenation into the rendered citation
if utilities.is_set (options_t.id) then -- here we wrap the rendered citation in <cite ...>...</cite> tags
table.insert (render_t, utilities.substitute (cfg.presentation['cite-id'], {mw.uri.anchorEncode(options_t.id), mw.text.nowiki(options_t.class), text})); -- when |ref= is set or when there is a namelist
else
table.insert (render_t, utilities.substitute (cfg.presentation['cite'], {mw.text.nowiki(options_t.class), text})); -- when |ref=none or when namelist_t empty and |ref= is missing or is empty
end
if OCinSoutput then -- blanked when citation is 'empty' so don't bother to add boilerplate metadata span
table.insert (render_t, utilities.substitute (cfg.presentation['ocins'], OCinSoutput)); -- format and append metadata to the citation
end
local template_name = ('citation' == config.CitationClass) and 'citation' or 'cite ' .. (cfg.citation_class_map_t[config.CitationClass] or config.CitationClass);
local template_link = '[[Template:' .. template_name .. '|' .. template_name .. ']]';
local msg_prefix = ' ';
if 0 ~= #z.error_msgs_t then
mw.addWarning (utilities.substitute (cfg.messages.warning_msg_e, template_link));
table.insert (render_t, ' '); -- insert a space between citation and its error messages
table.sort (z.error_msgs_t); -- sort the error messages list; sorting includes wrapping <span> and <code> tags; hidden-error sorts ahead of visible-error
local hidden = true; -- presume that the only error messages emited by this template are hidden
for _, v in ipairs (z.error_msgs_t) do -- spin through the list of error messages
if v:find ('cs1-visible-error', 1, true) then -- look for the visible error class name
hidden = false; -- found one; so don't hide the error message prefix
break; -- and done because no need to look further
end
end
z.error_msgs_t[1] = table.concat ({utilities.error_comment (msg_prefix, hidden), z.error_msgs_t[1]}); -- add error message prefix to first error message to prevent extraneous punctuation
table.insert (render_t, table.concat (z.error_msgs_t, '; ')); -- make a big string of error messages and add it to the rendering
end
if 0 ~= #z.maint_cats_t then
mw.addWarning (utilities.substitute (cfg.messages.warning_msg_m, template_link));
table.sort (z.maint_cats_t); -- sort the maintenance messages list
local maint_msgs_t = {}; -- here we collect all of the maint messages
if 0 == #z.error_msgs_t then -- if no error messages
table.insert (maint_msgs_t, msg_prefix); -- insert message prefix in maint message livery
end
for _, v in ipairs( z.maint_cats_t ) do -- append maintenance categories
table.insert (maint_msgs_t, -- assemble new maint message and add it to the maint_msgs_t table
table.concat ({v, ' (', utilities.substitute (cfg.messages[':cat wikilink'], v), ')'})
);
end
table.insert (render_t, utilities.substitute (cfg.presentation['hidden-maint'], table.concat (maint_msgs_t, ' '))); -- wrap the group of maint messages with proper presentation and save
end
if not no_tracking_cats then
local sort_key;
local cat_wikilink = 'cat wikilink';
if cfg.enable_sort_keys then -- when namespace sort keys enabled
local namespace_number = mw.title.getCurrentTitle().namespace; -- get namespace number for this wikitext
sort_key = (0 ~= namespace_number and (cfg.name_space_sort_keys[namespace_number] or cfg.name_space_sort_keys.other)) or nil; -- get sort key character; nil for mainspace
cat_wikilink = (not sort_key and 'cat wikilink') or 'cat wikilink sk'; -- make <cfg.messages> key
end
for _, v in ipairs (z.error_cats_t) do -- append error categories
table.insert (render_t, utilities.substitute (cfg.messages[cat_wikilink], {v, sort_key}));
end
if cfg.id_limits_data_load_fail then -- boolean true when load failed
utilities.set_message ('maint_id_limit_load_fail'); -- done here because this maint cat emits no message
end
for _, v in ipairs (z.maint_cats_t) do -- append maintenance categories
table.insert (render_t, utilities.substitute (cfg.messages[cat_wikilink], {v, sort_key}));
end
for _, v in ipairs (z.prop_cats_t) do -- append properties categories
table.insert (render_t, utilities.substitute (cfg.messages['cat wikilink'], v)); -- no sort keys
end
end
return table.concat (render_t); -- make a big string and done
end
--[[--------------------------< V A L I D A T E >--------------------------------------------------------------
Looks for a parameter's name in one of several whitelists.
Parameters in the whitelist can have three values:
true - active, supported parameters
false - deprecated, supported parameters
nil - unsupported parameters
]]
local function validate (name, cite_class, empty)
local name = tostring (name);
local enum_name; -- parameter name with enumerator (if any) replaced with '#'
local state;
local function state_test (state, name) -- local function to do testing of state values
if true == state then return true; end -- valid actively supported parameter
if false == state then
if empty then return nil; end -- empty deprecated parameters are treated as unknowns
deprecated_parameter (name); -- parameter is deprecated but still supported
return true;
end
if 'tracked' == state then
local base_name = name:gsub ('%d', ''); -- strip enumerators from parameter names that have them to get the base name
utilities.add_prop_cat ('tracked-param', {base_name}, base_name); -- add a properties category; <base_name> modifies <key>
return true;
end
return nil;
end
if name:find ('#') then -- # is a cs1|2 reserved character so parameters with # not permitted
return nil;
end
-- replace enumerator digit(s) with # (|last25= becomes |last#=) (mw.ustring because non-Western 'local' digits)
enum_name = mw.ustring.gsub (name, '%d+$', '#'); -- where enumerator is last charaters in parameter name (these to protect |s2cid=)
enum_name = mw.ustring.gsub (enum_name, '%d+([%-l])', '#%1'); -- where enumerator is in the middle of the parameter name; |author#link= is the oddity
if 'document' == cite_class then -- special case for {{cite document}}
state = whitelist.document_parameters_t[enum_name]; -- this list holds enumerated and nonenumerated parameters
if true == state_test (state, name) then return true; end
return false;
end
if utilities.in_array (cite_class, whitelist.preprint_template_list_t) then -- limited parameter sets allowed for these templates
state = whitelist.limited_parameters_t[enum_name]; -- this list holds enumerated and nonenumerated parameters
if true == state_test (state, name) then return true; end
state = whitelist.preprint_arguments_t[cite_class][name]; -- look in the parameter-list for the template identified by cite_class
if true == state_test (state, name) then return true; end
return false; -- not supported because not found or name is set to nil
end -- end limited parameter-set templates
if utilities.in_array (cite_class, whitelist.unique_param_template_list_t) then -- template-specific parameters for templates that accept parameters from the basic argument list
state = whitelist.unique_arguments_t[cite_class][name]; -- look in the template-specific parameter-lists for the template identified by cite_class
if true == state_test (state, name) then return true; end
end -- if here, fall into general validation
state = whitelist.common_parameters_t[enum_name]; -- all other templates; all normal parameters allowed; this list holds enumerated and nonenumerated parameters
if true == state_test (state, name) then return true; end
return false; -- not supported because not found or name is set to nil
end
--[=[-------------------------< I N T E R _ W I K I _ C H E C K >----------------------------------------------
check <value> for inter-language interwiki-link markup. <prefix> must be a MediaWiki-recognized language
code. when these values have the form (without leading colon):
[[<prefix>:link|label]] return label as plain-text
[[<prefix>:link]] return <prefix>:link as plain-text
return value as is else
]=]
local function inter_wiki_check (parameter, value)
local prefix = value:match ('%[%[(%a+):'); -- get an interwiki prefix if one exists
local _;
if prefix and cfg.inter_wiki_map[prefix:lower()] then -- if prefix is in the map, needs preceding colon so
utilities.set_message ('err_bad_paramlink', parameter); -- emit an error message
_, value, _ = utilities.is_wikilink (value); -- extract label portion from wikilink
end
return value;
end
--[[--------------------------< M I S S I N G _ P I P E _ C H E C K >------------------------------------------
Look at the contents of a parameter. If the content has a string of characters and digits followed by an equal
sign, compare the alphanumeric string to the list of cs1|2 parameters. If found, then the string is possibly a
parameter that is missing its pipe. There are two tests made:
{{cite ... |title=Title access-date=2016-03-17}} -- the first parameter has a value and whitespace separates that value from the missing pipe parameter name
{{cite ... |title=access-date=2016-03-17}} -- the first parameter has no value (whitespace after the first = is trimmed by MediaWiki)
cs1|2 shares some parameter names with XML/HTML attributes: class=, title=, etc. To prevent false positives XML/HTML
tags are removed before the search.
If a missing pipe is detected, this function adds the missing pipe maintenance category.
]]
local function missing_pipe_check (parameter, value)
local capture;
value = value:gsub ('%b<>', ''); -- remove XML/HTML tags because attributes: class=, title=, etc.
capture = value:match ('%s+(%a[%w%-]+)%s*=') or value:match ('^(%a[%w%-]+)%s*='); -- find and categorize parameters with possible missing pipes
if capture and validate (capture) then -- if the capture is a valid parameter name
utilities.set_message ('err_missing_pipe', parameter);
end
end
--[[--------------------------< H A S _ E X T R A N E O U S _ P U N C T >--------------------------------------
look for extraneous terminal punctuation in most parameter values; parameters listed in skip table are not checked
]]
local function has_extraneous_punc (param, value)
if 'number' == type (param) then
return;
end
param = param:gsub ('%d+', '#'); -- enumerated name-list mask params allow terminal punct; normalize
if cfg.punct_skip[param] then
return; -- parameter name found in the skip table so done
end
if value:match ('[,;:]$') then
utilities.set_message ('maint_extra_punct'); -- has extraneous punctuation; add maint cat
end
if value:match ('^=') then -- sometimes an extraneous '=' character appears ...
utilities.set_message ('maint_extra_punct'); -- has extraneous punctuation; add maint cat
end
end
--[[--------------------------< H A S _ T W L _ U R L >--------------------------------------------------------
look for The Wikipedia Library urls in url-holding parameters. TWL urls are accessible for readers who are not
active extended confirmed Wikipedia editors. This function emits an error message when such urls are discovered.
looks for: '.wikipedialibrary.idm.oclc.org'
]]
local function has_twl_url (url_params_t)
local url_error_t = {}; -- sequence of url-holding parameters that have a TWL url
for param, value in pairs (url_params_t) do
if value:find ('%.wikipedialibrary%.idm%.oclc%.org') then -- has the TWL base url?
table.insert (url_error_t, utilities.wrap_style ('parameter', param)); -- add parameter name to the list
end
end
if 0 ~= #url_error_t then -- non-zero when there are errors
table.sort (url_error_t);
utilities.set_message ('err_param_has_twl_url', {utilities.make_sep_list (#url_error_t, url_error_t)}); -- add this error message
return true;
end
end
--[[--------------------------< H A S _ E X T R A N E O U S _ U R L >------------------------------------------
look for extraneous url parameter values; parameters listed in skip table are not checked
]]
local function has_extraneous_url (non_url_param_t)
local url_error_t = {};
check_for_url (non_url_param_t, url_error_t); -- extraneous url check
if 0 ~= #url_error_t then -- non-zero when there are errors
table.sort (url_error_t);
utilities.set_message ('err_param_has_ext_link', {utilities.make_sep_list (#url_error_t, url_error_t)}); -- add this error message
end
end
--[[--------------------------< _ C I T A T I O N >------------------------------------------------------------
Module entry point
frame – from template call (citation()); may be nil when called from another module
args – table of all cs1|2 parameters in the template (the template frame)
config – table of template-supplied parameter (the #invoke frame)
]]
local function _citation (frame, args, config) -- save a copy in case we need to display an error message in preview mode
if not frame then
frame = mw.getCurrentFrame(); -- if called from another module, get a frame for frame-provided functions
end
-- i18n: set the name that your wiki uses to identify sandbox subpages from sandbox template invoke (or can be set here)
local sandbox = ((config.SandboxPath and '' ~= config.SandboxPath) and config.SandboxPath) or '/sandbox'; -- sandbox path from {{#invoke:Citation/CS1/sandbox|citation|SandboxPath=/...}}
is_sandbox = nil ~= string.find (frame:getTitle(), sandbox, 1, true); -- is this invoke the sandbox module?
sandbox = is_sandbox and sandbox or ''; -- use i18n sandbox to load sandbox modules when this module is the sandox; live modules else
cfg = mw.loadData ('Module:Citation/CS1/Configuration' .. sandbox); -- load sandbox versions of support modules when {{#invoke:Citation/CS1/sandbox|...}}; live modules else
whitelist = mw.loadData ('Module:Citation/CS1/Whitelist' .. sandbox);
utilities = require ('Module:Citation/CS1/Utilities' .. sandbox);
validation = require ('Module:Citation/CS1/Date_validation' .. sandbox);
identifiers = require ('Module:Citation/CS1/Identifiers' .. sandbox);
metadata = require ('Module:Citation/CS1/COinS' .. sandbox);
utilities.set_selected_modules (cfg); -- so that functions in Utilities can see the selected cfg tables
identifiers.set_selected_modules (cfg, utilities); -- so that functions in Identifiers can see the selected cfg tables and selected Utilities module
validation.set_selected_modules (cfg, utilities); -- so that functions in Date validataion can see selected cfg tables and the selected Utilities module
metadata.set_selected_modules (cfg, utilities); -- so that functions in COinS can see the selected cfg tables and selected Utilities module
z = utilities.z; -- table of error and category tables in Module:Citation/CS1/Utilities
is_preview_mode = not utilities.is_set (frame:preprocess ('{{REVISIONID}}'));
local suggestions = {}; -- table where we store suggestions if we need to loadData them
local error_text; -- used as a flag
local capture; -- the single supported capture when matching unknown parameters using patterns
local empty_unknowns = {}; -- sequence table to hold empty unknown params for error message listing
for k, v in pairs( args ) do -- get parameters from the parent (template) frame
v = mw.ustring.gsub (v, '^%s*(.-)%s*$', '%1'); -- trim leading/trailing whitespace; when v is only whitespace, becomes empty string
if v ~= '' then
if ('string' == type (k)) then
k = mw.ustring.gsub (k, '%d', cfg.date_names.local_digits); -- for enumerated parameters, translate 'local' digits to Western 0-9
end
if not validate( k, config.CitationClass ) then
if type (k) ~= 'string' then -- exclude empty numbered parameters
if v:match("%S+") ~= nil then
error_text = utilities.set_message ('err_text_ignored', {v});
end
elseif validate (k:lower(), config.CitationClass) then
error_text = utilities.set_message ('err_parameter_ignored_suggest', {k, k:lower()}); -- suggest the lowercase version of the parameter
else
if nil == suggestions.suggestions then -- if this table is nil then we need to load it
suggestions = mw.loadData ('Module:Citation/CS1/Suggestions' .. sandbox); --load sandbox version of suggestion module when {{#invoke:Citation/CS1/sandbox|...}}; live module else
end
for pattern, param in pairs (suggestions.patterns) do -- loop through the patterns to see if we can suggest a proper parameter
capture = k:match (pattern); -- the whole match if no capture in pattern else the capture if a match
if capture then -- if the pattern matches
param = utilities.substitute (param, capture); -- add the capture to the suggested parameter (typically the enumerator)
if validate (param, config.CitationClass) then -- validate the suggestion to make sure that the suggestion is supported by this template (necessary for limited parameter lists)
error_text = utilities.set_message ('err_parameter_ignored_suggest', {k, param}); -- set the suggestion error message
else
error_text = utilities.set_message ('err_parameter_ignored', {k}); -- suggested param not supported by this template
v = ''; -- unset
end
end
end
if not utilities.is_set (error_text) then -- couldn't match with a pattern, is there an explicit suggestion?
if (suggestions.suggestions[ k:lower() ] ~= nil) and validate (suggestions.suggestions[ k:lower() ], config.CitationClass) then
utilities.set_message ('err_parameter_ignored_suggest', {k, suggestions.suggestions[ k:lower() ]});
else
utilities.set_message ('err_parameter_ignored', {k});
v = ''; -- unset value assigned to unrecognized parameters (this for the limited parameter lists)
end
end
end
end
args[k] = v; -- save this parameter and its value
elseif not utilities.is_set (v) then -- for empty parameters
if not validate (k, config.CitationClass, true) then -- is this empty parameter a valid parameter
k = ('' == k) and '(empty string)' or k; -- when k is empty string (or was space(s) trimmed to empty string), replace with descriptive text
table.insert (empty_unknowns, utilities.wrap_style ('parameter', k)); -- format for error message and add to the list
end
-- crude debug support that allows us to render a citation from module {{#invoke:}} TODO: keep?
-- elseif args[k] ~= nil or (k == 'postscript') then -- when args[k] has a value from {{#invoke}} frame (we don't normally do that)
-- args[k] = v; -- overwrite args[k] with empty string from pframe.args[k] (template frame); v is empty string here
end -- not sure about the postscript bit; that gets handled in parameter validation; historical artifact?
end
if 0 ~= #empty_unknowns then -- create empty unknown error message
utilities.set_message ('err_param_unknown_empty', {
1 == #empty_unknowns and '' or 's',
utilities.make_sep_list (#empty_unknowns, empty_unknowns)
});
end
local non_url_param_t = {}; -- table of parameters and values that are not url-holding parameters
local url_param_t = {}; -- table of url-holding paramters and their values
for k, v in pairs( args ) do
if 'string' == type (k) then -- don't evaluate positional parameters
has_invisible_chars (k, v); -- look for invisible characters
end
has_extraneous_punc (k, v); -- look for extraneous terminal punctuation in parameter values
missing_pipe_check (k, v); -- do we think that there is a parameter that is missing a pipe?
args[k] = inter_wiki_check (k, v); -- when language interwiki-linked parameter missing leading colon replace with wiki-link label
if 'string' == type (k) then -- when parameter k is not positional
if not cfg.url_skip[k] then -- and not in url skip table
non_url_param_t[k] = v; -- make a parameter/value list for extraneous url check
else -- and is in url skip table (a url-holding parameter)
url_param_t[k] = v; -- make a parameter/value list to check for values that are The Wikipedia Library url
end
end
end
has_extraneous_url (non_url_param_t); -- look for url in parameter values where a url does not belong
if has_twl_url (url_param_t) then -- look for url-holding parameters that hold a The Wikipedia Library url
args['url-access'] = 'subscription';
end
return table.concat ({
frame:extensionTag ('templatestyles', '', {src='Module:Citation/CS1' .. sandbox .. '/styles.css'}),
citation0( config, args)
});
end
--[[--------------------------< C I T A T I O N >--------------------------------------------------------------
Template entry point
]]
local function citation (frame)
local config_t = {}; -- table to store parameters from the module {{#invoke:}}
local args_t = frame:getParent().args; -- get template's preset parameters
for k, v in pairs (frame.args) do -- get parameters from the {{#invoke}} frame
config_t[k] = v;
-- args_t[k] = v; -- crude debug support that allows us to render a citation from module {{#invoke:}}; skips parameter validation; TODO: keep?
end
return _citation (frame, args_t, config_t)
end
--[[--------------------------< E X P O R T E D F U N C T I O N S >------------------------------------------
]]
return {
citation = citation, -- template entry point
_citation = _citation, -- module entry point
}
0bktpjlf4c4vexqmy1ttletlscgx616
Modul:Citation/CS1/Configuration
828
44264
731559
730880
2026-11-07T11:04:58Z
Rafka Aditia
29183
731559
Scribunto
text/plain
local lang_obj = mw.language.getContentLanguage(); -- make a language object for the local language; used here for languages and dates
--[[--------------------------< S E T T I N G S >--------------------------------------------------------------
boolean settings used to control various things. these setting located here to make them easy to find
]]
-- these settings local to this module only
local local_digits_from_mediawiki = false; -- for i18n; when true, module fills date_names['local_digits'] from MediaWiki; manual fill required else; always false at en.wiki
-- these settings exported to other modules
local use_identifier_redirects = true; -- when true use redirect name for identifier label links; always true at en.wiki
local local_lang_cat_enable = false; -- when true categorizes pages where |language=<local wiki's language>; always false at en.wiki
local date_name_auto_xlate_enable = false; -- when true translates English month-names to the local-wiki's language month names; always false at en.wiki
local date_digit_auto_xlate_enable = false; -- when true translates Western date digit to the local-wiki's language digits (date_names['local_digits']); always false at en.wiki
local enable_sort_keys = true; -- when true module adds namespace sort keys to error and maintenance category links
local suppress_archive_today_urls = true; -- when true module suppresses all parameters with an archive.today url and any deprendent parameters
--[[--------------------------< U N C A T E G O R I Z E D _ N A M E S P A C E S >------------------------------
List of namespaces identifiers for namespaces that will not be included in citation error categories.
Same as setting notracking = true by default.
For wikis that have a current version of Module:cs1 documentation support, this #invoke will return an unordered
list of namespace names and their associated identifiers:
{{#invoke:cs1 documentation support|uncategorized_namespace_lister|all=<anything>}}
]]
local uncategorized_namespaces_t = {[2]=true}; -- init with user namespace id
for k, _ in pairs (mw.site.talkNamespaces) do -- add all talk namespace ids
uncategorized_namespaces_t[k] = true;
end
local uncategorized_subpages = {'/[Kk]otrétan', '/[Kk]asus uji', '/[^/]*[Ll]og', '/[Aa]rsip'}; -- list of Lua patterns found in page names of pages we should not categorize
--[[
at en.wiki Greek characters are used as sort keys for certain items in a category so that those items are
placed at the end of a category page. See Wikipedia:Categorization#Sort_keys. That works well for en.wiki
because English is written using the Latn script. This may not work well for other languages. At en.wiki it
is desireable to place content from certain namespaces at the end of a category listing so the module adds sort
keys to error and maintenance category links when rendering a cs1|2 template on a page in that namespace.
i18n: if this does not work well for your language, set <enable_sort_keys> to false.
]]
local name_space_sort_keys = { -- sort keys to be used with these namespaces:
[4] = 'ω', -- wikipedia; omega
[10] = 'τ', -- template; tau
[118] = 'Δ', -- draft; delta
['other'] = 'ο', -- all other non-talk namespaces except main (article); omicron
}
--[[--------------------------< M E S S A G E S >--------------------------------------------------------------
Translation table
The following contains fixed text that may be output as part of a citation.
This is separated from the main body to aid in future translations of this
module.
]]
local messages = {
['agency'] = '$1 $2', -- $1 is sepc, $2 is agency
['archived-dead'] = 'Diarsipkeun ti $1 tanggal $2',
['archived-live'] = '$1 ti vérsi aslina tanggal $2',
['archived-missing'] = 'Diarsipkeun ti vérsi asli $1 tanggal $2',
['archived-unfit'] = 'Diarsipkeun ti vérsi asli pada ',
['archived'] = 'Diarsipkeun',
['by'] = 'Ku', -- contributions to authored works: introduction, foreword, afterword
['cartography'] = 'Kartografi ku $1',
['editor'] = 'éd.',
['editors'] = 'éd.',
['edition'] = '(Édisi $1)',
['episode'] = 'Épisode $1',
['et al'] = 'et al.',
['in'] = 'Dina', -- edited works
['inactive'] = 'teu aktip',
['inset'] = 'insét $1',
['interview'] = 'Diwawancara ku $1',
['mismatch'] = '<code class="cs1-code">|$1=</code> / <code class="cs1-code">|$2=</code> mismatch', -- $1 is year param name; $2 is date param name
['newsgroup'] = '[[Usenet newsgroup|Newsgroup]]: $1',
['notitle'] = 'Tanpa judul', -- for |title=(()) and (in the future) |title=none
['original'] = 'asli',
['origdate'] = ' [$1]',
['published'] = ' (dipublikasikeun $1)',
['retrieved'] = 'Diaksés tanggal $1',
['season'] = 'Musim $1',
['section'] = '§ $1',
['sections'] = '§§ $1',
['series'] = '$1 $2', -- $1 is sepc, $2 is series
['seriesnum'] = 'Séri $1',
['translated'] = 'Ditarjamahkeun ku $1',
['type'] = ' ($1)', -- for titletype
['written'] = 'Ditulis ku $1',
['vol'] = '$1 Vol. $2', -- $1 is sepc; bold journal style volume is in presentation{}
['vol-no'] = '$1 Vol. $2, no. $3', -- sepc, volume, issue (alternatively insert $1 after $2, but then we'd also have to change capitalization)
['issue'] = '$1 No. $2', -- $1 is sepc
['art'] = '$1 Art. $2', -- $1 is sepc; for {{cite conference}} only
['vol-art'] = '$1 Vol. $2, art. $3', -- sepc, volume, article-number; for {{cite conference}} only
['j-vol'] = '$1 $2', -- sepc, volume; bold journal volume is in presentation{}
['j-issue'] = ' ($1)',
['j-article-num'] = ' $1', -- TODO: any punctuation here? static text?
['nopp'] = '$1 $2'; -- page(s) without prefix; $1 is sepc
['p-prefix'] = "$1 hlm. $2", -- $1 is sepc
['pp-prefix'] = "$1 hlm. $2", -- $1 is sepc
['j-page(s)'] = ': $1', -- same for page and pages
['sheet'] = '$1 Lembar $2', -- $1 is sepc
['sheets'] = '$1 Lembar $2', -- $1 is sepc
['j-sheet'] = ': Lembar $1',
['j-sheets'] = ': Lembar $1',
['language'] = '(Dina basa $1)',
['via'] = " – via $1",
['event'] = 'Kajadian di',
['minutes'] = 'menit di',
-- Determines the location of the help page
['help page link'] = ' ',
['help page label'] = ' ',
-- categories
['cat wikilink'] = '[[Kategori:$1]]', -- $1 is the category name
['cat wikilink sk'] = '[[Kategori:$1|$2]]', -- $1 is the category name; $2 is namespace sort key
[':cat wikilink'] = '[[:Kategori:$1|tumbu]]', -- category name as maintenance message wikilink; $1 is the category name
-- Internal errors (should only occur if configuration is bad)
['undefined_error'] = 'Dipanggil kalawan kondisi kasalahan nu teu ditangtukeun',
['unknown_ID_key'] = 'Konci ID teu dipikanyaho: ', -- an ID key in id_handlers not found in ~/Identifiers func_map{}
['unknown_ID_access'] = 'Kata kunci akses ID tidak dikenal: ', -- an ID access keyword in id_handlers not found in keywords_lists['id-access']{}
['unknown_argument_map'] = 'Peta argumén teu ditangtukeun pikeun variabel ieu',
['bare_url_no_origin'] = 'URL kosong kapanggih tapi indikator asal nyaéta nihil atawa kosong',
['warning_msg_e'] = '<span style="color:#d33">Hiji atawa leuwih citakan <code style="color: inherit; background: inherit; border: none; padding: inherit;">{{$1}}</code> ngabogaan kasalahan</span>; talatah bisa disumputkeun ([[Help:Galat_CS1#Controlling_error_message_display|pitulung]]).'; -- $1 is template link
['warning_msg_m'] = '<span style="color:#3a3">Hiji atawa leuwih citakan <code style="color: inherit; background: inherit; border: none; padding: inherit;">{{$1}}</code> ngabogaan talatah pamiaraan</span>; talatah bisa disumputkeun ([[Help:Galat_CS1#Controlling_error_message_display|pitulung]]).'; -- $1 is template link
}
--[[--------------------------< C I T A T I O N _ C L A S S _ M A P >------------------------------------------
this table maps the value assigned to |CitationClass= in the cs1|2 templates to the canonical template name when
the value assigned to |CitationClass= is different from the canonical template name. |CitationClass= values are
used as class attributes in the <cite> tag that encloses the citation so these names may not contain spaces while
the canonical template name may. These names are used in warning_msg_e and warning_msg_m to create links to the
template's documentation when an article is displayed in preview mode.
Most cs1|2 template |CitationClass= values at en.wiki match their canonical template names so are not listed here.
]]
local citation_class_map_t = { -- TODO: if kept, these and all other config.CitationClass 'names' require some sort of i18n
['arxiv'] = 'arXiv',
['audio-visual'] = 'AV media',
['AV-media-notes'] = 'AV media notes',
['biorxiv'] = 'bioRxiv',
['citeseerx'] = 'CiteSeerX',
['encyclopaedia'] = 'encyclopedia',
['mailinglist'] = 'mailing list',
['medrxiv'] = 'medRxiv',
['pressrelease'] = 'press release',
['ssrn'] = 'SSRN',
['techreport'] = 'tech report',
}
--[=[-------------------------< E T _ A L _ P A T T E R N S >--------------------------------------------------
This table provides Lua patterns for the phrase "et al" and variants in name text
(author, editor, etc.). The main module uses these to identify and emit the 'etal' message.
]=]
local et_al_patterns = {
"[;,]? *[\"']*%f[%a][Ee][Tt]%.? *[Aa][Ll][%.;,\"']*$", -- variations on the 'et al' theme
"[;,]? *[\"']*%f[%a][Ee][Tt]%.? *[Aa][Ll][Ii][AaIi][Ee]?[%.;,\"']*$", -- variations on the 'et alia', 'et alii' and 'et aliae' themes (false positive 'et aliie' unlikely to match)
"[;,]? *%f[%a]and [Oo]thers", -- an alternative to et al.
"%[%[ *[Ee][Tt]%.? *[Aa][Ll]%.? *%]%]", -- a wikilinked form
"%(%( *[Ee][Tt]%.? *[Aa][Ll]%.? *%)%)", -- a double-bracketed form (to counter partial removal of ((...)) syntax)
"[%(%[] *[Ee][Tt]%.? *[Aa][Ll]%.? *[%)%]]", -- a bracketed form
}
--[[--------------------------< P R E S E N T A T I O N >------------------------
Fixed presentation markup. Originally part of citation_config.messages it has
been moved into its own, more semantically correct place.
]]
local presentation =
{
-- .citation-comment class is specified at Help:CS1_errors#Controlling_error_message_display
['hidden-error'] = '<span class="cs1-hidden-error citation-comment">$1</span>',
['visible-error'] = '<span class="cs1-visible-error citation-comment">$1</span>',
['hidden-maint'] = '<span class="cs1-maint citation-comment">$1</span>',
['accessdate'] = '<span class="reference-accessdate">$1$2</span>', -- to allow editors to hide accessdate using personal CSS
['bdi'] = '<bdi$1>$2</bdi>', -- bidirectional isolation used with |script-title= and the like
['cite'] = '<cite class="$1">$2</cite>'; -- for use when citation does not have a namelist and |ref= not set so no id="..." attribute
['cite-id'] = '<cite id="$1" class="$2">$3</cite>'; -- for use when when |ref= is set or when citation has a namelist
['format'] = ' <span class="cs1-format">($1)</span>', -- for |format=, |chapter-format=, etc.
['interwiki'] = ' <span class="cs1-format">[in $1]</span>', -- for interwiki-language-linked author, editor, etc
['interproj'] = ' <span class="cs1-format">[at $1]</span>', -- for interwiki-project-linked author, editor, etc (:d: and :s: supported; :w: ignored)
-- various access levels, for |access=, |doi-access=, |arxiv=, ...
-- narrow no-break space   may work better than nowrap CSS. Or not? Browser support?
['ext-link-access-signal'] = '<span class="$1" title="$2">$3</span>', -- external link with appropriate lock icon
['free'] = {class='id-lock-free', title='Freely accessible'}, -- classes defined in Module:Citation/CS1/styles.css
['registration'] = {class='id-lock-registration', title='Free registration required'},
['limited'] = {class='id-lock-limited', title='Free access subject to limited trial, subscription normally required'},
['subscription'] = {class='id-lock-subscription', title='Paid subscription required'},
['interwiki-icon'] = '<span class="$1" title="$2">$3</span>',
['class-wikisource'] = 'cs1-ws-icon',
['italic-title'] = "''$1''",
['kern-left'] = '<span class="cs1-kern-left"></span>$1', -- spacing to use when title contains leading single or double quote mark
['kern-right'] = '$1<span class="cs1-kern-right"></span>', -- spacing to use when title contains trailing single or double quote mark
['nowrap1'] = '<span class="nowrap">$1</span>', -- for nowrapping an item: <span ...>yyyy-mm-dd</span>
['nowrap2'] = '<span class="nowrap">$1</span> $2', -- for nowrapping portions of an item: <span ...>dd mmmm</span> yyyy (note white space)
['ocins'] = '<span title="$1" class="Z3988"></span>',
['parameter'] = '<code class="cs1-code">|$1=</code>',
['ps_cs1'] = '.'; -- CS1 style postscript (terminal) character
['ps_cs2'] = ''; -- CS2 style postscript (terminal) character (empty string)
['quoted-text'] = '<q>$1</q>', -- for wrapping |quote= content
['quoted-title'] = '"$1"',
['sep_cs1'] = '.', -- CS1 element separator
['sep_cs2'] = ',', -- CS2 separator
['sep_nl'] = ';', -- CS1|2 style name-list separator between names is a semicolon
['sep_nl_and'] = ' and ', -- used as last nl sep when |name-list-style=and and list has 2 items
['sep_nl_end'] = '; and ', -- used as last nl sep when |name-list-style=and and list has 3+ names
['sep_name'] = ', ', -- CS1|2 style last/first separator is <comma><space>
['sep_nl_vanc'] = ',', -- Vancouver style name-list separator between authors is a comma
['sep_name_vanc'] = ' ', -- Vancouver style last/first separator is a space
['sep_list'] = ', ', -- used for |language= when list has 3+ items except for last sep which uses sep_list_end
['sep_list_pair'] = ' and ', -- used for |language= when list has 2 items
['sep_list_end'] = ', and ', -- used as last list sep for |language= when list has 3+ items
['trans-italic-title'] = "[''$1'']",
['trans-quoted-title'] = "[$1]", -- for |trans-title= and |trans-quote=
['vol-bold'] = '$1 <b>$2</b>', -- sepc, volume; for bold journal cites; for other cites ['vol'] in messages{}
}
--[[--------------------------< A L I A S E S >---------------------------------
Aliases table for commonly passed parameters.
Parameter names on the right side in the assignments in this table must have been
defined in the Whitelist before they will be recognized as valid parameter names
]]
local aliases = {
['AccessDate'] = {'access-date', 'accessdate'}, -- Used by InternetArchiveBot
['Agency'] = 'agency',
['ArchiveDate'] = {'archive-date', 'archivedate'}, -- Used by InternetArchiveBot
['ArchiveFormat'] = 'archive-format',
['ArchiveURL'] = {'archive-url', 'archiveurl'}, -- Used by InternetArchiveBot
['ArticleNumber'] = 'article-number',
['ASINTLD'] = 'asin-tld',
['At'] = 'at', -- Used by InternetArchiveBot
['Authors'] = {'people', 'credits'},
['BookTitle'] = {'book-title', 'booktitle'},
['Cartography'] = 'cartography',
['Chapter'] = {'chapter', 'contribution', 'entry', 'article', 'section'},
['ChapterFormat'] = {'chapter-format', 'contribution-format', 'entry-format',
'article-format', 'section-format'};
['ChapterURL'] = {'chapter-url', 'contribution-url', 'entry-url', 'article-url', 'section-url'}, -- Used by InternetArchiveBot
['ChapterUrlAccess'] = {'chapter-url-access', 'contribution-url-access',
'entry-url-access', 'article-url-access', 'section-url-access'}, -- Used by InternetArchiveBot
['Class'] = 'class', -- cite arxiv and arxiv identifier
['Collaboration'] = 'collaboration',
['Conference'] = {'conference', 'event'},
['ConferenceFormat'] = 'conference-format',
['ConferenceURL'] = 'conference-url', -- Used by InternetArchiveBot
['Date'] = {'date', 'air-date', 'airdate'}, -- air-date and airdate for cite episode and cite serial only
['Degree'] = 'degree',
['DF'] = 'df',
['DisplayAuthors'] = {'display-authors', 'display-subjects'},
['DisplayContributors'] = 'display-contributors',
['DisplayEditors'] = 'display-editors',
['DisplayInterviewers'] = 'display-interviewers',
['DisplayTranslators'] = 'display-translators',
['Docket'] = 'docket',
['DoiBroken'] = 'doi-broken-date',
['Edition'] = 'edition',
['Embargo'] = 'pmc-embargo-date',
['Encyclopedia'] = {'encyclopedia', 'encyclopaedia', 'dictionary'}, -- cite encyclopedia only
['Episode'] = 'episode', -- cite serial only TODO: make available to cite episode?
['Format'] = 'format',
['ID'] = {'id', 'ID'},
['Inset'] = 'inset',
['Issue'] = {'issue', 'number'},
['Language'] = {'language', 'lang'},
['MailingList'] = {'mailing-list', 'mailinglist'}, -- cite mailing list only
['Map'] = 'map', -- cite map only
['MapFormat'] = 'map-format', -- cite map only
['MapURL'] = {'map-url', 'mapurl'}, -- cite map only -- Used by InternetArchiveBot
['MapUrlAccess'] = 'map-url-access', -- cite map only -- Used by InternetArchiveBot
['Minutes'] = 'minutes',
['Mode'] = 'mode',
['NameListStyle'] = 'name-list-style',
['Network'] = 'network',
['Newsgroup'] = 'newsgroup', -- cite newsgroup only
['NoPP'] = {'no-pp', 'nopp'},
['NoTracking'] = {'no-tracking', 'template-doc-demo'},
['Number'] = 'number', -- this case only for cite techreport
['OrigDate'] = {'orig-date', 'orig-year', 'origyear'},
['Others'] = 'others',
['Page'] = {'page', 'p'}, -- Used by InternetArchiveBot
['Pages'] = {'pages', 'pp'}, -- Used by InternetArchiveBot
['Periodical'] = {'journal', 'magazine', 'newspaper', 'periodical', 'website', 'work'},
['Place'] = {'place', 'location'},
['PostScript'] = 'postscript',
['PublicationDate'] = {'publication-date', 'publicationdate'},
['PublicationPlace'] = {'publication-place', 'publicationplace'},
['PublisherName'] = {'publisher', 'institution'},
['Quote'] = {'quote', 'quotation'},
['QuotePage'] = 'quote-page',
['QuotePages'] = 'quote-pages',
['Ref'] = 'ref',
['Scale'] = 'scale',
['ScriptChapter'] = {'script-chapter', 'script-contribution', 'script-entry',
'script-article', 'script-section'},
['ScriptEncyclopedia'] = {'script-encyclopedia', 'script-encyclopaedia'}, -- cite encyclopedia only
['ScriptMap'] = 'script-map',
['ScriptPeriodical'] = {'script-journal', 'script-magazine', 'script-newspaper',
'script-periodical', 'script-website', 'script-work'},
['ScriptQuote'] = 'script-quote',
['ScriptTitle'] = 'script-title', -- Used by InternetArchiveBot
['Season'] = 'season',
['Sections'] = 'sections', -- cite map only
['Series'] = {'series', 'version'},
['SeriesLink'] = {'series-link', 'serieslink'},
['SeriesNumber'] = {'series-number', 'series-no'},
['Sheet'] = 'sheet', -- cite map only
['Sheets'] = 'sheets', -- cite map only
['Station'] = 'station',
['Time'] = 'time',
['TimeCaption'] = 'time-caption',
['Title'] = 'title', -- Used by InternetArchiveBot
['TitleLink'] = {'title-link', 'episode-link', 'episodelink'}, -- Used by InternetArchiveBot
['TitleNote'] = {'title-note', 'department'},
['TitleType'] = {'type', 'medium'},
['TransChapter'] = {'trans-article', 'trans-chapter', 'trans-contribution',
'trans-entry', 'trans-section'},
['Transcript'] = 'transcript',
['TranscriptFormat'] = 'transcript-format',
['TranscriptURL'] = 'transcript-url', -- Used by InternetArchiveBot
['TransEncyclopedia'] = {'trans-encyclopedia', 'trans-encyclopaedia'}, -- cite encyclopedia only
['TransMap'] = 'trans-map', -- cite map only
['TransPeriodical'] = {'trans-journal', 'trans-magazine', 'trans-newspaper',
'trans-periodical', 'trans-website', 'trans-work'},
['TransQuote'] = 'trans-quote',
['TransTitle'] = 'trans-title', -- Used by InternetArchiveBot
['URL'] = {'url', 'URL'}, -- Used by InternetArchiveBot
['UrlAccess'] = 'url-access', -- Used by InternetArchiveBot
['UrlStatus'] = 'url-status', -- Used by InternetArchiveBot
['Vauthors'] = 'vauthors',
['Veditors'] = 'veditors',
['Via'] = 'via',
['Volume'] = 'volume',
['Year'] = 'year',
['AuthorList-First'] = {"first#", "author-first#", "author#-first", "author-given#", "author#-given",
"subject-first#", "subject#-first", "subject-given#", "subject#-given",
"given#"},
['AuthorList-Last'] = {"last#", "author-last#", "author#-last", "author-surname#", "author#-surname",
"subject-last#", "subject#-last", "subject-surname#", "subject#-surname",
"author#", 'host#', "subject#", "surname#"},
['AuthorList-Link'] = {"author-link#", "author#-link", "subject-link#",
"subject#-link", "authorlink#", "author#link"},
['AuthorList-Mask'] = {"author-mask#", "author#-mask", "subject-mask#", "subject#-mask"},
['ContributorList-First'] = {'contributor-first#', 'contributor#-first',
'contributor-given#', 'contributor#-given'},
['ContributorList-Last'] = {'contributor-last#', 'contributor#-last',
'contributor-surname#', 'contributor#-surname', 'contributor#'},
['ContributorList-Link'] = {'contributor-link#', 'contributor#-link'},
['ContributorList-Mask'] = {'contributor-mask#', 'contributor#-mask'},
['EditorList-First'] = {"editor-first#", "editor#-first", "editor-given#", "editor#-given"},
['EditorList-Last'] = {"editor-last#", "editor#-last", "editor-surname#",
"editor#-surname", "editor#"},
['EditorList-Link'] = {"editor-link#", "editor#-link"},
['EditorList-Mask'] = {"editor-mask#", "editor#-mask"},
['InterviewerList-First'] = {'interviewer-first#', 'interviewer#-first',
'interviewer-given#', 'interviewer#-given'},
['InterviewerList-Last'] = {'interviewer-last#', 'interviewer#-last',
'interviewer-surname#', 'interviewer#-surname', 'interviewer#'},
['InterviewerList-Link'] = {'interviewer-link#', 'interviewer#-link'},
['InterviewerList-Mask'] = {'interviewer-mask#', 'interviewer#-mask'},
['TranslatorList-First'] = {'translator-first#', 'translator#-first',
'translator-given#', 'translator#-given'},
['TranslatorList-Last'] = {'translator-last#', 'translator#-last',
'translator-surname#', 'translator#-surname', 'translator#'},
['TranslatorList-Link'] = {'translator-link#', 'translator#-link'},
['TranslatorList-Mask'] = {'translator-mask#', 'translator#-mask'},
}
--[[--------------------------< D E P E N D E N C I E S _ T >--------------------------------------------------
a table of url-holding parameters upon which other parameters are dependent.
this table is used by has_archive_today_url() to suppress parameters that are dependant upon a url-holding parameter
that has as a value an archive.today (or alternate tld) url.
archive.today is deprecated at en.wiki; see Wikipedia:Requests_for_comment/Archive.is_RFC_5
]]
local url_dependency_map_t = { -- table of url-holding parameters and the meta-parameters that are dependent on them
['url'] = {'AccessDate', 'ArchiveURL', 'Format', 'UrlAccess'},
['archive-url'] = {'ArchiveDate', 'ArchiveFormat', 'UrlStatus'},
['archiveurl'] = {'ArchiveDate', 'ArchiveFormat', 'UrlStatus'},
['article-url'] = {'ChapterFormat', 'ChapterUrlAccess'},
['chapter-url'] = {'ChapterFormat', 'ChapterUrlAccess'},
['conference-url'] = {'ConferenceFormat'},
['contribution-url'] = {'ChapterFormat', 'ChapterUrlAccess'},
['entry-url'] = {'ChapterFormat', 'ChapterUrlAccess'},
['map-url'] = {'MapFormat', 'MapUrlAccess'},
['section-url'] = {'ChapterFormat', 'ChapterUrlAccess'},
['transcript-url'] = {'TranscriptFormat'},
};
local dependencies_t = {}; -- the table of url-holding parameters and their dependent parameters
for url_param, meta_parameters_t in pairs (url_dependency_map_t) do -- sequence of meta-parameters (indexes) into <aliases> table
local dependant_params_t = {}; -- collection of parameters that are dependent on this <url_param>
for _, meta_parameter in ipairs (meta_parameters_t) do -- for each <aliases> meta-parameter (index)
if 'string' == type (aliases[meta_parameter]) then -- for single parameter names (no aliases), <aliases[meta_parameter]> will be a string
table.insert (dependant_params_t, aliases[meta_parameter]); -- add to this <url_param>'s dependency sequence
else
for _, alias in ipairs (aliases[meta_parameter]) do -- for each alias in aliases[meta_parameter] (aliases['AccessDate'] for example)
table.insert (dependant_params_t, alias); -- add to this <url_param>'s dependency sequence
end
end
end
dependencies_t[url_param] = dependant_params_t; -- add the <url_param> and its dependent parameters
end
--[[--------------------------< P U N C T _ S K I P >---------------------------
builds a table of parameter names that the extraneous terminal punctuation check should not check.
]]
local punct_meta_params = { -- table of aliases[] keys (meta parameters); each key has a table of parameter names for a value
'BookTitle', 'Chapter', 'ScriptChapter', 'ScriptTitle', 'Title', 'TransChapter', 'Transcript', 'TransMap', 'TransTitle', -- title-holding parameters
'AuthorList-Mask', 'ContributorList-Mask', 'EditorList-Mask', 'InterviewerList-Mask', 'TranslatorList-Mask', -- name-list mask may have name separators
'PostScript', 'Quote', 'ScriptQuote', 'TransQuote', 'Ref', -- miscellaneous
'ArchiveURL', 'ChapterURL', 'ConferenceURL', 'MapURL', 'TranscriptURL', 'URL', -- URL-holding parameters
}
local url_meta_params = { -- table of aliases[] keys (meta parameters); each key has a table of parameter names for a value
'ArchiveURL', 'ChapterURL', 'ConferenceURL', 'ID', 'MapURL', 'TranscriptURL', 'URL', -- parameters allowed to hold urls
'Page', 'Pages', 'At', 'QuotePage', 'QuotePages', -- insource locators allowed to hold urls
}
local function build_skip_table (skip_t, meta_params)
for _, meta_param in ipairs (meta_params) do -- for each meta parameter key
local params = aliases[meta_param]; -- get the parameter or the table of parameters associated with the meta parameter name
if 'string' == type (params) then
skip_t[params] = 1; -- just a single parameter
else
for _, param in ipairs (params) do -- get the parameter name
skip_t[param] = 1; -- add the parameter name to the skip table
local count;
param, count = param:gsub ('#', ''); -- remove enumerator marker from enumerated parameters
if 0 ~= count then -- if removed
skip_t[param] = 1; -- add param name without enumerator marker
end
end
end
end
return skip_t;
end
local punct_skip = {};
local url_skip = {};
--[[--------------------------< S I N G L E - L E T T E R S E C O N D - L E V E L D O M A I N S >----------
this is a list of tlds that are known to have single-letter second-level domain names. This list does not include
ccTLDs which are accepted in is_domain_name().
]]
local single_letter_2nd_lvl_domains_t = {'cash', 'company', 'foundation', 'media', 'org', 'today'};
--[[-----------< S P E C I A L C A S E T R A N S L A T I O N S >------------
This table is primarily here to support internationalization. Translations in
this table are used, for example, when an error message, category name, etc.,
is extracted from the English alias key. There may be other cases where
this translation table may be useful.
]]
local is_Latn = 'A-Za-z\195\128-\195\150\195\152-\195\182\195\184-\198\191\199\132-\201\143\225\184\128-\225\187\191';
local special_case_translation = {
['AuthorList'] = 'daptar panulis', -- used to assemble maintenance category names
['ContributorList'] = 'daptar kontributor', -- translation of these names plus translation of the base maintenance category names in maint_cats{} table below
['EditorList'] = 'daptar éditor', -- must match the names of the actual categories
['InterviewerList'] = 'daptar pangwawancara', -- this group or translations used by name_has_ed_markup() and name_has_mult_names()
['TranslatorList'] = 'daptar panarjamah',
-- Lua patterns to match pseudo-titles used by InternetArchiveBot and others as placeholder for unknown |title= value
['archived_copy'] = { -- used with CS1 maint: Archive[d] copy as title
['en'] = '^archived?%s+copy$', -- for English; translators: keep this because templates imported from en.wiki
['local'] = '^salinan?%s+arsip$', -- translators: replace ['local'] = nil with lowercase translation only when bots or tools create generic titles in your language
},
-- Lua patterns to match generic titles; usually created by bots or reference filling tools
-- translators: replace ['local'] = nil with lowercase translation only when bots or tools create generic titles in your language
-- generic titles and patterns in this table should be lowercase only
-- leave ['local'] nil except when there is a matching generic title in your language
-- boolean 'true' for plain-text searches; 'false' for pattern searches
['generic_titles'] = {
['accept'] = {
},
['reject'] = {
{['en'] = {'^wayback%s+machine$', false}, ['local'] = nil},
{['en'] = {'are you a robot', true}, ['local'] = nil},
{['en'] = {'hugedomains', true}, ['local'] = nil},
{['en'] = {'^[%(%[{<]?no +title[>}%]%)]?$', false}, ['local'] = {'^[%(%[{<]?euweuh +judul[>}%]%)]?$', false}},
{['en'] = {'page not found', true}, ['local'] = {'kaca teu kapanggih', true}},
{['en'] = {'subscribe to read', true}, ['local'] = {'ngalanggan pikeun maca', true}},
{['en'] = {'^[%(%[{<]?unknown[>}%]%)]?$', false}, ['local'] = {'^[%(%[{<]?teu +dipikanyaho[>}%]%)]?$', false}},
{['en'] = {'website is for sale', true}, ['local'] = {'ramatloka dijual', true}},
{['en'] = {'^404', false}, ['local'] = nil},
{['en'] = {'error[ %-]404', false}, ['local'] = {'kasalahan[ %-]404', false}},
{['en'] = {'internet archive wayback machine', true}, ['local'] = nil},
{['en'] = {'log into facebook', true}, ['local'] = {'asup log ka facebook', true}},
{['en'] = {'login • instagram', true}, ['local'] = {'asup log • instagram', true}},
{['en'] = {'redirecting...', true}, ['local'] = {'ngalihkeun...', true}},
{['en'] = {'usurped title', true}, ['local'] = {'gelar nu direbut', true}}, -- added by a GreenC bot
{['en'] = {'webcite query result', true}, ['local'] = nil},
{['en'] = {'wikiwix\'s cache', true}, ['local'] = nil},
}
},
-- boolean 'true' for plain-text searches, search string must be lowercase only
-- boolean 'false' for pattern searches
-- leave ['local'] nil except when there is a matching generic name in your language
['generic_names'] = {
['accept'] = {
{['en'] = {'%[%[[^|]*%(author%) *|[^%]]*%]%]', false}, ['local'] = {'%[%[[^|]*%(panulis%) *|[^%]]*%]%]', false}},
},
['reject'] = {
{['en'] = {'about us', true}, ['local'] = {'ngeunaan arurang', true}},
{['en'] = {'%f[%a][Aa]dvisor%f[%A]', false}, ['local'] = nil},
{['en'] = {'allmusic', true}, ['local'] = nil},
{['en'] = {'%f[%a][Aa]uthor%f[%A]', false}, ['local'] = {'%f[%a][Pp]anulis%f[%A]', false}},
{['en'] = {'^[Bb]ureau$', false}, ['local'] = {'^[Bb]iro$', false}},
{['en'] = {'business', true}, ['local'] = {'bisnis', true}},
{['en'] = {'cnn', true}, ['local'] = nil},
{['en'] = {'collaborator', true}, ['local'] = {'kolaborator', true}},
{['en'] = {'^[Cc]ompany$', false}, ['local'] = {'^[Pp]ausahaan$', false}},
{['en'] = {'contributor', true}, ['local'] = {'kontributor', true}},
{['en'] = {'contact us', true}, ['local'] = {'hubungi arurang', true}},
{['en'] = {'correspondent', true}, ['local'] = {'koréspondén', true}},
{['en'] = {'^[Dd]esk$', false}, ['local'] = {'^[Mm]éja +[Kk]erja$', false}},
{['en'] = {'directory', true}, ['local'] = {'diréktori', true}},
{['en'] = {'%f[%(%[][%(%[]%s*eds?%.?%s*[%)%]]?$', false}, ['local'] = nil},
{['en'] = {'[,%.%s]%f[e]eds?%.?$', false}, ['local'] = nil},
{['en'] = {'^eds?[%.,;]', false}, ['local'] = nil},
{['en'] = {'^[%(%[]%s*[Ee][Dd][Ss]?%.?%s*[%)%]]', false}, ['local'] = nil},
{['en'] = {'%f[%a][Ee]dited%f[%A]', false}, ['local'] = {'%f[%a][Dd]iédit%f[%A]', false}},
{['en'] = {'%f[%a][Ee]ditors?%f[%A]', false}, ['local'] = {'%f[%a][Éé]ditor?%f[%A]', false}},
{['en'] = {'%f[%a][Ee]mail%f[%A]', false}, ['local'] = {'%f[%a][Ss]urélék%f[%A]', false}},
{['en'] = {'facebook', true}, ['local'] = nil},
{['en'] = {'google', true}, ['local'] = nil},
{['en'] = {'^[Gg]roup$', false}, ['local'] = {'^[Gg]orombolan$', false}},
{['en'] = {'home page', true}, ['local'] = {'beranda', true}},
{['en'] = {'^[Ii]nc%.?$', false}, ['local'] = nil},
{['en'] = {'instagram', true}, ['local'] = nil},
{['en'] = {'interviewer', true}, ['local'] = {'pangwawancara', true}},
{['en'] = {'^[Ll]imited$', false}, ['local'] = nil},
{['en'] = {'linkedIn', true}, ['local'] = nil},
{['en'] = {'^[Nn]ews$', false}, ['local'] = {'^[Ww]arta$', false}},
{['en'] = {'[Nn]ews[ %-]?[Rr]oom', false}, ['local'] = nil},
{['en'] = {'pinterest', true}, ['local'] = nil},
{['en'] = {'policy', true}, ['local'] = {'kawijakan', true}},
{['en'] = {'privacy', true}, ['local'] = {'privasi', true}},
{['en'] = {'reuters', true}, ['local'] = nil},
{['en'] = {'translator', true}, ['local'] = {'panarjamah', true}},
{['en'] = {'tumblr', true}, ['local'] = nil},
{['en'] = {'twitter', true}, ['local'] = nil},
{['en'] = {'site name', true}, ['local'] = {'ngaran loka', true}},
{['en'] = {'statement', true}, ['local'] = nil},
{['en'] = {'submitted', true}, ['local'] = nil},
{['en'] = {'super.?user', false}, ['local'] = nil},
{['en'] = {'%f['..is_Latn..'][Uu]ser%f[^'..is_Latn..']', false}, ['local'] = {'%f['..is_Latn..'][Pp]amaké%f[^'..is_Latn..']', false}},
{['en'] = {'verfasser', true}, ['local'] = nil},
}
}
}
--[[--------------------------< D A T E _ N A M E S >----------------------------------------------------------
This table of tables lists local language date names and fallback English date names.
The code in Date_validation will look first in the local table for valid date names.
If date names are not found in the local table, the code will look in the English table.
Because citations can be copied to the local wiki from en.wiki, the English is
required when the date-name translation function date_name_xlate() is used.
In these tables, season numbering is defined by
Extended Date/Time Format (EDTF) Specification (https://www.loc.gov/standards/datetime/)
which became part of ISO 8601 in 2019. See '§Sub-year groupings'. The standard
defines various divisions using numbers 21-41. CS1|2 only supports generic seasons.
EDTF does support the distinction between north and south hemisphere seasons
but CS1|2 has no way to make that distinction.
33-36 = Quarter 1, Quarter 2, Quarter 3, Quarter 4 (3 months each)
The standard does not address 'named' dates so, for the purposes of CS1|2,
Easter and Christmas are defined here as 98 and 99, which should be out of the
ISO 8601 (EDTF) range of uses for a while.
local_date_names_from_mediawiki is a boolean. When set to:
true – module will fetch local month names from MediaWiki for both date_names['local']['long'] and date_names['local']['short']; this will unconditionally overwrite manual translations
false – module will *not* fetch local month names from MediaWiki
Caveat lector: There is no guarantee that MediaWiki will provide short month names. At your wiki you can test
the results of the MediaWiki fetch in the debug console with this command (the result is alpha sorted):
=mw.dumpObject (p.date_names['local'])
While the module can fetch month names from MediaWiki, it cannot fetch the quarter, season, and named date names
from MediaWiki. Those must be translated manually.
]]
local local_date_names_from_mediawiki = true; -- when false, manual translation required for date_names['local']['long'] and date_names['local']['short']; overwrites manual translations
-- when true, module fetches long and short month names from MediaWiki
local date_names = {
['en'] = { -- English
['long'] = {['January'] = 1, ['February'] = 2, ['March'] = 3, ['April'] = 4, ['May'] = 5, ['June'] = 6, ['July'] = 7, ['August'] = 8, ['September'] = 9, ['October'] = 10, ['November'] = 11, ['December'] = 12},
['short'] = {['Jan'] = 1, ['Feb'] = 2, ['Mar'] = 3, ['Apr'] = 4, ['May'] = 5, ['Jun'] = 6, ['Jul'] = 7, ['Aug'] = 8, ['Sep'] = 9, ['Oct'] = 10, ['Nov'] = 11, ['Dec'] = 12},
['quarter'] = {['First Quarter'] = 33, ['Second Quarter'] = 34, ['Third Quarter'] = 35, ['Fourth Quarter'] = 36},
['season'] = {['Winter'] = 24, ['Spring'] = 21, ['Summer'] = 22, ['Fall'] = 23, ['Autumn'] = 23},
['named'] = {['Easter'] = 98, ['Christmas'] = 99},
},
-- when local_date_names_from_mediawiki = false
['local'] = { -- replace these English date names with the local language equivalents
['long'] = {['Januari'] = 1, ['Pébruari'] = 2, ['Maret'] = 3, ['April'] = 4, ['Méi'] = 5, ['Juni'] = 6, ['Juli'] = 7, ['Agustus'] = 8, ['Séptémber'] = 9, ['Oktober'] = 10, ['Nopémber'] = 11, ['Désémber'] = 12},
['short'] = {['Jan'] = 1, ['Péb'] = 2, ['Mar'] = 3, ['Apr'] = 4, ['Méi'] = 5, ['Jun'] = 6, ['Jul'] = 7, ['Agu'] = 8, ['Sép'] = 9, ['Okt'] = 10, ['Nop'] = 11, ['Dés'] = 12},
['quarter'] = {['Kuartal Kahiji'] = 33, ['Kuartal Kadua'] = 34, ['Kuartal Katilu'] = 35, ['Kuartal Kaopat'] = 36},
['season'] = {['Usum Tiris'] = 24, ['Usum Semi'] = 21, ['Usum Panas'] = 22, ['Usum Gugur'] = 23,},
['named'] = {['Paskah'] = 98, ['Natal'] = 99},
},
['inv_local_long'] = {}, -- used in date reformatting & translation; copy of date_names['local'].long where k/v are inverted: [1]='<local name>' etc.
['inv_local_short'] = {}, -- used in date reformatting & translation; copy of date_names['local'].short where k/v are inverted: [1]='<local name>' etc.
['inv_local_quarter'] = {}, -- used in date translation; copy of date_names['local'].quarter where k/v are inverted: [1]='<local name>' etc.
['inv_local_season'] = {}, -- used in date translation; copy of date_names['local'].season where k/v are inverted: [1]='<local name>' etc.
['inv_local_named'] = {}, -- used in date translation; copy of date_names['local'].named where k/v are inverted: [1]='<local name>' etc.
['local_digits'] = {['0'] = '0', ['1'] = '1', ['2'] = '2', ['3'] = '3', ['4'] = '4', ['5'] = '5', ['6'] = '6', ['7'] = '7', ['8'] = '8', ['9'] = '9'}, -- used to convert local language digits to Western 0-9
['xlate_digits'] = {},
}
if local_date_names_from_mediawiki then -- if fetching local month names from MediaWiki is enabled
local long_t = {};
local short_t = {};
for i=1, 12 do -- loop 12x and
local name = lang_obj:formatDate('F', '2022-' .. i .. '-1'); -- get long month name for each i
long_t[name] = i; -- save it
name = lang_obj:formatDate('M', '2022-' .. i .. '-1'); -- get short month name for each i
short_t[name] = i; -- save it
end
date_names['local']['long'] = long_t; -- write the long table – overwrites manual translation
date_names['local']['short'] = short_t; -- write the short table – overwrites manual translation
end
-- create inverted date-name tables for reformatting and/or translation
for _, invert_t in pairs {{'long', 'inv_local_long'}, {'short', 'inv_local_short'}, {'quarter', 'inv_local_quarter'}, {'season', 'inv_local_season'}, {'named', 'inv_local_named'}} do
for name, i in pairs (date_names['local'][invert_t[1]]) do -- this table is ['name'] = i
date_names[invert_t[2]][i] = name; -- invert to get [i] = 'name' for conversions from ymd
end
end
if local_digits_from_mediawiki then -- if fetching local digits from MediaWiki is enabled
local digits_t = {};
for i=0, 9 do -- loop 10x and
digits_t [lang_obj:formatNum (i)] = tostring (i); -- format the loop indexer as local lang table index and assign loop indexer (a string) as the value
end
date_names['local_digits'] = digits_t;
end
for ld, ed in pairs (date_names.local_digits) do -- make a digit translation table for simple date translation from en to local language using local_digits table
date_names.xlate_digits [ed] = ld; -- en digit becomes index with local digit as the value
end
local df_template_patterns = { -- table of redirects to {{Use dmy dates}} and {{Use mdy dates}}
'{{ *[Uu]se +(dmy) +dates *[|}]', -- 1159k -- sorted by approximate transclusion count
'{{ *[Uu]se +(mdy) +dates *[|}]', -- 212k
'{{ *[Uu]se +(MDY) +dates *[|}]', -- 788
'{{ *[Uu]se +(DMY) +dates *[|}]', -- 343
'{{ *([Mm]dy) *[|}]', -- 176
'{{ *[Uu]se *(dmy) *[|}]', -- 156 + 18
'{{ *[Uu]se *(mdy) *[|}]', -- 149 + 11
'{{ *([Dd]my) *[|}]', -- 56
'{{ *[Uu]se +(MDY) *[|}]', -- 5
'{{ *([Dd]MY) *[|}]', -- 3
'{{ *[Uu]se(mdy)dates *[|}]', -- 1
'{{ *[Uu]se +(DMY) *[|}]', -- 0
'{{ *([Mm]DY) *[|}]', -- 0
}
local title_object = mw.title.getCurrentTitle();
local content; -- done this way so that unused templates appear in unused-template-reports; self-transcluded makes them look like they are used
if 10 ~= title_object.namespace then -- all namespaces except Template
content = title_object:getContent() or ''; -- get the content of the article or ''; new pages edited w/ve do not have 'content' until saved; ve does not preview; phab:T221625
end
local function get_date_format ()
if not content then -- nil content when we're in template
return nil; -- auto-formatting does not work in Template space so don't set global_df
end
for _, pattern in ipairs (df_template_patterns) do -- loop through the patterns looking for {{Use dmy dates}} or {{Use mdy dates}} or any of their redirects
local start, _, match = content:find(pattern); -- match is the three letters indicating desired date format
if match then
local use_dates_template = content:match ('%b{}', start); -- get the whole template
if use_dates_template:match ('| *cs1%-dates *= *[lsy][sy]?') then -- look for |cs1-dates=publication date length access-/archive-date length
return match:lower() .. '-' .. use_dates_template:match ('| *cs1%-dates *= *([lsy][sy]?)');
else
return match:lower() .. '-all'; -- no |cs1-dates= k/v pair; return value appropriate for use in |df=
end
end
end
end
--[[-----------------< V O L U M E , I S S U E , P A G E S >------------------
These tables hold cite class values (from the template invocation) and identify those templates that support
|volume=, |issue=, and |page(s)= parameters. Cite conference and cite map require further qualification which
is handled in the main module.
]]
local templates_using_volume = {'citation', 'audio-visual', 'book', 'conference', 'encyclopaedia', 'interview', 'journal', 'magazine', 'map', 'news', 'report', 'techreport', 'thesis'}
local templates_using_issue = {'citation', 'conference', 'episode', 'interview', 'journal', 'magazine', 'map', 'news', 'podcast'}
local templates_not_using_page = {'audio-visual', 'episode', 'mailinglist', 'newsgroup', 'podcast', 'serial', 'sign', 'speech'}
--[[
These tables control when it is appropriate for {{citation}} to render |volume= and/or |issue=. The parameter
names in the tables constrain {{citation}} so that its renderings match the renderings of the equivalent cs1
templates. For example, {{cite web}} does not support |volume= so the equivalent {{citation |website=...}} must
not support |volume=.
]]
local citation_no_volume_t = { -- {{citation}} does not render |volume= when these parameters are used
'website', 'mailinglist', 'script-website',
}
local citation_issue_t = { -- {{citation}} may render |issue= when these parameters are used
'journal', 'magazine', 'newspaper', 'periodical', 'work',
'script-journal', 'script-magazine', 'script-newspaper', 'script-periodical', 'script-work',
}
--[[
Patterns for finding extra text in |volume=, |issue=, |page=, |pages=
]]
local vol_iss_pg_patterns = {
good_ppattern = '^P[^%.PpGg]', -- OK to begin with uppercase P: P7 (page 7 of section P), but not p123 (page 123); TODO: this allows 'Pages' which it should not
bad_ppatterns = { -- patterns for |page= and |pages=
'^[Pp][PpGg]?%.?[ %d]',
'^[Pp][Pp]?%. ', -- from {{p.}} and {{pp.}} templates
'^[Pp]ages?',
'^[Pp]gs.?',
},
vi_patterns_t = { -- combined to catch volume-like text in |issue= and issue-like text in |volume=
'^volumes?', -- volume-like text
'^vols?[%.:=]?',
'^issues?', --issue-like text
'^iss[%.:=]?',
'^numbers?',
'^nos?%A', -- don't match 'november' or 'nostradamus'
'^nr[%.:=]?',
'^n[%.:= ]', -- might be a valid issue without separator (space char is sep char here)
'^n°', -- 'n' with degree sign (U+00B0)
'^№', -- precomposed unicode numero character (U+2116)
},
}
--[[--------------------------< K E Y W O R D S >-------------------------------
These tables hold keywords for those parameters that have defined sets of acceptable keywords.
]]
--[[-------------------< K E Y W O R D S T A B L E >--------------------------
this is a list of keywords; each key in the list is associated with a table of
synonymous keywords possibly from different languages.
for I18N: add local-language keywords to value table; do not change the key.
For example, adding the German keyword 'ja':
['affirmative'] = {'yes', 'true', 'y', 'ja'},
Because CS1|2 templates from en.wiki articles are often copied to other local wikis,
it is recommended that the English keywords remain in these tables.
]]
local keywords = {
['amp'] = {'&', 'amp', 'ampersand'}, -- |name-list-style=
['and'] = {'and', 'serial'}, -- |name-list-style=
['affirmative'] = {'yes', 'true', 'y'}, -- |no-tracking=, |no-pp= -- Used by InternetArchiveBot
['afterword'] = {'afterword'}, -- |contribution=
['bot: unknown'] = {'bot: unknown'}, -- |url-status= -- Used by InternetArchiveBot
['cs1'] = {'cs1'}, -- |mode=
['cs2'] = {'cs2'}, -- |mode=
['dead'] = {'dead', 'deviated'}, -- |url-status= -- Used by InternetArchiveBot
['dmy'] = {'dmy'}, -- |df=
['dmy-all'] = {'dmy-all'}, -- |df=
['foreword'] = {'foreword'}, -- |contribution=
['free'] = {'free'}, -- |<id>-access= -- Used by InternetArchiveBot
['harv'] = {'harv'}, -- |ref=; this no longer supported; is_valid_parameter_value() called with <invert> = true
['introduction'] = {'introduction'}, -- |contribution=
['limited'] = {'limited'}, -- |url-access= -- Used by InternetArchiveBot
['live'] = {'live'}, -- |url-status= -- Used by InternetArchiveBot
['mdy'] = {'mdy'}, -- |df=
['mdy-all'] = {'mdy-all'}, -- |df=
['none'] = {'none'}, -- |postscript=, |ref=, |title=, |type= -- Used by InternetArchiveBot
['off'] = {'off'}, -- |title= (potentially also: |title-link=, |postscript=, |ref=, |type=)
['preface'] = {'preface'}, -- |contribution=
['registration'] = {'registration'}, -- |url-access= -- Used by InternetArchiveBot
['subscription'] = {'subscription'}, -- |url-access= -- Used by InternetArchiveBot
['unfit'] = {'unfit'}, -- |url-status= -- Used by InternetArchiveBot
['usurped'] = {'usurped'}, -- |url-status= -- Used by InternetArchiveBot
['vanc'] = {'vanc'}, -- |name-list-style=
['ymd'] = {'ymd'}, -- |df=
['ymd-all'] = {'ymd-all'}, -- |df=
-- ['yMd'] = {'yMd'}, -- |df=; not supported at en.wiki
-- ['yMd-all'] = {'yMd-all'}, -- |df=; not supported at en.wiki
}
--[[------------------------< X L A T E _ K E Y W O R D S >---------------------
this function builds a list, keywords_xlate{}, of the keywords found in keywords{} where the values from keywords{}
become the keys in keywords_xlate{} and the keys from keywords{} become the values in keywords_xlate{}:
['affirmative'] = {'yes', 'true', 'y'}, -- in keywords{}
becomes
['yes'] = 'affirmative', -- in keywords_xlate{}
['true'] = 'affirmative',
['y'] = 'affirmative',
the purpose of this function is to act as a translator between a non-English keyword and its English equivalent
that may be used in other modules of this suite
]]
local function xlate_keywords ()
local out_table = {}; -- output goes here
for k, keywords_t in pairs (keywords) do -- spin through the keywords table
for _, keyword in ipairs (keywords_t) do -- for each keyword
out_table[keyword] = k; -- create an entry in the output table where keyword is the key
end
end
return out_table;
end
local keywords_xlate = xlate_keywords (); -- the list of translated keywords
--[[----------------< M A K E _ K E Y W O R D S _ L I S T >---------------------
this function assembles, for parameter-value validation, the list of keywords appropriate to that parameter.
keywords_lists{}, is a table of tables from keywords{}
]]
local function make_keywords_list (keywords_lists)
local out_table = {}; -- output goes here
for _, keyword_list in ipairs (keywords_lists) do -- spin through keywords_lists{} and get a table of keywords
for _, keyword in ipairs (keyword_list) do -- spin through keyword_list{} and add each keyword, ...
table.insert (out_table, keyword); -- ... as plain text, to the output list
end
end
return out_table;
end
--[[----------------< K E Y W O R D S _ L I S T S >-----------------------------
this is a list of lists of valid keywords for the various parameters in [key].
Generally the keys in this table are the canonical en.wiki parameter names though
some are contrived because of use in multiple differently named parameters:
['yes_true_y'], ['id-access'].
The function make_keywords_list() extracts the individual keywords from the
appropriate list in keywords{}.
The lists in this table are used to validate the keyword assignment for the
parameters named in this table's keys.
]]
local keywords_lists = {
['yes_true_y'] = make_keywords_list ({keywords.affirmative}),
['contribution'] = make_keywords_list ({keywords.afterword, keywords.foreword, keywords.introduction, keywords.preface}),
['df'] = make_keywords_list ({keywords.dmy, keywords['dmy-all'], keywords.mdy, keywords['mdy-all'], keywords.ymd, keywords['ymd-all']}),
-- ['df'] = make_keywords_list ({keywords.dmy, keywords['dmy-all'], keywords.mdy, keywords['mdy-all'], keywords.ymd, keywords['ymd-all'], keywords.yMd, keywords['yMd-all']}), -- not supported at en.wiki
['mode'] = make_keywords_list ({keywords.cs1, keywords.cs2}),
['name-list-style'] = make_keywords_list ({keywords.amp, keywords['and'], keywords.vanc}),
['ref'] = make_keywords_list ({keywords.harv}), -- inverted check; |ref=harv no longer supported
['url-access'] = make_keywords_list ({keywords.subscription, keywords.limited, keywords.registration}),
['url-status'] = make_keywords_list ({keywords.dead, keywords.live, keywords.unfit, keywords.usurped, keywords['bot: unknown']}),
['id-access'] = make_keywords_list ({keywords.free}),
}
--[[--------------------------< C S 1 _ C O N F I G _ G E T >--------------------------------------------------
fetch and validate values from {{cs1 config}} template to fill <global_cs1_config_t>
no error messages; when errors are detected, the parameter value from {{cs1 config}} is blanked.
Supports all parameters and aliases associated with the metaparameters: DisplayAuthors, DisplayContributors,
DisplayEditors, DisplayInterviewers, DisplayTranslators, NameListStyle, and Mode. The DisplayWhatever metaparameters
accept numeric values only (|display-authors=etal and the like is not supported).
]]
local global_cs1_config_t = {}; -- TODO: add value returned from get_date_format() to this table?
local function get_cs1_config ()
if not content then -- nil content when we're in template
return nil; -- auto-formatting does not work in Template space so don't set global_df
end
local start = content:find('{{ *[Cc][Ss]1 config *[|}]'); -- <start> is offset into <content> when {{cs1 config}} found; nil else
if start then
local cs1_config_template = content:match ('%b{}', start); -- get the whole template
if not cs1_config_template then
return nil;
end
local params_t = mw.text.split (cs1_config_template:gsub ('^{{%s*', ''):gsub ('%s*}}$', ''), '%s*|%s*'); -- remove '{{' and '}}'; make a sequence of parameter/value pairs (split on the pipe)
table.remove (params_t, 1); -- remove the template name because it isn't a parameter/value pair
local config_meta_params_t = {'DisplayAuthors', 'DisplayContributors', 'DisplayEditors', 'DisplayInterviewers', 'DisplayTranslators', 'NameListStyle', 'Mode'};
local meta_param_map_t = {}; -- list of accepted parameter names usable in {{cs1 config}} goes here
for _, meta_param in ipairs (config_meta_params_t) do -- for i18n using <config_meta_params_t>, map template parameter names to their metaparameter equivalents
if 'table' == type (aliases[meta_param]) then -- if <meta_param> is a sequence,
for _, param in ipairs (aliases[meta_param]) do -- extract its contents
meta_param_map_t[param] = meta_param; -- and add to <meta_param_map_t>
end
else
meta_param_map_t[aliases[meta_param]] = meta_param; -- not a sequence so just add the parameter to <meta_param_map_t>
end
end
local keywords_t = {}; -- map valid keywords to their associate metaparameter; reverse form of <keyword_lists[key] for these metaparameters
for _, metaparam_t in ipairs ({{'NameListStyle', 'name-list-style'}, {'Mode', 'mode'}}) do -- only these metaparameter / keywords_lists key pairs
for _, keyword in ipairs (keywords_lists[metaparam_t[2]]) do -- spin through the list of keywords
keywords_t[keyword] = metaparam_t[1]; -- add [keyword] = metaparameter to the map
end
end
for _, param in ipairs (params_t) do -- spin through the {{cs1 config}} parameters and fill <global_cs1_config_t>
local k, v = param:match ('([^=]-)%s*=%s*(.+)'); -- <k> is the parameter name; <v> is parameter's assigned value
if k then
if k:find ('^display') then -- if <k> is one of the |display-<namelist>= parameters
if v:match ('%d+') then -- the assigned value must be digits; doesn't accept 'etal'
global_cs1_config_t[meta_param_map_t[k]]=v; -- add the display param and its value to globals table
end
else
if keywords_t[v] == meta_param_map_t[k] then -- keywords_t[v] returns nil or the metaparam name; these must be the same
global_cs1_config_t[meta_param_map_t[k]]=v; -- add the parameter and its value to globals table
end
end
end
end
end
end
get_cs1_config (); -- fill <global_cs1_config_t>
--[[---------------------< S T R I P M A R K E R S >----------------------------
Common pattern definition location for stripmarkers so that we don't have to go
hunting for them if (when) MediaWiki changes their form.
]]
local stripmarkers = {
['any'] = '\127[^\127]*UNIQ%-%-(%a+)%-[%a%d]+%-QINU[^\127]*\127', -- capture returns name of stripmarker
['math'] = '\127[^\127]*UNIQ%-%-math%-[%a%d]+%-QINU[^\127]*\127' -- math stripmarkers used in coins_cleanup() and coins_replace_math_stripmarker()
}
--[[------------< I N V I S I B L E _ C H A R A C T E R S >---------------------
This table holds non-printing or invisible characters indexed either by name or
by Unicode group. Values are decimal representations of UTF-8 codes. The table
is organized as a table of tables because the Lua pairs keyword returns table
data in an arbitrary order. Here, we want to process the table from top to bottom
because the entries at the top of the table are also found in the ranges specified
by the entries at the bottom of the table.
Also here is a pattern that recognizes stripmarkers that begin and end with the
delete characters. The nowiki stripmarker is not an error but some others are
because the parameter values that include them become part of the template's
metadata before stripmarker replacement.
]]
local invisible_defs = {
del = '\127', -- used to distinguish between stripmarker and del char
zwj = '\226\128\141', -- used with capture because zwj may be allowed
}
local invisible_chars = {
{'replacement', '\239\191\189'}, -- U+FFFD, EF BF BD
{'zero width joiner', '('.. invisible_defs.zwj .. ')'}, -- U+200D, E2 80 8D; capture because zwj may be allowed
{'zero width space', '\226\128\139'}, -- U+200B, E2 80 8B
{'hair space', '\226\128\138'}, -- U+200A, E2 80 8A
{'soft hyphen', '\194\173'}, -- U+00AD, C2 AD
{'horizontal tab', '\009'}, -- U+0009 (HT), 09
{'line feed', '\010'}, -- U+000A (LF), 0A
{'no-break space', '\194\160'}, -- U+00A0 (NBSP), C2 A0
{'carriage return', '\013'}, -- U+000D (CR), 0D
{'stripmarker', stripmarkers.any}, -- stripmarker; may or may not be an error; capture returns the stripmaker type
{'delete', '('.. invisible_defs.del .. ')'}, -- U+007F (DEL), 7F; must be done after stripmarker test; capture to distinguish isolated del chars not part of stripmarker
{'C0 control', '[\000-\008\011\012\014-\031]'}, -- U+0000–U+001F (NULL–US), 00–1F (except HT, LF, CR (09, 0A, 0D))
{'C1 control', '[\194\128-\194\159]'}, -- U+0080–U+009F (XXX–APC), C2 80 – C2 9F
-- {'Specials', '[\239\191\185-\239\191\191]'}, -- U+FFF9-U+FFFF, EF BF B9 – EF BF BF
-- {'Private use area', '[\238\128\128-\239\163\191]'}, -- U+E000–U+F8FF, EE 80 80 – EF A3 BF
-- {'Supplementary Private Use Area-A', '[\243\176\128\128-\243\191\191\189]'}, -- U+F0000–U+FFFFD, F3 B0 80 80 – F3 BF BF BD
-- {'Supplementary Private Use Area-B', '[\244\128\128\128-\244\143\191\189]'}, -- U+100000–U+10FFFD, F4 80 80 80 – F4 8F BF BD
}
--[[
Indic script makes use of zero width joiner as a character modifier so zwj
characters must be left in. This pattern covers all of the unicode characters
for these languages:
Devanagari 0900–097F – https://unicode.org/charts/PDF/U0900.pdf
Devanagari extended A8E0–A8FF – https://unicode.org/charts/PDF/UA8E0.pdf
Bengali 0980–09FF – https://unicode.org/charts/PDF/U0980.pdf
Gurmukhi 0A00–0A7F – https://unicode.org/charts/PDF/U0A00.pdf
Gujarati 0A80–0AFF – https://unicode.org/charts/PDF/U0A80.pdf
Oriya 0B00–0B7F – https://unicode.org/charts/PDF/U0B00.pdf
Tamil 0B80–0BFF – https://unicode.org/charts/PDF/U0B80.pdf
Telugu 0C00–0C7F – https://unicode.org/charts/PDF/U0C00.pdf
Kannada 0C80–0CFF – https://unicode.org/charts/PDF/U0C80.pdf
Malayalam 0D00–0D7F – https://unicode.org/charts/PDF/U0D00.pdf
plus the not-necessarily Indic scripts for Sinhala and Burmese:
Sinhala 0D80-0DFF - https://unicode.org/charts/PDF/U0D80.pdf
Myanmar 1000-109F - https://unicode.org/charts/PDF/U1000.pdf
Myanmar extended A AA60-AA7F - https://unicode.org/charts/PDF/UAA60.pdf
Myanmar extended B A9E0-A9FF - https://unicode.org/charts/PDF/UA9E0.pdf
the pattern is used by has_invisible_chars() and coins_cleanup()
]]
local indic_script = '[\224\164\128-\224\181\191\224\163\160-\224\183\191\225\128\128-\225\130\159\234\167\160-\234\167\191\234\169\160-\234\169\191]';
-- list of emoji that use a zwj character (U+200D) to combine with another emoji
-- from: https://unicode.org/Public/emoji/16.0/emoji-zwj-sequences.txt; version: 16.0; 2024-08-14
-- table created by: [[:en:Module:Make emoji zwj table]]
local emoji_t = { -- indexes are decimal forms of the hex values in U+xxxx
[8596] = true, -- U+2194 ↔ left right arrow
[8597] = true, -- U+2195 ↕ up down arrow
[9760] = true, -- U+2620 ☠ skull and crossbones
[9792] = true, -- U+2640 ♀ female sign
[9794] = true, -- U+2642 ♂ male sign
[9877] = true, -- U+2695 ⚕ staff of aesculapius
[9878] = true, -- U+2696 ⚖ scales
[9895] = true, -- U+26A7 ⚧ male with stroke and male and female sign
[9992] = true, -- U+2708 ✈ airplane
[10052] = true, -- U+2744 ❄ snowflake
[10084] = true, -- U+2764 ❤ heavy black heart
[10145] = true, -- U+27A1 ➡ black rightwards arrow
[11035] = true, -- U+2B1B ⬛ black large square
[127752] = true, -- U+1F308 🌈 rainbow
[127787] = true, -- U+1F32B 🌫 fog
[127806] = true, -- U+1F33E 🌾 ear of rice
[127859] = true, -- U+1F373 🍳 cooking
[127868] = true, -- U+1F37C 🍼 baby bottle
[127876] = true, -- U+1F384 🎄 christmas tree
[127891] = true, -- U+1F393 🎓 graduation cap
[127908] = true, -- U+1F3A4 🎤 microphone
[127912] = true, -- U+1F3A8 🎨 artist palette
[127979] = true, -- U+1F3EB 🏫 school
[127981] = true, -- U+1F3ED 🏭 factory
[128102] = true, -- U+1F466 👦 boy
[128103] = true, -- U+1F467 👧 girl
[128104] = true, -- U+1F468 👨 man
[128105] = true, -- U+1F469 👩 woman
[128139] = true, -- U+1F48B 💋 kiss mark
[128165] = true, -- U+1F4A5 💥 collision symbol
[128168] = true, -- U+1F4A8 💨 dash symbol
[128171] = true, -- U+1F4AB 💫 dizzy symbol
[128187] = true, -- U+1F4BB 💻 personal computer
[128188] = true, -- U+1F4BC 💼 brief case
[128293] = true, -- U+1F525 🔥 fire
[128295] = true, -- U+1F527 🔧 wrench
[128300] = true, -- U+1F52C 🔬 microscope
[128488] = true, -- U+1F5E8 🗨 left speech bubble
[128640] = true, -- U+1F680 🚀 rocket
[128658] = true, -- U+1F692 🚒 fire engine
[129001] = true, -- U+1F7E9 🟩 large green square
[129003] = true, -- U+1F7EB 🟫 large brown square
[129309] = true, -- U+1F91D 🤝 handshake
[129455] = true, -- U+1F9AF 🦯 probing cane
[129456] = true, -- U+1F9B0 🦰 emoji component red hair
[129457] = true, -- U+1F9B1 🦱 emoji component curly hair
[129458] = true, -- U+1F9B2 🦲 emoji component bald
[129459] = true, -- U+1F9B3 🦳 emoji component white hair
[129466] = true, -- U+1F9BA 🦺 safety vest
[129468] = true, -- U+1F9BC 🦼 motorized wheelchair
[129469] = true, -- U+1F9BD 🦽 manual wheelchair
[129489] = true, -- U+1F9D1 🧑 adult
[129490] = true, -- U+1F9D2 🧒 child
[129657] = true, -- U+1FA79 🩹 adhesive bandage
[129778] = true, -- U+1FAF2 🫲 leftwards hand
}
--[[----------------------< L A N G U A G E S U P P O R T >-------------------
These tables and constants support various language-specific functionality.
]]
--local this_wiki_code = mw.getContentLanguage():getCode(); -- get this wiki's language code
local this_wiki_code = lang_obj:getCode(); -- get this wiki's language code
if string.match (mw.site.server, 'wikidata') then
this_wiki_code = mw.getCurrentFrame():callParserFunction('int', {'lang'}); -- on Wikidata so use interface language setting instead
end
local mw_languages_by_tag_t = mw.language.fetchLanguageNames (this_wiki_code, 'all'); -- get a table of language tag/name pairs known to Wikimedia; used for interwiki tests
local mw_languages_by_name_t = {};
for k, v in pairs (mw_languages_by_tag_t) do -- build a 'reversed' table name/tag language pairs know to MediaWiki; used for |language=
v = mw.ustring.lower (v); -- lowercase for tag fetch; get name's proper case from mw_languages_by_tag_t[<tag>]
if mw_languages_by_name_t[v] then -- when name already in the table
if 2 == #k or 3 == #k then -- if tag does not have subtags
mw_languages_by_name_t[v] = k; -- prefer the shortest tag for this name
end
else -- here when name not in the table
mw_languages_by_name_t[v] = k; -- so add name and matching tag
end
end
local inter_wiki_map = {}; -- map of interwiki prefixes that are language-code prefixes
for k, v in pairs (mw.site.interwikiMap ('local')) do -- spin through the base interwiki map (limited to local)
if mw_languages_by_tag_t[v["prefix"]] then -- if the prefix matches a known language tag
inter_wiki_map[v["prefix"]] = true; -- add it to our local map
end
end
--[[--------------------< S C R I P T _ L A N G _ C O D E S >-------------------
This table is used to hold ISO 639-1 two-character and ISO 639-3 three-character
language codes that apply only to |script-title= and |script-chapter=
]]
local script_lang_codes = {
'ab', 'am', 'ar', 'az', 'be', 'bg', 'bn', 'bo', 'bs', 'ce', 'chr', 'dv', 'dz',
'el', 'fa', 'grc', 'gu', 'he', 'hi', 'hy', 'ja', 'ka', 'kk', 'km', 'kn', 'ko',
'ku', 'ky', 'lo', 'mk', 'ml', 'mn', 'mni', 'mr', 'my', 'ne', 'or', 'ota',
'pa', 'ps', 'ru', 'sd', 'si', 'sr', 'syc', 'ta', 'te', 'tg', 'th', 'ti', 'tt',
'ug', 'uk', 'ur', 'uz', 'yi', 'yue', 'zh', 'zgh'
};
--[[---------------< L A N G U A G E R E M A P P I N G >----------------------
These tables hold language information that is different (correct) from MediaWiki's definitions
For each ['<tag>'] = 'language name' in lang_code_remap{} there must be a matching ['language name'] = {'language name', '<tag>'} in lang_name_remap{}
lang_tag_remap{}:
key is always lowercase ISO 639-1, -2, -3 language tag or a valid lowercase IETF language tag
value is properly spelled and capitalized language name associated with <tag>
only one language name per <tag>;
key/value pair must have matching entry in lang_name_remap{}
lang_name_remap{}:
key is always lowercase language name
value is a table the holds correctly spelled and capitalized language name [1] and associated tag [2] (tag must match a tag key in lang_tag_remap{})
may have multiple keys referring to a common preferred name and tag; For example:
['kolsch'] and ['kölsch'] both refer to 'Kölsch' and 'ksh'
]]
local lang_tag_remap = { -- used for |language= and |script-title= / |script-chapter=
['als'] = 'Tosk Albanian', -- MediaWiki returns Alemannisch
['bh'] = 'Bihari', -- MediaWiki uses 'bh' as a subdomain name for Bhojpuri Wikipedia: bh.wikipedia.org
['bla'] = 'Blackfoot', -- MediaWiki/IANA/ISO 639: Siksika; use en.wiki preferred name
['bn'] = 'Bengali', -- MediaWiki returns Bangla
['ca-valencia'] = 'Valencian', -- IETF variant of Catalan
['fkv'] = 'Kven', -- MediaWiki returns Kvensk
['gsw'] = 'Swiss German',
['ilo'] = 'Ilocano', -- MediaWiki/IANA/ISO 639: Iloko; use en.wiki preferred name
['ksh'] = 'Kölsch', -- MediaWiki: Colognian; use IANA/ISO 639 preferred name
['ksh-x-colog'] = 'Colognian', -- override MediaWiki ksh; no IANA/ISO 639 code for Colognian; IETF private code created at Module:Lang/data
['mis-x-ripuar'] = 'Ripuarian', -- override MediaWiki ksh; no IANA/ISO 639 code for Ripuarian; IETF private code created at Module:Lang/data
['nan-tw'] = 'Taiwanese Hokkien', -- make room for MediaWiki/IANA/ISO 639 nan: Min Nan Chinese and support en.wiki preferred name
['sr-ec'] = 'Serbian (Cyrillic script)', -- MediaWiki returns српски (ћирилица)
['sr-el'] = 'Serbian (Latin script)', -- MediaWiki returns srpski (latinica)
}
local lang_name_remap = { -- used for |language=; names require proper capitalization; tags must be lowercase
['alemannic'] = {'Swiss German', 'gsw'}, -- ISO 639-2, -3 alternate for Swiss German; MediaWiki mediawiki returns Alemannic for gsw; en.wiki preferred name
['alemannisch'] = {'Swiss German', 'gsw'}, -- not an ISO or IANA language name; MediaWiki uses 'als' as a subdomain name for Alemannic Wikipedia: als.wikipedia.org
['bangla'] = {'Bengali', 'bn'}, -- MediaWiki returns Bangla (the endonym) but we want Bengali (the exonym); here we remap
['bengali'] = {'Bengali', 'bn'}, -- MediaWiki doesn't use exonym so here we provide correct language name and 639-1 code
['bhojpuri'] = {'Bhojpuri', 'bho'}, -- MediaWiki uses 'bh' as a subdomain name for Bhojpuri Wikipedia: bh.wikipedia.org
['bihari'] = {'Bihari', 'bh'}, -- MediaWiki replaces 'Bihari' with 'Bhojpuri' so 'Bihari' cannot be found
['blackfoot'] = {'Blackfoot', 'bla'}, -- MediaWiki/IANA/ISO 639: Siksika; use en.wiki preferred name
['colognian'] = {'Colognian', 'ksh-x-colog'}, -- MediaWiki preferred name for ksh
['ilocano'] = {'Ilocano', 'ilo'}, -- MediaWiki/IANA/ISO 639: Iloko; use en.wiki preferred name
['kolsch'] = {'Kölsch', 'ksh'}, -- use IANA/ISO 639 preferred name (use non-diacritical o instead of umlaut ö)
['kölsch'] = {'Kölsch', 'ksh'}, -- use IANA/ISO 639 preferred name
['kven'] = {'Kven', 'fkv'}, -- Unicode CLDR have decided not to support English language name for these two...
['kvensk'] = {'Kven', 'fkv'}, -- ...they say to refer to IANA registry for English names
['ripuarian'] = {'Ripuarian', 'mis-x-ripuar'}, -- group of dialects; no code in MediaWiki or in IANA/ISO 639
['serbian (cyrillic script)'] = {'Serbian (Cyrillic script)', 'sr-cyrl'}, -- special case to get correct tag when |language=sr-ec
['serbian (latin script)'] = {'Serbian (Latin script)', 'sr-latn'}, -- special case to get correct tag when |language=sr-el
['swiss german'] = {'Swiss German', 'gsw'},
['taiwanese hokkien'] = {'Taiwanese Hokkien', 'nan-tw'}, -- make room for MediaWiki/IANA/ISO 639 nan: Min Nan Chinese
['tosk albanian'] = {'Tosk Albanian', 'als'}, -- MediaWiki replaces 'Tosk Albanian' with 'Alemannisch' so 'Tosk Albanian' cannot be found
['valencian'] = {'Valencian', 'ca-valencia'}, -- variant of Catalan; categorizes as Valencian
}
--[[---------------< P R O P E R T I E S _ C A T E G O R I E S >----------------
Properties categories. These are used for investigating qualities of citations.
]]
local prop_cats = {
['foreign-lang-source'] = 'CS1 sumber basa $1 ($2)', -- |language= categories; $1 is foreign-language name, $2 is ISO639-1 code
['foreign-lang-source-2'] = 'CS1 sumber basa asing (ISO 639-2)|$1', -- |language= category; a cat for ISO639-2 languages; $1 is the ISO 639-2 code used as a sort key
['interproj-linked-name'] = 'CS1 ngaran kalawan tutumbu antarproyék|$1', -- any author, editor, etc that has an interproject link; $1 is interproject tag used as a sort key
['interwiki-linked-name'] = 'CS1 ngaran kalawan tutumbu interwiki|$1', -- any author, editor, etc that has an interwiki link; $1 is interwiki tag used as a sort key; yeilds to interproject
['local-lang-source'] = 'CS1 sumber basa $1 ($2)', -- |language= categories; $1 is local-language name, $2 is ISO639-1 code; not emitted when local_lang_cat_enable is false
['location-test'] = 'CS1 uji lokasi',
['long-vol'] = 'CS1: volume bernilai panjang', -- probably temporary cat to identify scope of |volume= values longer than 4 characters
['script'] = 'CS1 maké aksara basa $1 ($2)', -- |script-title=xx: has matching category; $1 is language name, $2 is language tag
['tracked-param'] = 'CS1 paraméter kalacak: $1', -- $1 is base (enumerators removed) parameter name
['unfit'] = 'CS1: URL teu cocok', -- |url-status=unfit or |url-status=usurped; used to be a maint cat
['vanc-accept'] = 'CS1:Vancouver names with accept markup', -- for |vauthors=/|veditors= with accept-as-written markup
['year-range-abbreviated'] = 'CS1: rentang taun disingget', -- probably temporary cat to identify scope of |date=, |year= values using YYYY–YY form
}
--[[-------------------< T I T L E _ T Y P E S >--------------------------------
Here we map a template's CitationClass to TitleType (default values for |type= parameter)
]]
local title_types = {
['AV-media-notes'] = 'Media notes',
['document'] = 'Document',
['interview'] = 'Interview',
['mailinglist'] = 'Mailing list',
['map'] = 'Map',
['podcast'] = 'Podcast',
['pressrelease'] = 'Press release',
['report'] = 'Report',
['speech'] = 'Speech',
['techreport'] = 'Technical report',
['thesis'] = 'Thesis',
}
--[[--------------------------< B U I L D _ K N O W N _ F R E E _ D O I _ R E G I S T R A N T S _ T A B L E >--
build a table of doi registrants known to be free-to-read In a doi, the registrant ID is the series of digits
between the '10.' and the first '/': in doi 10.1100/sommat, 1100 is the registrant ID
see §3.2.2 DOI prefix of the Doi Handbook p. 43
https://www.doi.org/doi-handbook/DOI_Handbook_Final.pdf#page=43
]]
local function build_free_doi_registrants_table()
local registrants_t = {};
for _, v in ipairs ({
'1045', '1074', '1096', '1100', '1155', '1186', '1194', '1371', '1629', '1989', '1999', '2147', '2196', '3285', '3389', '3390',
'3748', '3814', '3847', '3897', '4061', '4089', '4103', '4172', '4175', '4230', '4236', '4239', '4240', '4249', '4251',
'4252', '4253', '4254', '4291', '4292', '4329', '4330', '4331', '5194', '5210', '5306', '5312', '5313', '5314',
'5315', '5316', '5317', '5318', '5319', '5320', '5321', '5334', '5402', '5409', '5410', '5411', '5412',
'5492', '5493', '5494', '5495', '5496', '5497', '5498', '5499', '5500', '5501', '5527', '5528', '5662',
'6064', '6219', '7167', '7217', '7287', '7482', '7490', '7554', '7717', '7759', '7766', '11131', '11569', '11647',
'11648', '12688', '12703', '12715', '12942', '12998', '13105', '14256', '14293', '14303', '15215', '15347', '15412', '15560', '16995',
'17645', '18637', '19080', '19173', '20944', '21037', '21468', '21767', '22261', '22323', '22459', '24105', '24196', '24966',
'26775', '30845', '32545', '35711', '35712', '35713', '35995', '36648', '37126', '37532', '37871', '47128',
'47622', '47959', '52437', '52975', '53288', '54081', '54947', '55667', '55914', '57009', '58647', '59081',
}) do
registrants_t[v] = true; -- build a k/v table of known free-to-read doi registrants
end
return registrants_t;
end
local extended_registrants_t = { -- known free registrants identifiable by the doi suffix incipit
['1002'] = {'aelm', 'leap'}, -- Advanced Electronic Materials, Learned Publishing
['1016'] = {'j.heliyon', 'j.nlp', 'j.proche'}, -- Heliyon, Natural Language Processing, Procedia Chemistry
['1017'] = {'nlp'}, -- Natural Language Processing Journal
['1046'] = {'j.1365-8711', 'j.1365-246x'}, -- MNRAS, GJI
['1093'] = {'mnras', 'mnrasl', 'gji', 'rasti'}, -- MNRAS, MNRAS Letters, GJI, RASTI
['1099'] = {'acmi', 'mic', '00221287', 'mgen'}, -- Access Microbiology, Microbiology, Journal of General Microbiology, Microbial Genomics
['1111'] = {'j.1365-2966', 'j.1745-3933', 'j.1365-246X'}, -- MNRAS, MNRAS Letters, GJI
['1210'] = {'jendso','jcemcr'}, -- Journal of the Endocrine Society, JCEM Case Reports
['4171'] = {'dm','mag'}, -- Documenta Mathematica, EMS Magazine
['14231'] = {'ag'}, -- Algebraic Geometry
}
--[[===================<< E R R O R M E S S A G I N G >>======================
]]
--[[----------< E R R O R M E S S A G E S U P P L I M E N T S >-------------
I18N for those messages that are supplemented with additional specific text that
describes the reason for the error
TODO: merge this with special_case_translations{}?
]]
local err_msg_supl = {
['char'] = 'invalid character', -- |isbn=, |sbn=
['check'] = 'checksum', -- |isbn=, |sbn=
['flag'] = 'flag', -- |archive-url=
['form'] = 'invalid form', -- |isbn=, |sbn=
['group'] = 'invalid group id', -- |isbn=
['initials'] = 'initials', -- Vancouver
['invalid language code'] = 'invalid language code', -- |script-<param>=
['journal'] = 'journal', -- |bibcode=
['length'] = 'length', -- |isbn=, |bibcode=, |sbn=
['liveweb'] = 'liveweb', -- |archive-url=
['missing comma'] = 'missing comma', -- Vancouver
['missing prefix'] = 'missing prefix', -- |script-<param>=
['missing title part'] = 'missing title part', -- |script-<param>=
['name'] = 'name', -- Vancouver
['non-Latin char'] = 'non-Latin character', -- Vancouver
['path'] = 'path', -- |archive-url=
['prefix'] = 'invalid prefix', -- |isbn=
['punctuation'] = 'punctuation', -- Vancouver
['save'] = 'save command', -- |archive-url=
['suffix'] = 'suffix', -- Vancouver
['timestamp'] = 'timestamp', -- |archive-url=
['unknown language code'] = 'unknown language code', -- |script-<param>=
['value'] = 'value', -- |bibcode=
['year'] = 'year', -- |bibcode=
}
--[[--------------< E R R O R _ C O N D I T I O N S >---------------------------
Error condition table. This table has two sections: errors at the top, maintenance
at the bottom. Maint 'messaging' does not have a 'message' (message=nil)
The following contains a list of IDs for various error conditions defined in the
code. For each ID, we specify a text message to display, an error category to
include, and whether the error message should be wrapped as a hidden comment.
Anchor changes require identical changes to matching anchor in Help:CS1 errors
TODO: rename error_conditions{} to something more generic; create separate error
and maint tables inside that?
]]
local error_conditions = {
err_accessdate_missing_url = {
message = ' ',
anchor = 'accessdate_missing_url',
category = 'Kasalahan CS1: tanggal aksés tanpa URL',
hidden = false
},
err_apostrophe_markup = {
message = ' ', -- $1 is parameter name
anchor = 'apostrophe_markup',
category = 'Kasalahan CS1: markah',
hidden = false
},
err_archive_date_missing_url = {
message = ' ',
anchor = 'archive_date_missing_url',
category = 'Kasalahan CS1: URL arsipan',
hidden = false
},
err_archive_date_url_ts_mismatch = {
message = ' ',
anchor = 'archive_date_url_ts_mismatch',
category = 'Kasalahan CS1: URL arsipan',
hidden = false
},
err_archive_missing_date = {
message = ' ',
anchor = 'archive_missing_date',
category = 'Kasalahan CS1: URL arsipan',
hidden = false
},
err_archive_missing_url = {
message = ' ',
anchor = 'archive_missing_url',
category = 'Kasalahan CS1: URL arsipan',
hidden = false
},
err_archive_url = {
message = ' ', -- $1 is error message detail
anchor = 'archive_url',
category = 'Kasalahan CS1: URL arsipan',
hidden = false
},
err_arxiv_missing = {
message = ' ',
anchor = 'arxiv_missing',
category = 'Kasalahan CS1: arXiv', -- same as bad arxiv
hidden = false
},
err_asintld_missing_asin = {
message = ' ', -- $1 is parameter name
anchor = 'asintld_missing_asin',
category = 'Kasalahan CS1: ASIN',
hidden = false
},
err_bad_arxiv = {
message = ' ',
anchor = 'bad_arxiv',
category = 'Kasalahan CS1: arXiv',
hidden = false
},
err_bad_asin = {
message = ' ',
anchor = 'bad_asin',
category ='Kasalahan CS1: ASIN',
hidden = false
},
err_bad_asin_tld = {
message = ' ',
anchor = 'bad_asin_tld',
category ='Kasalahan CS1: ASIN',
hidden = false
},
err_bad_bibcode = {
message = ' ', -- $1 is error message detail
anchor = 'bad_bibcode',
category = 'Kasalahan CS1: bibcode',
hidden = false
},
err_bad_biorxiv = {
message = ' ',
anchor = 'bad_biorxiv',
category = 'Kasalahan CS1: bioRxiv',
hidden = false
},
err_bad_citeseerx = {
message = ' ',
anchor = 'bad_citeseerx',
category = 'Kasalahan CS1: citeseerx',
hidden = false
},
err_bad_date = {
message = ' ', -- $1 is a parameter name list
anchor = 'bad_date',
category = 'Kasalahan CS1: tanggal',
hidden = false
},
err_bad_doi = {
message = ' ',
anchor = 'bad_doi',
category = 'Kasalahan CS1: DOI',
hidden = false
},
err_bad_hdl = {
message = ' ',
anchor = 'bad_hdl',
category = 'Kasalahan CS1: HDL',
hidden = false
},
err_bad_isbn = {
message = ' ', -- $1 is error message detail
anchor = 'bad_isbn',
category = 'Kasalahan CS1: ISBN',
hidden = false
},
err_bad_ismn = {
message = ' ',
anchor = 'bad_ismn',
category = 'Kasalahan CS1: ISMN',
hidden = false
},
err_bad_issn = {
message = ' ', -- $1 is 'e' or '' for eissn or issn
anchor = 'bad_issn',
category = 'Kasalahan CS1: ISSN',
hidden = false
},
err_bad_jfm = {
message = ' ',
anchor = 'bad_jfm',
category = 'Kasalahan CS1: JFM',
hidden = false
},
err_bad_jstor = {
message = ' ',
anchor = 'bad_jstor',
category = 'Kasalahan CS1: JSTOR',
hidden = false
},
err_bad_lccn = {
message = ' ',
anchor = 'bad_lccn',
category = 'Kasalahan CS1: LCCN',
hidden = false
},
err_bad_medrxiv = {
message = ' ',
anchor = 'bad_medrxiv',
category = 'Kasalahan CS1: medRxiv',
hidden = false
},
err_bad_mr = {
message = ' ',
anchor = 'bad_mr',
category = 'Kasalahan CS1: MR',
hidden = false
},
err_bad_oclc = {
message = ' ',
anchor = 'bad_oclc',
category = 'Kasalahan CS1: OCLC',
hidden = false
},
err_bad_ol = {
message = ' ',
anchor = 'bad_ol',
category = 'Kasalahan CS1: OL',
hidden = false
},
err_bad_osti = {
message = ' ',
anchor = 'bad_osti',
category = 'Kasalahan CS1: OSTI',
hidden = false
},
err_bad_paramlink = { -- for |title-link=, |author/editor/translator-link=, |series-link=, |episode-link=
message = ' ', -- $1 is parameter name
anchor = 'bad_paramlink',
category = 'Kasalahan CS1: tutumbu paraméter',
hidden = false
},
err_bad_pmc = {
message = ' ',
anchor = 'bad_pmc',
category = 'Kasalahan CS1: PMC',
hidden = false
},
err_bad_pmid = {
message = ' ',
anchor = 'bad_pmid',
category = 'Kasalahan CS1: PMID',
hidden = false
},
err_bad_rfc = {
message = ' ',
anchor = 'bad_rfc',
category = 'Kasalahan CS1: RFC',
hidden = false
},
err_bad_s2cid = {
message = ' ',
anchor = 'bad_s2cid',
category = 'Kasalahan CS1: S2CID',
hidden = false
},
err_bad_sbn = {
message = ' ', -- $1 is error message detail
anchor = 'bad_sbn',
category = 'Kasalahan CS1: SBN',
hidden = false
},
err_bad_ssrn = {
message = ' ',
anchor = 'bad_ssrn',
category = 'Kasalahan CS1: SSRN',
hidden = false
},
err_bad_url = {
message = ' ', -- $1 is parameter name
anchor = 'bad_url',
category = 'Kasalahan CS1: URL',
hidden = false
},
err_bad_usenet_id = {
message = ' ',
anchor = 'bad_message_id',
category = 'Kasalahan CS1: ID talatah',
hidden = false
},
err_bad_zbl = {
message = ' ',
anchor = 'bad_zbl',
category = 'Kasalahan CS1: Zbl',
hidden = false
},
err_bare_url_missing_title = {
message = ' ', -- $1 is parameter name
anchor = 'bare_url_missing_title',
category = 'Kasalahan CS1: tutumbu atah',
hidden = false
},
err_biorxiv_missing = {
message = ' ',
anchor = 'biorxiv_missing',
category = 'Kasalahan CS1: bioRxiv', -- same as bad bioRxiv
hidden = false
},
err_chapter_ignored = {
message = ' ', -- $1 is parameter name
anchor = 'chapter_ignored',
category = 'Kasalahan CS1: bab dipaliré',
hidden = false
},
err_citation_missing_title = {
message = ' ', -- $1 is parameter name
anchor = 'citation_missing_title',
category = 'Kasalahan CS1: judul leungit',
hidden = false
},
err_citeseerx_missing = {
message = ' ',
anchor = 'citeseerx_missing',
category = 'Kasalahan CS1: citeseerx', -- same as bad citeseerx
hidden = false
},
err_cite_web_url = { -- this error applies to cite web and to cite podcast
message = ' ',
anchor = 'cite_web_url',
category = 'Kasalahan CS1: butuh URL',
hidden = false
},
err_class_ignored = {
message = ' ',
anchor = 'class_ignored',
category = 'Kasalahan CS1: kelas',
hidden = false
},
err_contributor_ignored = {
message = ' ',
anchor = 'contributor_ignored',
category = 'Kasalahan CS1: kontributor',
hidden = false
},
err_contributor_missing_required_param = {
message = ' ', -- $1 is parameter name
anchor = 'contributor_missing_required_param',
category = 'Kasalahan CS1: kontributor',
hidden = false
},
err_deprecated_params = {
message = ' ', -- $1 is parameter name
anchor = 'deprecated_params',
category = 'Kasalahan CS1: paraméter nu teu dipaké deui',
hidden = false
},
err_disp_name = {
message = ' ', -- $1 is parameter name; $2 is the assigned value
anchor = 'disp_name',
category = 'Kasalahan CS1: ngaran pidangan',
hidden = false,
},
err_doibroken_missing_doi = {
message = ' ', -- $1 is parameter name
anchor = 'doibroken_missing_doi',
category = 'Kasalahan CS1: DOI',
hidden = false
},
err_embargo_missing_pmc = {
message = ' ', -- $1 is parameter name
anchor = 'embargo_missing_pmc',
category = 'Kasalahan CS1: PMC embargo',
hidden = false
},
err_empty_citation = {
message = ' ',
anchor = 'empty_citation',
category = 'Kasalahan CS1: cutatan kosong',
hidden = false
},
err_etal = {
message = ' ', -- $1 is parameter name
anchor = 'explicit_et_al',
category = 'Kasalahan CS1: pamakéan et al.',
hidden = false
},
err_extra_text_edition = {
message = ' ',
anchor = 'extra_text_edition',
category = 'Kasalahan CS1: téks tambahan: édisi',
hidden = false,
},
err_extra_text_issue = {
message = ' ', -- $1 is parameter name
anchor = 'extra_text_issue',
category = 'Kasalahan CS1: téks tambahan: nomer',
hidden = false,
},
err_extra_text_pages = {
message = ' ', -- $1 is parameter name
anchor = 'extra_text_pages',
category = 'Kasalahan CS1: téks tambahan: kaca',
hidden = false,
},
err_extra_text_volume = {
message = ' ', -- $1 is parameter name
anchor = 'extra_text_volume',
category = 'Kasalahan CS1: téks tambahan: volume',
hidden = false,
},
err_first_missing_last = {
message = ' ', -- $1 is first alias, $2 is matching last alias
anchor = 'first_missing_last',
category = 'Kasalahan CS1: tanpa ngaran', -- author, contributor, editor, interviewer, translator
hidden = false
},
err_format_missing_url = {
message = ' ', -- $1 is format parameter $2 is url parameter
anchor = 'format_missing_url',
category = 'Kasalahan CS1: format tanpa URL',
hidden = false
},
err_generic_name = {
message = ' ', -- $1 is parameter name
anchor = 'generic_name',
category = 'Kasalahan CS1: ngaran generik',
hidden = false,
},
err_generic_title = {
message = ' ',
anchor = 'generic_title',
category = 'Kasalahan CS1: judul generik',
hidden = false,
},
err_invalid_isbn_date = {
message = ' ',
anchor = 'invalid_isbn_date',
category = 'Kasalahan CS1: tanggal ISBN',
hidden = true
},
err_invalid_param_val = {
message = ' ', -- $1 is parameter name $2 is parameter value
anchor = 'invalid_param_val',
category = 'Kasalahan CS1: nilai paraméter teu sah',
hidden = false
},
err_invisible_char = {
message = ' ', -- $1 is invisible char $2 is parameter name $3 is position number
anchor = 'invisible_char',
category = 'Kasalahan CS1: karakter teu katémbong',
hidden = false
},
err_medrxiv_missing = {
message = ' ',
anchor = 'medrxiv_missing',
category = 'Kasalahan CS1: medRxiv', -- same as bad medRxiv
hidden = false
},
err_missing_name = {
message = ' ', -- $1 is modified NameList; $2 is enumerator
anchor = 'missing_name',
category = 'Kasalahan CS1: teu ngabogaan panulis atawa éditor', -- author, contributor, editor, interviewer, translator
hidden = false
},
err_missing_periodical = {
message = ' ', -- $1 is cs1 template name; $2 is canonical periodical parameter name for cite $1
anchor = 'missing_periodical',
category = 'Kasalahan CS1: periode leungit',
hidden = false
},
err_missing_pipe = {
message = ' ', -- $1 is parameter name
anchor = 'missing_pipe',
category = 'Kasalahan CS1: pipa leungit',
hidden = false
},
err_missing_publisher = {
message = ' ', -- $1 is cs1 template name; $2 is canonical publisher parameter name for cite $1
anchor = 'missing_publisher',
category = 'Kasalahan CS1: pamedal leungit',
hidden = false
},
err_numeric_names = {
message = ' ', -- $1 is parameter name',
anchor = 'numeric_names',
category = 'Kasalahan CS1: ngaran dina wangun angka',
hidden = false,
},
err_param_access_requires_param = {
message = ' ', -- $1 is parameter name
anchor = 'param_access_requires_param',
category = 'Kasalahan CS1: aksés paraméter',
hidden = false
},
err_param_has_ext_link = {
message = ' ', -- $1 is parameter name
anchor = 'param_has_ext_link',
category = 'Kasalahan CS1: tumbu luar',
hidden = false
},
err_param_has_twl_url = {
message = ' ', -- $1 is parameter name
anchor = 'param_has_twl_url',
category = 'Kasalahan CS1: URL',
hidden = false
},
err_parameter_ignored = {
message = ' ', -- $1 is parameter name
anchor = 'parameter_ignored',
category = 'Kasalahan CS1: paraméter teu didukung',
hidden = false
},
err_parameter_ignored_suggest = {
message = ' ', -- $1 is unknown parameter $2 is suggested parameter name
anchor = 'parameter_ignored_suggest',
category = 'Kasalahan CS1: parameter teu didukung',
hidden = false
},
err_periodical_ignored = {
message = ' ', -- $1 is parameter name
anchor = 'periodical_ignored',
category = 'Kasalahan CS1: periode dipaliré',
hidden = false
},
err_redundant_parameters = {
message = ' ', -- $1 is error message detail
anchor = 'redundant_parameters',
category = 'Kasalahan CS1: paraméter leuwihan',
hidden = false
},
err_script_parameter = {
message = ' ', -- $1 is parameter name $2 is script language code or error detail
anchor = 'script_parameter',
category = 'Kasalahan CS1: paraméter aksara',
hidden = false
},
err_ssrn_missing = {
message = ' ',
anchor = 'ssrn_missing',
category = 'Kasalahan CS1: SSRN',
hidden = false
},
err_text_ignored = {
message = ' ', -- $1 is ignored text
anchor = 'text_ignored',
category = 'Kasalahan CS1: paraméter nu teu dipikanyaho',
hidden = false
},
err_trans_missing_title = {
message = ' ', -- $1 is base parameter name
anchor = 'trans_missing_title',
category = 'Kasalahan CS1: judul teu ditarjamahkeun',
hidden = false
},
err_param_unknown_empty = {
message = ' ', -- $1 is 's' or empty space; $2 is emty unknown param list
anchor = 'param_unknown_empty',
category = 'Kasalahan CS1: paraméter kosong nu teu dipikanyaho',
hidden = false
},
err_vancouver = {
message = ' ', -- $1 is error detail, $2 is the nth name
anchor = 'vancouver',
category = 'Kasalahan CS1: gaya Vancouver',
hidden = false
},
err_wikilink_in_url = {
message = ' ', -- uses ndash
anchor = 'wikilink_in_url',
category = 'Kasalahan CS1: konflik URL–wikilink', -- uses ndash
hidden = false
},
--[[--------------------------< M A I N T >-------------------------------------
maint messages do not have a message (message = nil); otherwise the structure
is the same as error messages
]]
maint_archived_copy = {
message = nil,
anchor = 'archived_copy',
category = 'Pamiaraan CS1: Salinan kaarsipkeun salaku judul',
hidden = true,
},
maint_bibcode = {
message = nil,
anchor = 'bibcode',
category = 'Pamiaraan CS1: bibcode',
hidden = true,
},
maint_location_no_publisher = { -- cite book, conference, encyclopedia; citation as book cite or encyclopedia cite
message = nil,
anchor = 'location_no_publisher',
category = 'Pamiaraan CS1: Lokasi tanpa pamedal',
hidden = true,
},
maint_bot_unknown = {
message = nil,
anchor = 'bot:_unknown',
category = 'Pamiaraan CS1: BOT: status url asli teu dipikanyaho',
hidden = true,
},
maint_date_auto_xlated = { -- date auto-translation not supported by en.wiki
message = nil,
anchor = 'date_auto_xlated',
category = 'Pamiaraan CS1: Tanggal ditarjamahkeun otomatis',
hidden = true,
},
maint_date_format = {
message = nil,
anchor = 'date_format',
category = 'Pamiaraan CS1: Format tanggal',
hidden = true,
},
maint_date_year = {
message = nil,
anchor = 'date_year',
category = 'Pamiaraan CS1: Tanggal jeung taun',
hidden = true,
},
maint_doi_ignore = {
message = nil,
anchor = 'doi_ignore',
category = 'Pamiaraan CS1: Kasalahan DOI nu dipaliré',
hidden = true,
},
maint_doi_inactive = {
message = nil,
anchor = 'doi_inactive',
category = 'Pamiaraan CS1: DOI nonaktif',
hidden = true,
},
maint_doi_inactive_dated = {
message = nil,
anchor = 'doi_inactive_dated',
category = 'Pamiaraan CS1: DOI nonaktif per $2$3$1', -- $1 is year, $2 is month-name or empty string, $3 is space or empty string
hidden = true,
},
maint_doi_unflagged_free = {
message = nil,
anchor = 'doi_unflagged_free',
category = 'Pamiaraan CS1: DOI bébas tanpa ditandaan',
hidden = true,
},
maint_extra_punct = {
message = nil,
anchor = 'extra_punct',
category = 'Pamiaraan CS1: Tanda baca tambahan',
hidden = true,
},
maint_id_limit_load_fail = { -- applies to all cs1|2 templates on a page;
message = nil, -- maint message (category link) never emitted
anchor = 'id_limit_load_fail',
category = 'Pamiaraan CS1: pamuatan wates ID gagal',
hidden = true,
},
maint_isbn_ignore = {
message = nil,
anchor = 'ignore_isbn_err',
category = 'Pamiaraan CS1: Kasalahn ISBN nu dipaliré',
hidden = true,
},
maint_issn_ignore = {
message = nil,
anchor = 'ignore_issn',
category = 'Pamiaraan CS1: Kasalahan ISSN nu dipaliré',
hidden = true,
},
maint_jfm_format = {
message = nil,
anchor = 'jfm_format',
category = 'Pamiaraan CS1: Format JFM',
hidden = true,
},
maint_location = {
message = nil,
anchor = 'location',
category = 'Pamiaraan CS1: Lokasi',
hidden = true,
},
maint_mr_format = {
message = nil,
anchor = 'mr_format',
category = 'Pamiaraan CS1: Format MR',
hidden = true,
},
maint_mult_names = {
message = nil,
anchor = 'mult_names',
category = 'Pamiaraan CS1: Loba ngaran: $1', -- $1 is '<name>s list'; gets value from special_case_translation table
hidden = true,
},
maint_numeric_names = {
message = nil,
anchor = 'numeric_names',
category = 'Pamiaraan CS1: Ngaran numerik: $1', -- $1 is '<name>s list'; gets value from special_case_translation table
hidden = true,
},
maint_others = {
message = nil,
anchor = 'others',
category = 'Pamiaraan CS1: Nu lian',
hidden = true,
},
maint_others_avm = {
message = nil,
anchor = 'others_avm',
category = 'Pamiaraan CS1: Nu lian dina cite AV media (notes)',
hidden = true,
},
maint_overridden_setting = {
message = nil,
anchor = 'overridden',
category = 'Pamiaraan CS1: Pangaturan diambil alih',
hidden = true,
},
maint_pmc_embargo = {
message = nil,
anchor = 'embargo',
category = 'Pamiaraan CS1: Émbargo PMC kadaluwarsa',
hidden = true,
},
maint_pmc_format = {
message = nil,
anchor = 'pmc_format',
category = 'Pamiaraan CS1: Format PMC',
hidden = true,
},
maint_postscript = {
message = nil,
anchor = 'postscript',
category = 'Pamiaraan CS1: Postscript',
hidden = true,
},
maint_publisher_location = {
message = nil,
anchor = 'publisher_location',
category = 'Pamiaraan CS1: Lokasi pamedal',
hidden = true,
},
maint_ref_duplicates_default = {
message = nil,
anchor = 'ref_default',
category = 'Pamiaraan CS1: Ref ngaduplikasi bawaan',
hidden = true,
},
maint_unknown_lang = {
message = nil,
anchor = 'unknown_lang',
category = 'Pamiaraan CS1: Basa nu teu dipikanyaho',
hidden = true,
},
maint_untitled = {
message = nil,
anchor = 'untitled',
category = 'Pamiaraan CS1: Periode tanpa judul',
hidden = true,
},
maint_url_status = {
message = nil,
anchor = 'url_status',
category = 'Pamiaraan CS1: Status URL',
hidden = true,
},
maint_year= {
message = nil,
anchor = 'year',
category = 'Pamiaraan CS1: Taun',
hidden = true,
},
maint_zbl = {
message = nil,
anchor = 'zbl',
category = 'Pamiaraan CS1: Zbl',
hidden = true,
},
}
--[[--------------------------< I D _ L I M I T S _ D A T A _ T >----------------------------------------------
fetch id limits for certain identifiers from c:Data:CS1/Identifier limits.tab. This source is a json tabular
data file maintained at wikipedia commons. Convert the json format to a table of k/v pairs.
The values from <id_limits_data_t> are used to set handle.id_limit.
From 2025-02-21, MediaWiki is broken. Use this link to edit the tablular data file:
https://commons.wikimedia.org/w/index.php?title=Data:CS1/Identifier_limits.tab&action=edit
See Phab:T389105
]]
local id_limits_data_t;
local id_limits_data_load_fail = false; -- flag; assume that we will be successful when loading json id limit tabular data
local use_commons_data = true; -- set to false if your wiki does not have access to mediawiki commons; then,
if false == use_commons_data then -- update this table from https://commons.wikimedia.org/wiki/Data:CS1/Identifier_limits.tab; last update: 2025-02-21
id_limits_data_t = {['OCLC'] = 10450000000, ['OSTI'] = 23010000, ['PMC'] = 11900000, ['PMID'] = 40400000, ['RFC'] = 9300, ['SSRN'] = 5200000, ['S2CID'] = 276000000}; -- this table must be maintained locally
else -- here for wikis that do have access to mediawiki commons
local load_fail_limit = 99999999999; -- very high number to avoid error messages on load failure
id_limits_data_t = {['OCLC'] = load_fail_limit, ['OSTI'] = load_fail_limit, ['PMC'] = load_fail_limit, ['PMID'] = load_fail_limit, ['RFC'] = load_fail_limit, ['SSRN'] = load_fail_limit, ['S2CID'] = load_fail_limit};
if mw.ext.data == nil then
id_limits_data_load_fail = true; -- safeguard against Lua errors if mw.ext.data is not available for some reason
else
local tab_data_t = mw.ext.data.get ('CS1/Identifier limits.tab').data; -- attempt to load the json limit data from commons into <tab_data_t>
if false == tab_data_t then -- undocumented 'feature': mw.ext.data.get() sometimes returns false
id_limits_data_load_fail = true; -- set the flag so that Module:Citation/CS1 can create an unannotated maint category
else
for _, limit_t in ipairs (tab_data_t.data) do -- overwrite default <load_fail_limit> values from the data table in the tabular data
id_limits_data_t[limit_t[1]] = limit_t[2]; -- <limit[1]> is identifier; <limit[2]> is upper limit for that identifier
end
end
end
end
--[[--------------------------< I D _ H A N D L E R S >--------------------------------------------------------
The following contains a list of values for various defined identifiers. For each
identifier we specify a variety of information necessary to properly render the
identifier in the citation.
parameters: a list of parameter aliases for this identifier; first in the list is the canonical form
link: Wikipedia article name
redirect: a local redirect to a local Wikipedia article name; at en.wiki, 'ISBN (identifier)' is a redirect to 'International Standard Book Number'
q: Wikidata q number for the identifier
label: the label preceding the identifier; label is linked to a Wikipedia article (in this order):
redirect from id_handlers['<id>'].redirect when use_identifier_redirects is true
Wikidata-supplied article name for the local wiki from id_handlers['<id>'].q
local article name from id_handlers['<id>'].link
prefix: the first part of a URL that will be concatenated with a second part which usually contains the identifier
suffix: optional third part to be added after the identifier
encode: true if URI should be percent-encoded; otherwise false
COinS: identifier link or keyword for use in COinS:
for identifiers registered at info-uri.info use: info:.... where '...' is the appropriate identifier label
for identifiers that have COinS keywords, use the keyword: rft.isbn, rft.issn, rft.eissn
for |asin= and |ol=, which require assembly, use the keyword: url
for others make a URL using the value in prefix/suffix and #label, use the keyword: pre (not checked; any text other than 'info', 'rft', or 'url' works here)
set to nil to leave the identifier out of the COinS
separator: character or text between label and the identifier in the rendered citation
id_limit: for those identifiers with established limits, this property holds the upper limit
access: use this parameter to set the access level for all instances of this identifier.
the value must be a valid access level for an identifier (see ['id-access'] in this file).
custom_access: to enable custom access level for an identifier, set this parameter
to the parameter that should control it (normally 'id-access')
]]
local id_handlers = {
['ARXIV'] = {
parameters = {'arxiv', 'eprint'},
link = 'arXiv',
redirect = 'arXiv',
q = 'Q118398',
label = 'arXiv',
prefix = 'https://arxiv.org/abs/',
encode = false,
COinS = 'info:arxiv',
separator = ':',
access = 'free', -- free to read
},
['ASIN'] = {
parameters = { 'asin', 'ASIN' },
link = 'Nomor Idéntifikasi Standar Amazon',
redirect = 'ASIN',
q = 'Q1753278',
label = 'ASIN',
prefix = 'https://www.amazon.',
COinS = 'url',
separator = ' ',
encode = false;
},
['BIBCODE'] = {
parameters = {'bibcode'},
link = 'Bibcode',
redirect = 'Bibcode',
q = 'Q25754',
label = 'Bibcode',
prefix = 'https://ui.adsabs.harvard.edu/abs/',
encode = false,
COinS = 'info:bibcode',
separator = ':',
custom_access = 'bibcode-access',
},
['BIORXIV'] = {
parameters = {'biorxiv'},
link = 'bioRxiv',
redirect = 'bioRxiv',
q = 'Q19835482',
label = 'bioRxiv',
prefix = 'https://doi.org/',
COinS = 'pre', -- use prefix value
access = 'free', -- free to read
encode = true,
separator = ' ',
},
['CITESEERX'] = {
parameters = {'citeseerx'},
link = 'CiteSeerX',
redirect = 'CiteSeerX',
q = 'Q2715061',
label = 'CiteSeerX',
prefix = 'https://citeseerx.ist.psu.edu/viewdoc/summary?doi=',
COinS = 'pre', -- use prefix value
access = 'free', -- free to read
encode = true,
separator = ' ',
},
['DOI'] = { -- Used by InternetArchiveBot
parameters = { 'doi', 'DOI'},
link = 'Pangidentifikasi Objék Digital',
redirect = 'DOI',
q = 'Q25670',
label = 'doi',
prefix = 'https://doi.org/',
COinS = 'info:doi',
separator = ':',
encode = true,
custom_access = 'doi-access',
},
['EISSN'] = {
parameters = {'eissn', 'EISSN'},
link = 'Nomor Séri Standar Internasional#ISSN Elektronik',
redirect = 'ISSN',
q = 'Q46339674',
label = 'eISSN',
prefix = 'https://search.worldcat.org/issn/',
COinS = 'rft.eissn',
encode = false,
separator = ' ',
},
['HDL'] = {
parameters = { 'hdl', 'HDL' },
link = 'Handle System',
redirect = 'hdl',
q = 'Q3126718',
label = 'hdl',
prefix = 'https://hdl.handle.net/',
COinS = 'info:hdl',
separator = ':',
encode = true,
custom_access = 'hdl-access',
},
['ISBN'] = { -- Used by InternetArchiveBot
parameters = {'isbn', 'ISBN'},
link = 'Nomor Buku Standar Internasional',
redirect = 'ISBN',
q = 'Q33057',
label = 'ISBN',
prefix = 'Special:BookSources/',
COinS = 'rft.isbn',
separator = ' ',
},
['ISMN'] = {
parameters = {'ismn', 'ISMN'},
link = 'Nomor Musik Standar Internasional',
redirect = 'ISMN',
q = 'Q1666938',
label = 'ISMN',
prefix = '', -- not currently used;
COinS = nil, -- nil because we can't use pre or rft or info:
separator = ' ',
},
['ISSN'] = {
parameters = {'issn', 'ISSN'},
link = 'Nomor Séri Standar Internasional',
redirect = 'ISSN',
q = 'Q131276',
label = 'ISSN',
prefix = 'https://search.worldcat.org/issn/',
COinS = 'rft.issn',
encode = false,
separator = ' ',
},
['JFM'] = {
parameters = {'jfm', 'JFM'},
link = 'Jahrbuch über die Fortschritte der Mathematik',
redirect = 'Jahrbuch über die Fortschritte der Mathematik',
q = '',
label = 'JFM',
prefix = 'https://zbmath.org/?format=complete&q=an:',
COinS = 'pre', -- use prefix value
encode = true,
separator = ' ',
},
['JSTOR'] = {
parameters = {'jstor', 'JSTOR'},
link = 'JSTOR',
redirect = 'JSTOR',
q = 'Q1420342',
label = 'JSTOR',
prefix = 'https://www.jstor.org/stable/',
COinS = 'pre', -- use prefix value
encode = false,
separator = ' ',
custom_access = 'jstor-access',
},
['LCCN'] = {
parameters = {'lccn', 'LCCN'},
link = 'Library of Congress Control Number',
redirect = 'LCCN',
q = 'Q620946',
label = 'LCCN',
prefix = 'https://lccn.loc.gov/',
COinS = 'info:lccn',
encode = false,
separator = ' ',
},
['MEDRXIV'] = {
parameters = {'medrxiv'},
link = 'medRxiv',
redirect = 'medRxiv',
q = 'Q58465838',
label = 'medRxiv',
prefix = 'https://www.medrxiv.org/content/',
COinS = 'pre', -- use prefix value
access = 'free', -- free to read
encode = false,
separator = ' ',
},
['MR'] = {
parameters = {'mr', 'MR'},
link = 'Mathematical Reviews',
redirect = 'Mathematical Reviews',
q = 'Q211172',
label = 'MR',
prefix = 'https://mathscinet.ams.org/mathscinet-getitem?mr=',
COinS = 'pre', -- use prefix value
encode = true,
separator = ' ',
},
['OCLC'] = {
parameters = {'oclc', 'OCLC'},
link = 'OCLC',
redirect = 'OCLC ',
q = 'Q190593',
label = 'OCLC',
prefix = 'https://search.worldcat.org/oclc/',
COinS = 'info:oclcnum',
encode = true,
separator = ' ',
id_limit = id_limits_data_t.OCLC or 0,
},
['OL'] = {
parameters = { 'ol', 'OL' },
link = 'Open Library',
redirect = 'Open Library',
q = 'Q1201876',
label = 'OL',
prefix = 'https://openlibrary.org/',
COinS = 'url',
separator = ' ',
encode = true,
custom_access = 'ol-access',
},
['OSTI'] = {
parameters = {'osti', 'OSTI'},
link = 'Office of Scientific and Technical Information',
redirect = 'Office of Scientific and Technical Information',
q = 'Q2015776',
label = 'OSTI',
prefix = 'https://www.osti.gov/biblio/',
COinS = 'pre', -- use prefix value
encode = true,
separator = ' ',
id_limit = id_limits_data_t.OSTI or 0,
custom_access = 'osti-access',
},
['PMC'] = {
parameters = {'pmc', 'PMC'},
link = 'PubMed Central',
redirect = 'PubMed',
q = 'Q229883',
label = 'PMC',
prefix = 'https://www.ncbi.nlm.nih.gov/pmc/articles/PMC',
suffix = '',
COinS = 'pre', -- use prefix value
encode = true,
separator = ' ',
id_limit = id_limits_data_t.PMC or 0,
access = 'free', -- free to read
},
['PMID'] = {
parameters = {'pmid', 'PMID'},
link = 'PubMed Identifier',
redirect = 'PubMed',
q = 'Q2082879',
label = 'PMID',
prefix = 'https://pubmed.ncbi.nlm.nih.gov/',
COinS = 'info:pmid',
encode = false,
separator = ' ',
id_limit = id_limits_data_t.PMID or 0,
},
['RFC'] = {
parameters = {'rfc', 'RFC'},
link = 'Request for Comments',
redirect = 'Request for Comments',
q = 'Q212971',
label = 'RFC',
prefix = 'https://tools.ietf.org/html/rfc',
COinS = 'pre', -- use prefix value
encode = false,
separator = ' ',
id_limit = id_limits_data_t.RFC or 0,
access = 'free', -- free to read
},
['SBN'] = {
parameters = {'sbn', 'SBN'},
link = 'Standard Book Number', -- redirect to International_Standard_Book_Number#History
redirect = 'Standard Book Number',
label = 'SBN',
prefix = 'Special:BookSources/0-', -- prefix has leading zero necessary to make 9-digit sbn a 10-digit isbn
COinS = nil, -- nil because we can't use pre or rft or info:
separator = ' ',
},
['SSRN'] = {
parameters = {'ssrn', 'SSRN'},
link = 'Social Science Research Network',
redirect = 'SSRN',
q = 'Q7550801',
label = 'SSRN',
prefix = 'https://papers.ssrn.com/sol3/papers.cfm?abstract_id=',
COinS = 'pre', -- use prefix value
encode = true,
separator = ' ',
id_limit = id_limits_data_t.SSRN or 0,
custom_access = 'ssrn-access',
},
['S2CID'] = {
parameters = {'s2cid', 'S2CID'},
link = 'Semantic Scholar',
redirect = 'S2CID',
q = 'Q22908627',
label = 'S2CID',
prefix = 'https://api.semanticscholar.org/CorpusID:',
COinS = 'pre', -- use prefix value
encode = false,
separator = ' ',
id_limit = id_limits_data_t.S2CID or 0,
custom_access = 's2cid-access',
},
['USENETID'] = {
parameters = {'message-id'},
link = 'Usenet',
redirect = 'Usenet',
q = 'Q193162',
label = 'Usenet:',
prefix = 'news:',
encode = false,
COinS = 'pre', -- use prefix value
separator = ' ',
},
['ZBL'] = {
parameters = {'zbl', 'ZBL' },
link = 'Zentralblatt MATH',
redirect = 'Zentralblatt',
q = 'Q190269',
label = 'Zbl',
prefix = 'https://zbmath.org/?format=complete&q=an:',
COinS = 'pre', -- use prefix value
encode = true,
separator = ' ',
},
}
--[[--------------------------< E X P O R T S >---------------------------------
]]
return {
use_identifier_redirects = use_identifier_redirects, -- booleans defined in the settings at the top of this module
local_lang_cat_enable = local_lang_cat_enable,
date_name_auto_xlate_enable = date_name_auto_xlate_enable,
date_digit_auto_xlate_enable = date_digit_auto_xlate_enable,
enable_sort_keys = enable_sort_keys,
suppress_archive_today_urls = suppress_archive_today_urls,
-- tables and variables created when this module is loaded
dependencies_t = dependencies_t, -- used to suppress parameters dependent on url-holding parameters holding archive.today urls
enum_needs_xlation = '1' ~= date_names.xlate_digits['1'], -- to avoid unnecessary enumerator translation
global_df = get_date_format (), -- this line can be replaced with "global_df = 'dmy-all'," to have all dates auto translated to dmy format.
global_cs1_config_t = global_cs1_config_t, -- global settings from {{cs1 config}}
punct_skip = build_skip_table (punct_skip, punct_meta_params),
url_skip = build_skip_table (url_skip, url_meta_params),
known_free_doi_registrants_t = build_free_doi_registrants_table(),
id_limits_data_load_fail = id_limits_data_load_fail, -- true when commons tabular identifier-limit data fails to load
name_space_sort_keys = name_space_sort_keys,
aliases = aliases,
special_case_translation = special_case_translation,
date_names = date_names,
err_msg_supl = err_msg_supl,
error_conditions = error_conditions,
et_al_patterns = et_al_patterns,
extended_registrants_t = extended_registrants_t,
id_handlers = id_handlers,
keywords_lists = keywords_lists,
keywords_xlate = keywords_xlate,
stripmarkers = stripmarkers,
invisible_chars = invisible_chars,
invisible_defs = invisible_defs,
indic_script = indic_script,
emoji_t = emoji_t,
messages = messages,
presentation = presentation,
prop_cats = prop_cats,
script_lang_codes = script_lang_codes,
lang_tag_remap = lang_tag_remap,
lang_name_remap = lang_name_remap,
this_wiki_code = this_wiki_code,
title_types = title_types,
uncategorized_namespaces = uncategorized_namespaces_t,
uncategorized_subpages = uncategorized_subpages,
templates_using_volume = templates_using_volume,
templates_using_issue = templates_using_issue,
templates_not_using_page = templates_not_using_page,
vol_iss_pg_patterns = vol_iss_pg_patterns,
single_letter_2nd_lvl_domains_t = single_letter_2nd_lvl_domains_t,
inter_wiki_map = inter_wiki_map,
mw_languages_by_tag_t = mw_languages_by_tag_t,
mw_languages_by_name_t = mw_languages_by_name_t,
citation_class_map_t = citation_class_map_t,
citation_issue_t = citation_issue_t,
citation_no_volume_t = citation_no_volume_t,
}
nw7lvrfsqf6y8393yk8zy33z8ikwoe1
731561
731559
2026-11-07T11:26:09Z
Rafka Aditia
29183
Ngabolaykeun révisi [[Special:Diff/731559|731559]] ku [[Special:Contributions/Rafka Aditia|Rafka Aditia]] ([[User talk:Rafka Aditia|Obrolan]]): Mangaruhan seueur kaca kalayan kasalahan
731561
Scribunto
text/plain
local lang_obj = mw.language.getContentLanguage(); -- make a language object for the local language; used here for languages and dates
--[[--------------------------< S E T T I N G S >--------------------------------------------------------------
boolean settings used to control various things. these setting located here to make them easy to find
]]
-- these settings local to this module only
local local_digits_from_mediawiki = false; -- for i18n; when true, module fills date_names['local_digits'] from MediaWiki; manual fill required else; always false at en.wiki
local local_date_names_from_mediawiki = false; -- for i18n; when true, module fills date_names['local']['long'] and date_names['local']['short'] from MediaWiki;
-- manual translation required else; ; always false at en.wiki
-- these settings exported to other modules
local use_identifier_redirects = true; -- when true use redirect name for identifier label links; always true at en.wiki
local local_lang_cat_enable = false; -- when true categorizes pages where |language=<local wiki's language>; always false at en.wiki
local date_name_auto_xlate_enable = false; -- when true translates English month-names to the local-wiki's language month names; always false at en.wiki
local date_digit_auto_xlate_enable = false; -- when true translates Western date digit to the local-wiki's language digits (date_names['local_digits']); always false at en.wiki
local enable_sort_keys = true; -- when true module adds namespace sort keys to error and maintenance category links
--[[--------------------------< U N C A T E G O R I Z E D _ N A M E S P A C E S >------------------------------
List of namespaces identifiers for namespaces that will not be included in citation error categories.
Same as setting notracking = true by default.
For wikis that have a current version of Module:cs1 documentation support, this #invoke will return an unordered
list of namespace names and their associated identifiers:
{{#invoke:cs1 documentation support|uncategorized_namespace_lister|all=<anything>}}
]]
local uncategorized_namespaces_t = {[2]=true}; -- init with user namespace id
for k, _ in pairs (mw.site.talkNamespaces) do -- add all talk namespace ids
uncategorized_namespaces_t[k] = true;
end
local uncategorized_subpages = {'/[Kk]otrétan', '/[Kk]asus uji', '/[^/]*[Ll]og', '/[Aa]rsip'}; -- list of Lua patterns found in page names of pages we should not categorize
--[[
at en.wiki Greek characters are used as sort keys for certain items in a category so that those items are
placed at the end of a category page. See Wikipedia:Categorization#Sort_keys. That works well for en.wiki
because English is written using the Latn script. This may not work well for other languages. At en.wiki it
is desireable to place content from certain namespaces at the end of a category listing so the module adds sort
keys to error and maintenance category links when rendering a cs1|2 template on a page in that namespace.
i18n: if this does not work well for your language, set <enable_sort_keys> to false.
]]
local name_space_sort_keys = { -- sort keys to be used with these namespaces:
[4] = 'ω', -- wikipedia; omega
[10] = 'τ', -- template; tau
[118] = 'Δ', -- draft; delta
['other'] = 'ο', -- all other non-talk namespaces except main (article); omicron
}
--[[--------------------------< M E S S A G E S >--------------------------------------------------------------
Translation table
The following contains fixed text that may be output as part of a citation.
This is separated from the main body to aid in future translations of this
module.
]]
local messages = {
['agency'] = '$1 $2', -- $1 is sepc, $2 is agency
['archived-dead'] = 'Diarsipkeun ti $1 tanggal $2',
['archived-live'] = '$1 ti vérsi aslina tanggal $2',
['archived-missing'] = 'Diarsipkeun ti vérsi asli $1 tanggal $2',
['archived-unfit'] = 'Diarsipkeun ti vérsi asli pada ',
['archived'] = 'Diarsipkeun',
['by'] = 'Ku', -- contributions to authored works: introduction, foreword, afterword
['cartography'] = 'Kartografi ku $1',
['editor'] = 'éd.',
['editors'] = 'éd.',
['edition'] = '(Édisi $1)',
['episode'] = 'Épisode $1',
['et al'] = 'et al.',
['in'] = 'Dina', -- edited works
['inactive'] = 'teu aktip',
['inset'] = 'insét $1',
['interview'] = 'Diwawancara ku $1',
['mismatch'] = '<code class="cs1-code">|$1=</code> / <code class="cs1-code">|$2=</code> mismatch', -- $1 is year param name; $2 is date param name
['newsgroup'] = '[[Usenet newsgroup|Newsgroup]]: $1',
['notitle'] = 'Tanpa judul', -- for |title=(()) and (in the future) |title=none
['original'] = 'asli',
['origdate'] = ' [$1]',
['published'] = ' (dipublikasikeun $1)',
['retrieved'] = 'Diaksés tanggal $1',
['season'] = 'Musim $1',
['section'] = '§ $1',
['sections'] = '§§ $1',
['series'] = '$1 $2', -- $1 is sepc, $2 is series
['seriesnum'] = 'Séri $1',
['translated'] = 'Ditarjamahkeun ku $1',
['type'] = ' ($1)', -- for titletype
['written'] = 'Ditulis ku $1',
['vol'] = '$1 Vol. $2', -- $1 is sepc; bold journal style volume is in presentation{}
['vol-no'] = '$1 Vol. $2, no. $3', -- sepc, volume, issue (alternatively insert $1 after $2, but then we'd also have to change capitalization)
['issue'] = '$1 No. $2', -- $1 is sepc
['art'] = '$1 Art. $2', -- $1 is sepc; for {{cite conference}} only
['vol-art'] = '$1 Vol. $2, art. $3', -- sepc, volume, article-number; for {{cite conference}} only
['j-vol'] = '$1 $2', -- sepc, volume; bold journal volume is in presentation{}
['j-issue'] = ' ($1)',
['j-article-num'] = ' $1', -- TODO: any punctuation here? static text?
['nopp'] = '$1 $2'; -- page(s) without prefix; $1 is sepc
['p-prefix'] = "$1 hlm. $2", -- $1 is sepc
['pp-prefix'] = "$1 hlm. $2", -- $1 is sepc
['j-page(s)'] = ': $1', -- same for page and pages
['sheet'] = '$1 Lembar $2', -- $1 is sepc
['sheets'] = '$1 Lembar $2', -- $1 is sepc
['j-sheet'] = ': Lembar $1',
['j-sheets'] = ': Lembar $1',
['language'] = '(Dina basa $1)',
['via'] = " – via $1",
['event'] = 'Kajadian di',
['minutes'] = 'menit di',
-- Determines the location of the help page
['help page link'] = ' ',
['help page label'] = ' ',
-- categories
['cat wikilink'] = '[[Kategori:$1]]', -- $1 is the category name
['cat wikilink sk'] = '[[Kategori:$1|$2]]', -- $1 is the category name; $2 is namespace sort key
[':cat wikilink'] = '[[:Kategori:$1|tumbu]]', -- category name as maintenance message wikilink; $1 is the category name
-- Internal errors (should only occur if configuration is bad)
['undefined_error'] = 'Dipanggil kalawan kondisi kasalahan nu teu ditangtukeun',
['unknown_ID_key'] = 'Konci ID teu dipikanyaho: ', -- an ID key in id_handlers not found in ~/Identifiers func_map{}
['unknown_ID_access'] = 'Kata kunci akses ID tidak dikenal: ', -- an ID access keyword in id_handlers not found in keywords_lists['id-access']{}
['unknown_argument_map'] = 'Peta argumén teu ditangtukeun pikeun variabel ieu',
['bare_url_no_origin'] = 'URL kosong kapanggih tapi indikator asal nyaéta nihil atawa kosong',
['warning_msg_e'] = '<span style="color:#d33">Hiji atawa leuwih citakan <code style="color: inherit; background: inherit; border: none; padding: inherit;">{{$1}}</code> ngabogaan kasalahan</span>; talatah bisa disumputkeun ([[Help:Galat_CS1#Controlling_error_message_display|pitulung]]).'; -- $1 is template link
['warning_msg_m'] = '<span style="color:#3a3">Hiji atawa leuwih citakan <code style="color: inherit; background: inherit; border: none; padding: inherit;">{{$1}}</code> ngabogaan talatah pamiaraan</span>; talatah bisa disumputkeun ([[Help:Galat_CS1#Controlling_error_message_display|pitulung]]).'; -- $1 is template link
}
--[[--------------------------< C I T A T I O N _ C L A S S _ M A P >------------------------------------------
this table maps the value assigned to |CitationClass= in the cs1|2 templates to the canonical template name when
the value assigned to |CitationClass= is different from the canonical template name. |CitationClass= values are
used as class attributes in the <cite> tag that encloses the citation so these names may not contain spaces while
the canonical template name may. These names are used in warning_msg_e and warning_msg_m to create links to the
template's documentation when an article is displayed in preview mode.
Most cs1|2 template |CitationClass= values at en.wiki match their canonical template names so are not listed here.
]]
local citation_class_map_t = { -- TODO: if kept, these and all other config.CitationClass 'names' require some sort of i18n
['arxiv'] = 'arXiv',
['audio-visual'] = 'AV media',
['AV-media-notes'] = 'AV media notes',
['biorxiv'] = 'bioRxiv',
['citeseerx'] = 'CiteSeerX',
['encyclopaedia'] = 'encyclopedia',
['mailinglist'] = 'mailing list',
['medrxiv'] = 'medRxiv',
['pressrelease'] = 'press release',
['ssrn'] = 'SSRN',
['techreport'] = 'tech report',
}
--[=[-------------------------< E T _ A L _ P A T T E R N S >--------------------------------------------------
This table provides Lua patterns for the phrase "et al" and variants in name text
(author, editor, etc.). The main module uses these to identify and emit the 'etal' message.
]=]
local et_al_patterns = {
"[;,]? *[\"']*%f[%a][Ee][Tt]%.? *[Aa][Ll][%.;,\"']*$", -- variations on the 'et al' theme
"[;,]? *[\"']*%f[%a][Ee][Tt]%.? *[Aa][Ll][Ii][AaIi][Ee]?[%.;,\"']*$", -- variations on the 'et alia', 'et alii' and 'et aliae' themes (false positive 'et aliie' unlikely to match)
"[;,]? *%f[%a]and [Oo]thers", -- an alternative to et al.
"%[%[ *[Ee][Tt]%.? *[Aa][Ll]%.? *%]%]", -- a wikilinked form
"%(%( *[Ee][Tt]%.? *[Aa][Ll]%.? *%)%)", -- a double-bracketed form (to counter partial removal of ((...)) syntax)
"[%(%[] *[Ee][Tt]%.? *[Aa][Ll]%.? *[%)%]]", -- a bracketed form
}
--[[--------------------------< P R E S E N T A T I O N >------------------------
Fixed presentation markup. Originally part of citation_config.messages it has
been moved into its own, more semantically correct place.
]]
local presentation =
{
-- .citation-comment class is specified at Help:CS1_errors#Controlling_error_message_display
['hidden-error'] = '<span class="cs1-hidden-error citation-comment">$1</span>',
['visible-error'] = '<span class="cs1-visible-error citation-comment">$1</span>',
['hidden-maint'] = '<span class="cs1-maint citation-comment">$1</span>',
['accessdate'] = '<span class="reference-accessdate">$1$2</span>', -- to allow editors to hide accessdate using personal CSS
['bdi'] = '<bdi$1>$2</bdi>', -- bidirectional isolation used with |script-title= and the like
['cite'] = '<cite class="$1">$2</cite>'; -- for use when citation does not have a namelist and |ref= not set so no id="..." attribute
['cite-id'] = '<cite id="$1" class="$2">$3</cite>'; -- for use when when |ref= is set or when citation has a namelist
['format'] = ' <span class="cs1-format">($1)</span>', -- for |format=, |chapter-format=, etc.
['interwiki'] = ' <span class="cs1-format">[in $1]</span>', -- for interwiki-language-linked author, editor, etc
['interproj'] = ' <span class="cs1-format">[at $1]</span>', -- for interwiki-project-linked author, editor, etc (:d: and :s: supported; :w: ignored)
-- various access levels, for |access=, |doi-access=, |arxiv=, ...
-- narrow no-break space   may work better than nowrap CSS. Or not? Browser support?
['ext-link-access-signal'] = '<span class="$1" title="$2">$3</span>', -- external link with appropriate lock icon
['free'] = {class='id-lock-free', title='Freely accessible'}, -- classes defined in Module:Citation/CS1/styles.css
['registration'] = {class='id-lock-registration', title='Free registration required'},
['limited'] = {class='id-lock-limited', title='Free access subject to limited trial, subscription normally required'},
['subscription'] = {class='id-lock-subscription', title='Paid subscription required'},
['interwiki-icon'] = '<span class="$1" title="$2">$3</span>',
['class-wikisource'] = 'cs1-ws-icon',
['italic-title'] = "''$1''",
['kern-left'] = '<span class="cs1-kern-left"></span>$1', -- spacing to use when title contains leading single or double quote mark
['kern-right'] = '$1<span class="cs1-kern-right"></span>', -- spacing to use when title contains trailing single or double quote mark
['nowrap1'] = '<span class="nowrap">$1</span>', -- for nowrapping an item: <span ...>yyyy-mm-dd</span>
['nowrap2'] = '<span class="nowrap">$1</span> $2', -- for nowrapping portions of an item: <span ...>dd mmmm</span> yyyy (note white space)
['ocins'] = '<span title="$1" class="Z3988"></span>',
['parameter'] = '<code class="cs1-code">|$1=</code>',
['ps_cs1'] = '.'; -- CS1 style postscript (terminal) character
['ps_cs2'] = ''; -- CS2 style postscript (terminal) character (empty string)
['quoted-text'] = '<q>$1</q>', -- for wrapping |quote= content
['quoted-title'] = '"$1"',
['sep_cs1'] = '.', -- CS1 element separator
['sep_cs2'] = ',', -- CS2 separator
['sep_nl'] = ';', -- CS1|2 style name-list separator between names is a semicolon
['sep_nl_and'] = ' and ', -- used as last nl sep when |name-list-style=and and list has 2 items
['sep_nl_end'] = '; and ', -- used as last nl sep when |name-list-style=and and list has 3+ names
['sep_name'] = ', ', -- CS1|2 style last/first separator is <comma><space>
['sep_nl_vanc'] = ',', -- Vancouver style name-list separator between authors is a comma
['sep_name_vanc'] = ' ', -- Vancouver style last/first separator is a space
['sep_list'] = ', ', -- used for |language= when list has 3+ items except for last sep which uses sep_list_end
['sep_list_pair'] = ' and ', -- used for |language= when list has 2 items
['sep_list_end'] = ', and ', -- used as last list sep for |language= when list has 3+ items
['trans-italic-title'] = "[''$1'']",
['trans-quoted-title'] = "[$1]", -- for |trans-title= and |trans-quote=
['vol-bold'] = '$1 <b>$2</b>', -- sepc, volume; for bold journal cites; for other cites ['vol'] in messages{}
}
--[[--------------------------< A L I A S E S >---------------------------------
Aliases table for commonly passed parameters.
Parameter names on the right side in the assignments in this table must have been
defined in the Whitelist before they will be recognized as valid parameter names
]]
local aliases = {
['AccessDate'] = {'access-date', 'accessdate'}, -- Used by InternetArchiveBot
['Agency'] = 'agency',
['ArchiveDate'] = {'archive-date', 'archivedate'}, -- Used by InternetArchiveBot
['ArchiveFormat'] = 'archive-format',
['ArchiveURL'] = {'archive-url', 'archiveurl'}, -- Used by InternetArchiveBot
['ArticleNumber'] = 'article-number',
['ASINTLD'] = 'asin-tld',
['At'] = 'at', -- Used by InternetArchiveBot
['Authors'] = {'people', 'credits'},
['BookTitle'] = {'book-title', 'booktitle'},
['Cartography'] = 'cartography',
['Chapter'] = {'chapter', 'contribution', 'entry', 'article', 'section'},
['ChapterFormat'] = {'chapter-format', 'contribution-format', 'entry-format',
'article-format', 'section-format'};
['ChapterURL'] = {'chapter-url', 'contribution-url', 'entry-url', 'article-url', 'section-url'}, -- Used by InternetArchiveBot
['ChapterUrlAccess'] = {'chapter-url-access', 'contribution-url-access',
'entry-url-access', 'article-url-access', 'section-url-access'}, -- Used by InternetArchiveBot
['Class'] = 'class', -- cite arxiv and arxiv identifier
['Collaboration'] = 'collaboration',
['Conference'] = {'conference', 'event'},
['ConferenceFormat'] = 'conference-format',
['ConferenceURL'] = 'conference-url', -- Used by InternetArchiveBot
['Date'] = {'date', 'air-date', 'airdate'}, -- air-date and airdate for cite episode and cite serial only
['Degree'] = 'degree',
['DF'] = 'df',
['DisplayAuthors'] = {'display-authors', 'display-subjects'},
['DisplayContributors'] = 'display-contributors',
['DisplayEditors'] = 'display-editors',
['DisplayInterviewers'] = 'display-interviewers',
['DisplayTranslators'] = 'display-translators',
['Docket'] = 'docket',
['DoiBroken'] = 'doi-broken-date',
['Edition'] = 'edition',
['Embargo'] = 'pmc-embargo-date',
['Encyclopedia'] = {'encyclopedia', 'encyclopaedia', 'dictionary'}, -- cite encyclopedia only
['Episode'] = 'episode', -- cite serial only TODO: make available to cite episode?
['Format'] = 'format',
['ID'] = {'id', 'ID'},
['Inset'] = 'inset',
['Issue'] = {'issue', 'number'},
['Language'] = {'language', 'lang'},
['MailingList'] = {'mailing-list', 'mailinglist'}, -- cite mailing list only
['Map'] = 'map', -- cite map only
['MapFormat'] = 'map-format', -- cite map only
['MapURL'] = {'map-url', 'mapurl'}, -- cite map only -- Used by InternetArchiveBot
['MapUrlAccess'] = 'map-url-access', -- cite map only -- Used by InternetArchiveBot
['Minutes'] = 'minutes',
['Mode'] = 'mode',
['NameListStyle'] = 'name-list-style',
['Network'] = 'network',
['Newsgroup'] = 'newsgroup', -- cite newsgroup only
['NoPP'] = {'no-pp', 'nopp'},
['NoTracking'] = {'no-tracking', 'template-doc-demo'},
['Number'] = 'number', -- this case only for cite techreport
['OrigDate'] = {'orig-date', 'orig-year', 'origyear'},
['Others'] = 'others',
['Page'] = {'page', 'p'}, -- Used by InternetArchiveBot
['Pages'] = {'pages', 'pp'}, -- Used by InternetArchiveBot
['Periodical'] = {'journal', 'magazine', 'newspaper', 'periodical', 'website', 'work'},
['Place'] = {'place', 'location'},
['PostScript'] = 'postscript',
['PublicationDate'] = {'publication-date', 'publicationdate'},
['PublicationPlace'] = {'publication-place', 'publicationplace'},
['PublisherName'] = {'publisher', 'institution'},
['Quote'] = {'quote', 'quotation'},
['QuotePage'] = 'quote-page',
['QuotePages'] = 'quote-pages',
['Ref'] = 'ref',
['Scale'] = 'scale',
['ScriptChapter'] = {'script-chapter', 'script-contribution', 'script-entry',
'script-article', 'script-section'},
['ScriptEncyclopedia'] = {'script-encyclopedia', 'script-encyclopaedia'}, -- cite encyclopedia only
['ScriptMap'] = 'script-map',
['ScriptPeriodical'] = {'script-journal', 'script-magazine', 'script-newspaper',
'script-periodical', 'script-website', 'script-work'},
['ScriptQuote'] = 'script-quote',
['ScriptTitle'] = 'script-title', -- Used by InternetArchiveBot
['Season'] = 'season',
['Sections'] = 'sections', -- cite map only
['Series'] = {'series', 'version'},
['SeriesLink'] = {'series-link', 'serieslink'},
['SeriesNumber'] = {'series-number', 'series-no'},
['Sheet'] = 'sheet', -- cite map only
['Sheets'] = 'sheets', -- cite map only
['Station'] = 'station',
['Time'] = 'time',
['TimeCaption'] = 'time-caption',
['Title'] = 'title', -- Used by InternetArchiveBot
['TitleLink'] = {'title-link', 'episode-link', 'episodelink'}, -- Used by InternetArchiveBot
['TitleNote'] = {'title-note', 'department'},
['TitleType'] = {'type', 'medium'},
['TransChapter'] = {'trans-article', 'trans-chapter', 'trans-contribution',
'trans-entry', 'trans-section'},
['Transcript'] = 'transcript',
['TranscriptFormat'] = 'transcript-format',
['TranscriptURL'] = 'transcript-url', -- Used by InternetArchiveBot
['TransEncyclopedia'] = {'trans-encyclopedia', 'trans-encyclopaedia'}, -- cite encyclopedia only
['TransMap'] = 'trans-map', -- cite map only
['TransPeriodical'] = {'trans-journal', 'trans-magazine', 'trans-newspaper',
'trans-periodical', 'trans-website', 'trans-work'},
['TransQuote'] = 'trans-quote',
['TransTitle'] = 'trans-title', -- Used by InternetArchiveBot
['URL'] = {'url', 'URL'}, -- Used by InternetArchiveBot
['UrlAccess'] = 'url-access', -- Used by InternetArchiveBot
['UrlStatus'] = 'url-status', -- Used by InternetArchiveBot
['Vauthors'] = 'vauthors',
['Veditors'] = 'veditors',
['Via'] = 'via',
['Volume'] = 'volume',
['Year'] = 'year',
['AuthorList-First'] = {"first#", "author-first#", "author#-first", "author-given#", "author#-given",
"subject-first#", "subject#-first", "subject-given#", "subject#-given",
"given#"},
['AuthorList-Last'] = {"last#", "author-last#", "author#-last", "author-surname#", "author#-surname",
"subject-last#", "subject#-last", "subject-surname#", "subject#-surname",
"author#", 'host#', "subject#", "surname#"},
['AuthorList-Link'] = {"author-link#", "author#-link", "subject-link#",
"subject#-link", "authorlink#", "author#link"},
['AuthorList-Mask'] = {"author-mask#", "author#-mask", "subject-mask#", "subject#-mask"},
['ContributorList-First'] = {'contributor-first#', 'contributor#-first',
'contributor-given#', 'contributor#-given'},
['ContributorList-Last'] = {'contributor-last#', 'contributor#-last',
'contributor-surname#', 'contributor#-surname', 'contributor#'},
['ContributorList-Link'] = {'contributor-link#', 'contributor#-link'},
['ContributorList-Mask'] = {'contributor-mask#', 'contributor#-mask'},
['EditorList-First'] = {"editor-first#", "editor#-first", "editor-given#", "editor#-given"},
['EditorList-Last'] = {"editor-last#", "editor#-last", "editor-surname#",
"editor#-surname", "editor#"},
['EditorList-Link'] = {"editor-link#", "editor#-link"},
['EditorList-Mask'] = {"editor-mask#", "editor#-mask"},
['InterviewerList-First'] = {'interviewer-first#', 'interviewer#-first',
'interviewer-given#', 'interviewer#-given'},
['InterviewerList-Last'] = {'interviewer-last#', 'interviewer#-last',
'interviewer-surname#', 'interviewer#-surname', 'interviewer#'},
['InterviewerList-Link'] = {'interviewer-link#', 'interviewer#-link'},
['InterviewerList-Mask'] = {'interviewer-mask#', 'interviewer#-mask'},
['TranslatorList-First'] = {'translator-first#', 'translator#-first',
'translator-given#', 'translator#-given'},
['TranslatorList-Last'] = {'translator-last#', 'translator#-last',
'translator-surname#', 'translator#-surname', 'translator#'},
['TranslatorList-Link'] = {'translator-link#', 'translator#-link'},
['TranslatorList-Mask'] = {'translator-mask#', 'translator#-mask'},
}
--[[--------------------------< P U N C T _ S K I P >---------------------------
builds a table of parameter names that the extraneous terminal punctuation check should not check.
]]
local punct_meta_params = { -- table of aliases[] keys (meta parameters); each key has a table of parameter names for a value
'BookTitle', 'Chapter', 'ScriptChapter', 'ScriptTitle', 'Title', 'TransChapter', 'Transcript', 'TransMap', 'TransTitle', -- title-holding parameters
'AuthorList-Mask', 'ContributorList-Mask', 'EditorList-Mask', 'InterviewerList-Mask', 'TranslatorList-Mask', -- name-list mask may have name separators
'PostScript', 'Quote', 'ScriptQuote', 'TransQuote', 'Ref', -- miscellaneous
'ArchiveURL', 'ChapterURL', 'ConferenceURL', 'MapURL', 'TranscriptURL', 'URL', -- URL-holding parameters
}
local url_meta_params = { -- table of aliases[] keys (meta parameters); each key has a table of parameter names for a value
'ArchiveURL', 'ChapterURL', 'ConferenceURL', 'ID', 'MapURL', 'TranscriptURL', 'URL', -- parameters allowed to hold urls
'Page', 'Pages', 'At', 'QuotePage', 'QuotePages', -- insource locators allowed to hold urls
}
local function build_skip_table (skip_t, meta_params)
for _, meta_param in ipairs (meta_params) do -- for each meta parameter key
local params = aliases[meta_param]; -- get the parameter or the table of parameters associated with the meta parameter name
if 'string' == type (params) then
skip_t[params] = 1; -- just a single parameter
else
for _, param in ipairs (params) do -- get the parameter name
skip_t[param] = 1; -- add the parameter name to the skip table
local count;
param, count = param:gsub ('#', ''); -- remove enumerator marker from enumerated parameters
if 0 ~= count then -- if removed
skip_t[param] = 1; -- add param name without enumerator marker
end
end
end
end
return skip_t;
end
local punct_skip = {};
local url_skip = {};
--[[--------------------------< S I N G L E - L E T T E R S E C O N D - L E V E L D O M A I N S >----------
this is a list of tlds that are known to have single-letter second-level domain names. This list does not include
ccTLDs which are accepted in is_domain_name().
]]
local single_letter_2nd_lvl_domains_t = {'cash', 'company', 'foundation', 'media', 'org', 'today'};
--[[-----------< S P E C I A L C A S E T R A N S L A T I O N S >------------
This table is primarily here to support internationalization. Translations in
this table are used, for example, when an error message, category name, etc.,
is extracted from the English alias key. There may be other cases where
this translation table may be useful.
]]
local is_Latn = 'A-Za-z\195\128-\195\150\195\152-\195\182\195\184-\198\191\199\132-\201\143\225\184\128-\225\187\191';
local special_case_translation = {
['AuthorList'] = 'daptar pangarang', -- used to assemble maintenance category names
['ContributorList'] = 'daptar kontributor', -- translation of these names plus translation of the base maintenance category names in maint_cats{} table below
['EditorList'] = 'daptar éditor', -- must match the names of the actual categories
['InterviewerList'] = 'daptar pangwawancara', -- this group or translations used by name_has_ed_markup() and name_has_mult_names()
['TranslatorList'] = 'daptar panarjamah',
-- Lua patterns to match pseudo-titles used by InternetArchiveBot and others as placeholder for unknown |title= value
['archived_copy'] = { -- used with CS1 maint: Archive[d] copy as title
['en'] = '^archived?%s+copy$', -- for English; translators: keep this because templates imported from en.wiki
['local'] = '^salinan?%s+arsip$', -- translators: replace ['local'] = nil with lowercase translation only when bots or tools create generic titles in your language
},
-- Lua patterns to match generic titles; usually created by bots or reference filling tools
-- translators: replace ['local'] = nil with lowercase translation only when bots or tools create generic titles in your language
-- generic titles and patterns in this table should be lowercase only
-- leave ['local'] nil except when there is a matching generic title in your language
-- boolean 'true' for plain-text searches; 'false' for pattern searches
['generic_titles'] = {
['accept'] = {
},
['reject'] = {
{['en'] = {'^wayback%s+machine$', false}, ['local'] = nil},
{['en'] = {'are you a robot', true}, ['local'] = nil},
{['en'] = {'hugedomains', true}, ['local'] = nil},
{['en'] = {'^[%(%[{<]?no +title[>}%]%)]?$', false}, ['local'] = {'^[%(%[{<]?euweuh +judul[>}%]%)]?$', false}},
{['en'] = {'page not found', true}, ['local'] = {'kaca teu kapanggih', true}},
{['en'] = {'subscribe to read', true}, ['local'] = {'ngalanggan pikeun maca', true}},
{['en'] = {'^[%(%[{<]?unknown[>}%]%)]?$', false}, ['local'] = {'^[%(%[{<]?teu +dipikanyaho[>}%]%)]?$', false}},
{['en'] = {'website is for sale', true}, ['local'] = {'ramatloka dijual', true}},
{['en'] = {'^404', false}, ['local'] = nil},
{['en'] = {'error[ %-]404', false}, ['local'] = {'kasalahan[ %-]404', false}},
{['en'] = {'internet archive wayback machine', true}, ['local'] = nil},
{['en'] = {'log into facebook', true}, ['local'] = {'asup log ka facebook', true}},
{['en'] = {'login • instagram', true}, ['local'] = {'asup log • instagram', true}},
{['en'] = {'redirecting...', true}, ['local'] = {'ngalihkeun...', true}},
{['en'] = {'usurped title', true}, ['local'] = {'gelar nu direbut', true}}, -- added by a GreenC bot
{['en'] = {'webcite query result', true}, ['local'] = nil},
{['en'] = {'wikiwix\'s cache', true}, ['local'] = nil},
}
},
-- boolean 'true' for plain-text searches, search string must be lowercase only
-- boolean 'false' for pattern searches
-- leave ['local'] nil except when there is a matching generic name in your language
['generic_names'] = {
['accept'] = {
{['en'] = {'%[%[[^|]*%(author%) *|[^%]]*%]%]', false}, ['local'] = {'%[%[[^|]*%(pangarang%) *|[^%]]*%]%]', false}},
},
['reject'] = {
{['en'] = {'about us', true}, ['local'] = {'ngeunaan arurang', true}},
{['en'] = {'%f[%a][Aa]dvisor%f[%A]', false}, ['local'] = nil},
{['en'] = {'allmusic', true}, ['local'] = nil},
{['en'] = {'%f[%a][Aa]uthor%f[%A]', false}, ['local'] = {'%f[%a][Pp]angarang%f[%A]', false}},
{['en'] = {'^[Bb]ureau$', false}, ['local'] = {'^[Bb]iro$', false}},
{['en'] = {'business', true}, ['local'] = {'bisnis', true}},
{['en'] = {'cnn', true}, ['local'] = nil},
{['en'] = {'collaborator', true}, ['local'] = {'kolaborator', true}},
{['en'] = {'^[Cc]ompany$', false}, ['local'] = {'^[Pp]ausahaan$', false}},
{['en'] = {'contributor', true}, ['local'] = {'kontributor', true}},
{['en'] = {'contact us', true}, ['local'] = {'hubungi arurang', true}},
{['en'] = {'correspondent', true}, ['local'] = {'koréspondén', true}},
{['en'] = {'^[Dd]esk$', false}, ['local'] = {'^[Mm]éja +[Kk]erja$', false}},
{['en'] = {'directory', true}, ['local'] = {'diréktori', true}},
{['en'] = {'%f[%(%[][%(%[]%s*eds?%.?%s*[%)%]]?$', false}, ['local'] = nil},
{['en'] = {'[,%.%s]%f[e]eds?%.?$', false}, ['local'] = nil},
{['en'] = {'^eds?[%.,;]', false}, ['local'] = nil},
{['en'] = {'^[%(%[]%s*[Ee][Dd][Ss]?%.?%s*[%)%]]', false}, ['local'] = nil},
{['en'] = {'%f[%a][Ee]dited%f[%A]', false}, ['local'] = {'%f[%a][Dd]iédit%f[%A]', false}},
{['en'] = {'%f[%a][Ee]ditors?%f[%A]', false}, ['local'] = {'%f[%a][Éé]ditor?%f[%A]', false}},
{['en'] = {'%f[%a][Ee]mail%f[%A]', false}, ['local'] = {'%f[%a][Ss]urélék%f[%A]', false}},
{['en'] = {'facebook', true}, ['local'] = nil},
{['en'] = {'google', true}, ['local'] = nil},
{['en'] = {'^[Gg]roup$', false}, ['local'] = {'^[Gg]orombolan$', false}},
{['en'] = {'home page', true}, ['local'] = {'beranda', true}},
{['en'] = {'^[Ii]nc%.?$', false}, ['local'] = nil},
{['en'] = {'instagram', true}, ['local'] = nil},
{['en'] = {'interviewer', true}, ['local'] = {'pangwawancara', true}},
{['en'] = {'^[Ll]imited$', false}, ['local'] = nil},
{['en'] = {'linkedIn', true}, ['local'] = nil},
{['en'] = {'^[Nn]ews$', false}, ['local'] = {'^[Ww]arta$', false}},
{['en'] = {'[Nn]ews[ %-]?[Rr]oom', false}, ['local'] = nil},
{['en'] = {'pinterest', true}, ['local'] = nil},
{['en'] = {'policy', true}, ['local'] = {'kawijakan', true}},
{['en'] = {'privacy', true}, ['local'] = {'privasi', true}},
{['en'] = {'reuters', true}, ['local'] = nil},
{['en'] = {'translator', true}, ['local'] = {'panarjamah', true}},
{['en'] = {'tumblr', true}, ['local'] = nil},
{['en'] = {'twitter', true}, ['local'] = nil},
{['en'] = {'site name', true}, ['local'] = {'ngaran loka', true}},
{['en'] = {'statement', true}, ['local'] = nil},
{['en'] = {'submitted', true}, ['local'] = nil},
{['en'] = {'super.?user', false}, ['local'] = nil},
{['en'] = {'%f['..is_Latn..'][Uu]ser%f[^'..is_Latn..']', false}, ['local'] = {'%f['..is_Latn..'][Pp]amaké%f[^'..is_Latn..']', false}},
{['en'] = {'verfasser', true}, ['local'] = nil},
}
}
}
--[[--------------------------< D A T E _ N A M E S >----------------------------------------------------------
This table of tables lists local language date names and fallback English date names.
The code in Date_validation will look first in the local table for valid date names.
If date names are not found in the local table, the code will look in the English table.
Because citations can be copied to the local wiki from en.wiki, the English is
required when the date-name translation function date_name_xlate() is used.
In these tables, season numbering is defined by
Extended Date/Time Format (EDTF) Specification (https://www.loc.gov/standards/datetime/)
which became part of ISO 8601 in 2019. See '§Sub-year groupings'. The standard
defines various divisions using numbers 21-41. CS1|2 only supports generic seasons.
EDTF does support the distinction between north and south hemisphere seasons
but CS1|2 has no way to make that distinction.
33-36 = Quarter 1, Quarter 2, Quarter 3, Quarter 4 (3 months each)
The standard does not address 'named' dates so, for the purposes of CS1|2,
Easter and Christmas are defined here as 98 and 99, which should be out of the
ISO 8601 (EDTF) range of uses for a while.
local_date_names_from_mediawiki is a boolean. When set to:
true – module will fetch local month names from MediaWiki for both date_names['local']['long'] and date_names['local']['short']; this will unconditionally overwrite manual translations
false – module will *not* fetch local month names from MediaWiki
Caveat lector: There is no guarantee that MediaWiki will provide short month names. At your wiki you can test
the results of the MediaWiki fetch in the debug console with this command (the result is alpha sorted):
=mw.dumpObject (p.date_names['local'])
While the module can fetch month names from MediaWiki, it cannot fetch the quarter, season, and named date names
from MediaWiki. Those must be translated manually.
]]
local local_date_names_from_mediawiki = true; -- when false, manual translation required for date_names['local']['long'] and date_names['local']['short']; overwrites manual translations
-- when true, module fetches long and short month names from MediaWiki
local date_names = {
['en'] = { -- English
['long'] = {['January'] = 1, ['February'] = 2, ['March'] = 3, ['April'] = 4, ['May'] = 5, ['June'] = 6, ['July'] = 7, ['August'] = 8, ['September'] = 9, ['October'] = 10, ['November'] = 11, ['December'] = 12},
['short'] = {['Jan'] = 1, ['Feb'] = 2, ['Mar'] = 3, ['Apr'] = 4, ['May'] = 5, ['Jun'] = 6, ['Jul'] = 7, ['Aug'] = 8, ['Sep'] = 9, ['Oct'] = 10, ['Nov'] = 11, ['Dec'] = 12},
['quarter'] = {['First Quarter'] = 33, ['Second Quarter'] = 34, ['Third Quarter'] = 35, ['Fourth Quarter'] = 36},
['season'] = {['Winter'] = 24, ['Spring'] = 21, ['Summer'] = 22, ['Fall'] = 23, ['Autumn'] = 23},
['named'] = {['Easter'] = 98, ['Christmas'] = 99},
},
-- when local_date_names_from_mediawiki = false
['local'] = { -- replace these English date names with the local language equivalents
['long'] = {['Januari'] = 1, ['Pébruari'] = 2, ['Maret'] = 3, ['April'] = 4, ['Méi'] = 5, ['Juni'] = 6, ['Juli'] = 7, ['Agustus'] = 8, ['Séptémber'] = 9, ['Oktober'] = 10, ['Nopémber'] = 11, ['Désémber'] = 12},
['short'] = {['Jan'] = 1, ['Péb'] = 2, ['Mar'] = 3, ['Apr'] = 4, ['Méi'] = 5, ['Jun'] = 6, ['Jul'] = 7, ['Agu'] = 8, ['Sép'] = 9, ['Okt'] = 10, ['Nop'] = 11, ['Dés'] = 12},
['quarter'] = {['Kuartal Kahiji'] = 33, ['Kuartal Kadua'] = 34, ['Kuartal Katilu'] = 35, ['Kuartal Kaopat'] = 36},
['season'] = {['Usum Tiris'] = 24, ['Usum Semi'] = 21, ['Usum Panas'] = 22, ['Usum Gugur'] = 23,},
['named'] = {['Paskah'] = 98, ['Natal'] = 99},
},
['inv_local_long'] = {}, -- used in date reformatting & translation; copy of date_names['local'].long where k/v are inverted: [1]='<local name>' etc.
['inv_local_short'] = {}, -- used in date reformatting & translation; copy of date_names['local'].short where k/v are inverted: [1]='<local name>' etc.
['inv_local_quarter'] = {}, -- used in date translation; copy of date_names['local'].quarter where k/v are inverted: [1]='<local name>' etc.
['inv_local_season'] = {}, -- used in date translation; copy of date_names['local'].season where k/v are inverted: [1]='<local name>' etc.
['inv_local_named'] = {}, -- used in date translation; copy of date_names['local'].named where k/v are inverted: [1]='<local name>' etc.
['local_digits'] = {['0'] = '0', ['1'] = '1', ['2'] = '2', ['3'] = '3', ['4'] = '4', ['5'] = '5', ['6'] = '6', ['7'] = '7', ['8'] = '8', ['9'] = '9'}, -- used to convert local language digits to Western 0-9
['xlate_digits'] = {},
}
if local_date_names_from_mediawiki then -- if fetching local month names from MediaWiki is enabled
local long_t = {};
local short_t = {};
for i=1, 12 do -- loop 12x and
local name = lang_obj:formatDate('F', '2022-' .. i .. '-1'); -- get long month name for each i
long_t[name] = i; -- save it
name = lang_obj:formatDate('M', '2022-' .. i .. '-1'); -- get short month name for each i
short_t[name] = i; -- save it
end
date_names['local']['long'] = long_t; -- write the long table – overwrites manual translation
date_names['local']['short'] = short_t; -- write the short table – overwrites manual translation
end
-- create inverted date-name tables for reformatting and/or translation
for _, invert_t in pairs {{'long', 'inv_local_long'}, {'short', 'inv_local_short'}, {'quarter', 'inv_local_quarter'}, {'season', 'inv_local_season'}, {'named', 'inv_local_named'}} do
for name, i in pairs (date_names['local'][invert_t[1]]) do -- this table is ['name'] = i
date_names[invert_t[2]][i] = name; -- invert to get [i] = 'name' for conversions from ymd
end
end
if local_digits_from_mediawiki then -- if fetching local digits from MediaWiki is enabled
local digits_t = {};
for i=0, 9 do -- loop 10x and
digits_t [lang_obj:formatNum (i)] = tostring (i); -- format the loop indexer as local lang table index and assign loop indexer (a string) as the value
end
date_names['local_digits'] = digits_t;
end
for ld, ed in pairs (date_names.local_digits) do -- make a digit translation table for simple date translation from en to local language using local_digits table
date_names.xlate_digits [ed] = ld; -- en digit becomes index with local digit as the value
end
local df_template_patterns = { -- table of redirects to {{Use dmy dates}} and {{Use mdy dates}}
'{{ *[Uu]se +(dmy) +dates *[|}]', -- 1159k -- sorted by approximate transclusion count
'{{ *[Uu]se +(mdy) +dates *[|}]', -- 212k
'{{ *[Uu]se +(MDY) +dates *[|}]', -- 788
'{{ *[Uu]se +(DMY) +dates *[|}]', -- 343
'{{ *([Mm]dy) *[|}]', -- 176
'{{ *[Uu]se *(dmy) *[|}]', -- 156 + 18
'{{ *[Uu]se *(mdy) *[|}]', -- 149 + 11
'{{ *([Dd]my) *[|}]', -- 56
'{{ *[Uu]se +(MDY) *[|}]', -- 5
'{{ *([Dd]MY) *[|}]', -- 3
'{{ *[Uu]se(mdy)dates *[|}]', -- 1
'{{ *[Uu]se +(DMY) *[|}]', -- 0
'{{ *([Mm]DY) *[|}]', -- 0
}
local title_object = mw.title.getCurrentTitle();
local content; -- done this way so that unused templates appear in unused-template-reports; self-transcluded makes them look like they are used
if 10 ~= title_object.namespace then -- all namespaces except Template
content = title_object:getContent() or ''; -- get the content of the article or ''; new pages edited w/ve do not have 'content' until saved; ve does not preview; phab:T221625
end
local function get_date_format ()
if not content then -- nil content when we're in template
return nil; -- auto-formatting does not work in Template space so don't set global_df
end
for _, pattern in ipairs (df_template_patterns) do -- loop through the patterns looking for {{Use dmy dates}} or {{Use mdy dates}} or any of their redirects
local start, _, match = content:find(pattern); -- match is the three letters indicating desired date format
if match then
local use_dates_template = content:match ('%b{}', start); -- get the whole template
if use_dates_template:match ('| *cs1%-dates *= *[lsy][sy]?') then -- look for |cs1-dates=publication date length access-/archive-date length
return match:lower() .. '-' .. use_dates_template:match ('| *cs1%-dates *= *([lsy][sy]?)');
else
return match:lower() .. '-all'; -- no |cs1-dates= k/v pair; return value appropriate for use in |df=
end
end
end
end
local global_df; -- TODO: add this to <global_cs1_config_t>?
--[[-----------------< V O L U M E , I S S U E , P A G E S >------------------
These tables hold cite class values (from the template invocation) and identify those templates that support
|volume=, |issue=, and |page(s)= parameters. Cite conference and cite map require further qualification which
is handled in the main module.
]]
local templates_using_volume = {'citation', 'audio-visual', 'book', 'conference', 'encyclopaedia', 'interview', 'journal', 'magazine', 'map', 'news', 'report', 'techreport', 'thesis'}
local templates_using_issue = {'citation', 'conference', 'episode', 'interview', 'journal', 'magazine', 'map', 'news', 'podcast'}
local templates_not_using_page = {'audio-visual', 'episode', 'mailinglist', 'newsgroup', 'podcast', 'serial', 'sign', 'speech'}
--[[
These tables control when it is appropriate for {{citation}} to render |volume= and/or |issue=. The parameter
names in the tables constrain {{citation}} so that its renderings match the renderings of the equivalent cs1
templates. For example, {{cite web}} does not support |volume= so the equivalent {{citation |website=...}} must
not support |volume=.
]]
local citation_no_volume_t = { -- {{citation}} does not render |volume= when these parameters are used
'website', 'mailinglist', 'script-website',
}
local citation_issue_t = { -- {{citation}} may render |issue= when these parameters are used
'journal', 'magazine', 'newspaper', 'periodical', 'work',
'script-journal', 'script-magazine', 'script-newspaper', 'script-periodical', 'script-work',
}
--[[
Patterns for finding extra text in |volume=, |issue=, |page=, |pages=
]]
local vol_iss_pg_patterns = {
good_ppattern = '^P[^%.PpGg]', -- OK to begin with uppercase P: P7 (page 7 of section P), but not p123 (page 123); TODO: this allows 'Pages' which it should not
bad_ppatterns = { -- patterns for |page= and |pages=
'^[Pp][PpGg]?%.?[ %d]',
'^[Pp][Pp]?%. ', -- from {{p.}} and {{pp.}} templates
'^[Pp]ages?',
'^[Pp]gs.?',
},
vi_patterns_t = { -- combined to catch volume-like text in |issue= and issue-like text in |volume=
'^volumes?', -- volume-like text
'^vols?[%.:=]?',
'^issues?', --issue-like text
'^iss[%.:=]?',
'^numbers?',
'^nos?%A', -- don't match 'november' or 'nostradamus'
'^nr[%.:=]?',
'^n[%.:= ]', -- might be a valid issue without separator (space char is sep char here)
'^n°', -- 'n' with degree sign (U+00B0)
'^№', -- precomposed unicode numero character (U+2116)
},
}
--[[--------------------------< K E Y W O R D S >-------------------------------
These tables hold keywords for those parameters that have defined sets of acceptable keywords.
]]
--[[-------------------< K E Y W O R D S T A B L E >--------------------------
this is a list of keywords; each key in the list is associated with a table of
synonymous keywords possibly from different languages.
for I18N: add local-language keywords to value table; do not change the key.
For example, adding the German keyword 'ja':
['affirmative'] = {'yes', 'true', 'y', 'ja'},
Because CS1|2 templates from en.wiki articles are often copied to other local wikis,
it is recommended that the English keywords remain in these tables.
]]
local keywords = {
['amp'] = {'&', 'amp', 'ampersand'}, -- |name-list-style=
['and'] = {'and', 'serial'}, -- |name-list-style=
['affirmative'] = {'yes', 'true', 'y'}, -- |no-tracking=, |no-pp= -- Used by InternetArchiveBot
['afterword'] = {'afterword'}, -- |contribution=
['bot: unknown'] = {'bot: unknown'}, -- |url-status= -- Used by InternetArchiveBot
['cs1'] = {'cs1'}, -- |mode=
['cs2'] = {'cs2'}, -- |mode=
['dead'] = {'dead', 'deviated'}, -- |url-status= -- Used by InternetArchiveBot
['dmy'] = {'dmy'}, -- |df=
['dmy-all'] = {'dmy-all'}, -- |df=
['foreword'] = {'foreword'}, -- |contribution=
['free'] = {'free'}, -- |<id>-access= -- Used by InternetArchiveBot
['harv'] = {'harv'}, -- |ref=; this no longer supported; is_valid_parameter_value() called with <invert> = true
['introduction'] = {'introduction'}, -- |contribution=
['limited'] = {'limited'}, -- |url-access= -- Used by InternetArchiveBot
['live'] = {'live'}, -- |url-status= -- Used by InternetArchiveBot
['mdy'] = {'mdy'}, -- |df=
['mdy-all'] = {'mdy-all'}, -- |df=
['none'] = {'none'}, -- |postscript=, |ref=, |title=, |type= -- Used by InternetArchiveBot
['off'] = {'off'}, -- |title= (potentially also: |title-link=, |postscript=, |ref=, |type=)
['preface'] = {'preface'}, -- |contribution=
['registration'] = {'registration'}, -- |url-access= -- Used by InternetArchiveBot
['subscription'] = {'subscription'}, -- |url-access= -- Used by InternetArchiveBot
['unfit'] = {'unfit'}, -- |url-status= -- Used by InternetArchiveBot
['usurped'] = {'usurped'}, -- |url-status= -- Used by InternetArchiveBot
['vanc'] = {'vanc'}, -- |name-list-style=
['ymd'] = {'ymd'}, -- |df=
['ymd-all'] = {'ymd-all'}, -- |df=
-- ['yMd'] = {'yMd'}, -- |df=; not supported at en.wiki
-- ['yMd-all'] = {'yMd-all'}, -- |df=; not supported at en.wiki
}
--[[------------------------< X L A T E _ K E Y W O R D S >---------------------
this function builds a list, keywords_xlate{}, of the keywords found in keywords{} where the values from keywords{}
become the keys in keywords_xlate{} and the keys from keywords{} become the values in keywords_xlate{}:
['affirmative'] = {'yes', 'true', 'y'}, -- in keywords{}
becomes
['yes'] = 'affirmative', -- in keywords_xlate{}
['true'] = 'affirmative',
['y'] = 'affirmative',
the purpose of this function is to act as a translator between a non-English keyword and its English equivalent
that may be used in other modules of this suite
]]
local function xlate_keywords ()
local out_table = {}; -- output goes here
for k, keywords_t in pairs (keywords) do -- spin through the keywords table
for _, keyword in ipairs (keywords_t) do -- for each keyword
out_table[keyword] = k; -- create an entry in the output table where keyword is the key
end
end
return out_table;
end
local keywords_xlate = xlate_keywords (); -- the list of translated keywords
--[[----------------< M A K E _ K E Y W O R D S _ L I S T >---------------------
this function assembles, for parameter-value validation, the list of keywords appropriate to that parameter.
keywords_lists{}, is a table of tables from keywords{}
]]
local function make_keywords_list (keywords_lists)
local out_table = {}; -- output goes here
for _, keyword_list in ipairs (keywords_lists) do -- spin through keywords_lists{} and get a table of keywords
for _, keyword in ipairs (keyword_list) do -- spin through keyword_list{} and add each keyword, ...
table.insert (out_table, keyword); -- ... as plain text, to the output list
end
end
return out_table;
end
--[[----------------< K E Y W O R D S _ L I S T S >-----------------------------
this is a list of lists of valid keywords for the various parameters in [key].
Generally the keys in this table are the canonical en.wiki parameter names though
some are contrived because of use in multiple differently named parameters:
['yes_true_y'], ['id-access'].
The function make_keywords_list() extracts the individual keywords from the
appropriate list in keywords{}.
The lists in this table are used to validate the keyword assignment for the
parameters named in this table's keys.
]]
local keywords_lists = {
['yes_true_y'] = make_keywords_list ({keywords.affirmative}),
['contribution'] = make_keywords_list ({keywords.afterword, keywords.foreword, keywords.introduction, keywords.preface}),
['df'] = make_keywords_list ({keywords.dmy, keywords['dmy-all'], keywords.mdy, keywords['mdy-all'], keywords.ymd, keywords['ymd-all']}),
-- ['df'] = make_keywords_list ({keywords.dmy, keywords['dmy-all'], keywords.mdy, keywords['mdy-all'], keywords.ymd, keywords['ymd-all'], keywords.yMd, keywords['yMd-all']}), -- not supported at en.wiki
['mode'] = make_keywords_list ({keywords.cs1, keywords.cs2}),
['name-list-style'] = make_keywords_list ({keywords.amp, keywords['and'], keywords.vanc}),
['ref'] = make_keywords_list ({keywords.harv}), -- inverted check; |ref=harv no longer supported
['url-access'] = make_keywords_list ({keywords.subscription, keywords.limited, keywords.registration}),
['url-status'] = make_keywords_list ({keywords.dead, keywords.live, keywords.unfit, keywords.usurped, keywords['bot: unknown']}),
['id-access'] = make_keywords_list ({keywords.free}),
}
--[[--------------------------< C S 1 _ C O N F I G _ G E T >--------------------------------------------------
fetch and validate values from {{cs1 config}} template to fill <global_cs1_config_t>
no error messages; when errors are detected, the parameter value from {{cs1 config}} is blanked.
Supports all parameters and aliases associated with the metaparameters: DisplayAuthors, DisplayContributors,
DisplayEditors, DisplayInterviewers, DisplayTranslators, NameListStyle, and Mode. The DisplayWhatever metaparameters
accept numeric values only (|display-authors=etal and the like is not supported).
]]
local global_cs1_config_t = {}; -- TODO: add value returned from get_date_format() to this table?
local function get_cs1_config ()
if not content then -- nil content when we're in template
return nil; -- auto-formatting does not work in Template space so don't set global_df
end
local start = content:find('{{ *[Cc][Ss]1 config *[|}]'); -- <start> is offset into <content> when {{cs1 config}} found; nil else
if start then
local cs1_config_template = content:match ('%b{}', start); -- get the whole template
if not cs1_config_template then
return nil;
end
local params_t = mw.text.split (cs1_config_template:gsub ('^{{%s*', ''):gsub ('%s*}}$', ''), '%s*|%s*'); -- remove '{{' and '}}'; make a sequence of parameter/value pairs (split on the pipe)
table.remove (params_t, 1); -- remove the template name because it isn't a parameter/value pair
local config_meta_params_t = {'DisplayAuthors', 'DisplayContributors', 'DisplayEditors', 'DisplayInterviewers', 'DisplayTranslators', 'NameListStyle', 'Mode'};
local meta_param_map_t = {}; -- list of accepted parameter names usable in {{cs1 config}} goes here
for _, meta_param in ipairs (config_meta_params_t) do -- for i18n using <config_meta_params_t>, map template parameter names to their metaparameter equivalents
if 'table' == type (aliases[meta_param]) then -- if <meta_param> is a sequence,
for _, param in ipairs (aliases[meta_param]) do -- extract its contents
meta_param_map_t[param] = meta_param; -- and add to <meta_param_map_t>
end
else
meta_param_map_t[aliases[meta_param]] = meta_param; -- not a sequence so just add the parameter to <meta_param_map_t>
end
end
local keywords_t = {}; -- map valid keywords to their associate metaparameter; reverse form of <keyword_lists[key] for these metaparameters
for _, metaparam_t in ipairs ({{'NameListStyle', 'name-list-style'}, {'Mode', 'mode'}}) do -- only these metaparameter / keywords_lists key pairs
for _, keyword in ipairs (keywords_lists[metaparam_t[2]]) do -- spin through the list of keywords
keywords_t[keyword] = metaparam_t[1]; -- add [keyword] = metaparameter to the map
end
end
for _, param in ipairs (params_t) do -- spin through the {{cs1 config}} parameters and fill <global_cs1_config_t>
local k, v = param:match ('([^=]-)%s*=%s*(.+)'); -- <k> is the parameter name; <v> is parameter's assigned value
if k then
if k:find ('^display') then -- if <k> is one of the |display-<namelist>= parameters
if v:match ('%d+') then -- the assigned value must be digits; doesn't accept 'etal'
global_cs1_config_t[meta_param_map_t[k]]=v; -- add the display param and its value to globals table
end
else
if keywords_t[v] == meta_param_map_t[k] then -- keywords_t[v] returns nil or the metaparam name; these must be the same
global_cs1_config_t[meta_param_map_t[k]]=v; -- add the parameter and its value to globals table
end
end
end
end
end
end
get_cs1_config (); -- fill <global_cs1_config_t>
--[[---------------------< S T R I P M A R K E R S >----------------------------
Common pattern definition location for stripmarkers so that we don't have to go
hunting for them if (when) MediaWiki changes their form.
]]
local stripmarkers = {
['any'] = '\127[^\127]*UNIQ%-%-(%a+)%-[%a%d]+%-QINU[^\127]*\127', -- capture returns name of stripmarker
['math'] = '\127[^\127]*UNIQ%-%-math%-[%a%d]+%-QINU[^\127]*\127' -- math stripmarkers used in coins_cleanup() and coins_replace_math_stripmarker()
}
--[[------------< I N V I S I B L E _ C H A R A C T E R S >---------------------
This table holds non-printing or invisible characters indexed either by name or
by Unicode group. Values are decimal representations of UTF-8 codes. The table
is organized as a table of tables because the Lua pairs keyword returns table
data in an arbitrary order. Here, we want to process the table from top to bottom
because the entries at the top of the table are also found in the ranges specified
by the entries at the bottom of the table.
Also here is a pattern that recognizes stripmarkers that begin and end with the
delete characters. The nowiki stripmarker is not an error but some others are
because the parameter values that include them become part of the template's
metadata before stripmarker replacement.
]]
local invisible_defs = {
del = '\127', -- used to distinguish between stripmarker and del char
zwj = '\226\128\141', -- used with capture because zwj may be allowed
}
local invisible_chars = {
{'replacement', '\239\191\189'}, -- U+FFFD, EF BF BD
{'zero width joiner', '('.. invisible_defs.zwj .. ')'}, -- U+200D, E2 80 8D; capture because zwj may be allowed
{'zero width space', '\226\128\139'}, -- U+200B, E2 80 8B
{'hair space', '\226\128\138'}, -- U+200A, E2 80 8A
{'soft hyphen', '\194\173'}, -- U+00AD, C2 AD
{'horizontal tab', '\009'}, -- U+0009 (HT), 09
{'line feed', '\010'}, -- U+000A (LF), 0A
{'no-break space', '\194\160'}, -- U+00A0 (NBSP), C2 A0
{'carriage return', '\013'}, -- U+000D (CR), 0D
{'stripmarker', stripmarkers.any}, -- stripmarker; may or may not be an error; capture returns the stripmaker type
{'delete', '('.. invisible_defs.del .. ')'}, -- U+007F (DEL), 7F; must be done after stripmarker test; capture to distinguish isolated del chars not part of stripmarker
{'C0 control', '[\000-\008\011\012\014-\031]'}, -- U+0000–U+001F (NULL–US), 00–1F (except HT, LF, CR (09, 0A, 0D))
{'C1 control', '[\194\128-\194\159]'}, -- U+0080–U+009F (XXX–APC), C2 80 – C2 9F
-- {'Specials', '[\239\191\185-\239\191\191]'}, -- U+FFF9-U+FFFF, EF BF B9 – EF BF BF
-- {'Private use area', '[\238\128\128-\239\163\191]'}, -- U+E000–U+F8FF, EE 80 80 – EF A3 BF
-- {'Supplementary Private Use Area-A', '[\243\176\128\128-\243\191\191\189]'}, -- U+F0000–U+FFFFD, F3 B0 80 80 – F3 BF BF BD
-- {'Supplementary Private Use Area-B', '[\244\128\128\128-\244\143\191\189]'}, -- U+100000–U+10FFFD, F4 80 80 80 – F4 8F BF BD
}
--[[
Indic script makes use of zero width joiner as a character modifier so zwj
characters must be left in. This pattern covers all of the unicode characters
for these languages:
Devanagari 0900–097F – https://unicode.org/charts/PDF/U0900.pdf
Devanagari extended A8E0–A8FF – https://unicode.org/charts/PDF/UA8E0.pdf
Bengali 0980–09FF – https://unicode.org/charts/PDF/U0980.pdf
Gurmukhi 0A00–0A7F – https://unicode.org/charts/PDF/U0A00.pdf
Gujarati 0A80–0AFF – https://unicode.org/charts/PDF/U0A80.pdf
Oriya 0B00–0B7F – https://unicode.org/charts/PDF/U0B00.pdf
Tamil 0B80–0BFF – https://unicode.org/charts/PDF/U0B80.pdf
Telugu 0C00–0C7F – https://unicode.org/charts/PDF/U0C00.pdf
Kannada 0C80–0CFF – https://unicode.org/charts/PDF/U0C80.pdf
Malayalam 0D00–0D7F – https://unicode.org/charts/PDF/U0D00.pdf
plus the not-necessarily Indic scripts for Sinhala and Burmese:
Sinhala 0D80-0DFF - https://unicode.org/charts/PDF/U0D80.pdf
Myanmar 1000-109F - https://unicode.org/charts/PDF/U1000.pdf
Myanmar extended A AA60-AA7F - https://unicode.org/charts/PDF/UAA60.pdf
Myanmar extended B A9E0-A9FF - https://unicode.org/charts/PDF/UA9E0.pdf
the pattern is used by has_invisible_chars() and coins_cleanup()
]]
local indic_script = '[\224\164\128-\224\181\191\224\163\160-\224\183\191\225\128\128-\225\130\159\234\167\160-\234\167\191\234\169\160-\234\169\191]';
-- list of emoji that use a zwj character (U+200D) to combine with another emoji
-- from: https://unicode.org/Public/emoji/16.0/emoji-zwj-sequences.txt; version: 16.0; 2024-08-14
-- table created by: [[:en:Module:Make emoji zwj table]]
local emoji_t = { -- indexes are decimal forms of the hex values in U+xxxx
[8596] = true, -- U+2194 ↔ left right arrow
[8597] = true, -- U+2195 ↕ up down arrow
[9760] = true, -- U+2620 ☠ skull and crossbones
[9792] = true, -- U+2640 ♀ female sign
[9794] = true, -- U+2642 ♂ male sign
[9877] = true, -- U+2695 ⚕ staff of aesculapius
[9878] = true, -- U+2696 ⚖ scales
[9895] = true, -- U+26A7 ⚧ male with stroke and male and female sign
[9992] = true, -- U+2708 ✈ airplane
[10052] = true, -- U+2744 ❄ snowflake
[10084] = true, -- U+2764 ❤ heavy black heart
[10145] = true, -- U+27A1 ➡ black rightwards arrow
[11035] = true, -- U+2B1B ⬛ black large square
[127752] = true, -- U+1F308 🌈 rainbow
[127787] = true, -- U+1F32B 🌫 fog
[127806] = true, -- U+1F33E 🌾 ear of rice
[127859] = true, -- U+1F373 🍳 cooking
[127868] = true, -- U+1F37C 🍼 baby bottle
[127876] = true, -- U+1F384 🎄 christmas tree
[127891] = true, -- U+1F393 🎓 graduation cap
[127908] = true, -- U+1F3A4 🎤 microphone
[127912] = true, -- U+1F3A8 🎨 artist palette
[127979] = true, -- U+1F3EB 🏫 school
[127981] = true, -- U+1F3ED 🏭 factory
[128102] = true, -- U+1F466 👦 boy
[128103] = true, -- U+1F467 👧 girl
[128104] = true, -- U+1F468 👨 man
[128105] = true, -- U+1F469 👩 woman
[128139] = true, -- U+1F48B 💋 kiss mark
[128165] = true, -- U+1F4A5 💥 collision symbol
[128168] = true, -- U+1F4A8 💨 dash symbol
[128171] = true, -- U+1F4AB 💫 dizzy symbol
[128187] = true, -- U+1F4BB 💻 personal computer
[128188] = true, -- U+1F4BC 💼 brief case
[128293] = true, -- U+1F525 🔥 fire
[128295] = true, -- U+1F527 🔧 wrench
[128300] = true, -- U+1F52C 🔬 microscope
[128488] = true, -- U+1F5E8 🗨 left speech bubble
[128640] = true, -- U+1F680 🚀 rocket
[128658] = true, -- U+1F692 🚒 fire engine
[129001] = true, -- U+1F7E9 🟩 large green square
[129003] = true, -- U+1F7EB 🟫 large brown square
[129309] = true, -- U+1F91D 🤝 handshake
[129455] = true, -- U+1F9AF 🦯 probing cane
[129456] = true, -- U+1F9B0 🦰 emoji component red hair
[129457] = true, -- U+1F9B1 🦱 emoji component curly hair
[129458] = true, -- U+1F9B2 🦲 emoji component bald
[129459] = true, -- U+1F9B3 🦳 emoji component white hair
[129466] = true, -- U+1F9BA 🦺 safety vest
[129468] = true, -- U+1F9BC 🦼 motorized wheelchair
[129469] = true, -- U+1F9BD 🦽 manual wheelchair
[129489] = true, -- U+1F9D1 🧑 adult
[129490] = true, -- U+1F9D2 🧒 child
[129657] = true, -- U+1FA79 🩹 adhesive bandage
[129778] = true, -- U+1FAF2 🫲 leftwards hand
}
--[[----------------------< L A N G U A G E S U P P O R T >-------------------
These tables and constants support various language-specific functionality.
]]
--local this_wiki_code = mw.getContentLanguage():getCode(); -- get this wiki's language code
local this_wiki_code = lang_obj:getCode(); -- get this wiki's language code
if string.match (mw.site.server, 'wikidata') then
this_wiki_code = mw.getCurrentFrame():callParserFunction('int', {'lang'}); -- on Wikidata so use interface language setting instead
end
local mw_languages_by_tag_t = mw.language.fetchLanguageNames (this_wiki_code, 'all'); -- get a table of language tag/name pairs known to Wikimedia; used for interwiki tests
local mw_languages_by_name_t = {};
for k, v in pairs (mw_languages_by_tag_t) do -- build a 'reversed' table name/tag language pairs know to MediaWiki; used for |language=
v = mw.ustring.lower (v); -- lowercase for tag fetch; get name's proper case from mw_languages_by_tag_t[<tag>]
if mw_languages_by_name_t[v] then -- when name already in the table
if 2 == #k or 3 == #k then -- if tag does not have subtags
mw_languages_by_name_t[v] = k; -- prefer the shortest tag for this name
end
else -- here when name not in the table
mw_languages_by_name_t[v] = k; -- so add name and matching tag
end
end
local inter_wiki_map = {}; -- map of interwiki prefixes that are language-code prefixes
for k, v in pairs (mw.site.interwikiMap ('local')) do -- spin through the base interwiki map (limited to local)
if mw_languages_by_tag_t[v["prefix"]] then -- if the prefix matches a known language tag
inter_wiki_map[v["prefix"]] = true; -- add it to our local map
end
end
--[[--------------------< S C R I P T _ L A N G _ C O D E S >-------------------
This table is used to hold ISO 639-1 two-character and ISO 639-3 three-character
language codes that apply only to |script-title= and |script-chapter=
]]
local script_lang_codes = {
'ab', 'am', 'ar', 'az', 'be', 'bg', 'bn', 'bo', 'bs', 'ce', 'chr', 'dv', 'dz',
'el', 'fa', 'grc', 'gu', 'he', 'hi', 'hy', 'ja', 'ka', 'kk', 'km', 'kn', 'ko',
'ku', 'ky', 'lo', 'mk', 'ml', 'mn', 'mni', 'mr', 'my', 'ne', 'or', 'ota',
'pa', 'ps', 'ru', 'sd', 'si', 'sr', 'syc', 'ta', 'te', 'tg', 'th', 'ti', 'tt',
'ug', 'uk', 'ur', 'uz', 'yi', 'yue', 'zh', 'zgh'
};
--[[---------------< L A N G U A G E R E M A P P I N G >----------------------
These tables hold language information that is different (correct) from MediaWiki's definitions
For each ['<tag>'] = 'language name' in lang_code_remap{} there must be a matching ['language name'] = {'language name', '<tag>'} in lang_name_remap{}
lang_tag_remap{}:
key is always lowercase ISO 639-1, -2, -3 language tag or a valid lowercase IETF language tag
value is properly spelled and capitalized language name associated with <tag>
only one language name per <tag>;
key/value pair must have matching entry in lang_name_remap{}
lang_name_remap{}:
key is always lowercase language name
value is a table the holds correctly spelled and capitalized language name [1] and associated tag [2] (tag must match a tag key in lang_tag_remap{})
may have multiple keys referring to a common preferred name and tag; For example:
['kolsch'] and ['kölsch'] both refer to 'Kölsch' and 'ksh'
]]
local lang_tag_remap = { -- used for |language= and |script-title= / |script-chapter=
['als'] = 'Tosk Albanian', -- MediaWiki returns Alemannisch
['bh'] = 'Bihari', -- MediaWiki uses 'bh' as a subdomain name for Bhojpuri Wikipedia: bh.wikipedia.org
['bla'] = 'Blackfoot', -- MediaWiki/IANA/ISO 639: Siksika; use en.wiki preferred name
['bn'] = 'Bengali', -- MediaWiki returns Bangla
['ca-valencia'] = 'Valencian', -- IETF variant of Catalan
['fkv'] = 'Kven', -- MediaWiki returns Kvensk
['gsw'] = 'Swiss German',
['ilo'] = 'Ilocano', -- MediaWiki/IANA/ISO 639: Iloko; use en.wiki preferred name
['ksh'] = 'Kölsch', -- MediaWiki: Colognian; use IANA/ISO 639 preferred name
['ksh-x-colog'] = 'Colognian', -- override MediaWiki ksh; no IANA/ISO 639 code for Colognian; IETF private code created at Module:Lang/data
['mis-x-ripuar'] = 'Ripuarian', -- override MediaWiki ksh; no IANA/ISO 639 code for Ripuarian; IETF private code created at Module:Lang/data
['nan-tw'] = 'Taiwanese Hokkien', -- make room for MediaWiki/IANA/ISO 639 nan: Min Nan Chinese and support en.wiki preferred name
['sr-ec'] = 'Serbian (Cyrillic script)', -- MediaWiki returns српски (ћирилица)
['sr-el'] = 'Serbian (Latin script)', -- MediaWiki returns srpski (latinica)
}
local lang_name_remap = { -- used for |language=; names require proper capitalization; tags must be lowercase
['alemannic'] = {'Swiss German', 'gsw'}, -- ISO 639-2, -3 alternate for Swiss German; MediaWiki mediawiki returns Alemannic for gsw; en.wiki preferred name
['alemannisch'] = {'Swiss German', 'gsw'}, -- not an ISO or IANA language name; MediaWiki uses 'als' as a subdomain name for Alemannic Wikipedia: als.wikipedia.org
['bangla'] = {'Bengali', 'bn'}, -- MediaWiki returns Bangla (the endonym) but we want Bengali (the exonym); here we remap
['bengali'] = {'Bengali', 'bn'}, -- MediaWiki doesn't use exonym so here we provide correct language name and 639-1 code
['bhojpuri'] = {'Bhojpuri', 'bho'}, -- MediaWiki uses 'bh' as a subdomain name for Bhojpuri Wikipedia: bh.wikipedia.org
['bihari'] = {'Bihari', 'bh'}, -- MediaWiki replaces 'Bihari' with 'Bhojpuri' so 'Bihari' cannot be found
['blackfoot'] = {'Blackfoot', 'bla'}, -- MediaWiki/IANA/ISO 639: Siksika; use en.wiki preferred name
['colognian'] = {'Colognian', 'ksh-x-colog'}, -- MediaWiki preferred name for ksh
['ilocano'] = {'Ilocano', 'ilo'}, -- MediaWiki/IANA/ISO 639: Iloko; use en.wiki preferred name
['kolsch'] = {'Kölsch', 'ksh'}, -- use IANA/ISO 639 preferred name (use non-diacritical o instead of umlaut ö)
['kölsch'] = {'Kölsch', 'ksh'}, -- use IANA/ISO 639 preferred name
['kven'] = {'Kven', 'fkv'}, -- Unicode CLDR have decided not to support English language name for these two...
['kvensk'] = {'Kven', 'fkv'}, -- ...they say to refer to IANA registry for English names
['ripuarian'] = {'Ripuarian', 'mis-x-ripuar'}, -- group of dialects; no code in MediaWiki or in IANA/ISO 639
['serbian (cyrillic script)'] = {'Serbian (Cyrillic script)', 'sr-cyrl'}, -- special case to get correct tag when |language=sr-ec
['serbian (latin script)'] = {'Serbian (Latin script)', 'sr-latn'}, -- special case to get correct tag when |language=sr-el
['swiss german'] = {'Swiss German', 'gsw'},
['taiwanese hokkien'] = {'Taiwanese Hokkien', 'nan-tw'}, -- make room for MediaWiki/IANA/ISO 639 nan: Min Nan Chinese
['tosk albanian'] = {'Tosk Albanian', 'als'}, -- MediaWiki replaces 'Tosk Albanian' with 'Alemannisch' so 'Tosk Albanian' cannot be found
['valencian'] = {'Valencian', 'ca-valencia'}, -- variant of Catalan; categorizes as Valencian
}
--[[---------------< P R O P E R T I E S _ C A T E G O R I E S >----------------
Properties categories. These are used for investigating qualities of citations.
]]
local prop_cats = {
['foreign-lang-source'] = 'CS1 sumber basa $1 ($2)', -- |language= categories; $1 is foreign-language name, $2 is ISO639-1 code
['foreign-lang-source-2'] = 'CS1 sumber basa asing (ISO 639-2)|$1', -- |language= category; a cat for ISO639-2 languages; $1 is the ISO 639-2 code used as a sort key
['interproj-linked-name'] = 'CS1 ngaran kalawan tutumbu antarproyék|$1', -- any author, editor, etc that has an interproject link; $1 is interproject tag used as a sort key
['interwiki-linked-name'] = 'CS1 ngaran kalawan tutumbu interwiki|$1', -- any author, editor, etc that has an interwiki link; $1 is interwiki tag used as a sort key; yeilds to interproject
['local-lang-source'] = 'CS1 sumber basa $1 ($2)', -- |language= categories; $1 is local-language name, $2 is ISO639-1 code; not emitted when local_lang_cat_enable is false
['location-test'] = 'CS1 uji lokasi',
['long-vol'] = 'CS1: volume bernilai panjang', -- probably temporary cat to identify scope of |volume= values longer than 4 characters
['script'] = 'CS1 maké aksara basa $1 ($2)', -- |script-title=xx: has matching category; $1 is language name, $2 is language tag
['tracked-param'] = 'CS1 paraméter kalacak: $1', -- $1 is base (enumerators removed) parameter name
['unfit'] = 'CS1: URL teu cocok', -- |url-status=unfit or |url-status=usurped; used to be a maint cat
['vanc-accept'] = 'CS1:Vancouver names with accept markup', -- for |vauthors=/|veditors= with accept-as-written markup
['year-range-abbreviated'] = 'CS1: rentang tahun disingkat', -- probably temporary cat to identify scope of |date=, |year= values using YYYY–YY form
}
--[[-------------------< T I T L E _ T Y P E S >--------------------------------
Here we map a template's CitationClass to TitleType (default values for |type= parameter)
]]
local title_types = {
['AV-media-notes'] = 'Media notes',
['document'] = 'Document',
['interview'] = 'Interview',
['mailinglist'] = 'Mailing list',
['map'] = 'Map',
['podcast'] = 'Podcast',
['pressrelease'] = 'Press release',
['report'] = 'Report',
['speech'] = 'Speech',
['techreport'] = 'Technical report',
['thesis'] = 'Thesis',
}
--[[--------------------------< B U I L D _ K N O W N _ F R E E _ D O I _ R E G I S T R A N T S _ T A B L E >--
build a table of doi registrants known to be free-to-read In a doi, the registrant ID is the series of digits
between the '10.' and the first '/': in doi 10.1100/sommat, 1100 is the registrant ID
see §3.2.2 DOI prefix of the Doi Handbook p. 43
https://www.doi.org/doi-handbook/DOI_Handbook_Final.pdf#page=43
]]
local function build_free_doi_registrants_table()
local registrants_t = {};
for _, v in ipairs ({
'1045', '1074', '1096', '1100', '1155', '1186', '1194', '1371', '1629', '1989', '1999', '2147', '2196', '3285', '3389', '3390',
'3748', '3814', '3847', '3897', '4061', '4089', '4103', '4172', '4175', '4230', '4236', '4239', '4240', '4249', '4251',
'4252', '4253', '4254', '4291', '4292', '4329', '4330', '4331', '5194', '5210', '5306', '5312', '5313', '5314',
'5315', '5316', '5317', '5318', '5319', '5320', '5321', '5334', '5402', '5409', '5410', '5411', '5412',
'5492', '5493', '5494', '5495', '5496', '5497', '5498', '5499', '5500', '5501', '5527', '5528', '5662',
'6064', '6219', '7167', '7217', '7287', '7482', '7490', '7554', '7717', '7759', '7766', '11131', '11569', '11647',
'11648', '12688', '12703', '12715', '12942', '12998', '13105', '14256', '14293', '14303', '15215', '15347', '15412', '15560', '16995',
'17645', '18637', '19080', '19173', '20944', '21037', '21468', '21767', '22261', '22323', '22459', '24105', '24196', '24966',
'26775', '30845', '32545', '35711', '35712', '35713', '35995', '36648', '37126', '37532', '37871', '47128',
'47622', '47959', '52437', '52975', '53288', '54081', '54947', '55667', '55914', '57009', '58647', '59081',
}) do
registrants_t[v] = true; -- build a k/v table of known free-to-read doi registrants
end
return registrants_t;
end
local extended_registrants_t = { -- known free registrants identifiable by the doi suffix incipit
['1002'] = {'aelm', 'leap'}, -- Advanced Electronic Materials, Learned Publishing
['1016'] = {'j.heliyon', 'j.nlp', 'j.proche'}, -- Heliyon, Natural Language Processing, Procedia Chemistry
['1017'] = {'nlp'}, -- Natural Language Processing Journal
['1046'] = {'j.1365-8711', 'j.1365-246x'}, -- MNRAS, GJI
['1093'] = {'mnras', 'mnrasl', 'gji', 'rasti'}, -- MNRAS, MNRAS Letters, GJI, RASTI
['1099'] = {'acmi', 'mic', '00221287', 'mgen'}, -- Access Microbiology, Microbiology, Journal of General Microbiology, Microbial Genomics
['1111'] = {'j.1365-2966', 'j.1745-3933', 'j.1365-246X'}, -- MNRAS, MNRAS Letters, GJI
['1210'] = {'jendso','jcemcr'}, -- Journal of the Endocrine Society, JCEM Case Reports
['4171'] = {'dm','mag'}, -- Documenta Mathematica, EMS Magazine
['14231'] = {'ag'}, -- Algebraic Geometry
}
--[[===================<< E R R O R M E S S A G I N G >>======================
]]
--[[----------< E R R O R M E S S A G E S U P P L I M E N T S >-------------
I18N for those messages that are supplemented with additional specific text that
describes the reason for the error
TODO: merge this with special_case_translations{}?
]]
local err_msg_supl = {
['char'] = 'invalid character', -- |isbn=, |sbn=
['check'] = 'checksum', -- |isbn=, |sbn=
['flag'] = 'flag', -- |archive-url=
['form'] = 'invalid form', -- |isbn=, |sbn=
['group'] = 'invalid group id', -- |isbn=
['initials'] = 'initials', -- Vancouver
['invalid language code'] = 'invalid language code', -- |script-<param>=
['journal'] = 'journal', -- |bibcode=
['length'] = 'length', -- |isbn=, |bibcode=, |sbn=
['liveweb'] = 'liveweb', -- |archive-url=
['missing comma'] = 'missing comma', -- Vancouver
['missing prefix'] = 'missing prefix', -- |script-<param>=
['missing title part'] = 'missing title part', -- |script-<param>=
['name'] = 'name', -- Vancouver
['non-Latin char'] = 'non-Latin character', -- Vancouver
['path'] = 'path', -- |archive-url=
['prefix'] = 'invalid prefix', -- |isbn=
['punctuation'] = 'punctuation', -- Vancouver
['save'] = 'save command', -- |archive-url=
['suffix'] = 'suffix', -- Vancouver
['timestamp'] = 'timestamp', -- |archive-url=
['unknown language code'] = 'unknown language code', -- |script-<param>=
['value'] = 'value', -- |bibcode=
['year'] = 'year', -- |bibcode=
}
--[[--------------< E R R O R _ C O N D I T I O N S >---------------------------
Error condition table. This table has two sections: errors at the top, maintenance
at the bottom. Maint 'messaging' does not have a 'message' (message=nil)
The following contains a list of IDs for various error conditions defined in the
code. For each ID, we specify a text message to display, an error category to
include, and whether the error message should be wrapped as a hidden comment.
Anchor changes require identical changes to matching anchor in Help:CS1 errors
TODO: rename error_conditions{} to something more generic; create separate error
and maint tables inside that?
]]
local error_conditions = {
err_accessdate_missing_url = {
message = ' ',
anchor = 'accessdate_missing_url',
category = 'Kasalahan CS1: tanggal aksés tanpa URL',
hidden = false
},
err_apostrophe_markup = {
message = ' ', -- $1 is parameter name
anchor = 'apostrophe_markup',
category = 'Kasalahan CS1: markah',
hidden = false
},
err_archive_date_missing_url = {
message = ' ',
anchor = 'archive_date_missing_url',
category = 'Kasalahan CS1: URL arsipan',
hidden = false
},
err_archive_date_url_ts_mismatch = {
message = ' ',
anchor = 'archive_date_url_ts_mismatch',
category = 'Kasalahan CS1: URL arsipan',
hidden = false
},
err_archive_missing_date = {
message = ' ',
anchor = 'archive_missing_date',
category = 'Kasalahan CS1: URL arsipan',
hidden = false
},
err_archive_missing_url = {
message = ' ',
anchor = 'archive_missing_url',
category = 'Kasalahan CS1: URL arsipan',
hidden = false
},
err_archive_url = {
message = ' ', -- $1 is error message detail
anchor = 'archive_url',
category = 'Kasalahan CS1: URL arsipan',
hidden = false
},
err_arxiv_missing = {
message = ' ',
anchor = 'arxiv_missing',
category = 'Kasalahan CS1: arXiv', -- same as bad arxiv
hidden = false
},
err_asintld_missing_asin = {
message = ' ', -- $1 is parameter name
anchor = 'asintld_missing_asin',
category = 'Kasalahan CS1: ASIN',
hidden = false
},
err_bad_arxiv = {
message = ' ',
anchor = 'bad_arxiv',
category = 'Kasalahan CS1: arXiv',
hidden = false
},
err_bad_asin = {
message = ' ',
anchor = 'bad_asin',
category ='Kasalahan CS1: ASIN',
hidden = false
},
err_bad_asin_tld = {
message = ' ',
anchor = 'bad_asin_tld',
category ='Kasalahan CS1: ASIN',
hidden = false
},
err_bad_bibcode = {
message = ' ', -- $1 is error message detail
anchor = 'bad_bibcode',
category = 'Kasalahan CS1: bibcode',
hidden = false
},
err_bad_biorxiv = {
message = ' ',
anchor = 'bad_biorxiv',
category = 'Kasalahan CS1: bioRxiv',
hidden = false
},
err_bad_citeseerx = {
message = ' ',
anchor = 'bad_citeseerx',
category = 'Kasalahan CS1: citeseerx',
hidden = false
},
err_bad_date = {
message = ' ', -- $1 is a parameter name list
anchor = 'bad_date',
category = 'Kasalahan CS1: tanggal',
hidden = false
},
err_bad_doi = {
message = ' ',
anchor = 'bad_doi',
category = 'Kasalahan CS1: DOI',
hidden = false
},
err_bad_hdl = {
message = ' ',
anchor = 'bad_hdl',
category = 'Kasalahan CS1: HDL',
hidden = false
},
err_bad_isbn = {
message = ' ', -- $1 is error message detail
anchor = 'bad_isbn',
category = 'Kasalahan CS1: ISBN',
hidden = false
},
err_bad_ismn = {
message = ' ',
anchor = 'bad_ismn',
category = 'Kasalahan CS1: ISMN',
hidden = false
},
err_bad_issn = {
message = ' ', -- $1 is 'e' or '' for eissn or issn
anchor = 'bad_issn',
category = 'Kasalahan CS1: ISSN',
hidden = false
},
err_bad_jfm = {
message = ' ',
anchor = 'bad_jfm',
category = 'Kasalahan CS1: JFM',
hidden = false
},
err_bad_jstor = {
message = ' ',
anchor = 'bad_jstor',
category = 'Kasalahan CS1: JSTOR',
hidden = false
},
err_bad_lccn = {
message = ' ',
anchor = 'bad_lccn',
category = 'Kasalahan CS1: LCCN',
hidden = false
},
err_bad_medrxiv = {
message = ' ',
anchor = 'bad_medrxiv',
category = 'Kasalahan CS1: medRxiv',
hidden = false
},
err_bad_mr = {
message = ' ',
anchor = 'bad_mr',
category = 'Kasalahan CS1: MR',
hidden = false
},
err_bad_oclc = {
message = ' ',
anchor = 'bad_oclc',
category = 'Kasalahan CS1: OCLC',
hidden = false
},
err_bad_ol = {
message = ' ',
anchor = 'bad_ol',
category = 'Kasalahan CS1: OL',
hidden = false
},
err_bad_osti = {
message = ' ',
anchor = 'bad_osti',
category = 'Kasalahan CS1: OSTI',
hidden = false
},
err_bad_paramlink = { -- for |title-link=, |author/editor/translator-link=, |series-link=, |episode-link=
message = ' ', -- $1 is parameter name
anchor = 'bad_paramlink',
category = 'Kasalahan CS1: tutumbu paraméter',
hidden = false
},
err_bad_pmc = {
message = ' ',
anchor = 'bad_pmc',
category = 'Kasalahan CS1: PMC',
hidden = false
},
err_bad_pmid = {
message = ' ',
anchor = 'bad_pmid',
category = 'Kasalahan CS1: PMID',
hidden = false
},
err_bad_rfc = {
message = ' ',
anchor = 'bad_rfc',
category = 'Kasalahan CS1: RFC',
hidden = false
},
err_bad_s2cid = {
message = ' ',
anchor = 'bad_s2cid',
category = 'Kasalahan CS1: S2CID',
hidden = false
},
err_bad_sbn = {
message = ' ', -- $1 is error message detail
anchor = 'bad_sbn',
category = 'Kasalahan CS1: SBN',
hidden = false
},
err_bad_ssrn = {
message = ' ',
anchor = 'bad_ssrn',
category = 'Kasalahan CS1: SSRN',
hidden = false
},
err_bad_url = {
message = ' ', -- $1 is parameter name
anchor = 'bad_url',
category = 'Kasalahan CS1: URL',
hidden = false
},
err_bad_usenet_id = {
message = ' ',
anchor = 'bad_message_id',
category = 'Kasalahan CS1: ID talatah',
hidden = false
},
err_bad_zbl = {
message = ' ',
anchor = 'bad_zbl',
category = 'Kasalahan CS1: Zbl',
hidden = false
},
err_bare_url_missing_title = {
message = ' ', -- $1 is parameter name
anchor = 'bare_url_missing_title',
category = 'Kasalahan CS1: tutumbu atah',
hidden = false
},
err_biorxiv_missing = {
message = ' ',
anchor = 'biorxiv_missing',
category = 'Kasalahan CS1: bioRxiv', -- same as bad bioRxiv
hidden = false
},
err_chapter_ignored = {
message = ' ', -- $1 is parameter name
anchor = 'chapter_ignored',
category = 'Kasalahan CS1: bab dipaliré',
hidden = false
},
err_citation_missing_title = {
message = ' ', -- $1 is parameter name
anchor = 'citation_missing_title',
category = 'Kasalahan CS1: judul leungit',
hidden = false
},
err_citeseerx_missing = {
message = ' ',
anchor = 'citeseerx_missing',
category = 'Kasalahan CS1: citeseerx', -- same as bad citeseerx
hidden = false
},
err_cite_web_url = { -- this error applies to cite web and to cite podcast
message = ' ',
anchor = 'cite_web_url',
category = 'Kasalahan CS1: butuh URL',
hidden = false
},
err_class_ignored = {
message = ' ',
anchor = 'class_ignored',
category = 'Kasalahan CS1: kelas',
hidden = false
},
err_contributor_ignored = {
message = ' ',
anchor = 'contributor_ignored',
category = 'Kasalahan CS1: kontributor',
hidden = false
},
err_contributor_missing_required_param = {
message = ' ', -- $1 is parameter name
anchor = 'contributor_missing_required_param',
category = 'Kasalahan CS1: kontributor',
hidden = false
},
err_deprecated_params = {
message = ' ', -- $1 is parameter name
anchor = 'deprecated_params',
category = 'Kasalahan CS1: paraméter nu teu dipaké deui',
hidden = false
},
err_disp_name = {
message = ' ', -- $1 is parameter name; $2 is the assigned value
anchor = 'disp_name',
category = 'Kasalahan CS1: ngaran pidangan',
hidden = false,
},
err_doibroken_missing_doi = {
message = ' ', -- $1 is parameter name
anchor = 'doibroken_missing_doi',
category = 'Kasalahan CS1: DOI',
hidden = false
},
err_embargo_missing_pmc = {
message = ' ', -- $1 is parameter name
anchor = 'embargo_missing_pmc',
category = 'Kasalahan CS1: PMC embargo',
hidden = false
},
err_empty_citation = {
message = ' ',
anchor = 'empty_citation',
category = 'Kasalahan CS1: cutatan kosong',
hidden = false
},
err_etal = {
message = ' ', -- $1 is parameter name
anchor = 'explicit_et_al',
category = 'Kasalahan CS1: pamakéan et al.',
hidden = false
},
err_extra_text_edition = {
message = ' ',
anchor = 'extra_text_edition',
category = 'Kasalahan CS1: téks tambahan: édisi',
hidden = false,
},
err_extra_text_issue = {
message = ' ', -- $1 is parameter name
anchor = 'extra_text_issue',
category = 'Kasalahan CS1: téks tambahan: nomer',
hidden = false,
},
err_extra_text_pages = {
message = ' ', -- $1 is parameter name
anchor = 'extra_text_pages',
category = 'Kasalahan CS1: téks tambahan: kaca',
hidden = false,
},
err_extra_text_volume = {
message = ' ', -- $1 is parameter name
anchor = 'extra_text_volume',
category = 'Kasalahan CS1: téks tambahan: volume',
hidden = false,
},
err_first_missing_last = {
message = ' ', -- $1 is first alias, $2 is matching last alias
anchor = 'first_missing_last',
category = 'Kasalahan CS1: tanpa ngaran', -- author, contributor, editor, interviewer, translator
hidden = false
},
err_format_missing_url = {
message = ' ', -- $1 is format parameter $2 is url parameter
anchor = 'format_missing_url',
category = 'Kasalahan CS1: format tanpa URL',
hidden = false
},
err_generic_name = {
message = ' ', -- $1 is parameter name
anchor = 'generic_name',
category = 'Kasalahan CS1: ngaran generik',
hidden = false,
},
err_generic_title = {
message = ' ',
anchor = 'generic_title',
category = 'Kasalahan CS1: judul generik',
hidden = false,
},
err_invalid_isbn_date = {
message = ' ',
anchor = 'invalid_isbn_date',
category = 'Kasalahan CS1: tanggal ISBN',
hidden = true
},
err_invalid_param_val = {
message = ' ', -- $1 is parameter name $2 is parameter value
anchor = 'invalid_param_val',
category = 'Kasalahan CS1: nilai paraméter teu sah',
hidden = false
},
err_invisible_char = {
message = ' ', -- $1 is invisible char $2 is parameter name $3 is position number
anchor = 'invisible_char',
category = 'Kasalahan CS1: karakter teu katémbong',
hidden = false
},
err_medrxiv_missing = {
message = ' ',
anchor = 'medrxiv_missing',
category = 'Kasalahan CS1: medRxiv', -- same as bad medRxiv
hidden = false
},
err_missing_name = {
message = ' ', -- $1 is modified NameList; $2 is enumerator
anchor = 'missing_name',
category = 'Kasalahan CS1: teu ngabogaan panulis atawa éditor', -- author, contributor, editor, interviewer, translator
hidden = false
},
err_missing_periodical = {
message = ' ', -- $1 is cs1 template name; $2 is canonical periodical parameter name for cite $1
anchor = 'missing_periodical',
category = 'Kasalahan CS1: periode leungit',
hidden = false
},
err_missing_pipe = {
message = ' ', -- $1 is parameter name
anchor = 'missing_pipe',
category = 'Kasalahan CS1: pipa leungit',
hidden = false
},
err_missing_publisher = {
message = ' ', -- $1 is cs1 template name; $2 is canonical publisher parameter name for cite $1
anchor = 'missing_publisher',
category = 'Kasalahan CS1: pamedal leungit',
hidden = false
},
err_numeric_names = {
message = ' ', -- $1 is parameter name',
anchor = 'numeric_names',
category = 'Kasalahan CS1: ngaran dina wangun angka',
hidden = false,
},
err_param_access_requires_param = {
message = ' ', -- $1 is parameter name
anchor = 'param_access_requires_param',
category = 'Kasalahan CS1: aksés paraméter',
hidden = false
},
err_param_has_ext_link = {
message = ' ', -- $1 is parameter name
anchor = 'param_has_ext_link',
category = 'Kasalahan CS1: tumbu luar',
hidden = false
},
err_param_has_twl_url = {
message = ' ', -- $1 is parameter name
anchor = 'param_has_twl_url',
category = 'Kasalahan CS1: URL',
hidden = false
},
err_parameter_ignored = {
message = ' ', -- $1 is parameter name
anchor = 'parameter_ignored',
category = 'Kasalahan CS1: paraméter teu didukung',
hidden = false
},
err_parameter_ignored_suggest = {
message = ' ', -- $1 is unknown parameter $2 is suggested parameter name
anchor = 'parameter_ignored_suggest',
category = 'Kasalahan CS1: parameter teu didukung',
hidden = false
},
err_periodical_ignored = {
message = ' ', -- $1 is parameter name
anchor = 'periodical_ignored',
category = 'Kasalahan CS1: periode dipaliré',
hidden = false
},
err_redundant_parameters = {
message = ' ', -- $1 is error message detail
anchor = 'redundant_parameters',
category = 'Kasalahan CS1: paraméter leuwihan',
hidden = false
},
err_script_parameter = {
message = ' ', -- $1 is parameter name $2 is script language code or error detail
anchor = 'script_parameter',
category = 'Kasalahan CS1: paraméter aksara',
hidden = false
},
err_ssrn_missing = {
message = ' ',
anchor = 'ssrn_missing',
category = 'Kasalahan CS1: SSRN',
hidden = false
},
err_text_ignored = {
message = ' ', -- $1 is ignored text
anchor = 'text_ignored',
category = 'Kasalahan CS1: paraméter nu teu dipikanyaho',
hidden = false
},
err_trans_missing_title = {
message = ' ', -- $1 is base parameter name
anchor = 'trans_missing_title',
category = 'Kasalahan CS1: judul teu ditarjamahkeun',
hidden = false
},
err_param_unknown_empty = {
message = ' ', -- $1 is 's' or empty space; $2 is emty unknown param list
anchor = 'param_unknown_empty',
category = 'Kasalahan CS1: paraméter kosong nu teu dipikanyaho',
hidden = false
},
err_vancouver = {
message = ' ', -- $1 is error detail, $2 is the nth name
anchor = 'vancouver',
category = 'Kasalahan CS1: gaya Vancouver',
hidden = false
},
err_wikilink_in_url = {
message = ' ', -- uses ndash
anchor = 'wikilink_in_url',
category = 'Kasalahan CS1: konflik URL–wikilink', -- uses ndash
hidden = false
},
--[[--------------------------< M A I N T >-------------------------------------
maint messages do not have a message (message = nil); otherwise the structure
is the same as error messages
]]
maint_archived_copy = {
message = nil,
anchor = 'archived_copy',
category = 'Pamiaraan CS1: Salinan kaarsipkeun salaku judul',
hidden = true,
},
maint_bibcode = {
message = nil,
anchor = 'bibcode',
category = 'Pamiaraan CS1: bibcode',
hidden = true,
},
maint_location_no_publisher = { -- cite book, conference, encyclopedia; citation as book cite or encyclopedia cite
message = nil,
anchor = 'location_no_publisher',
category = 'Pamiaraan CS1: Lokasi tanpa pamedal',
hidden = true,
},
maint_bot_unknown = {
message = nil,
anchor = 'bot:_unknown',
category = 'Pamiaraan CS1: BOT: status url asli teu dipikanyaho',
hidden = true,
},
maint_date_auto_xlated = { -- date auto-translation not supported by en.wiki
message = nil,
anchor = 'date_auto_xlated',
category = 'Pamiaraan CS1: Tanggal ditarjamahkeun otomatis',
hidden = true,
},
maint_date_format = {
message = nil,
anchor = 'date_format',
category = 'Pamiaraan CS1: Format tanggal',
hidden = true,
},
maint_date_year = {
message = nil,
anchor = 'date_year',
category = 'Pamiaraan CS1: Tanggal jeung taun',
hidden = true,
},
maint_doi_ignore = {
message = nil,
anchor = 'doi_ignore',
category = 'Pamiaraan CS1: Kasalahan DOI nu dipaliré',
hidden = true,
},
maint_doi_inactive = {
message = nil,
anchor = 'doi_inactive',
category = 'Pamiaraan CS1: DOI nonaktif',
hidden = true,
},
maint_doi_inactive_dated = {
message = nil,
anchor = 'doi_inactive_dated',
category = 'Pamiaraan CS1: DOI nonaktif per $2$3$1', -- $1 is year, $2 is month-name or empty string, $3 is space or empty string
hidden = true,
},
maint_doi_unflagged_free = {
message = nil,
anchor = 'doi_unflagged_free',
category = 'Pamiaraan CS1: DOI bébas tanpa ditandaan',
hidden = true,
},
maint_extra_punct = {
message = nil,
anchor = 'extra_punct',
category = 'Pamiaraan CS1: Tanda baca tambahan',
hidden = true,
},
maint_id_limit_load_fail = { -- applies to all cs1|2 templates on a page;
message = nil, -- maint message (category link) never emitted
anchor = 'id_limit_load_fail',
category = 'Pamiaraan CS1: pamuatan wates ID gagal',
hidden = true,
},
maint_isbn_ignore = {
message = nil,
anchor = 'ignore_isbn_err',
category = 'Pamiaraan CS1: Kasalahn ISBN nu dipaliré',
hidden = true,
},
maint_issn_ignore = {
message = nil,
anchor = 'ignore_issn',
category = 'Pamiaraan CS1: Kasalahan ISSN nu dipaliré',
hidden = true,
},
maint_jfm_format = {
message = nil,
anchor = 'jfm_format',
category = 'Pamiaraan CS1: Format JFM',
hidden = true,
},
maint_location = {
message = nil,
anchor = 'location',
category = 'Pamiaraan CS1: Lokasi',
hidden = true,
},
maint_mr_format = {
message = nil,
anchor = 'mr_format',
category = 'Pamiaraan CS1: Format MR',
hidden = true,
},
maint_mult_names = {
message = nil,
anchor = 'mult_names',
category = 'Pamiaraan CS1: Loba ngaran: $1', -- $1 is '<name>s list'; gets value from special_case_translation table
hidden = true,
},
maint_numeric_names = {
message = nil,
anchor = 'numeric_names',
category = 'Pamiaraan CS1: Ngaran numerik: $1', -- $1 is '<name>s list'; gets value from special_case_translation table
hidden = true,
},
maint_others = {
message = nil,
anchor = 'others',
category = 'Pamiaraan CS1: Nu lian',
hidden = true,
},
maint_others_avm = {
message = nil,
anchor = 'others_avm',
category = 'Pamiaraan CS1: Nu lian dina cite AV media (notes)',
hidden = true,
},
maint_overridden_setting = {
message = nil,
anchor = 'overridden',
category = 'Pamiaraan CS1: Pangaturan diambil alih',
hidden = true,
},
maint_pmc_embargo = {
message = nil,
anchor = 'embargo',
category = 'Pamiaraan CS1: Émbargo PMC kadaluwarsa',
hidden = true,
},
maint_pmc_format = {
message = nil,
anchor = 'pmc_format',
category = 'Pamiaraan CS1: Format PMC',
hidden = true,
},
maint_postscript = {
message = nil,
anchor = 'postscript',
category = 'Pamiaraan CS1: Postscript',
hidden = true,
},
maint_publisher_location = {
message = nil,
anchor = 'publisher_location',
category = 'Pamiaraan CS1: Lokasi pamedal',
hidden = true,
},
maint_ref_duplicates_default = {
message = nil,
anchor = 'ref_default',
category = 'Pamiaraan CS1: Ref ngaduplikasi bawaan',
hidden = true,
},
maint_unknown_lang = {
message = nil,
anchor = 'unknown_lang',
category = 'Pamiaraan CS1: Basa nu teu dipikanyaho',
hidden = true,
},
maint_untitled = {
message = nil,
anchor = 'untitled',
category = 'Pamiaraan CS1: Periode tanpa judul',
hidden = true,
},
maint_url_status = {
message = nil,
anchor = 'url_status',
category = 'Pamiaraan CS1: Status URL',
hidden = true,
},
maint_year= {
message = nil,
anchor = 'year',
category = 'Pamiaraan CS1: Taun',
hidden = true,
},
maint_zbl = {
message = nil,
anchor = 'zbl',
category = 'Pamiaraan CS1: Zbl',
hidden = true,
},
}
--[[--------------------------< I D _ L I M I T S _ D A T A _ T >----------------------------------------------
fetch id limits for certain identifiers from c:Data:CS1/Identifier limits.tab. This source is a json tabular
data file maintained at wikipedia commons. Convert the json format to a table of k/v pairs.
The values from <id_limits_data_t> are used to set handle.id_limit.
From 2025-02-21, MediaWiki is broken. Use this link to edit the tablular data file:
https://commons.wikimedia.org/w/index.php?title=Data:CS1/Identifier_limits.tab&action=edit
See Phab:T389105
]]
local id_limits_data_t = {};
local use_commons_data = true; -- set to false if your wiki does not have access to mediawiki commons; then,
if false == use_commons_data then -- update this table from https://commons.wikimedia.org/wiki/Data:CS1/Identifier_limits.tab; last update: 2025-02-21
id_limits_data_t = {['OCLC'] = 10450000000, ['OSTI'] = 23010000, ['PMC'] = 11900000, ['PMID'] = 40400000, ['RFC'] = 9300, ['SSRN'] = 5200000, ['S2CID'] = 276000000}; -- this table must be maintained locally
else -- here for wikis that do have access to mediawiki commons
local load_fail_limit = 99999999999; -- very high number to avoid error messages on load failure
id_limits_data_t = {['OCLC'] = load_fail_limit, ['OSTI'] = load_fail_limit, ['PMC'] = load_fail_limit, ['PMID'] = load_fail_limit, ['RFC'] = load_fail_limit, ['SSRN'] = load_fail_limit, ['S2CID'] = load_fail_limit};
local id_limits_data_load_fail = false; -- flag; assume that we will be successful when loading json id limit tabular data
local tab_data_t = mw.ext.data.get ('CS1/Identifier limits.tab').data; -- attempt to load the json limit data from commons into <tab_data_t>
if false == tab_data_t then -- undocumented 'feature': mw.ext.data.get() sometimes returns false
id_limits_data_load_fail = true; -- set the flag so that Module:Citation/CS1 can create an unannotated maint category
else
for _, limit_t in ipairs (tab_data_t) do -- overwrite default <load_fail_limit> values
id_limits_data_t[limit_t[1]] = limit_t[2]; -- <limit[1]> is identifier; <limit[2]> is upper limit for that identifier
end
end
end
--[[--------------------------< I D _ H A N D L E R S >--------------------------------------------------------
The following contains a list of values for various defined identifiers. For each
identifier we specify a variety of information necessary to properly render the
identifier in the citation.
parameters: a list of parameter aliases for this identifier; first in the list is the canonical form
link: Wikipedia article name
redirect: a local redirect to a local Wikipedia article name; at en.wiki, 'ISBN (identifier)' is a redirect to 'International Standard Book Number'
q: Wikidata q number for the identifier
label: the label preceding the identifier; label is linked to a Wikipedia article (in this order):
redirect from id_handlers['<id>'].redirect when use_identifier_redirects is true
Wikidata-supplied article name for the local wiki from id_handlers['<id>'].q
local article name from id_handlers['<id>'].link
prefix: the first part of a URL that will be concatenated with a second part which usually contains the identifier
suffix: optional third part to be added after the identifier
encode: true if URI should be percent-encoded; otherwise false
COinS: identifier link or keyword for use in COinS:
for identifiers registered at info-uri.info use: info:.... where '...' is the appropriate identifier label
for identifiers that have COinS keywords, use the keyword: rft.isbn, rft.issn, rft.eissn
for |asin= and |ol=, which require assembly, use the keyword: url
for others make a URL using the value in prefix/suffix and #label, use the keyword: pre (not checked; any text other than 'info', 'rft', or 'url' works here)
set to nil to leave the identifier out of the COinS
separator: character or text between label and the identifier in the rendered citation
id_limit: for those identifiers with established limits, this property holds the upper limit
access: use this parameter to set the access level for all instances of this identifier.
the value must be a valid access level for an identifier (see ['id-access'] in this file).
custom_access: to enable custom access level for an identifier, set this parameter
to the parameter that should control it (normally 'id-access')
]]
local id_handlers = {
['ARXIV'] = {
parameters = {'arxiv', 'eprint'},
link = 'arXiv',
redirect = 'arXiv',
q = 'Q118398',
label = 'arXiv',
prefix = 'https://arxiv.org/abs/',
encode = false,
COinS = 'info:arxiv',
separator = ':',
access = 'free', -- free to read
},
['ASIN'] = {
parameters = { 'asin', 'ASIN' },
link = 'Nomor Idéntifikasi Standar Amazon',
redirect = 'ASIN',
q = 'Q1753278',
label = 'ASIN',
prefix = 'https://www.amazon.',
COinS = 'url',
separator = ' ',
encode = false;
},
['BIBCODE'] = {
parameters = {'bibcode'},
link = 'Bibcode',
redirect = 'Bibcode',
q = 'Q25754',
label = 'Bibcode',
prefix = 'https://ui.adsabs.harvard.edu/abs/',
encode = false,
COinS = 'info:bibcode',
separator = ':',
custom_access = 'bibcode-access',
},
['BIORXIV'] = {
parameters = {'biorxiv'},
link = 'bioRxiv',
redirect = 'bioRxiv',
q = 'Q19835482',
label = 'bioRxiv',
prefix = 'https://doi.org/',
COinS = 'pre', -- use prefix value
access = 'free', -- free to read
encode = true,
separator = ' ',
},
['CITESEERX'] = {
parameters = {'citeseerx'},
link = 'CiteSeerX',
redirect = 'CiteSeerX',
q = 'Q2715061',
label = 'CiteSeerX',
prefix = 'https://citeseerx.ist.psu.edu/viewdoc/summary?doi=',
COinS = 'pre', -- use prefix value
access = 'free', -- free to read
encode = true,
separator = ' ',
},
['DOI'] = { -- Used by InternetArchiveBot
parameters = { 'doi', 'DOI'},
link = 'Pangidentifikasi Objék Digital',
redirect = 'DOI',
q = 'Q25670',
label = 'doi',
prefix = 'https://doi.org/',
COinS = 'info:doi',
separator = ':',
encode = true,
custom_access = 'doi-access',
},
['EISSN'] = {
parameters = {'eissn', 'EISSN'},
link = 'Nomor Séri Standar Internasional#ISSN Elektronik',
redirect = 'ISSN',
q = 'Q46339674',
label = 'eISSN',
prefix = 'https://search.worldcat.org/issn/',
COinS = 'rft.eissn',
encode = false,
separator = ' ',
},
['HDL'] = {
parameters = { 'hdl', 'HDL' },
link = 'Handle System',
redirect = 'hdl',
q = 'Q3126718',
label = 'hdl',
prefix = 'https://hdl.handle.net/',
COinS = 'info:hdl',
separator = ':',
encode = true,
custom_access = 'hdl-access',
},
['ISBN'] = { -- Used by InternetArchiveBot
parameters = {'isbn', 'ISBN'},
link = 'Nomor Buku Standar Internasional',
redirect = 'ISBN',
q = 'Q33057',
label = 'ISBN',
prefix = 'Special:BookSources/',
COinS = 'rft.isbn',
separator = ' ',
},
['ISMN'] = {
parameters = {'ismn', 'ISMN'},
link = 'Nomor Musik Standar Internasional',
redirect = 'ISMN',
q = 'Q1666938',
label = 'ISMN',
prefix = '', -- not currently used;
COinS = nil, -- nil because we can't use pre or rft or info:
separator = ' ',
},
['ISSN'] = {
parameters = {'issn', 'ISSN'},
link = 'Nomor Séri Standar Internasional',
redirect = 'ISSN',
q = 'Q131276',
label = 'ISSN',
prefix = 'https://search.worldcat.org/issn/',
COinS = 'rft.issn',
encode = false,
separator = ' ',
},
['JFM'] = {
parameters = {'jfm', 'JFM'},
link = 'Jahrbuch über die Fortschritte der Mathematik',
redirect = 'Jahrbuch über die Fortschritte der Mathematik',
q = '',
label = 'JFM',
prefix = 'https://zbmath.org/?format=complete&q=an:',
COinS = 'pre', -- use prefix value
encode = true,
separator = ' ',
},
['JSTOR'] = {
parameters = {'jstor', 'JSTOR'},
link = 'JSTOR',
redirect = 'JSTOR',
q = 'Q1420342',
label = 'JSTOR',
prefix = 'https://www.jstor.org/stable/',
COinS = 'pre', -- use prefix value
encode = false,
separator = ' ',
custom_access = 'jstor-access',
},
['LCCN'] = {
parameters = {'lccn', 'LCCN'},
link = 'Library of Congress Control Number',
redirect = 'LCCN',
q = 'Q620946',
label = 'LCCN',
prefix = 'https://lccn.loc.gov/',
COinS = 'info:lccn',
encode = false,
separator = ' ',
},
['MEDRXIV'] = {
parameters = {'medrxiv'},
link = 'medRxiv',
redirect = 'medRxiv',
q = 'Q58465838',
label = 'medRxiv',
prefix = 'https://www.medrxiv.org/content/',
COinS = 'pre', -- use prefix value
access = 'free', -- free to read
encode = false,
separator = ' ',
},
['MR'] = {
parameters = {'mr', 'MR'},
link = 'Mathematical Reviews',
redirect = 'Mathematical Reviews',
q = 'Q211172',
label = 'MR',
prefix = 'https://mathscinet.ams.org/mathscinet-getitem?mr=',
COinS = 'pre', -- use prefix value
encode = true,
separator = ' ',
},
['OCLC'] = {
parameters = {'oclc', 'OCLC'},
link = 'OCLC',
redirect = 'OCLC ',
q = 'Q190593',
label = 'OCLC',
prefix = 'https://search.worldcat.org/oclc/',
COinS = 'info:oclcnum',
encode = true,
separator = ' ',
id_limit = id_limits_data_t.OCLC or 0,
},
['OL'] = {
parameters = { 'ol', 'OL' },
link = 'Open Library',
redirect = 'Open Library',
q = 'Q1201876',
label = 'OL',
prefix = 'https://openlibrary.org/',
COinS = 'url',
separator = ' ',
encode = true,
custom_access = 'ol-access',
},
['OSTI'] = {
parameters = {'osti', 'OSTI'},
link = 'Office of Scientific and Technical Information',
redirect = 'Office of Scientific and Technical Information',
q = 'Q2015776',
label = 'OSTI',
prefix = 'https://www.osti.gov/biblio/',
COinS = 'pre', -- use prefix value
encode = true,
separator = ' ',
id_limit = id_limits_data_t.OSTI or 0,
custom_access = 'osti-access',
},
['PMC'] = {
parameters = {'pmc', 'PMC'},
link = 'PubMed Central',
redirect = 'PubMed',
q = 'Q229883',
label = 'PMC',
prefix = 'https://www.ncbi.nlm.nih.gov/pmc/articles/PMC',
suffix = '',
COinS = 'pre', -- use prefix value
encode = true,
separator = ' ',
id_limit = id_limits_data_t.PMC or 0,
access = 'free', -- free to read
},
['PMID'] = {
parameters = {'pmid', 'PMID'},
link = 'PubMed Identifier',
redirect = 'PubMed',
q = 'Q2082879',
label = 'PMID',
prefix = 'https://pubmed.ncbi.nlm.nih.gov/',
COinS = 'info:pmid',
encode = false,
separator = ' ',
id_limit = id_limits_data_t.PMID or 0,
},
['RFC'] = {
parameters = {'rfc', 'RFC'},
link = 'Request for Comments',
redirect = 'Request for Comments',
q = 'Q212971',
label = 'RFC',
prefix = 'https://tools.ietf.org/html/rfc',
COinS = 'pre', -- use prefix value
encode = false,
separator = ' ',
id_limit = id_limits_data_t.RFC or 0,
access = 'free', -- free to read
},
['SBN'] = {
parameters = {'sbn', 'SBN'},
link = 'Standard Book Number', -- redirect to International_Standard_Book_Number#History
redirect = 'Standard Book Number',
label = 'SBN',
prefix = 'Special:BookSources/0-', -- prefix has leading zero necessary to make 9-digit sbn a 10-digit isbn
COinS = nil, -- nil because we can't use pre or rft or info:
separator = ' ',
},
['SSRN'] = {
parameters = {'ssrn', 'SSRN'},
link = 'Social Science Research Network',
redirect = 'SSRN',
q = 'Q7550801',
label = 'SSRN',
prefix = 'https://papers.ssrn.com/sol3/papers.cfm?abstract_id=',
COinS = 'pre', -- use prefix value
encode = true,
separator = ' ',
id_limit = id_limits_data_t.SSRN or 0,
custom_access = 'ssrn-access',
},
['S2CID'] = {
parameters = {'s2cid', 'S2CID'},
link = 'Semantic Scholar',
redirect = 'S2CID',
q = 'Q22908627',
label = 'S2CID',
prefix = 'https://api.semanticscholar.org/CorpusID:',
COinS = 'pre', -- use prefix value
encode = false,
separator = ' ',
id_limit = id_limits_data_t.S2CID or 0,
custom_access = 's2cid-access',
},
['USENETID'] = {
parameters = {'message-id'},
link = 'Usenet',
redirect = 'Usenet',
q = 'Q193162',
label = 'Usenet:',
prefix = 'news:',
encode = false,
COinS = 'pre', -- use prefix value
separator = ' ',
},
['ZBL'] = {
parameters = {'zbl', 'ZBL' },
link = 'Zentralblatt MATH',
redirect = 'Zentralblatt',
q = 'Q190269',
label = 'Zbl',
prefix = 'https://zbmath.org/?format=complete&q=an:',
COinS = 'pre', -- use prefix value
encode = true,
separator = ' ',
},
}
--[[--------------------------< E X P O R T S >---------------------------------
]]
return {
use_identifier_redirects = use_identifier_redirects, -- booleans defined in the settings at the top of this module
local_lang_cat_enable = local_lang_cat_enable,
date_name_auto_xlate_enable = date_name_auto_xlate_enable,
date_digit_auto_xlate_enable = date_digit_auto_xlate_enable,
enable_sort_keys = enable_sort_keys,
-- tables and variables created when this module is loaded
global_df = get_date_format (), -- this line can be replaced with "global_df = 'dmy-all'," to have all dates auto translated to dmy format.
global_cs1_config_t = global_cs1_config_t, -- global settings from {{cs1 config}}
punct_skip = build_skip_table (punct_skip, punct_meta_params),
url_skip = build_skip_table (url_skip, url_meta_params),
known_free_doi_registrants_t = build_free_doi_registrants_table(),
id_limits_data_load_fail = id_limits_data_load_fail, -- true when commons tabular identifier-limit data fails to load
name_space_sort_keys = name_space_sort_keys,
aliases = aliases,
special_case_translation = special_case_translation,
date_names = date_names,
err_msg_supl = err_msg_supl,
error_conditions = error_conditions,
editor_markup_patterns = editor_markup_patterns,
et_al_patterns = et_al_patterns,
extended_registrants_t = extended_registrants_t,
id_handlers = id_handlers,
keywords_lists = keywords_lists,
keywords_xlate = keywords_xlate,
stripmarkers = stripmarkers,
invisible_chars = invisible_chars,
invisible_defs = invisible_defs,
indic_script = indic_script,
emoji_t = emoji_t,
maint_cats = maint_cats,
messages = messages,
presentation = presentation,
prop_cats = prop_cats,
script_lang_codes = script_lang_codes,
lang_tag_remap = lang_tag_remap,
lang_name_remap = lang_name_remap,
this_wiki_code = this_wiki_code,
title_types = title_types,
uncategorized_namespaces = uncategorized_namespaces_t,
uncategorized_subpages = uncategorized_subpages,
templates_using_volume = templates_using_volume,
templates_using_issue = templates_using_issue,
templates_not_using_page = templates_not_using_page,
vol_iss_pg_patterns = vol_iss_pg_patterns,
single_letter_2nd_lvl_domains_t = single_letter_2nd_lvl_domains_t,
inter_wiki_map = inter_wiki_map,
mw_languages_by_tag_t = mw_languages_by_tag_t,
mw_languages_by_name_t = mw_languages_by_name_t,
citation_class_map_t = citation_class_map_t,
citation_issue_t = citation_issue_t,
citation_no_volume_t = citation_no_volume_t,
}
p3bynq9eu3yo94vc7eo9rrsicbelvus
Aktor
0
45879
731549
697547
2026-11-06T23:09:22Z
InternetArchiveBot
25926
Add 1 book for [[en:Wikipedia:Verifiability|verifiability]] (20261106) #IABot (v2.0.9.7) ([[User:GreenC bot|GreenC bot]])
731549
wikitext
text/x-wiki
[[File:Atkinson Rowan crop.jpg|thumb|Aktor]]
'''Aktor''' ὑποκριτής (hypokrites), harti [[harfiah]]na nyaéta "jalma anu nafsirkeun";<ref name="hypocrite">''Hypokrites'' (related to our word for [[Hypocrisy|hypocrite]]) also means, less often, "to answer" the [[Tragedy|tragic]] [[Greek chorus|chorus]]. See Weimann (1978, 2); see also Csapo and Slater, who offer translations of classical source material using the term ''hypocrisis'' ([[acting]]) (1994, 257, 265–267).</ref> Disawang tina [[terminologi]], aktor ([[aktris]] sebutan keur awéwé) nyaéta jalma anu midangkeun salah sahiji [[karakter]] atawa sipat (perbawa) jalma séjén dina [[produksi]] [[dramatis]] atawa [[komik]]; sok dipidangkeun ogé dina [[film]], [[tipi]], [[téater]],[[radio]], [[iklan]] atawa ogé [[video]] musik.<ref>[http://www.oxforddictionaries.com/definition/english/actor?q=actor] {{Webarchive|url=https://web.archive.org/web/20150905172604/http://www.oxforddictionaries.com/definition/english/actor?q=actor |date=2015-09-05 }} Oxforddictionaries, Ditempo 1 Agustus 2015</ref><ref>[http://www.kbbi.web.id/aktor] kbbi.web.id, Ditempo 2 Agustus 2015</ref>
[[Akting]] (nyandiwara) nyaéta hiji tarékah saurang aktor ngalampahkeun sipat atawa parbawa salah saurang anu arék dipintonkeun ku jalan nginget-nginget [[émosi]] atawa [[réaksi]] jeung laku lampah dina kahirupana. [[Akting]] [[présentasi]] ngajujut hubungan aktor jeung anu lalajona, boh dina pamakéan bahasana, pamintonan, gerak gerik atawa tanda-tanda séjén anu mertélakeun yén karakter atawa aktor nyaho yén manéhna keur dilalajoan. Sajeroeun pintonan akting, "aktor usaha sabisa-bisa sangkan anu lalajo percaya kana perbawana; sanajan manéhna api-api" <ref>[[Method acting]] is an approach in which the actor identifies with the portrayed character by recalling emotions or reactions from his or her own life. [[Presentational acting]] refers to a relationship between actor and audience, whether by direct address or indirectly by specific use of language, looks, gestures or other [[Semiotics|signs]] indicating that the character or actor is aware of the audience's presence. In [[representational acting]]."</ref>
==Sajarah==
Nurutkeun catetan, aktor téh mimiti pisan dipidangkeun dina 534 SM (hésé nangtukeun waktu anu pancegna, lantaran robah-robahna kalénder salila mangpuluh puluh taun) nalika pamaén [[Yunani]] [[Thespis]] undak kana [[Teater]] [[Dionysus]], jadi anu pangheulana dipikawanoh ngadongéng atawa ngahibur ku jalan mintonkeun [[karakter]] dina caritana. Saméméhna [[Thespis]] ', ieu carita dipintonkeun ngaliwatan lalaguan, [[tari]]an, ogé ku jalan [[narasi]] atawa dicaritakeun ku pihak kalitu. Pikeun ngahormat jeung miéling [[Thespis]], aktor biasa disebut ogé [[Thespians]]. Nepi ka kiwari [[légénda téater]] percaya pisan yén [[Thespis]] cenah arwahna téh wangkelang (bangor); lamun mah aya kacilakaan di sajeroeun [[teater]] sakapeung mah sok nuding cénah éta kalakuan jurigna [[Thespis]]. Jaman baheula, aktor téh lain mangrupa [[kadudukan]] luhur, béda jeung mangsa kiwari, malah mah dina mimiti abad pertengahan rombongan aktor anu arindit-inditan téh disapirakeun pisan.<ref>{{Cite book | author=Wilmeth, Don B.; Bigsby, C.W.E. | authorlink= | coauthors= | title=The Cambridge history of American theatre | url=https://archive.org/details/cambridgehistory0003unse_d6n9 | year=1998 | publisher=Cambridge University Press | location=Cambridge, U.K. | isbn=978-0-521-65179-0 | pages=[https://archive.org/details/cambridgehistory0003unse_d6n9/page/449 449]–450}}</ref>
==Lingkungan Aktor==
Aktor digawé dina téater, film, [[tipi|televisi]] jeung radio, ogé widang séjénna. Tangtuna kudu mibanda sahiji kamonésan anu jadi andelan. Téhnik jeung digawé anu hadé dina hiji widang akting éta can tangtu bisa akting kalawan hadé dina widang anu séjénna.
# [[Teater]]
# [[Film]]
# [[Televisi]]
# [[Radio]]
==Tumbu Kaluar==
*http://www.oxforddictionaries.com/definition/english/actor?q=actor {{Webarchive|url=https://web.archive.org/web/20150905172604/http://www.oxforddictionaries.com/definition/english/actor?q=actor |date=2015-09-05 }} Definisi Actor
*[http://www.sag.org/ Screen Actors Guild (SAG)]: a union representing U. S. film and TV actors.
==Dicutat tina==
{{reflist}}
{{pondok}}
[[Kategori:Pameran]]
[[Kategori:Aktor| ]]
nvw1ogp4dp5sdlrejj3ui1enhmvh10x
Fungsi kaasaman Hammett
0
87922
731548
711711
2026-11-06T23:08:37Z
InternetArchiveBot
25926
Add 1 book for [[en:Wikipedia:Verifiability|verifiability]] (20261106) #IABot (v2.0.9.7) ([[User:GreenC bot|GreenC bot]])
731548
wikitext
text/x-wiki
{{Asam-Basa}}
'''Fungsi kaasaman Hammett''' (H<sub>0</sub>) nyaéta panghitungan kaasaman nu digunakeun dina leyuran [[asam]] kuat nu konséntrasina ageng pisan, contona [[superasam]]. Fungsi ieu diusulkeun ku ahli kimia organik fisik, [[Louis Plack Hammett]],<ref>{{Cite journal |first1=L. P. |last1=Hammett |first2=A. J. |last2=Deyrup |date=1 Juni 1932 |title=A Series of Simple Basic Indicators. I. The Acidity Functions of Mixtures of Sulfuric and Perchloric Acids with Water |journal=J. Am. Chem. Soc |volume=54 |number=7 |pages=2721-2739}}</ref><ref>{{Cite book |first=L. P. |last=Hammett |date=Juli 1940 |title=Physical Organic Chemistry |url=https://archive.org/details/physicalorganicc0000loui |publisher=McGraw-Hill |location=New York}}</ref> kasohor ku [[fungsi kaasaman]]na nu digunakeun sangkan ngaluaskeun panghitungan kaasaman [[Téori asam–basa Brønsted–Lowry|Brønsted–Lowry]] di luar [[leyuran palarut cai]] éncér, nu mana skala [[pH]] penting dipaké.
Dina leyuran nu konséntrasina ageng pisan, pangdeukeutan nu basajan saperti [[persamaan Henderson–Hasselbalch]] teu valid deui ku sabab ayana variasi dina [[koéfisién aktivitas]]. Fungsi kaasaman Hammett digunakeun dina widang saperti [[kimia organik fisik]] pikeun panalungtikan réaksi [[katalisis asam|asam nu dikatalisis]] ku sabab sababaraha réaksi ieu ngagunakeun asam nu konséntrasina ageng pisan, malahan nepi ka murni.<ref name=poc>{{Cite book |first1=Gerrylynn K. |last1=Roberts |first2=Colin Archibald |last2=Russell |title=Chemical History: Reviews of the Recent Literature |url=https://archive.org/details/chemicalhistoryr0000unse |publisher=Royal Society of Chemistry |year=2005 |isbn=0-85404-464-7}}</ref>
== Rujukan ==
{{Reflist}}
{{taratas}}
[[Kategori:Kimia asam–basa]]
[[Kategori:Kimia organik fisik]]
[[Kategori:Superasam|*]]
ghxnzo31c8mmfayyaiyrqbim0jdif22
Kord
0
102714
731547
708592
2026-11-06T23:06:12Z
InternetArchiveBot
25926
Add 1 book for [[en:Wikipedia:Verifiability|verifiability]] (20261106) #IABot (v2.0.9.7) ([[User:GreenC bot|GreenC bot]])
731547
wikitext
text/x-wiki
[[File:Frets,_guitar_neck,_C-major chord.jpg|jmpl|ka|200px|Kord C Mayor (C) dina gitar (maké tuning standar)]]
'''Kord''' nyaéta kumpulan tilu [[nada]] atawa leuwih anu lamun dipaénkeun babarengan bakal kadéngé harmonis.<ref>Benward & Saker (2003). ''Music: In Theory and Practice, Vol. I'', p. 67&359. Seventh Edition. ISBN 978-0-07-294262-0.</ref><ref>{{cite book |title=Introducing Music |url=https://archive.org/details/introducingmusic00karo |last=Károlyi |first=Otto |year=1965 |publisher=Penguin Books |page=[https://archive.org/details/introducingmusic00karo/page/63 63]}}</ref> Kord bisa dipaénkeun ku cara pegat-pegatan atawa bisa ogé dibarengkeun. Kord téh dipaké pikeun ngaramékeun hiji [[lagu]]. Conto alat musik séjén anu bisa maénkeun kord nyaéta [[gitar]] (akustik jeung listrik), [[Organ (alat musik)|orjen]], ''[[electone|electona]]''. Kord anu ngaranna béda tapi lamun dipaénkeun sorana sarua disebut '''Kord Enharmonis'''. Contona: kord Cb (Ces mayor) jeung B (B mayor).
== Sajarah ==
Musik geus wanoh kana notasi ti saprak taun 590 anu ditimukeun ku Paus Agung Grégori. Notasi ieu disebut [[Notasi Grégorian]]. Namung karék disampurnakeun kira-kira taun 950 dibuktikeun ku ayana [[Notasi Balok]].
Mimitina mah nalika taun 1600-1750 (Mangsa [[Jaman Barok|Barok]]) ilaharna jelema mikawanoh kana [[musik polifoni]] jeung élmu [[kontrapung]], mangrupa mélodi séjén anu pasalingsingan. Karék, pas taun 1725 jelema wanoh kana kord dina [[musik homofoni]], Remeau nulis buku ''Traite d'Harmonie'' taun ''1722''. Mimitina kord disusun ku tilu nada, laju dibalikkeun, mangrupa mayor, minor, aughmented, diminished, jeung tuluy ditambah nada séptim, sixth, jsb.. Mulai taun 1750 dina Mangsa [[Zaman Klasik (Musik)|Klasik]] kord bisa mekar nepi ka ayeuna dina mangsa [[blues]] (1870), [[jazz]] (1920), [[pop]] jeung [[rock]] (1955), jsb. anu wanoh kana kord ninth, eleventh, thirdteenth, jsb.
== Bacaan nu patali ==
* Grout, Donald Jay (1960). ''A History Of Western Music''. Norton Publishing.
* Dahlhaus, Carl. Gjerdingen, Robert O. trans. (1990). ''Studies in the Origin of Harmonic Tonality'', p. 67. Princeton University Press. ISBN 0-691-09135-8.
* Goldman (1965). Cited in Nattiez (1990).
* Jones, George T. (1994). HarperCollins ''College Outline Music Theory''. ISBN 0-06-467168-2.
* Nattiez, Jean-Jacques (1990). ''Music and Discourse: Toward a Semiology of Music'' (''Musicologie générale et sémiologue'', 1987). Translated by Carolyn Abbate (1990). ISBN 0-691-02714-5.
* Norman Monath, Norman (1984). ''How To Play Popular Piano In 10 Easy Lessons''. Fireside Books. ISBN 0-671-53067-4.
* Stanley Sadie and John Tyrrell, eds. (2001). ''The New Grove Dictionary of Music and Musicians''. ISBN 1-56159-239-0.
* Surmani, Andrew (2004). ''Essentials of Music Theory: A Complete Self-Study Course for All Musicians''. ISBN 0-7390-3635-1.
* Benward, Bruce & Saker, Marilyn (2002). ''Music in Theory and Practice, Volumes I & II'' (7th ed.). New York: McGraw Hill. ISBN 0-07-294262-2.
* {{Cite journal | year= 2015 | last= Mailman| first= Joshua B. | title= Schoenberg's Chordal Experimentalism Revealed Through Representational Hierarchy Association (RHA), Contour Motives, and Binary State Switching | url= http://mts.oxfordjournals.org/cgi/reprint/mtv015.pdf?ijkey=y8meYfMe9MXF1xE&keytype=ref | journal= Music Theory Spectrum | volume= 37 | issue= 2 | pages= 224–252.}}
* {{Cite book|last=Persichetti |first=Vincent |authorlink=Vincent Persichetti |title=Twentieth-century Harmony: Creative Aspects and Practice |url=https://archive.org/details/isbn_9780393095395 |year=1961 |publisher=W. W. Norton |location=New York |isbn=0-393-09539-8 |oclc=398434}}
* {{Cite book | title = Music Fundamentals | first = Rod | last = Schejtman | publisher = The Piano Encyclopedia | year = 2008 | isbn = 978-987-25216-2-2 | url = http://www.pianoencyclopedia.com }} {{Webarchive|url=https://web.archive.org/web/20180831041144/http://pianoencyclopedia.com/ |date=2018-08-31 }}
== Rujukan ==
{{reflist}}
== Tutumbu kaluar ==
* {{Commons category-inline|Chords|Akor}}
{{Authority control}}
[[Kategori:Istilah musik]]
i7arrq3z7bpl8t0qmf5vk9hdgelyiww
MTV
0
105041
731546
665577
2026-11-06T23:05:44Z
InternetArchiveBot
25926
Add 1 book for [[en:Wikipedia:Verifiability|verifiability]] (20261106) #IABot (v2.0.9.7) ([[User:GreenC bot|GreenC bot]])
731546
wikitext
text/x-wiki
[[File:MTV 2021 (brand version).svg|thumbnail]]
'''MTV''' (singketan tina nami lengkepna '''Music Television''' dugi ka 2010 sareng deui saprak 2016) mangrupikeun saluran televisi kabel [[Amérika Sarikat]]. Ieu sacara resmi diluncurkeun dina 1 Agustus 1981. Dumasar di [[Kota New York]], éta boga fungsi minangka andalannya milik MTV Entertainment Group, bagian tina [[Paramount Media Networks]], divisi tina [[Paramount Global]].
Saluran ieu mimitina disiarkeun video musik sareng program anu aya hubunganana anu dipandu ku kapribadian televisi anu katelah joki vidéo, atanapi VJ.<ref>{{Cite news|url=http://www.cnn.com/SHOWBIZ/Music/9807/31/encore.mtv/index.html|title=CNN – MTV changed the music industry on August 1, 1981 – July 31, 1998|access-date=May 13, 2010|archive-date=May 8, 2019|archive-url=https://web.archive.org/web/20190508173419/http://www.cnn.com/SHOWBIZ/Music/9807/31/encore.mtv/index.html|url-status=live}}</ref> MTV mangrupikeun salah sahiji saluran kabel Amérika anu sayogi di nagara-nagara sanés anu janten kultus hit di sakumna dunya sareng mangrupikeun salah sahiji faktor naékna program kabel pikeun Kinérja sareng korporasi Amérika ngadominasi ékonomi televisi dina taun 1990-an.<ref name=":02">{{Cite book |last1=Jennings |first1=Peter |title=The Century |url=https://archive.org/details/centuryjenn00jenn |last2=Brewster |first2=Todd |date=November 1998 |publisher=[[Doubleday (publisher)|Doubleday]] |isbn=0-385-48327-9 |edition=1st |location=New York |pages=551 |author-link=Peter Jennings |author-link2=Todd Brewster}}</ref> Dina taun-taun ti saprak mimitina, éta sacara signifikan ngirangan fokus kana musik pikeun milih program realitas asli pikeun rumaja sareng déwasa ngora.
== Rujukan ==
{{reflist}}
== Tumbu éksternal ==
* {{Official website|https://www.mtv.com/}}
[[Kategori:Stasion televisi di Amérika Sarikat]]
oz80m3fstlywpdotuiva2rfqwopdb09
Digital object identifier
0
105895
731544
704859
2026-11-06T22:58:41Z
InternetArchiveBot
25926
Add 1 book for [[en:Wikipedia:Verifiability|verifiability]] (20261106) #IABot (v2.0.9.7) ([[User:GreenC bot|GreenC bot]])
731544
wikitext
text/x-wiki
{{Infobox identifier
|image = DOI logo.svg
| image_size = 130px
| image_caption =
| image_alt =
| image_border =
| full_name =
| acronym = DOI
| number =
| start_date = {{Start date|2000}}
| organisation = International DOI Foundation
| digits =
| check_digit =
| example = [https://doi.org/10.1000/182 10.1000/182]
| website = {{Official URL}}[[ttps://www.doi.org/]]
}}
'''''Digital object identifier''''' ('''DOI''') nyaéta hiji organisasi ''nirlaba'' anu museurkeun kagiatanna dina ''[[patén identifier]]'' atawa ''[[hand]]'' dipaké pikeun ngaidéntipikasi sacara ''unik'' rupa-rupa objék, anu dibakukeun ku [[ Organisasi Internasional pikeun Standardisasi]] (ISO).<ref name="iso">{{cite web | url = https://www.iso.org/obp/ui/#iso:std:iso:26324:ed-1:v1:en | title = ISO 26324:2012(en), Information and documentation – Digital object identifier system | publisher = [[ISO]] | access-date = 20 April 2016 | archive-date = 17 June 2016 | archive-url = https://web.archive.org/web/20160617031837/https://www.iso.org/obp/ui/#iso:std:iso:26324:ed-1:v1:en | url-status = live }}</ref>
DOI ''otoritasna'' dina (ISO 26324), mangrupa prak-prakanna tina ''[[Handle System]]''; ''hand'' singketan tina ''Human & Digital''. <ref>{{cite web |title=The Handle System |url=http://handle.net/ |url-status=live |archive-url=https://web.archive.org/web/20230107070646/http://www.handle.net/ |archive-date=Jan 7, 2023 |website=Handle.Net Registry}}</ref><ref>{{cite web |title=Factsheets |url=http://www.doi.org/factsheets.html |url-status=live |archive-url=https://web.archive.org/web/20221225082146/http://www.doi.org/factsheets.html |archive-date=Dec 25, 2022 |website=DOI}}</ref> aranjeunna ogé loyog dina sistem URI (''[[Uniform Resource Identifier]])''. Éta anu loba dipaké pikeun ngaidéntipikasi widang [[akademik]], [[profésional]], jeung inpormasi [[pamaréntah]], sapertos [[Jurnal Akademik]], [[artikel]], laporan panalungtikan, kumpulan data, jeung ''publikasi'' resmi.
DOI boga tujuan pikeun ngabéréskeun tujuanana, tangtuna DOI nujul kana obyék inpormasi. Ieu bisa kahontal kujalan ngumpulkeun DOI ngeunaan obyék kana ''[[metadata]]'', sapertos [[URL]] di mana aya objékna. Ku kituna, bisa dilakukeun tarékah satuluyna sarta ''[[Interoperability]]'' tayalian bisa silih tukeur data kalayan henteu aya halangan nanaon. DOI béda jeung [[ISBN]] atawa [[ISRC]] anu mangrupa idéntifikasi wungkul. Sistem DOI ngagunakeun ''[[indecs Content Model]]'' pikeun ngagambarkeun métadata.
DOI pikeun dokumén (tetep/''remains'') dina harti tetep salami umur dokumén éta, ari lokasina jeung métadata sanésna bisa robih. Ngarujuk kana dokumén ''online'' ku DOI na kedah nyayogikeun ''tautan'' anu langkung ''stabil'' ti batan langsung nganggo URL na. Tapi lamun URL na robah, ''penerbit'' kudu ngamutahirkeun métadatana sangkan DOI angger ngajaga link ka URL.<ref>{{Cite book |last=Witten |first=Ian H. |title=How to Build a Digital Library |url=https://archive.org/details/isbn_2900123748576 |last2=Bainbridge |first2=David |last3=Nichols |first3=David M. |date=2010 |publisher=Morgan Kaufmann |isbn=978-0-12-374857-7 |edition=2nd |pages=[https://archive.org/details/isbn_2900123748576/page/352 352]–253 |language=en |name-list-style=amp}}</ref><ref>{{Cite journal |last=Langston |first=Marc |last2=Tyler |first2=James |date=2004 |title=Linking to Journal Articles in an Online Teaching Environment: The Persistent Link, DOI, and OpenURL |journal=The Internet and Higher Education |language=en |volume=7 |issue=1 |pages=51–58 |doi=10.1016/j.iheduc.2003.11.004}}</ref><ref>{{Cite web |date=2001-07-23 |title=How the "Digital Object Identifier" Works |url=http://www.businessweek.com/magazine/content/01_30/b3742032.htm |archive-url=https://web.archive.org/web/20101002070758/http://www.businessweek.com/magazine/content/01_30/b3742032.htm |archive-date=2010-10-02 |access-date=2010-04-20 |website=BusinessWeek |language=en |quote=Assuming the publishers do their job of maintaining the databases, these centralized references, unlike current web links, should never become outdated or broken}}</ref> Éta tanggung jawab anu medalkeun pikeun ngapdét ''database'' DOI. Upami aranjeunna gagal ngalakukeunana, DOI pikeun megatkeun hiji sambungan ''[[dead link]]'', ninggalkeun DOI gunana.<ref>{{cite journal |last1=Liu |first1=Jia |title=Digital Object Identifier (DOI) Under the Context of Research Data Librarianship |journal=Journal of eScience Librarianship |date=2021 |volume=10 |issue=2 |pages=Article e1180 |doi=10.7191/jeslib.2021.1180|doi-access=free}}</ref>
''Pengembang'' jeung ''administrator'' sistem DOI nyaéta Yayasan DOI [[Internasional]] (IDF), nu diwanohkeun dina taun 2000.<ref>{{citation|last= Paskin|first= Norman|chapter= Digital Object Identifier (DOI) System|title= Encyclopedia of Library and Information Sciences|date= 2010|publisher= Taylor and Francis|pages= 1586–1592|edition= 3rd}}</ref> Organisasi anu nyumponan kawajiban kontrak tina sistem DOI jeung daék mayar kanggo jadi anggota sistem bisa napelkeun DOI.<ref name="dd">{{Cite journal |last=Davidson |first=Lloyd A. |last2=Douglas |first2=Kimberly |date=December 1998 |title=Digital Object Identifiers: Promise and problems for scholarly publishing |journal=Journal of Electronic Publishing |volume=4 |issue=2 |doi=10.3998/3336451.0004.203 |doi-access=free}}</ref> Sistem DOI dilaksanakeun ku jalan féderasi agénsi ''pendaptaran'' anu dikoordinir ku IDF.<ref>{{cite web |date=28 June 2010 |title=Welcome to the DOI System |url=https://doi.org/ |url-status=live |archive-url=https://web.archive.org/web/20100813030300/https://doi.org/ |archive-date=Aug 13, 2010 |access-date=7 August 2010 |publisher=Doi.org}}</ref> Nepi ka ahir [[April]] 2011 leuwih ti 50 juta ngaran DOI geus ditugaskeun di kira-kira 4,000 [[organisasi]],<ref>{{cite web |date=20 April 2011 |title=DOI News, April 2011: 1. DOI System exceeds 50 million assigned identifiers |url=https://doi.org/news/DOINewsApr11.html#1 |url-status=live |archive-url=https://web.archive.org/web/20110727193513/https://www.doi.org/news/DOINewsApr11.html |archive-date=27 July 2011 |access-date=3 July 2011 |website=Doi.org}}</ref> sarta dina [[April]] [[2013]] jumlah ieu geus tumuwuh nepi ka 85 juta ngaran DOI ditugaskeun leuwih ti 9.500 organisasi.
=={{anchor|Prefix|Suffix|DOI registrant}} ''Nomenklatur'' jeung ''sintaksis''==
DOI mangrupa jinis ''Handle System handle'', nu wangunna mangrupa ''[[string karakter]]'' dibagi jadi dua bagian, ''prefiks'' jeung ''sufiks'', dipisahkeun ku gurat déngdék.
: <code>prefix/suffix</code>
Mimitina ''prefix'' ngaidéntifikasi anu ngadaptar lajeng ''sufiks'' ngaidéntifikasi kana objék husus anu aya hubunganana jeung DOI. Sabagéan ageung karakter ''[[Unicode]]'' dina ieu tutumbu légal, anu diinterprétasi ku cara ''[[case-insensitive]]'' nyaéta teu aya bédana antara hurup gedé jeung hurup leutik. ''Prefiks'' biasana kawas <code>10.NNNN</code>, di mana <code>NNNN</code>nyaéta angka nu leuwih gedé atawa sarua jeung <code>1000</code>, anu watesna ngan ukur gumantung kana jumlah anu ngadaptar.<ref name="CrossRefDOI">{{cite web |url=http://www.crossref.org/01company/15doi_info.html |archive-url=https://web.archive.org/web/20021021021703/http://www.crossref.org/01company/15doi_info.html |archive-date=2002-10-21 |access-date=10 June 2016 |title=doi info & guidelines |website=CrossRef.org |publisher=Publishers International Linking Association, Inc. |date=2013 |quote=All DOI prefixes begin with "10" to distinguish the DOI from other implementations of the Handle System followed by a four-digit number or string (the prefix can be longer if necessary). }}</ref><ref name="DOIKeyFacts">{{cite web |url=https://doi.org/factsheets/DOIKeyFacts.html |access-date=10 June 2016 |title=Factsheet—Key Facts on Digital Object Identifier System |publisher=International DOI Foundation |date=6 June 2016 |quote=Over 18,000 DOI name prefixes within the DOI System |archive-date=5 June 2016 |archive-url=https://web.archive.org/web/20160605202057/http://www.doi.org/factsheets/DOIKeyFacts.html |url-status=live }}</ref> Mimitina bisa dibagi deui jeung titik, sapertos <code>10.NNNN.N</code>.<ref name="2.2.2">{{cite web |url=https://doi.org/doi_handbook/2_Numbering.html#2.2.2 |access-date=10 June 2016 |title=DOI Handbook—2 Numbering |publisher=International DOI Foundation |date=1 February 2016 |quote=The registrant code may be further divided into sub-elements for administrative convenience if desired. Each sub-element of the registrant code shall be preceded by a full stop. |archive-date=30 June 2014 |archive-url=https://web.archive.org/web/20140630181440/http://www.doi.org/doi_handbook/2_Numbering.html#2.2.2 |url-status=deviated }}</ref>
Contona, dina ngaran DOI<code>10.1000/182</code>,mimitina nyaéta <code>10.1000</code> jeung sufiksna nyaéta<code>182</code>. Bagian "10" anu mimitina ngabédakeun cecekelan salaku bagian tina ''namespace'' DOI, sabalikna sababaraha ngaran ''spasi'' Sistim cecekelan séjén,{{efn-ua|Other registries are identified by other strings at the start of the prefix.}} jeung dina karakter <code>1000</code> anu mimiti ngaidéntipikasi anu ngadaptar; dina hal ieu nu ngadaptar nyaéta ''International DOI Foundation'' sorangan. <code>182</code> mangrupa panungtung, atawa jinis ID, ngaidéntipikasi hiji objék (dina hal ieu, vérsi panganyarna tina ''DOI Handbook'').
Ngaran DOI bisa nangtukeun hasil anu kréatip (sapertos [[téks]], [[gambar]], [[audio]] atawa [[vidéo]], jeung ''parangkat lunak'') boh dina wujud [[éléktronik]] atawa pisik, [[pagelaran]], jeung karya [[abstrak]] sapertos lisénsi, ''transaksi'' husus jeung pihak sanés, jeung saja bana.<ref name="doifaq2">{{Cite web|date=3 July 2018|title=Frequently asked questions about the DOI system: 6. What can a DOI name be assigned to?|url=https://www.doi.org/the-identifier/resources/faqs#8-what-can-a-doi-name-be-assigned-to|website=DOI Foundation|access-date=19 July 2018|archive-date=16 February 2023|archive-url=https://web.archive.org/web/20230216162638/https://www.doi.org/the-identifier/resources/faqs#8-what-can-a-doi-name-be-assigned-to|url-status=live}}</ref>
Ngaranna bisa ngarujuk kana objék dina kadetilan anu béda-béda: kukituna ngaran DOI bisa nangtukeun hiji jurnal, hiji masalah individu jurnal, artikel individu dina jurnal, atawa tabél tunggal dina éta artikel. Pilihan ngeunaan kajéntréan dipasrahkeun ka nu tugasna, tapi dina sistem DOI kudu diébréhkeun salaku bagian tina métadata nu pakait jeung ngaran DOI, ngagunakeun hiji [[kamus data]] dumasar kana modélna/''[[indecs Content Model]]''.
===Pintonan===
"Buku Pituduh DOI" resmi sacara jéntré nyebutkeun yén DOI kedah dipintonkeun dina layar jeung dicitak dina ''format''.<code>doi:10.1000/182</code>.<ref name="C4WDefault-2811140">{{cite web |url=https://doi.org/doi_handbook/2_Numbering.html#2.6.1 |title=DOI Handbook – Numbering |date=13 February 2014 |access-date=30 June 2014 |work=doi.org |author1=<!--Staff writer(s); no by-line.--> |archive-url=https://web.archive.org/web/20140630181440/http://www.doi.org/doi_handbook/2_Numbering.html |archive-date=30 June 2014 |url-status=live |at=Section 2.6.1 Screen and print presentation}}</ref>
Sabalikna tina ''Buku Panduan DOI'', ''[[CrossRef]]'', agénsi ''pendaptaran'' DOI utama, nyarankeun mintonkeun URL (conto, <code><nowiki>https://doi.org/10.1000/182</nowiki></code>) tinimbang format resmi dieusian (conto, <code>[https://doi.org/10.1000/182 doi:10.1000/182]</code>)<ref>{{cite web| title=DOI Display Guidelines| url=http://www.crossref.org/02publishers/doi_display_guidelines.html| access-date=19 October 2016| archive-date=24 November 2016| archive-url=https://web.archive.org/web/20161124110844/http://www.crossref.org/02publishers/doi_display_guidelines.html| url-status=live}}</ref><ref>{{cite web| title=New Crossref DOI display guidelines are on the way| url=http://blog.crossref.org/2016/09/new-crossref-doi-display-guidelines.html| access-date=19 October 2016| archive-date=19 October 2016| archive-url=https://web.archive.org/web/20161019215501/http://blog.crossref.org/2016/09/new-crossref-doi-display-guidelines.html| url-status=live}} {{Webarchive|url=https://web.archive.org/web/20161019215501/http://blog.crossref.org/2016/09/new-crossref-doi-display-guidelines.html |date=19 October 2016 }}</ref> URL ieu pengkuh (aya kontrak nu puguh jeung daria/bener-bener dina domain DOI.ORG),jadi éta hiji [[PURL]]—nyadiakeun hiji lokasi [[Proxy server|HTTP proxy]] sérver nu bakal ngalihkeun aksés wéb ka lokasi ''online'' anu bener tina ''item'' anu numbu.<ref name="dd"/><ref>{{Cite journal|first=Andy|last=Powell|title=Resolving DOI Based URNs Using Squid: An Experimental System at UKOLN|journal=D-Lib Magazine|date=June 1998|url=http://www.dlib.org/dlib/june98/06powell.html|issn=1082-9873|doi=10.1045/june98-powell|doi-access=free|access-date=23 April 2010|archive-date=13 June 2010|archive-url=https://web.archive.org/web/20100613000342/http://dlib.org/dlib/june98/06powell.html|url-status=live}}</ref>
Rékoméndasi ''CrossRef'' utamana dumasar kana anggapan yén DOI keur dipintonkeun henteu nganggo ''hyperlinked'' ka URL nu luyu.—Argumenna nyaéta yén teu nganggo ''hyperlink'' éta henteu gampang pikeun nyalin-jeung-témpél URL lengkep pikeun bener-bener nyangking halaman keur DOI, sahingga sakabéh URL kudu ditembongkeun, sahingga jalma nempo kaca nu ngandung DOI pikeun nyalin-témpél URL, kana jandela anyar / tab di maranéhna [[Web browser|browser]] pikeun muka halaman anu cocog pikeun dokumén anu diwakilan ku DOI.<ref>{{cite web|url=https://www.crossref.org/news/2011-08-02-crossref-revises-doi-display-guidelines/|title=Crossref Revises DOI Display Guidelines - Crossref|last=ChrissieCW|website=crossref.org|access-date=25 April 2018|archive-date=25 April 2018|archive-url=https://web.archive.org/web/20180425114838/https://www.crossref.org/news/2011-08-02-crossref-revises-doi-display-guidelines/|url-status=live}}</ref>
Ku sabab DOI mangrupa rohangan ngaran dina ''[[System Handle]]'', sacara ''semantik'' bener pikeun ngagambarkeunana salaku URI.<code>info:doi/10.1000/182</code>.
==Kontén==
Eusi utama sistem DOI ayeuna ngawengku:
* [[Bahan-bahan ilmiah]] (artikel [[jurnal]], [[buku]]-buku, [[ebuku]], jeung sajabana.) ku jalan ''[[Crossref]]'', hiji ''konsorsium'' anu mibanda kurang leuwih 3,000 pedalan; ''[[Airiti]]'', Salaku pamingpin panyadia jurnal akademik éléktronik [[Cina]] jeung ''[[Taiwan]]''; jeung hiji [[Japan Link Center]] (JaLC)<ref>{{Cite web |title=Japan Link Center(JaLC) |url=https://japanlinkcenter.org/top/english.html |access-date=2022-08-06 |website=japanlinkcenter.org |language=en |archive-date=29 September 2020 |archive-url=https://web.archive.org/web/20200929085435/https://japanlinkcenter.org/top/english.html |url-status=live }}</ref> hiji organisasi anu nyayogikeun manajemén ''tautan'' jeung tugas DOI pikeun jurnal akademik éléktronik dina basa [[Jepang]].
* Datasét panalungtikan ku jalan cutatan/[[DataCite]], ''konsorsium'' pabukon anu ngahususkeun kana panalungtikan, panyadia inpormasi téknis, jeung puseur data ilmiah.
* ''[[Uni Éropa]]'' publikasi resmi ku jalan ''[[EU publications office]]'';
* Hiji [[CNKI]] |proyék Infrastruktur Pangaweruh Nasional Cina di [[Tsinghua University]] jeung [[Institute of Scientific and Technical Information of China]] (ISTIC), dua ''inisiatif'' anu dialpukahan ku pamaréntah Cina.
* ''Identifier global'' permanén pikeun judul eusi audio/visual komérsial sarta non-komérsial, pangropéa, jeung ''manifestasi'' ''pendaptaran'' ''Entertainment ID Registry'' , ilahar disebut [[EIDR]].
Dina ''[[Organisation for Economic Co-operation and Development]]'' ''publication service'' [[OECD iLibrary]], unggal tabél atawa ''grafik'' dina hiji OECD ''publikasi'' dipintonkeun ku hiji nami DOI anu nuju ka ''file Excel'' data dina dasar tabél jeung ''grafik''. Pangwangunan salajengna jasa sapertos direncanakeun.<ref>{{cite journal|doi=10.1787/603233448430 |title=We Need Publishing Standards for Datasets and Data Tables |date=2009|journal=Research Information |last=Green|first=T.|doi-access=free}}</ref>
''registries'' séjén kaasup [[Crossref]] jeung Badan Pendaptaran DOI Éropa multibasa (mEDRA)''.<ref>{{cite web |url=https://www.medra.org/ |title=multilingual European DOI Registration Agency |publisher=mEDRA.org |year=2003 |access-date=2 February 2018 |archive-date=1 February 2018 |archive-url=https://web.archive.org/web/20180201084709/https://www.medra.org/ |url-status=live }}</ref> Since 2015, [[Request for Comments|RFCs]] disarankeun salaku <code>doi:10.17487/rfc''...''</code>.<ref>{{cite web |url=https://tools.ietf.org/html/rfc7669#section-3 |title=Assigning Digital Object Identifiers to RFCs § DOIs for RFCs |rfc=7669 |first=John R. |last=Levine |author-link=John R. Levine |publisher=[[Internet Architecture Board|IAB]] |year=2015 |doi=10.17487/rfc7669 |access-date=2 February 2018 |archive-date=8 July 2017 |archive-url=https://web.archive.org/web/20170708114244/https://tools.ietf.org/html//rfc7669#section-3 |url-status=live }}</ref>
==''Fitur'' sarta kauntungan ==
IDF ngarancang sistem DOI pikeun nyadiakeun wangun ''[[persistent identification]]'', di mana unggal ngaran DOI sacara permanén jeung jelas ngidéntipikasi objék anu aya hubunganana (sanajan nalika anu medalkeun jurnal robih, sakapeung sagala DOI bakal dirobih, kalawan DOI heubeul euweuh jalan). Éta ogé ngahubungkeun [[metadata]] jeung objék, éta ngamungkin nyadiakeun pamaké kalawan potongan inpormasi anu aya hubungan ngeunaan objék jeung hubungan maranéhanana. Kaasup salaku bagian tina métadata ieu nyaéta tindakan ''jaringan'' anu ngamungkinkeun ngaran DOI diréngsékeun ka lokasi wéb di mana objék anu dijelaskeun bisa dipendakan. Pikeun ngahontal cita-citana, sistem DOI ngagabungkeun ''[[Handle System]]'' jeung ''[[indecs Content Modél]]'' jeung infrastruktur sosial.
Sistem ''Handle'' mastikeun yén nami DOI pikeun hiji obyék henteu dumasar kana ''atribut'' anu bisa dirobih tina objék sapertos [[lokasi]] pisik atawa kapamilikanana, yén ''atribut'' obyék disandikeun dina métadatana tinimbang dina nami DOI na, yén euweuh dua objék anu ditugaskeun DOI namina sami. Ku sabab ngaran DOI mangrupa ''string'' ''karakter'' pondok, aranjeunna bisa dibaca ku manusa, bisa ''disalin'' jeung ditempelkeun jadi téks, tur pas kana spésifikasi [[Uniform Resource Identifier|URI]]. Mékanisme résolusi ngaran DOI tindakan di ''balik layar'', ku kituna pamaké komunikasi jeung éta dina cara nu sarua sakumaha kalayan sagala jasa wéb lianna; éta diwangun dina [[arsitéktur kabuka]], ngawengku [[mékanisme kapercayaan]], jeung direkayasa pikeun dijalankeun sacara andal jeung fléksibel supados bisa diadaptasi pikeun ngarobih tungtutan jeung aplikasi anyar tina sistem DOI.<ref>{{cite web|url= https://arstechnica.com/science/2010/03/dois-and-their-discontents-1/|title= DOIs and their discontents|last= Timmer|first= John|date= 6 March 2010|work= [[Ars Technica]]|access-date= 5 March 2013|archive-date= 8 March 2013|archive-url= https://web.archive.org/web/20130308140654/http://arstechnica.com/science/2010/03/dois-and-their-discontents-1/|url-status= live}}</ref> Resolusi ngaran DOI bisa dipaké jeung ''[[OpenURL]]'' pikeun milih anu paling pas diantara sababaraha lokasi pikeun objék anu dipasihkeun, nurutkeun lokasi pamaké nyieun pamundut nu.<ref>{{Cite journal |last1=DeRisi |first1=Susanne |last2=Kennison |first2=Rebecca |last3=Twyman |first3=Nick |name-list-style=and |date=2003 |title=Editorial: The what and whys of DOIs |journal=[[PLoS Biology]] |volume=1 |issue=2 |page=e57 |doi=10.1371/journal.pbio.0000057 |doi-access=free|pmc=261894 |pmid=14624257}} {{open access}}</ref> Sanajan kitu, sanajan pangabisa ieu, sistem DOI geus narik kritik ti pustakawan pikeun ngarahkeun pamaké kana salinan non-gratis dokumén, nu bakal geus sadia sarta euweuh ''fee'' tambahan ti lokasi ''alternatif''.<ref>{{Cite book|contribution= Open access to scientific and technical information: the state of the art|first= Jack|last= Franklin|title= Open access to scientific and technical information: state of the art and future trends|editor1-first= Herbert|editor1-last= Grüttemeier|editor2-first= Barry|editor2-last= Mahon|publisher= IOS Press|date= 2003|page= 74|url= https://books.google.com/books?id=2X3gW1lUvN4C&pg=PA74|isbn= 978-1-58603-377-4|access-date= 7 August 2022|archive-date= 7 August 2022|archive-url= https://web.archive.org/web/20220807090158/https://books.google.com/books?id=2X3gW1lUvN4C&pg=PA74|url-status= live}}</ref>
''[[Model Konten indecs]]'' sakumaha anu dipaké dina ''sistem'' DOI ngahubungkeun métadata jeung objék. ''Kernel'' leutik métadata umum dibagi ku sakabéh ngaran DOI jeung bisa ''diperpanjang'' sacara ''opsional'' jeung data anu rélevan, nu bisa jadi umum atawa diwatesan. Anu ngadaptar bisa ngapdet métadata kanggo nami DOI na iraha waé, sapertos nalika inpormasi publikasi robih atawa nalika obyék pindah ka URL anu béda.
Yayasan DOI Internasional (IDF) ngawaskeun integrasi téknologi ieu jeung operasi sistem ku jalan infrastruktur téknis jeung sosial.Infrastruktur sosial federasi agénsi pendaptaran mandiri anu nawiskeun jasa DOI dimodelkeun kana ngarekahanna ''federasi'' anu suksés tina ''identifier'' sapertos [[GS1]] jeung [[ISBN]].
==Babandingan jeung skéma identifier séjén==
Hiji ngaran DOI béda ti ''pointer'' [[Internét]] ilahar dipaké pikeun bahan, sapertos ''[[Uniform Resource Locator]]'' (URL), dina éta ngaidentipikasi hiji obyék sorangan salaku [[Kelas kahiji (komputasi)|éntitas kelas kahiji]], tinimbang tempat husus di mana objék lokasina dina waktu nu tangtu. Éta ngalaksanakeun konsép ''[[Uniform Resource Identifier]] ([[Uniform Resource Name]])'' jeung nambahan modél data jeung ''infrastruktur'' sosial..<ref>{{cite web |url=https://doi.org/factsheets/DOIIdentifierSpecs.html |title=DOI System and Internet Identifier Specifications |publisher=Doi.org |date=18 May 2010 |access-date=7 August 2010 |archive-date=26 June 2010 |archive-url=https://web.archive.org/web/20100626072635/http://www.doi.org/factsheets/DOIIdentifierSpecs.html |url-status=live }}</ref>
Ngaran DOI ogé béda ti pendaptaran idéntifikasi standar sapertos [[ISBN]], [[Kode Rékam Standar Internasional|ISRC]], jeung saja bana. Tujuan tina ngadaptarkeun ''identifier'' nyaéta pikeun ngatur kumpulan identifier anu dipasihkeun, padahal tujuan utama sistem DOI nyaéta pikeun ngajantenkeun kumpulan identifiers bisa dilampahkeun jeung bisa dioperasikeun, di mana kempelan éta bisa ngawengku identifiers ti loba kumpulan séjén dikawasa.<ref>{{cite web |url=https://doi.org/factsheets/DOIIdentifiers.html |title=DOI System and standard identifier registries |publisher=Doi.org |access-date=7 August 2010 |archive-date=26 June 2010 |archive-url=https://web.archive.org/web/20100626071714/http://www.doi.org/factsheets/DOIIdentifiers.html |url-status=live }}</ref>
Sistem DOI nawiskeun pengkuh, ''[[semantically interoperable]]'' résolusi pikeun patali data ayeuna jeung paling cocog pikeun bahan anu bakal dipaké dina layanan di luar kontrol langsung ti nu ngaluarkeun ''assigner'' (misalna kutipan umum atawa ngatur eusi nilai). Éta ngagunakeun pendaptaran anu diurus (nyadiakeun infrastruktur sosial jeung téknis). Éta henteu nganggap naon waé modél bisnis khusus pikeun nyayogikeun pangidentifikasi atawa jasa jeung ngamungkinkeun jasa anu sanés aya pikeun ngaitkeun éta ku cara anu ditetepkeun. Sababaraha ''pendekatan'' pikeun nyieun ''identifiers'' pengkuh geus diajukeun.
''Perbandingan pendekatan'' identifier pengkuh hésé sabab henteu sakabéhna ngalakukeun hal anu sami. Ngarujuk kana sakumpulan skéma salaku "identifier" henteu hartosna aranjeunna bisa dibandingkeun kalayan gampang. "Sistem idéntifikasi" anu sanés bisa ngamungkinkeun téknologi kalayan halangan anu pikeun asup, nyadiakeun mékanisme panyiri anu gampang dipaké anu ngamungkinkeun saha waé pikeun nyetél conto énggal (conto kalebet ''[[Persistent Uniform Resource Locator]]'' (PURL), URLs, ''[[Globally Unique Identifier]]''s (GUIDs), jsb.), tapi bisawaé kakurangan sababaraha kagunaanna skéma anu dikontrol ku pendaptaran jeung biasana bakal kakurangan métadata dina skéma anu dikontrol.
Sistem DOI teu mibanda pendekatan ieu jeung teu kedah dibandingkeun langsung jeung skéma pengenal sapertos kitu. Rupa-rupa aplikasi anu ngagunakeun téknologi anu ngamungkinkeun sapertos kitu jeung ''fitur'' tambahan geus diciptakeun anu nyumponan sababaraha ''fitur'' anu ditawarkeun ku sistem DOI pikeun séktor husus (contona, [[ARK]]).
Hiji ngaran DOI henteu gumantung kana lokasi obyék sarta, kucara kieu, sarua jeung [[Uniform Resource Name]] (URN) atawa PURL tapi béda ti URL biasa. URL sering dipaké salaku identifier pengganti pikeun dokumén dina Internét sanajan dokumén anu sami di dua lokasi anu béda mibanda dua URL.Sabalikna, identifiers pengkuh kayaning ngaran DOI ngaidentipikasi objék salaku éntitas kelas munggaran: dua instansi tina objék anu sarua bakal boga ngaran DOI sarua.
==Resolusi==
Resolusi ngaran DOI disayogikeun ku jalan [[Handle System]],dikembangkeun ku [[Corporation for National Research Initiatives]], tur sadia kalawan bébas pikeun sagala pamaké encountering ngaran DOI. Resolusi alihan pamaké ti ngaran DOI ka hiji atawa leuwih lembar data diketik: URL ngalambangkeun instansi obyék, jasa sapertos e-mail, atawa hiji atawa leuwih item metadata. Pikeun Sistim cecekelan, ngaran DOI mangrupa cecekelan, jeung mibanda sakumpulan nilai anu ditugaskeun ka éta jeung bisa dianggap salaku rékaman anu diwangun ku sakelompok widang. Unggal nilai cecekelan kudu boga tipe data dieusian na <code><type></code> sawah, anu nangtukeun sintaksis jeung semantik datana. Bari DOI persistently tur uniquely ngaidentipikasi obyék nu éta ditugaskeun, Resolusi DOI bisa jadi teu pengkuh, alatan masalah teknis jeung administrasi.
Pikeun ngabéréskeun ngaran DOI, éta bisa jadi input ka resolver DOI, sapertos [https://doi.org/ doi.org].
Cara séjén, pikeun nyingkahan ngetik atawa [[motong-jeung-témpél]] kana ''resolver'' nyaéta ngasupkeun DOI dina dokumén salaku URL nu ngagunakeun ''resolver'' salaku ''proxy'' HTTP, sapertos <code><nowiki>https://doi.org/</nowiki></code> (disarankeun)<ref>{{cite web|author1=International DOI Foundation|title=Resolution|url=https://doi.org/doi_handbook/3_Resolution.html#3.7.3|website=DOI Handbook|access-date=19 March 2015|date=7 August 2014|archive-date=31 March 2015|archive-url=https://web.archive.org/web/20150331111224/http://www.doi.org/doi_handbook/3_Resolution.html#3.7.3|url-status=live}}</ref> or <code><nowiki>http://dx.doi.org/</nowiki></code>, duanana ngarojong HTTPS. Contona, DOI<code>10.1000/182</code> bisa diasupkeun kana rujukan atawa [[hyperlink]] salaku <code>https://doi.org/10.1000/182</code>. Cara ieu ngamungkinkeun pamaké pikeun ngaklik DOI salaku normal [[hyperlink]]. Memang, sakumaha disebutkeun saméméhna, ieu kumaha CrossRef nyarankeun yén DOIs salawasna digambarkeun (rleuwih milih HTTPS ti batan HTTP), ku kituna lamun aranjeunna motong-jeung-nempelkeun kana dokumén lianna, surélék, jeung saja bana, aranjeunna bakal ''actionable'' atawa bisa dituluykeun.
''Resolver'' DOI jeung HTTP ''Proxies'' sanésna kalebet ''[[System Handle]]'' jeung ''[[PANGAEA]].'' Dina awal taun 2016, kelas anyar tina ''resolvers'' DOI alternatip ieu dimimitian ku <nowiki>http://doai.io</nowiki>. Ladenan ieu teu biasa sabab nyobian mendakan vérsi [[paywall]] (sering [[Self-archiving|pangarang diarsipkeun]]) tina judul jeung alihan pangguna ka éta tinimbang ''[[versi penerbit ]]''.<ref name="capsh">{{cite web|url=http://doai.io/|title=DOAI|publisher=CAPSH (Committee for the Accessibility of Publications in Sciences and Humanities)|access-date=6 August 2016|archive-date=25 August 2016|archive-url=https://web.archive.org/web/20160825222550/http://doai.io/|url-status=live}}</ref><ref>{{cite web| last = Schonfeld| first = Roger C.| title = Co-opting 'Official' Channels through Infrastructures for Openness| work = The Scholarly Kitchen| access-date = 17 October 2016| date = 3 March 2016| url = https://scholarlykitchen.sspnet.org/2016/03/03/coopting-official-channels/| archive-date = 19 October 2016| archive-url = https://web.archive.org/web/20161019004636/https://scholarlykitchen.sspnet.org/2016/03/03/coopting-official-channels/| url-status = live}}</ref> Ti saprak éta, aksés kabuka séjén ngarojong DOI ''resolvers'' geus dijieun, utamana<nowiki>https://oadoi.org/</nowiki>dina [[Oktober]] 2016 <ref name="impactstory">{{cite web| last=Piwowar| first=Heather| title=Introducing oaDOI: resolve a DOI straight to OA| url=http://blog.impactstory.org/introducting-oadoi/| date=25 October 2016| access-date=17 March 2017| archive-date=17 March 2017| archive-url=https://web.archive.org/web/20170317235707/http://blog.impactstory.org/introducting-oadoi/| url-status=live}}</ref> (engké [[Unpaywall]]).Ari solvers DOI tradisional samata-mata ngandelkeun ''System'' cecekelan, ''solvers'' DOI pilihan munggaran ngobrolkeun sumberdaya aksés kabuka kayaning ''[[BASE]]'' ''(Bielefeld Academic Search Engine)''.<ref name="capsh"/><ref name="impactstory"/>
Pilihan pikeun ''proxy'' HTTP nyaéta ngagunakeun salah sahiji sajumlah ''add-on'' jeung ''plug-in'' pikeun ''[[Web browser]]'', kukituna nyingkahan konvérsi DOIs kana URL,<ref>{{cite web|url=https://www.doi.org/tools.html|title=DOI System Tools|access-date=7 February 2017|archive-date=8 February 2017|archive-url=https://web.archive.org/web/20170208033638/https://www.doi.org/tools.html|url-status=live}}</ref> nu gumantung kana ngaran ''domain'' jeung bisa jadi tunduk robah, bari tetep ngamungkinkeun DOI pikeun diolah salaku ''hyperlink'' normal. Bisa nimbulkeun masalah cara ieu lamun dipake, sahenteuna dina ayeuna, sabagéan ageung pangguna bakal mendakan DOI dina ''browser'', [[maca surat]], atawa ''software'' séjénna nu teu boga salah sahiji ''plug-in'' ieu dipasang.
==Struktur organisasi IDF==
[[File:DOI Foundation.svg|thumb|right|Logo yayasan]]
'''International DOI Foundation''' ('''IDF'''), hiji organisasi ''nirlaba'' dijieun dina 1997, nyaéta [[lembaga]] pamaréntahan sistem DOI.<ref>{{cite book |url=https://doi.org/doi_handbook/7_IDF.html#7.5 |title=DOI Handbook |chapter=Chapter 7: The International DOI Foundation |publisher=Doi.org |access-date=8 July 2015 |archive-date=10 July 2015 |archive-url=https://web.archive.org/web/20150710112821/http://www.doi.org/doi_handbook/7_IDF.html#7.5 |url-status=deviated }}</ref>Éta ngajaga sakabéh [[hak cipta intelektual]] anu aya hubunganana jeung sistem DOI, ngatur ''fitur'' operasional umum, tur ngarojong ngembangkeun sarta ngabéwarankeun sistem DOI. IDF mastikeun yén sagala pangropéa anu dilakukeun pikeun sistem DOI(kaasup nyiptakeun, pangropéa, ''pendaptaran'', résolusi jeung ''pembuatan'' kawijakan ngaran DOI) sadia pikeun sagala anu didaptar DOI. Éta ogé nyegah pihak katilu maksakeun sarat lisénsi tambahan saluareun anu IDF pikeun pangguna sistem DOI.
IDF dikawasa ku Déwan anu dipilih ku anggota Yayasan, jeung Agén ngatur anu ditunjuk anu tanggung jawab pikeun gawébareng jeung ngarencanakeun kagiatanana. Kaanggotaan dibuka pikeun sakabéh organisasi anu mibanda minat dina ''penerbitan'' éléktronik jeung téknologi anu aya hubunganana. IDF ngayakeun rapat nembrak taunan ngeunaan topik DOI jeung masalah anu aya hubunganana.
Agénsi ''pendaptaran'', diangkat ku IDF, nyadiakeun layanan pikeun ''pendaptaran'' DOI: aranjeunna disayagakeun awalan DOI, ngadaptar ngaran DOI, sarta nyadiakeun ''infrastruktur'' anu diperlukeun pikeun ngidinan anu daptar pikeun nembrakeun jeung ngajaga métadata jeung data kaayaan. Agénsi ''pendaptaran'' ogé dipiharep aktip ngamajukeun nyoko nyebar tina sistem DOI, pikeun gawé bareng jeung IDF dina ngembangkeun sistem DOI sakabéhna, jeung nyadiakeun ladenan ''atas ngaran'' komunitas pamaké husus maranéhanana. Daptar RA ayeuna dijaga ku International DOI Foundation. IDF diakuan salaku salah sahiji ''registrar federasi'' pikeun ''Sistem Handle'' ku Yayasan DONA (anu IDF mangrupa anggota déwan), sarta tanggung jawab pikeun nangtukeun awalan Sistim cecekelan handapeun tingkat luhur <code>10</code> prefix.<ref>{{cite web|url=https://dona.net/mpa/|website=DONA Foundation|title=Multi-Primary Administrators|access-date=7 February 2017|archive-url=https://web.archive.org/web/20170114120514/https://dona.net/mpa/|archive-date=14 January 2017|df=dmy-all}} {{Webarchive|url=https://web.archive.org/web/20170114120514/https://dona.net/mpa/ |date=14 January 2017 }}</ref>
Agénsi ''pendaptaran'' umumna ménta ''fee'' pikeun napelkeun ngaran DOI anyar; bagian tina waragad ieu dipaké pikeun ngarojong IDF. Sistim DOI sakabéh, ku jalan IDF, laju dina dasar ''recovery'' ongkos [[teu-kauntungan]]..
==Standarisasi ==
Sistem DOI mangrupa standar internasional anu dikembangkeun ku [[Organisasi Internasional pikeun Standardisasi]] dina panitia téknis ngeunaan idéntifikasi jeung déskripsi, TC46/SC9.<ref>{{cite web|url=http://www.iso.org/iso/pressrelease.htm?refid=Ref1561|url-status=dead |archive-url=https://archive.today/20120802195534/http://www.iso.org/iso/news.htm?refid=Ref1561 |archive-date=2012-08-02 |title=Digital object identifier (DOI) becomes an ISO standard |publisher=International Organization for Standardization |date=10 May 2012 |access-date=10 May 2012}}</ref> ''Draf Standar Internasional'' ISO/DIS 26324, ''Inpormasi jeung Dokuméntasi - Sistem Idéntifikasi Objek Digital'' nyumponan sarat ISO pikeun persetujuan.
Grup Gawé ISO aya patalina engké ngirimkeun vérsi anu diédit ka ISO pikeun disebarkeun salaku kartu sora FDIS ''(Final Draft International Standard)'',<ref>{{cite web |url=https://doi.org/about_the_doi.html#TC46 |title=Standards and Specifications |work=Overviews & Standards |publisher=Doi.org |date=28 June 2010 |access-date=7 August 2010 |archive-date=26 June 2010 |archive-url=https://web.archive.org/web/20100626071933/http://www.doi.org/about_the_doi.html#TC46 |url-status=deviated }}</ref> which was approved by 100% of those voting in a ballot closing on 15 November 2010.<ref>{{cite web |url=https://doi.org/about_the_doi.html#TC46 |work=Overviews & Standards |title=Standards and Specifications: 1. ISO TC46/SC9 Standards |publisher=Doi.org |date=18 November 2010 |access-date=3 July 2011 |archive-date=4 July 2011 |archive-url=https://web.archive.org/web/20110704112317/http://www.doi.org/about_the_doi.html#TC46 |url-status=deviated }}</ref> The final standard was published on 23 April 2012.<ref name="iso"/>
DOI mangrupa URI kadaptar dina [[skéma URI info]] dieusian ku IETF {{IETF RFC|4452}}.info: doi / nyaéta infoURI Namespace of Digital Objék Identifiers.<ref>{{cite web |url=http://info-uri.info/registry/docs/misc/faq.html#which_namespaces |title=About "info" URIs – Frequently Asked Questions |publisher=Info-uri.info |access-date=7 August 2010 |archive-date=27 September 2010 |archive-url=https://web.archive.org/web/20100927084025/http://info-uri.info/registry/docs/misc/faq.html#which_namespaces |url-status=live }}</ref>
Sintaksis DOI mangrupa standar [[NISO]], standarisasi munggaran taun 2000, ANSI/NISO Z39.84{{hyphen}}2005 ''Syntax for the Digital Object Identifier''.<ref>{{cite web |url=https://groups.niso.org/apps/group_public/download.php/14689/z39-84-2005_r2010.pdf |title=ANSI/NISO Z39.84{{hyphen}}2005 Syntax for the Digital Object Identifier |publisher=National Information Standards Organization |access-date=25 June 2021 |archive-date=25 June 2021 |archive-url=https://web.archive.org/web/20210625124108/https://groups.niso.org/apps/group_public/download.php/14689/z39-84-2005_r2010.pdf |url-status=live }}</ref>
Nu ngajaga sistem DOI ngahaja teu ngadaptarkeun rohangan ngaran DOI pikeun [[URN]], nembrakeun yén:
{{blockquote|URN architecture assumes a DNS-based [[Resolution Discovery Service]] (RDS) to find the service appropriate to the given URN scheme. However no such widely deployed RDS schemes currently exist.... DOI is not registered as a URN namespace, despite fulfilling all the functional requirements, since URN registration appears to offer no advantage to the DOI System. It requires an additional layer of administration for defining DOI as a URN namespace (the string {{code|urn:doi:10.1000/1}} rather than the simpler {{code|doi:10.1000/1}}) and an additional step of unnecessary redirection to access the resolution service, already achieved through either http proxy or native resolution. If RDS mechanisms supporting URN specifications become widely available, DOI will be registered as a URN.|International DOI Foundation|[https://www.doi.org/factsheets/DOIIdentifierSpecs.html Factsheet: DOI System and Internet Identifier Specifications]}}
==Tempo ogé==
{{columns-list|colwidth=22em|
* [[arXiv]]
* [[Bibcode]]
* [[DataCite]]
* [[Digital identity]]
* [[Metadata standard]]
* [[Object identifier]]
* [[ORCID]]
* [[PMID]]
* [[Publisher Item Identifier]] (PII)
* [[Permalink]]
* [[Scientific literature]]
* [[Universally unique identifier]] (UUID)
}}
==Catetan==
{{notelist-ua}}
==Dicutat tina==
{{Reflist|2}}
==Tutumbu ka Luar==
{{Wikidata property|P356}}
* {{official website|https://www.doi.org/}}
* [http://shortdoi.org Short DOI] – DOI Foundation service for converting long DOIs to shorter equivalents
* [https://doi.org/factsheets/DOIIdentifierSpecs.html Factsheet: DOI System and Internet Identifier Specifications]
* [http://search.crossref.org/ CrossRef DOI lookup]
{{Audiovisual works|state=uncollapsed}}
{{ISO standards}}
{{Authority control|state=expanded}}
{{DEFAULTSORT:Digital Object Identifier}}
[[Kategori: Éléktronik]]
[[Kategori:Internét]]
[[Kategori:ISO]]
[[Kategori:komputer]]
5ia1mjz50hdqk6gxcbsyi2lqppq425a
William McKinley
0
107977
731542
712339
2026-11-06T22:56:24Z
InternetArchiveBot
25926
Add 1 book for [[en:Wikipedia:Verifiability|verifiability]] (20261106) #IABot (v2.0.9.7) ([[User:GreenC bot|GreenC bot]])
731542
wikitext
text/x-wiki
{{Infobox officeholder
| name = William McKinley
| image = McKinley (cropped).jpg
| order = Présidén Amérika Sarikat ka-25
| office = Pangagung luhur di Amérika Sarikat
| vicepresident = {{plainlist|
* [[Garret Hobart]] (1897–1899)
* Lowong (1899–1901)
* Theodore Roosevelt (Mar–Sept 1901)
}}
| term_start = 4 Maret 1897
| term_end = 14 Séptémber 1901
| predecessor = [[Grover Cleveland]]
| successor = [[Theodore Roosevelt]]
| order1 = [[Gubernur Ohio]] ka-39
| lieutenant1 = [[Andrew L. Harris]]
| term_start1 = 11 Januari 1892
| term_end1 = 13 Januari 1896
| predecessor1 = [[James E. Campbell]]
| successor1 = [[Asa S. Bushnell]]
| office2 = Utusan rahayat (Kongrés)<br />ti [[Ohio]]
| term_start2 = 4 Maret 1885
| term_end2 = 3 Maret 1891
| birth_name = William McKinley Jr.
| birth_date = {{birth date|1843|1|29}}
| birth_place = [[Niles, Ohio]], AS
| death_date = {{death date and age|1901|9|14|1843|1|29}}
| death_place = [[Buffalo, New York]], AS
| death_cause = Dirogahala (Ditémbak)
| resting_place = Pamakaman McKinley,<br />[[Canton, Ohio]]
| party = [[Republican Party (United States)|Parté Républik]]
| father = [[William McKinley Sr.]]
| mother = [[Nancy Allison]]
| spouse = [[Ida Saxton McKinley|Ida Saxton]]
| children = 2
| education = [[Albany Law School]]
| profession = [[Janggaléman]] pulitik, ahli hukum
| branch = [[Balad campuh]] (Angkatan Darat)
| rank = Mayor (Brevet)
| unit = [[23rd Ohio Infantry]]
| battles = [[Perang Sadulur Amérika]] (Campuh Bull Run II, Antietam, jsté.)
| caption = McKinley kira-kira taun 1900
}}
'''William McKinley''' (29 Januari 1843 – 14 Séptémber 1901) téh mangrupa [[présidén Amérika Sarikat]] ka-25, anu ngawula ti taun 1897 nepi ka manéhna tilar dunya alatan dirogahala dina taun 1901.<ref>Gould, Lewis L. (1980). The Presidency of William McKinley. American Presidency Series. Lawrence, Kansas: University Press of Kansas. ISBN 978-0-7006-0206-3.</ref> Salaku inohong ti Parté Républik, manéhna mingpin parobahan pulitik anu ngajadikeun parténa ngagegedénan sarta dominan pisan di nagara-nagara bagian industri ogé di sakuliah nagara salila mangpuluh-puluh taun.<ref>Williams, R. Hal (2010). Realigning America: McKinley, Bryan, and the Remarkable Election of 1896. Lawrence, Kansas: University Press of Kansas. ISBN 978-0-7006-1721-0.</ref> McKinley hasil mawa Amérika unggul dina Perang Spanyol-Amérika sarta ngatur mangsa ékspansi wilayah, ku jalan nyangking Hawaii, Puerto Rico, Guam, Filipina, jeung Samoa Amérika.<ref>Morgan, H. Wayne (2003). William McKinley and His America (revised ed.). Kent, Ohio: The Kent State University Press. ISBN 978-0-87338-765-1.</ref>
McKinley téh présidén pamungkas anu kungsi aub jadi prajurit dina Perang Sadulur Amérika; manéhna hiji-hijina présidén anu ngamimitian lalakon jadi prajurit biasa nepi ka boga pangkat mayor brevet.<ref>Armstrong, William H. (2000). Major McKinley: William McKinley and the Civil War. Kent, Ohio: The Kent State University Press. ISBN 978-0-87338-657-9.</ref> Sabada perang réngsé, manéhna matuh di Canton, Ohio, pikeun muka prakték hukum sarta kawin ka Ida Saxton.<ref>Leech, Margaret (1959). In the Days of McKinley. New York: Harper and Brothers. OCLC 456809.</ref> Dina taun 1876, McKinley kapilih jadi anggota Déwan (Kongrés), di dinya manéhna jadi ahli ti pihak Républik dina urusan pajeg (tarif) panyalindungan, sabab yakin yén kawijakan proteksionisme bakal mawa kamakmuran.<ref>Morgan, H. Wayne (2003). William McKinley and His America (revised ed.). Kent, Ohio: The Kent State University Press. ISBN 978-0-87338-765-1.</ref> Aturan "McKinley Tariff" taun 1890 anu dijieun ku manéhna loba anu nyawad sarta, ditambahan deui ku ayana parobahan wewengkon pamilihan ku Parté Démokrat anu maksudna pikeun nyingkirkeun manéhna, ngabalukarkeun manéhna éléh dina pamilihan lulugu Déwan taun 1890.<ref>Phillips, Kevin (2003). William McKinley. New York: Times Books. ISBN 978-0-8050-6953-2.</ref> Manéhna tuluy kapilih jadi gubernur Ohio dina taun 1891 jeung 1893, bari nyoba nyokot jalan tengah antara kapentingan nu boga modal jeung kaum buruh.<ref>Gould, Lewis L. (1980). The Presidency of William McKinley. Lawrence, Kansas: University Press of Kansas. ISBN 978-0-7006-0206-3.</ref>
McKinley hasil meunangkeun pencalonan présidén ti Parté Républik dina taun 1896 nalika nagarana keur katarajang kasisitan ékonomi anu parna, sarta ngéléhkeun lawanna, William Jennings Bryan, sabada ngayakeun kampanye "front porch" (di buruan imah) bari ngadukung kawijakan "duit anu séhat" (standar emas) jeung jangji yén pajeg luhur bakal mulikkeun deui kajayaan ékonomi.<ref>Williams, R. Hal (2010). Realigning America: McKinley, Bryan, and the Remarkable Election of 1896. Lawrence, Kansas: University Press of Kansas. ISBN 978-0-7006-1721-0.</ref> Mangsa jabatanana diwarnaan ku kamekaran ékonomi anu gancang pisan.<ref>Leech, Margaret (1959). In the Days of McKinley. New York: Harper and Brothers. OCLC 456809.</ref> Manéhna nolak pamakéan pérak sacara bébas sarta leuwih milih nahan standar emas, ogé naékeun pajeg panyalindungan ku jalan nandatanganan "Dingley Tariff" taun 1897 pikeun nangtayungan pabrik jeung buruh tina saingan luar nagara, sarta ngasahkeun "Gold Standard Act" dina taun 1900.<ref>Gould, Lewis L. (1980). The Presidency of William McKinley. Lawrence, Kansas: University Press of Kansas. ISBN 978-0-7006-0206-3.</ref>
Kawijakan luar nagri McKinley nyonto kana jaman impérialisme kakuatan-kakuatan gedé dunya di wilayah Osiania, Asia, jeung Laut Karibia.<ref>Morgan, H. Wayne (2003). William McKinley and His America (revised ed.). Kent, Ohio: The Kent State University Press. ISBN 978-0-87338-765-1.</ref> Amérika Sarikat nyangking Républik Hawaii anu merdéka dina taun 1898, anu tuluy robah jadi Téritori Hawaii dina taun 1900.<ref>Leech, Margaret (1959). In the Days of McKinley. New York: Harper and Brothers. OCLC 456809.</ref> McKinley mimitina mah hayang ngolo Spanyol sangkan méré kamerdékaan ka Kuba tanpa aya campuh perang.<ref>Gould, Lewis L. (1980). The Presidency of William McKinley. Lawrence, Kansas: University Press of Kansas. ISBN 978-0-7006-0206-3.</ref> Sanajan kitu, nalika badami éta gagal, manéhna ménta sarta nandatanganan deklarasi perang ti Déwan pikeun ngamimitian Perang Spanyol-Amérika taun 1898, anu di éta perang Amérika hasil meunang mutlak kalayan gancang.<ref>Morgan, H. Wayne (2003). William McKinley and His America (revised ed.). Kent, Ohio: The Kent State University Press. ISBN 978-0-87338-765-1.</ref> Dina perjanjian daméy éta, Spanyol masrahkeun jajahan utamana kawas Puerto Rico, Guam, jeung Filipina ka Amérika, samentara Kuba dijangjikeun merdéka tapi angget aya dina pangawasan Amérika nepi ka sabada McKinley tilar dunya dina taun 1902.<ref>Phillips, Kevin (2003). William McKinley. New York: Times Books. ISBN 978-0-8050-6953-2.</ref> Di Filipina, aya pemberontakan anu hayang merdéka, tapi ahirna mah bisa ditalukkeun.<ref>Miller, Stuart Creighton (1982). "Benevolent Assimilation": The American Conquest of the Philippines, 1899–1903. New Haven, Connecticut: Yale University Press. ISBN 978-0-300-03081-5.</ref> McKinley ogé hasil nyangking wewengkon anu ayeuna disebut Samoa Amérika nalika pamaréntahanana ngabagi Kapuloan Samoa jeung karajaan Inggris sarta Jérman dina Konvénsi Tripartite.<ref>Leech, Margaret (1959). In the Days of McKinley. New York: Harper and Brothers. OCLC 456809.</ref>
McKinley ngéléhkeun Bryan deui dina pamilihan présidén taun 1900 dina kampanye anu museur kana urusan impérialisme, proteksionisme, jeung pérak bébas.<ref>Williams, R. Hal (2010). Realigning America: McKinley, Bryan, and the Remarkable Election of 1896. Lawrence, Kansas: University Press of Kansas. ISBN 978-0-7006-1721-0.</ref> Jabatan kaduana lekasan leuwih tiheula nalika manéhna ditémbak dina 6 Séptémber 1901 ku saurang anarkis, Leon Czolgosz.<ref>Rauchway, Eric (2004). Murdering McKinley: The Making of Theodore Roosevelt's America. New York: Hill and Wang. ISBN 978-0-8090-1638-9.</ref> McKinley tilar dunya dalapan poé sanggeusna sarta diganti ku Wakil Présidén Theodore Roosevelt, anu kawéntarna ngaleuwihan McKinley sorangan.<ref>Gould, Lewis L. (1980). The Presidency of William McKinley. Lawrence, Kansas: University Press of Kansas. ISBN 978-0-7006-0206-3.</ref> Ahli sajarah nganggep kameunangan McKinley taun 1896 téh minangka pamilihan anu ngarobah tatanan pulitik ti mangsa pasca-Perang Sadulur ka jaman anu didominasi ku Parté Républik.<ref>Phillips, Kevin (2003). William McKinley. New York: Times Books. ISBN 978-0-8050-6953-2.</ref> Amérika Sarikat angget ngawasa wilayah-wilayah anu dicaning ku McKinley, iwal ti Filipina anu ahirna merdéka dina taun 1946.<ref>Morgan, H. Wayne (2003). William McKinley and His America (revised ed.). Kent, Ohio: The Kent State University Press. ISBN 978-0-87338-765-1.</ref> McKinley umumna ditempatkeun dina jajaran tengah dina daptar peringkat présidén-présidén Amérika Sarikat.<ref>Gould, Lewis L. (1980). The Presidency of William McKinley. Lawrence, Kansas: University Press of Kansas. ISBN 978-0-7006-0206-3.</ref>
==Mangsa leutik jeung kulawarga==
[[File:McKinley boy.png|left|thumb|upright|McKinley, yuswa 15 taun]]
William McKinley Jr. dibabarkeun dina taun 1843 di [[Niles, Ohio]], mangrupa anak katujuh tina salapan duduluran pasangan [[William McKinley Sr.]] jeung Nancy (née Allison) McKinley.{{sfnm|Leech||1p=4|Morgan||2p=2}} Kulawarga McKinley téh turunan Inggris jeung Skotlandia-Irlandia anu geus matuh di wilayah kulon Pennsylvania ti abad ka-18.{{sfnm|Leech||1p=4|Morgan||2p=2}} Karuhun maranéhna anu mimiti datang (imigran) téh nyaéta David McKinley, anu dibabarkeun di [[Dervock]], County Antrim, di wilayah anu ayeuna jadi [[Irlandia Kalér]]. William McKinley Sr. sorangan dibabarkeun di Pennsylvania, perenahna di [[Pine Township, Mercer County, Pennsylvania|Pine Township, Mercer County]].{{sfnm|Leech||1p=4|Morgan||2p=2}}
Ieu kulawarga tuluy pindah ka Ohio nalika McKinley senior masih budak, sarta matuh di New Lisbon (ayeuna [[Lisbon, Ohio|Lisbon]]). Di dinya manéhna panggih jeung Nancy Allison, anu saterusna dikawin.{{sfnm|Leech||1p=4|Morgan||2p=2}} Kulawarga Allison lolobana turunan Inggris sarta kaasup salah sahiji kulawarga anu mimiti matuh di Pennsylvania.{{sfn|Morgan|p=3}} Pacabakan kulawarga ti dua pihakna téh nyaéta tukang nyieun beusi. McKinley senior ngokolakeun [[Foundry|pabrik citakan beusi]] di sakuliah Ohio, mimiti di New Lisbon, Niles, [[Poland, Ohio|Poland]], nepi ka ahirna di [[Canton, Ohio|Canton]].{{sfnm|Armstrong||1pp=4–6|Morgan||2pp=2–3|Phillips||3p=13}} Kaayaan rumah tangga McKinley, sakumaha ilaharna kulawarga ti wilayah Western Reserve di Ohio, kandel pisan ku pamadegan pulitik Whig sarta sumanget [[Abolitionism in the United States|abolisionis]] (anti-perbudakan), anu dumasar kana kayakinan agama maranéhna salaku panyekel ajaran [[Methodist Episcopal Church|Metodis]] anu pengkuh.{{sfnm|Phillips||1pp=17–18|Armstrong||2p=8|Morgan||3pp=10–11}}
William anu ngora ogé nuturkeun tatapakan agamana, manéhna mimiti aktif di [[First Methodist Episcopal Church (Canton, Ohio)|gréja Metodis lokal]] dina yuswa genep belas taun.{{sfnm|Phillips||1p=16|Leech||2pp=4–5}} Manéhna jadi saurang Metodis anu taat saumur hirupna.{{sfn|Morgan|pp=9–10}}
Dina taun 1852, ieu kulawarga pindah ti Niles ka Poland, Ohio, sangkan barudakna bisa sakola di tempat anu leuwih hadé. Sabada lulus ti [[Poland Seminary]] dina taun 1859, McKinley nuluykeun sakola ka [[Allegheny College]] di [[Meadville, Pennsylvania]] dina taun saterusna. Manéhna jadi anggota kahormatan di paguyuban [[Sigma Alpha Epsilon]].<ref>{{Cite book|title=History of the Sigma Alpha Epsilon Fraternity, Volume 2|last=Levere|first=William|publisher=Lakeside Press|year=1911|location=Chicago|pages=204–19}}</ref> Manéhna ngan kuat sataun di Allegheny, tuluy mulang ka imah dina taun 1860 alatan teu damang sarta ngarasa depressed (teu sumanget). Manéhna ogé kungsi sakola di Mount Union College, ayeuna [[University of Mount Union]], di [[Alliance, Ohio]], dimana manéhna saterusna jadi anggota dewan pangurus di dinya.{{sfnm|Phillips||1p=20|Armstrong||2p=5}} Sanajan kaséhatanana geus jagjag deui, kaayaan ékonomi kulawargana keur nyirorot (kasisitan), sahingga McKinley teu bisa balik deui ka Allegheny. Manéhna mimiti digawé jadi tanaga administrasi di kantor pos sarta sanggeusna jadi guru di hiji sakola deukeut Poland, Ohio.{{sfnm|Armstrong||1p=6|Morgan||2pp=11–12}}
Pasukan tuluy nuju ka [[Lynchburg, Virginia|Lynchburg]], bari ngaruksak jalur karéta api salila maranéhna maju.{{sfnm|1a=Hoogenboom|1pp=166–68|2a=Armstrong|2pp=66–69}} Sanajan kitu, Hunter boga kayakinan yén balad musuh di Lynchburg kuat teuing, sahingga pasukanana mulang deui ka West Virginia.{{sfnm|1a=Hoogenboom|1pp=166–68|2a=Armstrong|2pp=66–69}} Saméméh pasukan bisa ngayakeun panarajang deui, serangan ti Jenderal Konféderasi [[Jubal Early]] ka Maryland maksa maranéhna ditarik deui ka kalér.{{sfn|Armstrong|pp=70–71}}
Pasukan Early nyerang dadakan di [[Second Battle of Kernstown|Kernstown]] dina 24 Juli, dimana McKinley katarajang hujan pélor sarta pasukanana kasoran (éléh).{{sfnm|1a=Hoogenboom|1pp=168–69|2a=Armstrong|2pp=72–73}} Nalika mundur ka Maryland, pasukan disusun deui: Mayor Jenderal [[Philip Sheridan]] ngaganti Hunter, sarta McKinley, anu naék pangkat jadi [[Captain (United States O-3)|kaptén]] sabada campuh éta, dipindahkeun jadi staf Jenderal Crook.{{sfnm|1a=Hoogenboom||1pp=170–71|2a=Armstrong||2pp=75–77}} Dina bulan Agustus, Early mundur ka kidul, diburu ku pasukan Sheridan.{{sfn|Armstrong|pp=78–80}} Maranéhna hasil nahan panarajang Konféderasi di [[Battle of Berryville|Berryville]], dimana kuda anu ditumpakan ku McKinley ditémbak nepi ka tiwasna, tuluy pasukan maju ka [[Battle of Opequon|Opequon Creek]], dimana maranéhna hasil ngabobol pertahanan musuh sarta ngudag maranéhna leuwih jauh ka kidul.{{sfnm|1a=Hoogenboom||1pp=172–73|2a=Armstrong||2pp=80–82}} Kameunangan éta dituluykeun ku kameunangan séjénna di [[Battle of Fisher's Hill|Fisher's Hill]] dina 22 Séptémber sarta campuh deui di [[Battle of Cedar Creek|Cedar Creek]] dina 19 Oktober.{{sfn|Armstrong|pp=84–91}} Sanajan mimitina mundur alatan panarajang Konféderasi, McKinley milu ngagedurkeun deui sumanget baladna sarta hasil malikkeun kaayaan dina éta perang.{{sfn|Armstrong|pp=84–91}}
Sabada perang di Cedar Creek, pasukan tetep matuh di sabudeureun dinya nepi ka poéan pamilihan, nalika McKinley méré sora munggaranana pikeun calon présidén ti Parté Républik, [[Abraham Lincoln]].{{sfn|Armstrong|pp=84–91}} Poé saterusna, maranéhna pindah ka kalér nuju ka tempat pamatuhan usum tiris deukeut Kernstown.{{sfn|Armstrong|pp=84–91}} Dina bulan Pébruari 1865, Crook ditéwak ku pasukan panyerang Konféderasi.{{sfn|Armstrong|pp=95–96}} Kajadian ditéwakna Crook nambahan kabingung nalika pasukan keur disusun deui pikeun kampanye usum semi, sarta McKinley ngabantu staf opat jenderal anu béda dina waktu lima belas poé—Crook, [[John Dunlap Stevenson|John D. Stevenson]], [[Samuel S. Carroll]], jeung [[Winfield Scott Hancock|Winfield S. Hancock]].{{sfn|Armstrong|pp=95–96}} Ahirna ditépakeun deui jadi staf Carroll, McKinley purah jadi hiji-hijina [[adjutant|ajudan]] éta jenderal.{{sfn|Phillips|p=99}}
Jenderal Lee jeung pasukanana [[Battle of Appomattox Court House#Surrender|nyerah]] ka Jenderal [[Ulysses S. Grant]] sababaraha poé sanggeusna, anu jadi tanda réngséna peperangan. McKinley asup kana pakumpulan [[Freemasonry|Freemason]] (anu jagana dingaranan maké ngaran manéhna) di [[Winchester, Virginia]], méméh manéhna jeung Carroll dipindahkeun ka Korps Hanock di Washington.{{sfn|Phillips|p=99}} Sakedap méméh perang bérés, McKinley nampi undak pangkat pamungkas, nyaéta pangkat mayor brevet.{{sfn|Armstrong|pp=103–05}} Dina bulan Juli, unitna dibubarkeun sarta McKinley dibébaskeun tina pancénna.{{sfn|Armstrong|pp=103–05}} Carroll jeung Hancock ngajurung McKinley sangkan asup jadi tantara tetep dina mangsa daméy, tapi manéhna nolak sarta milih mulang deui ka Ohio dina bulan saterusna.{{sfn|Armstrong|pp=103–05}}
McKinley, bareng jeung Samuel M. Taylor sarta James C. Howe, milu nyusun sarta medarkeun karya dua belas jilid, ''Official Roster of the Soldiers of the State of Ohio in the War of the Rebellion, 1861–1866'', anu dipedarkeun dina taun 1886.<ref>[[#roster|McKinley, Taylor, Howe, 1886]]</ref>
==Karir hukum jeung kawin==
[[File:ISMcKinley.jpg|thumb|left|upright|Ida Saxton McKinley]]
[[File:Katherine McKinley.jpg|thumb|left|upright|Katherine McKinley]]
Sabada perang réngsé dina taun 1865, McKinley milih pikeun nyuprih élmu dina widang hukum sarta mimiti dialajar di kantor saurang ahli hukum di [[Poland, Ohio]].{{sfn|Morgan|pp=28–30}} Taun saterusna, manéhna nuluykeun sakolana ka [[Albany Law School]] di nagara bagian New York.{{sfn|Morgan|pp=30–31}} Sabada sakola di dinya kurang ti sataun, McKinley mulang deui ka lemburna sarta dina Maret 1867 resmi diaku jadi ahli hukum (admitted to the bar) di [[Warren, Ohio]].{{sfn|Morgan|pp=30–31}}
Dina taun éta kénéh, manéhna pindah ka Canton, puseur dayeuh [[Stark County, Ohio]], sarta muka kantor leutik.{{sfn|Morgan|pp=30–31}} Teu lila, manéhna gawé bareng jeung George W. Belden, saurang ahli hukum anu geus jembar ku pangalaman sarta urut hakim.{{sfnm|1a=Morgan||1pp=31–33|2a=Leech||2pp=12, 21}} Usahana hasil pisan nepi ka manéhna bisa meuli wangunan di Main Street, Canton, anu méré pangasilan tambahan tina séwa salila mangpuluh-puluh taun.{{sfnm|1a=Morgan||1pp=31–33|2a=Leech||2pp=12, 21}}</ref>
Nalika sobatna di tantara, Rutherford B. Hayes, dicalonkeun jadi gubernur dina taun 1867, McKinley mimiti ancrub ka dunya pulitik ku jalan biantara pikeun ngadukung Hayes di Stark County.{{sfn|Leech|pp=11–12}} Sanajan kaayaan sora rahayat di dinya ampir saimbang antara Parté Démokrat jeung Républik, Hayes hasil unggul di éta wilayah.{{sfn|Leech|pp=11–12}} Dina taun 1869, McKinley nyalonkeun diri jadi jaksa wilayah (prosecuting attorney) Stark County, kalungguhan anu biasana dicepeng ku urang Démokrat, tapi manéhna hasil kapilih kalayan teu disangka-sangka.{{sfn|Morgan|pp=34–35}} Nalika manéhna nyalonkeun deui dina taun 1871, urang Démokrat nyalonkeun [[William A. Lynch]], saurang ahli hukum kasohor, sarta McKinley kasoran ku sela 143 sora.{{sfn|Morgan|pp=34–35}}
Sajalan jeung karirna anu maju, lalakon asihna ogé mimiti kembangan: manéhna meungkeut asih ka [[Ida Saxton McKinley|Ida Saxton]], putri ti kulawarga jegud di Canton.{{sfn|Morgan|pp=34–35}}Maranéhna kawin dina 25 Januari 1871 di Grja Presbiterian Munggaran Canton anu anyar diwangun. Ida tuluy milu pindah agama jadi Metodis nuturkeun salakina.{{sfnm|1a=Morgan||1pp=37–39|2a=Leech||2pp=16–20}} Anak munggaranana, Katherine, dibabarkeun dina poé Natal taun 1871.{{sfnm|1a=Morgan||1pp=37|2a=Leech||2p=16}} Anak kadua, Ida, lahir dina taun 1873 tapi tilar dunya dina taun éta kénéh.{{sfnm|1a=Morgan||1pp=37–39|2a=Leech||2pp=16–20}} Pamajikan McKinley ngarasa dunya poék (depresi parna) alatan tilar dunya anakna, sarta kaséhatanana mimiti nyirorot.{{sfnm|1a=Morgan||1pp=37–39|2a=Leech||2pp=16–20}} Dua taun ti harita, Katherine tilar dunya alatan tipus. Ida henteu kungsi jagjag deui tina kasedihna, sarta kulawarga McKinley teu gaduh deui katurunan.{{sfnm|1a=Morgan||1pp=37–39|2a=Leech||2pp=16–20}} Ida ogé mimiti katarajang kasakit ayan (epilepsi) sarta gumantung pisan ka salakina. McKinley jadi salaki anu kacida tulaténna sarta terus ngajaga pamajikanana kalayan pinuh ku kaasih saumur hirupna.{{sfnm|1a=Morgan||1pp=37–39|2a=Leech||2pp=16–20}}
Ida ngajurung sangkan salakina terus maju dina karir hukum jeung pulitikna.{{sfn|Morgan|pp=39–40}} McKinley milu dina konvénsi nagara bagian Républik taun 1875 sarta mantuan kampanye Hayes pikeun jadi gubernur deui.{{sfn|Morgan|pp=39–40}} Taun saterusna, McKinley nyepeng pasualaan hukum anu jadi perhatian balaréa nalika ngabéla sakelompok buruh tambang batu bara anu ditéwak alatan karusuhan jeung pameupeus mogok (strikebreakers).{{sfnm|1a=Morgan||1pp=40–41|2a=Weisenburger||2pp=78–80}} Lynch, lawan McKinley dina pamilihan 1871, jadi jaksa lawanna, sarta salah sahiji nu boga tambangna téa nyaéta [[Mark Hanna]], pangusaha ti Cleveland.{{sfnm|1a=Morgan||1pp=40–41|2a=Weisenburger||2pp=78–80}} Ku jalan méré bantuan hukum haratis (''pro bono''), McKinley hasil ngabébaskeun ampir sakabéh buruh tambang éta.{{sfnm|1a=Morgan||1pp=40–41|2a=Weisenburger||2pp=78–80}} Ieu hal ngangkat ngaran McKinley di mata kaum buruh, sakaligus jadi awal wawuhna manéhna jeung Hanna, anu jagana bakal jadi pangrojong panguatna.{{sfnm|1a=Morgan||1pp=40–41|2a=Weisenburger||2pp=78–80}}
Kaharéman McKinley di mata kaum buruh mangpaat pisan nalika manéhna nyalonkeun diri pikeun Kongrés ti wilayah ka-17 Ohio.{{sfn|Morgan|p=42}} Para utusan yakin manéhna bisa narik sora ti kaum "kerah biru" (buruh), sarta dina Agustus 1876, McKinley resmi dicalonkeun.{{sfn|Morgan|p=42}} Harita, Hayes geus dicalonkeun jadi présidén, sarta McKinley milu mantuan kampanye Hayes bari ngajalankeun kampanyena sorangan.{{sfn|Morgan|p=43}} Duanana hasil unggul. McKinley ngéléhkeun utusan Démokrat, Levi L. Lamborn, ku sela 3.300 sora kalayan ngusung téma pajeg panyalindungan. Hayes ogé hasil jadi présidén sabada pamilihan anu pinuh ku perdebatan.{sfn|Morgan|p=43}} Kameunangan McKinley ieu miboga konsékuansi pribadi: bayaranana salaku anggota Kongrés ngan satengahna tina naon anu ditarima nalika jadi ahli hukum.{{sfn|McElroy|p=31}}
==Inohong anu keur nanjung (1877–1895)==
===Juru biantara panyalindungan pajeg===
{{quote box | align = right | width = 24em | salign = right
| quote = Dina kaayaan dagang bébas, tukang dagang téh jadi dunungan sarta nu nyieun barang (produsén) jadi badéga. Panyalindungan pajeg (proteksi) mah taya lian iwal ti hukum alam, hukum keur ngajaga diri, hukum kamekaran diri, sarta pikeun mastikeun darajat nu pangluhurna jeung panghadéna keur umat manusa. [Aya nu nyebut] yén proteksi téh teu boga moral... Naha, lamun proteksi bisa ngawangun jeung ngangkat darajat 63.000.000 [jumlah padumuk AS harita] jalma, pangaruh ti éta 63.000.000 jalma téh bakal ngangkat sakuliah dunya. Urang moal bisa ngaléngkah dina jalan kamajuan tanpa méré mangpaat ka umat manusa di mana waé. Heug, maranéhna nyebut, "Meuli mah di mana waé nu pangmurahna"... Tangtu, éta lumaku pikeun tanaga gawé sakumaha lumaku pikeun nu séjénna. Hayu kuring méré papatah (maksim) anu rébu kali leuwih hadé batan éta, nyaéta maksim proteksi: "Meuli mah di mana waé anjeun bisa mayar kalayan pang-enténgna." Sarta éta tempat di bumi téh nyaéta di mana tanaga gawé meunang ganjaran nu pangluhurna.
| source = William McKinley, biantara dina 4 Oktober 1892, Boston, Massachusetts}}
McKinley mimiti diuk dina korsé Kongrés dina Oktober 1877, nalika Présidén Hayes manggil Kongrés pikeun sidang istiméwa.{{sfn|Leech|p=20}} Kusabab urang Républik jadi minoritas, McKinley dipasihan pancén dina komisi-komisi anu dianggap kurang penting, anu ku manéhna dipidamel kalayan rancagé.{{sfn|Leech|p=20}} Sosobatan McKinley jeung Hayes teu pati méré kauntungan pikeun McKinley di [[Capitol Hill]], sabab éta présidén téh kurang dipihormat ku loba pamingpin di dinya.{{sfn|Leech|p=37}} Éta anggota Kongrés ngora téh béda pamadegan jeung Hayes dina urusan mata uang, tapi hal éta teu mangaruhan kana sosobatan maranéhna.{{sfn|Morgan|p=47}} Amérika Sarikat geus sacara éféktif maké standar emas dumasar kana Undang-Undang Mata Uang 1873; nalika harga pérak turun drastis, loba jalma hayang ngajadikeun pérak minangka alat mayar nu sah deui, saimbang jeung emas.{{sfn|Horner|pp=180–81}} McKinley milih ngadukung Undang-Undang Bland-Allison 1878 anu marentahkeun pamaréntah meuli pérak dina jumlah gedé pikeun dijadikeun duit, sarta manéhna ogé milu jeung sora mayoritas di unggal déwan pikeun ngabatalkeun véto (panolakan) Hayes kana éta aturan.{{sfnm|1a=Morgan|1pp=46–47|2a=Horner|2pp=181–82}} Dina peta éta, McKinley milih jalan anu béda jeung pamingpin Républik di Déwan, James Garfield, anu ogé urang Ohio sakaligus sobatna.{{sfnm|1a=Morgan|1pp=46–47|2a=Horner|2pp=181–82}}
[[File:Mckin.jpg|thumb|left|McKinley salaku utusan rahayat (Kongrés) kira-kira taun 1870-an]]
Ti mimiti jabatanana di Kongrés, McKinley geus jadi pangrojong panguatna pikeun pajeg panyalindungan (tarif proteksi). Maksud utama tina éta pajeg téh lain keur néangan pangasilan nagara, tapi sangkan industri manufaktur Amérika bisa mekar ku jalan méré kauntungan harga di pasar jero nagri batan saingan ti luar nagri.{{sfnm|1a=Leech||1pp=36–37|2a=Phillips||2pp=42–44}} Panulis biografi McKinley, Margaret Leech, nyatet yén kota Canton geus jadi maju minangka puseur manufaktur alat-alat tatanén alatan ayana panyalindungan pajeg, sarta hal ieu pisan anu mangaruhan pamadegan pulitikna.{{sfnm|1a=Leech||1pp=36–37|2a=Phillips||2pp=42–44}} Garfield anu kapilih jadi présidén dina taun 1880 nyésakeun lowongan dina Komisi Urusan Keuangan (Ways and Means Committee); McKinley kapilih pikeun ngeusian éta tempat, meunang posisi dina komisi pangkuatna ngan dina waktu dua mangsa jabatan.{{sfn|Morgan|p=55}}
McKinley beuki lila beuki jadi inohong penting dina pulitik nasional. Dina taun 1884, manéhna kapilih jadi utusan dina konvénsi Républik, dimana manéhna jadi ketua Komisi Resolusi sarta meunang pangalem alatan tapis dina mingpin jalanna konvénsi.{{sfn|Phillips|pp=60–61}} Dina taun 1886, McKinley, Sénator John Sherman, jeung Gubernur Joseph B. Foraker dianggap minangka pamingpin Parté Républik di Ohio.{{sfn|Phillips|pp=60–61}} Saenggeus konvénsi taun 1888 anu pinuh ku pacogrégan, Mark Hanna mimiti mikareueus McKinley sarta jadi sobat sakaligus panaséhat deukeutna.{{sfn|Horner|pp=80–81}} Sanajan Hanna tetep aktip dina dunya bisnis, dina taun-taun sabada 1888, manéhna méakkeun beuki loba waktu pikeun ngangkat karir pulitik McKinley.{{sfn|Horner|pp=80–81}}
Dina taun 1889, nalika urang Républik jadi mayoritas, McKinley nyoba nyalonkeun jadi Ketua Déwan (Speaker). Manéhna teu hasil nempatan éta posisi, anu ahirna dicepeng ku Thomas B. Reed ti Maine; sanajan kitu, Ketua Reed nunjuk McKinley jadi ketua Komisi Urusan Keuangan.{{sfn|Phillips|pp=27, 42–43}} Urang Ohio ieu tuluy mingpin panyusunan "McKinley Tariff" taun 1890 dina Kongrés; sanajan hasil gawéna loba dirobah alatan pangaruh kapentingan husus di Sénat, éta aturan hasil netepkeun sajumlah pajeg panyalindungan pikeun barang-barang ti luar nagri.{{sfn|Phillips|pp=27, 42–43}}
===Gerrymandering sarta kasoran dina pamilihan===
[[File:Judge cover September 1890 - On to Ohio.png|thumb|Gambar tina majalah ''Judge'' taun 1890, nembongkeun McKinley (kenca) keur rurusuhan nuju ka wilayahna di Ohio anu geus diacak-acak watesna (gerrymandered).]]
Kusabab urang Demokrat nempo aya kakuatan gede dina diri McKinley, unggal maranehna ngawasa legislatif di Ohio, maranehna sok nyoba ngalakukeun [[Gerrymandering|gerrymander]] atawa ngarobah wates wilayah pamilihan sangkan McKinley tumpas tina jabatanna.{{sfn|Phillips|p=27}} Dina taun 1878, wilayah McKinley dipindahkeun ka distrik ka-16; tapi manehna angget unggul, nepi ka Hayes nyebut kalayan bungah, "Wah, untung pisan McKinley mah! Manehna diacak-acak wilayahna tapi angget bisa ngéléhkeun gerrymander eta! Urang sarerea milu bungah."{{sfn|Morgan|p=54}} Sanggeus pamilihan taun 1882, McKinley kungsi kaleungitan korsi alatan ayana sengketa pamilihan dina sora di Dewan.{{sfn|Morgan|pp=59–60}} Sanajan kungsi ngarasa dunya poék alatan eta kasoran, manehna jangji bakal nyalonkeun deui. Urang Demokrat ngarobah deui wates wilayah Stark County pikeun pamilihan taun 1884; tapi McKinley tetep bisa mulang deui ka Kongrés.{{sfn|Morgan|pp=60–62}}
Dina taun 1890, urang Demokrat ngalakukeun gerrymander pamungkas ka McKinley, ku jalan ngahijikeun Stark County jeung wilayah Holmes County anu pangeusina teh urang [[Pennsylvania Dutch]] anu fanatik ka Demokrat. Dumasar itungan harita, Demokrat yakin wates anyar eta bakal méré kauntungan 2.000 nepi ka 3.000 sora keur maranehna. Urang Républik teu bisa ngalawan eta aturan, tapi maranehna ngerahkeun sagala kakuatan ka eta wilayah. Aturan pajeg "McKinley Tariff" jadi jejer utama kampanye Demokrat di sakuliah nagara, sarta loba jalma anu merhatikeun nasib McKinley. Parté Républik ngirimkeun juru biantara panghadena ka Canton, kaasup Blaine, Ketua Reed, sarta Présidén Harrison. McKinley sorangan teu aya kacape ngurilingan wilayahna pikeun nerangkeun ka 40.000 pamilih yén pajegna teh:
{{blockquote |
dijieun pikeun rahayat... salaku taméng keur industri maranehna, salaku panyalindungan pikeun tanaga gawena, salaku pangjaga pikeun imah-imah kaum buruh Amérika anu bagja, sarta salaku jaminan pikeun pendidikan, gajih, jeung investasi maranehna... Ieu hal bakal mawa kamakmuran anu taya tandingna dina sajarah urang sarta dunya."{{sfn|McKinley|p=464}}
}}
Demokrat nyalonkeun urut wakil gubernur [[John G. Warwick]] anu kacida kuatna. Pikeun ngahasut rahayat, maranehna nyewa budak ngora pikeun pura-pura jadi tukang dagang anu nawarkeun parabot tina kaleng harga 25 sen jadi 50 sen ka ibu-ibu rumah tangga, bari nerangkeun yen naekna harga teh alatan "McKinley Tariff". Ahirna, McKinley eleh ku sela 300 sora, tapi urang Républik unggul sacara nasional sarta ngarasa eta teh mangrupa "kameunangan moral".{{sfn|Jensen|pp=151–53}}
===Gubernur Ohio (1892–1896)===
Méméh jabatanana di Kongrés lekasan, McKinley geus didatangan ku utusan ti Ohio anu ngajurung manehna nyalonkeun jadi gubernur. Gubernur harita, James E. Campbell ti Demokrat, bakal nyalonkeun deui dina taun 1891. Parté Républik di Ohio harita keur peupeus, tapi McKinley hasil ngatur sangkan Foraker (lawan partena) nyalonkeun manehna dina konvénsi, sarta McKinley kapilih kalayan sora buleud. Manehna meunangkeun pamilihan taun 1891 ku sela 20,000 sora.{{sfn|Williams|p=50}}
[[File:Blaine breaks out.png|right|thumb|James G. Blaine (tengah) masih dianggap saingan kuat pikeun McKinley jeung Reed dina kartun taun 1890.]]
Gubernur Ohio sabenerna teu boga kakuatan gede—contona, manehna teu bisa ngabatalkeun (veto) undang-undang—tapi kusabab Ohio teh nagara bagian anu penting (swing state), gubernurna jadi inohong gede dina pulitik nasional.{{sfn|Williams|p=117}} Sanajan McKinley yakin yen kamajuan nagara gumantung kana dunya usaha, manehna tetep adil ka kaum buruh.{{sfn|Gould|p=7}} Manehna nyieun aturan pikeun nyeleseikeun pacogregan gawé sarta ngaluarkeun hukum anu ngadenda dunungan lamun macatkeun buruh alatan milu union (serikat buruh).{{sfn|Williams|p=122}}
Nalika ékonomi Amérika ambruk alatan [[Panic of 1893]], McKinley ampir bae bangkrut sabab kungsi nanggung hutang sobatna, Robert Walker, anu jumlahna leuwih ti $100,000 (sarua jeung loba pisan juta dollar jaman ayeuna).{{sfn|Morgan|pp=129–30}} McKinley sempet mikir hayang mundur jadi gubernur sangkan bisa neangan duit jadi pengacara deui.{{sfn|Morgan|pp=130–34}} Tapi, para pangrojongna anu baleunghar, kaasup Hanna, ngumpulkeun duit pikeun malayar eta hutang. Rahayat loba anu karunya sarta simpati ka McKinley, sahingga popularitasna kalahka beuki naek.{{sfn|Morgan|pp=130–34}} Manehna hasil kapilih deui jadi gubernur dina taun 1893 kalayan sora anu panglobana saprak jaman Perang Sadulur.{{sfn|Phillips|p=67}} Sabada hasil ngahijikeun kakuatan Républik di Ohio, McKinley mimiti museurkeun paneuteupna kana korsi présidén nasional.{{sfn|Phillips|pp=69–70}}
==Pamilihan taun 1896==
===Meunangkeun pencalonan===
[[File:Mark Hanna by WJ Root, 1896 cropped.jpg|thumb|upright|Mark Hanna, sobat sarta panaséhat deukeut McKinley]]
Henteu pati tétéla iraha William McKinley mimiti bener-bener nyiapkeun diri pikeun nyalonkeun présidén. Sakumaha anu dicatet ku panulis biografi McKinley, Kevin Phillips, "Taya dokumén, taya buku poéan, taya surat rusiah ka Mark Hanna (atawa ka sasaha waé) anu eusina mangrupa harepan rusiah atawa siasat anu nyamuni."{{sfn|Phillips|p=61}} Ti mimiti pisan, sagala persiapan McKinley téh dibantuan ku Hanna; panulis biografi Hanna, William T. Horner, nyatet yén, "Anu geus pasti bener nyaéta dina taun 1888, éta dua lalaki téh mimiti ngawangun hubungan gawé anu raket anu jagana mawa McKinley ka Gedong Bodas."{{sfn|Horner|p=81}} Kusabab Sherman teu nyalonkeun deui présidén sabada taun 1888, Hanna bisa bener-bener museurkeun pangrojongna kana ambisi McKinley.{{sfn|Horner|p=92}}
Ku modal duit jeung katapisan Hanna dina ngatur organisasi, McKinley lalaunan ngumpulkeun pangrojong sapanjang taun 1895 nepi ka awal 1896. Nalika calon séjén kawas Ketua Reed jeung Sénator William B. Allison ngirimkeun utusan ka luar nagara bagianana pikeun ngumpulkeun sora urang Républik, maranéhna manggihan yén utusan Hanna geus ti heula aya di dinya. Dumasar kana panalungtikan ahli sajarah Stanley Jones ngeunaan pamilihan 1896:
{{blockquote|Hal séjén anu sarua tina kampanye Reed jeung Allison nyaéta kagagalan maranéhna nahan arus anu nuju ka McKinley. Sabenerna, duanana kampanye éta geus mundur ti mimiti pisan digubragkeun. Kayakinan anu tenang ti unggal calon yén maranéhna bakal meunang pangrojong ti wilayahna sorangan, geuwat robah jadi... tuduhan pait yén Hanna geus ngalanggar aturan maén ku jalan meunangkeun pangrojong keur McKinley di wilayah maranéhna.{{sfn|Jones|p=103}}}}
Hanna, atas ngaran McKinley, ngayakeun riungan jeung para "bandar" (political bosses) Républik di wilayah wétan, kawas Thomas Platt ti New York jeung Sénator Matthew Quay ti Pennsylvania, anu sanggup ngajamin pencalonan McKinley asal aya imbalan mangrupa jangji jabatan. Sanajan kitu, McKinley geus panceg dina pamadeganana hayang meunangkeun pencalonan tanpa aya transaksi (dagang sapi), sarta Hanna narima kana éta putusan.{{sfn|Jones|p=105}} Kalolobaan usaha awal maranéhna difokuskeun ka wilayah Kidul; Hanna nepi ka meuli imah keur pelesir di Georgia kidul dimana McKinley sering datang sarta tepung jeung para inohong Républik di éta wilayah. McKinley butuh 453½ sora utusan (delegasi) pikeun meunangkeun pencalonan; manéhna hasil meunangkeun ampir satengahna ti wilayah Kidul jeung wilayah wates. Platt ngangluh dina catetanana, "[Hanna] geus miboga sora Kidul anu solid méméh urang hudang tina saré."{{sfn|Williams|p=57}}
[[File:Coronation of McKinley.jpg|thumb|left|upright=1.35|Kartun tina majalah ''Puck'' taun 1896, nembongkeun McKinley keur masangkeun makuta pencalonan ka dirina sorangan.]]
Quay jeung Platt masih boga harepan pikeun ngahalangan McKinley meunangkeun sora mayoritas dina konvénsi ku jalan ngangkat calon-calon "favorite son" (tokoh lokal) kawas Quay sorangan, Gubernur New York Levi P. Morton, jeung Sénator Illinois Shelby Cullom. Nagara bagian Illinois anu loba sorana jadi médan campuh anu penting, dimana pangrojong McKinley kawas Charles G. Dawes bajoang sangkan para delegasi milih McKinley dina konvénsi nasional di St. Louis. Cullom tétéla teu mampuh nahan kakuatan McKinley; dina konvénsi nagara bagian dina tungtung April, McKinley hasil nyapu bersih sora delegasi Illinois.{{sfn|Jones|pp=119–25}} Urut présidén Harrison ogé dianggap saingan kuat, tapi nalika Harrison méré béja yén manéhna moal nyalonkeun deui, organisasi McKinley geuwat ngawasa Indiana kalayan gancang pisan. Sénator Wyoming, Francis Warren, nulis, "Para pulitikus keur bajoang kacida kuatna ngalawan manéhna, tapi lamun rahayat leutik anu nyarita, McKinley téh mangrupa pilihan ti saeutikna 75% sakabéh pamilih Républik di Amérika".{{sfn|Phillips|pp=71–72}}
Nalika konvénsi nasional dimimitian di St. Louis dina 16 Juni 1896, McKinley geus miboga sora mayoritas anu cukup. Éta urut gubernur, anu angget matuh di Canton, nuturkeun jalanna konvénsi tina telepon, sarta hasil ngadéngé sapalih tina biantara Foraker anu nyalonkeun manéhna. Nalika nepi ka gilirannana sora nagara bagian Ohio, éta sora téh langsung ngajadikeun McKinley resmi dicalonkeun. Manéhna ngarayakeunana ku cara nangkeup pamajikan jeung indungna, samentara babaturanana kalaluar ti imah pikeun nyanghareupan balaréa anu geus mimiti kumpul. Peuting éta, rébu-rébu jalma datang ti Canton jeung sabudeureunana pikeun ngadéngékeun McKinley biantara ti buruan imahna. Konvénsi ogé milih Garret Hobart ti New Jersey salaku calon wakil présidén, pilihan anu saenyana mah ditangtukeun ku Hanna. Hobart téh saurang ahli hukum sarta pangusaha jegud anu teu pati kasohor, tapi sakumaha ceuk Herbert Croly, "sanajan manéhna teu pati méré kakuatan tambahan kana éta pasangan, manéhna ogé henteu ngaleutikan kakuatanana".{{sfn|Horner|pp=159–62}}{{sfn|Williams|p=59}}
===Kampanye pamilihan umum===
[[File:McKinley straddle.jpg|thumb|left|Méméh konvénsi 1896, McKinley nyoba nyokot jalan tengah dina urusan mata uang. Kartun William Allen Rogers nembongkeun McKinley keur "tumpak pager" (bingung milih) antara emas jeung pérak.]]
Méméh konvénsi Républik, McKinley téh siga "straddle bug" (jalma nu cangcaya) dina urusan mata uang, leuwih milih posisi tengah saperti bimetalismeu (maké emas jeung pérak) dumasar perjangjian internasional. Dina poéan pamungkas méméh konvénsi, McKinley mutuskeun—sanggeus ngadéngé masukan ti para pulitikus jeung pangusaha—yén parténa kudu ngadukung standar emas, sanajan tetep muka lolongkrang pikeun bimetalismeu mun aya gawé bareng jeung nagara séjén.{{sfn|Phillips|pp=52, 81–82}} Ieu putusan ngabalukarkeun sababaraha utusan ti wilayah kulon, anu dipingpin ku Sénator Henry M. Teller, milih kaluar (walk out) tina konvénsi. Sanajan kitu, pacogrégan di jero Républik mah leutik kénéh dibandingkeun jeung Parté Démokrat.{{sfn|Cherny|pp=55–56}}{{sfn|Jones|p=177}}
Kaayaan ékonomi anu keur werit nambahan kakuatan pikeun pihak anu ngadukung pérak bébas (free silver). Ieu pasualan misahkeun Parté Démokrat kalayan pait pisan; Présidén Cleveland panceg ngadukung standar emas, tapi beuki loba urang Démokrat di pilemburan, utamana di Kidul jeung Kulon, anu mikahayang pérak.{{sfn|Gould|pp=10–11}} Kaum pérak hasil ngawasa konvénsi Démokrat sarta milih [[William Jennings Bryan]] jadi calon présidén; manéhna hasil ngahudang sumanget utusan ku biantara "Cross of Gold". Radikalna pamadegan ékonomi Bryan ngajadikeun para bankir reuwas—maranéhna nyangka program Bryan bakal ngabangkrutkeun pausahaan karéta api sarta ngaruksak ékonomi. Hanna tuluy ménta pangrojong ti para pangusaha pikeun stratégi kameunanganana, sarta maranéhna méré bantuan $3,5 juta pikeun nyéwa juru biantara sarta nyitak leuwih ti 200 juta pamflét anu ngadukung posisi Républik.{{sfn|Leech|pp=85–87}}
{{listen
| filename = William McKinley campaign speech 1896.ogg
| title = McKinley biantara ti buruan imahna
| description = Rekaman sajarah William McKinley. Bagian pamungkas (ti menit 5:40) eusina cuplikan biantara kampanye taun 1896.
}}
Kampanye Bryan mah diukur ngan miboga modal paling loba $500,000. Ku modal kamonésan biantara sarta tanaga anu ngora kénéh, Bryan mutuskeun pikeun ngayakeun tur kampanye maké karéta api ka sakuliah nagara kalayan skala anu can aya tandingna.{{sfn|Williams|pp=130–31}} Hanna ngajurung McKinley sangkan nandingan éta tur, tapi McKinley nolak alatan ngarasa Bryan leuwih tapis dina biantara panggung: "Kuring mah mending masang trapeze (parabot sirkus) di buruan hareup sarta tanding jeung atlit profésional batan kudu indit biantara nandingan Bryan. Kuring mah kudu ''mikir'' heula mun biantara."{{sfn|Williams|pp=130–31}} Tibatan manéhna anu datang ka rahayat, McKinley milih cicing di imahna di Canton sarta ngantep rahayat anu datang ka manéhna. Strategi "Front Porch Campaign" (Kampanye Buruan Imah) ieu jagana jadi legenda dina sajarah pulitik Amérika.{{sfn|Williams|pp=130–31}}
[[File:Flower delegation.jpg|thumb|left|William jeung Ida McKinley poto bareng jeung rombongan "Flower Delegation" ti Oil City, Pennsylvania, di hareupeun imahna.]]
McKinley narima datangna rahayat unggal poé iwal poé Minggu, narima rombongan delegasi tina téras (buruan) hareup imahna.{{sfn|Leech|pp=88–89}} Pausahaan karéta api méré potongan harga murah pikeun para pangunjung—nepi ka koran anu pro-pérak, ''Cleveland Plain Dealer'', nyawad yén indit ka Canton téh geus dijadikeun "leuwih murah batan cicing di imah".{{sfn|Harpine|p=52}} Delegasi marawai ti stasion karéta api nuju ka imah McKinley di North Market Street. Di dinya, maranéhna kumpul hareupeun téras—malah aya anu rerencepan nyeték kai téras keur kenang-kenangan—nalika McKinley biantara. Kabéh biantara McKinley disusun kalayan taliti pisan sangkan teu aya omongan anu salah; malah omongan delegasi ogé kudu disatujuan heula ku McKinley.{{sfn|Williams|pp=131, 226}}
[[File:Man of Mark.png|thumb|right|upright|Kartun Homer Davenport taun 1896 anu nembongkeun McKinley salaku "bonéka" Mark Hanna.]]
Koran-koran Démokrat lolobana nolak ngadukung Bryan, iwal ''New York Journal'' milik William Randolph Hearst. Dina laporan anu beurat sabeulah sarta kartun seukeut ti Homer Davenport, Hanna digambarkeun jahat salaku inohong "plutokrat" anu nincak kaum buruh.{{sfn|Jones|pp=176–77}} McKinley sorangan digambarkeun salaku budak leutik anu gampang diatur ku pangusaha gedé. Nepi ka ayeuna, ieu gambaran masih mangaruhan pandangan jalma ka Hanna jeung McKinley.{{sfn|Horner|pp=272, 318}}
McKinley mimitina nyangka yén pasualaan pajeg bakal jadi pangbeuratna, tapi tétéla pasualaan pérak jeung emas anu ngawasa kampanye.{{sfn|Morgan|p=170}} Médan perang utama aya di Midwest—sabab wilayah Kidul jeung Kulon geus pasti milih Bryan.{{sfn|Kazin|p=68}} Nalika nagara bagian Maine jeung Vermont ngadukung McKinley dina Séptémber, geus kaciri yén pangrojong pikeun pérak mimiti nyirorot. Dina 3 Nopémber 1896, McKinley unggul mutlak di wilayah Northeast sarta Midwest kalayan sora 51%.{{sfn|Kazin|pp=76–77}} Bryan ngan fokus kana pasualaan pérak sarta teu bisa narik ati kaum buruh di kota-kota gedé; ampir sakabéh kota gedé (populasi luhureun 100.000) milih McKinley, iwal ti Denver, Colorado.{{sfn|Kazin|pp=76–77}}
[[File:ElectoralCollege1896.svg|right|thumb|upright=1.25|Hasil sora Electoral College 1896]]
Dina Januari 1898, Spanyol jangji bakal méré sababaraha kalongsoran (konsési) ka para pamberontak, tapi nalika konsul Amérika [[Fitzhugh Lee]] ngalaporkeun ayana karusuhan di [[Havana]], McKinley sapuk pikeun ngirimkeun kapal perang [[USS Maine (ACR-1)|USS ''Maine'']].{{sfn|Gould|pp=71–72}} Dina 15 Pébruari, kapal ''Maine'' bitu sarta tilelep nepi ka niwaskeun 266 urang prajurit.{{sfn|Gould|p=74}} Perhatian masarakat langsung museur kana éta krisis sarta miboga kacindekan yén saha waé anu masang bomna, Spanyol geus kaleungitan kadali ka Kuba. McKinley keukeuh yén pangadilan militer ([[Naval Board of Inquiry]]) kudu nangtukeun heula naha éta bitu téh alatan kacilakaan atawa lain.{{sfn|Leech|pp=171–72}} Badami jeung Spanyol terus lumaku nalika pangadilan mariksa bukti-bukti, tapi dina 20 Maret, pangadilan mutuskeun yén ''Maine'' téh dibitukeun ku ranjau jero cai.{{sfnm|1a=Leech|1p=173|2a=Gould|2pp=78–79}} Nalika desekan perang beuki kuat di Kongrés, McKinley terus nyoba badami pikeun kamerdékaan Kuba.{{sfn|Gould|pp=79–81}} Spanyol nolak usulan McKinley, sarta dina 11 April, McKinley masrahkeun ieu pasualan ka Kongrés. Manéhna teu ménta perang, tapi Kongrés nyokot putusan sarta ngumumkeun perang dina 20 April, kalayan tambahan [[Teller Amendment]] anu negeskeun yén Amérika teu boga niat pikeun nyangking (aneksasi) Kuba.{{sfn|Gould|pp=86–87}} Ahli sajarah Nick Kapur boga pamanggih yén léngkah McKinley téh dumasar kana ajén-inajén badami, sipat pasifis, kamanusaan, sarta kadali diri anu jalu (manly), lain alatan desekan ti luar.<ref>Nick Kapur, "William McKinley's Values and the Origins of the Spanish‐American War: A Reinterpretation." ''Presidential Studies Quarterly'' 41.1 (2011): 18–38.</ref>
Mekarna telegraf jeung telepon méré lolongkrang keur McKinley pikeun leuwih jero ngatur jalanna perang poé demi poé batan présidén-présidén saméméhna, sarta manéhna ngagunakeun éta téknologi anyar pikeun ngarahkeun gerakan angkatan darat jeung laut sakumaha mampuhna.{{sfn|Gould|pp=91–93}} McKinley ngarasa Alger kurang mampuh jadi Sekretaris Perang, sarta manéhna ogé kurang cocog jeung panglima luhur Angkatan Darat, [[Nelson A. Miles]].{{sfn|Gould|pp=102–03}} Ku jalan naliwangkeun maranéhna, manéhna ménta nasehat stratégi ti Jenderal John Schofield sarta Adjudan Jenderal Henry Clarke Corbin.{{sfn|Gould|pp=102–03}} Ieu perang ogé mawa parobahan dina kabinét McKinley, nalika présidén narima pangunduran diri Sherman salaku Sekretaris Nagara. [[William R. Day]] satuju pikeun ngagentos samentara nepi ka perang réngsé.{{sfnm|1a=Gould|1p=94|2a=Leech|2p=191}}
Dina waktu dua minggu, angkatan laut meunangkeun kameunangan munggaranana nalika Komodor [[George Dewey]] ngancurkeun armada Spanyol dina [[Battle of Manila Bay]] di Filipina.{{sfn|Leech|pp=203–07}} Kameunangan mutlak Dewey ieu ngalegaan wengkuan perang anu tadina museur di Karibia jadi nangtukeun nasib sakabéh jajahan Spanyol di Pasifik.{{sfn|Gould|p=96}} Bulan saterusna, McKinley nambahan jumlah [[Eighth Army Corps (Spanish-American War)|pasukan anu dikirim ka Filipina]] sarta méré kawenang ka Komandan Mayor Jenderal Wesley Merritt pikeun nyusun sistem hukum jeung pajeg—kabutuhan pikeun penjajahan jangka panjang.{{sfn|Gould|pp=97–98}} Nalika pasukan nepi di Filipina dina tungtung Juni 1898, McKinley geus mutuskeun yén Spanyol kudu masrahkeun éta kapuloan ka Amérika Sarikat.{{sfn|Gould|p=101}} Sanajan manéhna ngaku kabuka kana sagala pamadegan, manéhna yakin yén sajalan jeung majuna perang, masarakat bakal nungtut sangkan éta kapuloan dicepeng salaku hadiah perang.{{sfn|Morgan|pp=467–68}}
Samentara éta, di médan perang Karibia, pasukan badag anu diwangun ku tantara tetep jeung sukarelawan kumpul deukeut Tampa, Florida, pikeun nyerang Kuba.{{sfn|Leech|pp=214–15}} Sabada sababaraha kali nyangsang, angkatan darat anu dipingpin ku Mayor Jenderal William Rufus Shafter badarat deukeut [[Santiago de Cuba]] dina 22 Juni.{{sfn|Gould|pp=107–09}} Pasukan Shafter campuh jeung kakuatan Spanyol dina 2 Juli dina [[Battle of San Juan Hill]].{{sfn|Leech|pp=249–52}} Dina perang anu ragot sapoé jeput, pasukan Amérika hasil unggul, sanajan dua pihakna kudu kaleungitan loba korban.{{sfn|Gould|pp=109–110}} Poé saterusna, armada Karibia Spanyol anu nyumput di palabuan Santiago nyoba kabur ka laut leupas tapi diancurkeun ku Armada Atlantik Kalér dina [[Battle of Santiago de Cuba|perang laut pangbadagna di éta mangsa]].{{sfn|Leech|pp=253–58}} Shafter ngepung kota Santiago, anu ahirna nyerah dina 17 Juli, ngajadikeun Kuba sacara éféktif aya dina kadali Amérika.{{sfn|Gould|pp=110–12}} McKinley jeung Miles ogé maréntahkeun serangan ka [[Puerto Rico]], anu ampir taya perlawanan nalika pasukan badarat dina bulan Juli.{{sfn|Gould|pp=110–12}} Jauh na jarak ti nagara asal sarta hancurna angkatan laut Spanyol ngajadikeun maranéhna teu bisa ngirim deui bantuan, sahingga pamaréntah Spanyol mimiti néangan jalan pikeun mungkas peperangan.{{sfn|Gould|pp=112–13}}
===Daméy sarta nambahanana wilayah===
[[File:Jules Cambon signs Treaty of Paris, 1899.JPG|thumb|left|Nandatanganan [[Treaty of Paris (1898)|Perjangjian Paris]]]]
Kabinét McKinley sapuk jeung manéhna yén Spanyol kudu miang ti Kuba jeung Puerto Rico, tapi maranéhna béda pamadegan ngeunaan Filipina, aya anu hayang nyangking (aneksasi) sakabéh éta kapuloan sarta aya ogé anu ngan hayang miboga pangkalan angkatan laut di éta wilayah.{{sfn|Gould|p=117}} Sanajan sora rahayat kacirina leuwih condong kana nyangking Filipina, sababaraha inohong pulitik kasohor—kaasup urang Démokrat kawas Bryan jeung Cleveland, sarta Liga Anti-Impérialis Amérika anu anyar dibentuk—nembongkeun panolakanana.{{sfn|Gould|p=116}}
McKinley ngusulkeun pikeun muka badami jeung Spanyol dumasar kana dibébaskeunana Kuba sarta dicangkingna Puerto Rico, samentara status pamungkas Filipina bakal dibadamikeun deui saterusna.{{sfn|Gould|pp=118–19}} Manéhna panceg dina éta tungtutan sanajan kaayaan militer di Kuba mimiti nyirorot nalika tantara Amérika ditarajang ku kasakit [[yellow fever]] (muriang konéng).{{sfn|Gould|pp=118–19}} Spanyol ahirna satuju kana gencatan senjata dumasar kana éta pasaratan dina 12 Agustus, sarta badami perjangjian dimimitian di Paris dina bulan Séptémber 1898.{{sfn|Gould|pp=120–21}} Éta sawala lumangsung nepi ka 18 Désémber, nalika [[Treaty of Paris (1898)|Perjangjian Paris]] ditandatanganan.{{sfn|Gould|pp=142–43}} Amérika Sarikat meunangkeun Puerto Rico, Filipina, sarta pulo [[Guam]], samentara Spanyol masrahkeun hakna kana Kuba; salaku imbalanana, Amérika Sarikat sapuk pikeun mayar $20 juta ka Spanyol.{{sfn|Gould|pp=142–43}} McKinley rada hésé ngayakinkeun Sénat sangkan nyaluyuan éta perjangjian ku dua per tilu sora, tapi ku jalan lobi-lobi manéhna jeung Wakil Présidén Hobart, ahirna hasil, sarta Sénat méré sora satuju dina 6 Pébruari 1899 kalayan babandingan sora 57 lawan 27.{{sfnm|1a=Gould|1pp=144–50|2a=Morgan|2p=320}}
====Hawaii====
[[File:Annexation Here to Stay (edit).jpg|thumb|upright=0.85|[[Newlands Resolution|Panyangkingan]] [[Republic of Hawaii]] dina taun 1898]]
Nalika perang lumangsung, McKinley ogé narékahan pikeun nyangking [[Republic of Hawaii]]. Éta républik anyar, anu dikawasa ku kapentingan bisnis, geus [[Overthrow of the Kingdom of Hawaii|ngaléngsérkeun Ratu]] dina taun 1893 nalika sang Ratu nyoba mulikkeun deui kakawasaan monarki ku jalan ngabatalkeun konstitusi bayonét.{{sfn|Gould|p=48}} Aya rojongan kuat ti Amérika pikeun nyangking éta wilayah, sarta kabutuhan pangkalan di Pasifik dina mangsa perang jadi beuki tétéla sabada Perang Manila.{{sfn|Gould|pp=49–50}} McKinley mimiti ngajabat salaku pangrojong panyangkingan Hawaii, sarta ngalobi Kongrés sangkan gura-giru meta, bari méré gorowok pangéling yén lamun cicing baé bakal nimbulkeun révolusi ti pihak karajaan atawa dicangking ku Jepang.{{sfn|Gould|pp=49–50}} Kusabab geus neguh bakal hésé meunangkeun dua per tilu sora Sénat pikeun nyatujuan perjangjian panyangkingan, McKinley malah ngarojong tarekah utusan Démokrat [[Francis G. Newlands]] ti Nevada pikeun ngahontal éta hasil ku jalan [[joint resolution]] (résolusi babarengan) ti dua déwan di Kongrés.{{sfn|Phillips|p=99}} Hasilna, [[Newlands Resolution]] ditarima ku sora mayoritas di dua déwan, sarta McKinley nandatanganan éta aturan dina 8 Juli 1898.{{sfn|Phillips|p=99}} Panulis biografi McKinley, H. Wayne Morgan, nyatet, "McKinley téh mangrupa sumanget anu ngarahkeun panyangkingan Hawaii, nembongkeun... kapancegan dina narékahanana"; présidén nyarita ka Cortelyou, "Urang butuh Hawaii sakumaha butuhna, malah leuwih butuh batan urang butuh California. Ieu téh mangrupa [[manifest destiny]] (takdir anu nyata)."{{sfn|Morgan|pp=223, 225}}
===Ngalegaan pangaruh ka mancanagara===
====Panto kabuka di Cina====
Méméh badami daméy jeung Spanyol dimimitian, McKinley ménta Kongrés pikeun ngawangun komisi anu mariksa lolongkrang dagang di Asia sarta ngusung kabijakan "[[Open Door Policy]]" (Kawijakan Panto Kabuka), dimana sakabéh nagara bisa kalayan bébas dagang jeung Cina sarta taya sahiji nagara anu meunang ngarempak kadaulatan wilayah éta nagara.{{sfn|Gould|p=201}}
[[File:Siege of Peking, Boxer Rebellion.jpg|thumb|left|Prajurit Amérika naék kana témbok Beijing pikeun méré bantuan nalika dikepungna utusan internasional, Agustus 1900]]
Para misionaris Amérika kaancam pati nalika [[Boxer Rebellion]] ngancam urang deungeun di Cina.{{sfn|Gould|pp=220–22}} Urang Amérika jeung urang kulon séjénna di [[Peking]] dikepung, sarta ku jalan gawé bareng jeung kakuatan kulon séjénna, McKinley maréntahkeun 5.000 pasukan ka éta kota dina Juni 1900 dina misi [[China Relief Expedition]].{{sfn|Lafeber|p=714}} Urang kulon hasil disalametkeun dina bulan saterusna, tapi sababaraha urang Démokrat di Kongrés protés kana tindakan McKinley anu ngirimkeun pasukan tanpa badami heula jeung legislatif.{{sfn|Gould|pp=220–22}} Tindakan McKinley ieu jadi conto (présidén) anu dituturkeun ku lolobana panerusna dina ngagunakeun kakawasaan mandiri pikeun ngatur militer.{{sfn|Lafeber|p=714}} Sabada éta pemberontakan réngsé, Amérika Sarikat negeskeun deui komitmenna kana kawijakan Panto Kabuka, anu jadi dadasar kawijakan Amérika ka Cina.{{sfn|Gould|p=233}}
====Terusan Panama====
McKinley sareng [[John Hay|Hay]] ngayakeun badami sareng Inggris ngeunaan rencana pangwangunan terusan motong Amérika Tengah. Perjangjian Clayton–Bulwer taun 1850 ngalarang kadua nagara miboga kadali éksklusif kana éta terusan. Perang nu geus liwat nembongkeun héséna ngatur angkatan laut di dua samudra nalika kapal-kapal kudu ngurilingan Amérika Kidul pikeun nepi ka Pasifik.{{sfn|Gould|pp=196–98}} Ayeuna, kalayan kapentingan bisnis jeung militer Amérika anu beuki kuat di Asia, éta terusan téh karasa beuki penting, sarta McKinley ngadesek sangkan éta perjangjian dirobah.{{sfn|Gould|pp=196–98}} Hay jeung duta badag Inggris, [[Julian Pauncefote, 1st Baron Pauncefote|Julian Pauncefote]], sapuk yén Amérika Sarikat bisa ngatur éta terusan asal dibuka pikeun sakabéh kapal jeung teu dijadikeun bénténg militer.{{sfn|McCullough|pp=256–59}} McKinley satuju, tapi Sénat nolak sarta nungtut sangkan Amérika diidinan ngawangun bénténg di dinya.{{sfn|McCullough|pp=256–59}} Hay ngarasa éra ku éta panolakan sarta ménta mundur, tapi McKinley nolak sarta maréntahkeun manéhna pikeun neruskeun badami nepi ka tujuanna kahontal.{{sfn|McCullough|pp=256–59}} Usahana hasil, perjangjian anyar disusun sarta disatujuan, tapi éta kajadian sabada McKinley dimaotkeun dina taun 1901.{{sfn|McCullough|pp=256–59}} Hasilna di mangsa Roosevelt nyaéta [[Panama Canal|Terusan Panama]].
===Pajeg jeung bimetalismeu===
[[File:McKinley Prosperity.jpg|thumb|upright|Poster pamilihan taun 1900 kalayan téma McKinley geus mulikkeun kamakmuran Amérika]]
McKinley geus kakoncara di Kongrés alatan ngadukung pajeg (tarif) luhur pikeun ngajaga bisnis Amérika sarta buruh pabrik. Kalayan kontrol Républik di Kongrés, disahkeun [[Dingley Act]] anu naékeun tarif keur wol, gula, jeung barang méwah.{{sfn|Gould|pp=44–45}} McKinley nandatanganan éta aturan dina 24 Juli 1897.<ref name="ucsb">{{cite web | title=William McKinley Event Timeline | website=The American Presidency Project | url=https://www.presidency.ucsb.edu/documents/william-mckinley-event-timeline | access-date=May 24, 2024}}</ref>
Amérika ogé nyoba badami jeung Inggris ngeunaan [[bimetallism|bimetalismeu]] (pamakéan emas jeung pérak). Perdana Menteri Inggris [[Robert Gascoyne-Cecil, 3rd Marquess of Salisbury|Lord Salisbury]] mimitina némbokeun minat, tapi pamaréntahan India (jajahan Inggris) nolak éta usulan.{{sfn|Gould|pp=45–46}} Alatan éta usaha gagal, McKinley ahirna ninggalkeun pérak sarta milih standar emas.{{sfn|Morgan|pp=217–18}} Sumanget rahayat pikeun "pérak bébas" ogé ngurangan sabab ékonomi mimiti maju sarta lobana papanggihan emas anyar di Klondike sarta Australia.{{sfnm|1a=Nichols||1p=586|2a=Gould||2p=46}} Dina 14 Maret 1900, McKinley nandatanganan [[Gold Standard Act]] (Undang-Undang Standar Emas) maké pulpén emas.{{sfn|Gould|pp=169–71}}
===Hak sipil===
[[File:McKinley at Atlanta.jpg|thumb|right|McKinley (katuhu tengah) bareng jeung Gubernur Georgia Allen D. Candler sarta Jenderal Shafter dina parade di Atlanta, 15 Désémber 1898]]
Sabada McKinley kapilih dina 1896, urang kulit hideung miboga harepan aya kamajuan dina kasaruaan hak. McKinley kungsi nyarita ngalawan tindakan [[lynching]] (hakiman massa) nalika jadi gubernur.{{sfn|Gould|pp=153–54}} Sanajan kitu, McKinley leuwih mentingkeun ngahijikeun deui wilayah-wilayah Amérika (rekonsiliasi), sahingga kawijakanana dianggap nguciwakeun pikeun urang kulit hideung.{{sfn|Gould|pp=153–54}}
Tindakan McKinley kana kekerasan rasial dianggap minim pisan. Nalika aya pajabat pos kulit hideung di Georgia (1897) jeung South Carolina (1898) anu diserang, McKinley teu ngaluarkeun pernyataan nanaon.{{sfn|Gould|p=155}} Kitu ogé nalika kajadian [[Wilmington insurrection of 1898|pemberontakan Wilmington]], dimana sakelompok jalma anu percaya kaunggulan kulit bodas (white supremacists) ngaléngsérkeun pamaréntahan anu sah, McKinley nolak ngirimkeun pasukan pikeun ngajaga warga kulit hideung.<ref name=Time>{{cite news|title=The 1898 Wilmington Massacre Is an Essential Lesson in How State Violence Has Targeted Black Americans|url=https://time.com/5861644/1898-wilmington-massacre-essential-lesson-state-violence/|date=July 1, 2020|newspaper=Time Magazine}} {{Webarchive|url=https://web.archive.org/web/20210916115308/https://time.com/5861644/1898-wilmington-massacre-essential-lesson-state-violence/ |date=September 16, 2021 }}</ref>
Nurutkeun ahli sajarah Clarence A. Bacote, méméh Perang Amérika-Spanyol, urang kulit hideung nganggap McKinley salaku "sobat panghadéna".{{sfn|Bacote|p=234}} McKinley kungsi ngadatangan [[Tuskegee Institute]] sarta tokoh pendidikan kulit hideung [[Booker T. Washington]].{{sfn|Phillips|pp=156–57}} Sanajan kitu, dina tur ka wilayah Kidul, manéhna teu nyinggung pisan ngeunaan kakerasan ras. Ahli sajarah Gould nyimpulkeun yén McKinley kurang miboga tetempoan (visi) pikeun ngaluhuran prasangka dina zamanna pikeun masa depan Amérika anu leuwih hadé.{{sfnm|1a=Gould||1pp=159–60|2a=Phillips||2p=149}}
===Pamilihan 1900===
{{Main|1900 United States presidential election}}
[[File:The Administration's Promises Have Been Kept.jpg|thumb|McKinley kampanye dumasar kana catetan kasuksésan ékonomi sarta kameunangan perang dina taun 1900, sahingga gampang kapilih deui ngéléhkeun William Jennings Bryan.]]
Urang Républik umumna suksés dina pamilihan nagara bagian jeung lokal di sakuliah nagara dina taun 1899, sarta McKinley ngarasa optimis ngeunaan lolongkrangna pikeun kapilih deui dina taun 1900.{{sfn|Gould|pp=207–08}} Popularitas McKinley dina mangsa jabatan kahiji ngajamin manéhna dicalonkeun deui pikeun kadua kalina.{{sfn|Gould|pp=213–14}} Hiji-hijina pananya pikeun pasangan Républik nyaéta ngeunaan calon wakil présidén; McKinley butuh batur sapaan anyar sabab Hobart tilar dunya dina tungtung taun 1899.{{sfn|Gould|pp=215–17}} McKinley mimitina milih [[Elihu Root]], tapi manéhna ngarasa Root téh keur migawé pancén anu kacida hadéna di Departemén Perang sahingga lebar lamun kudu dipindahkeun.{{sfn|Gould|pp=215–17}} Manéhna mertimbangkeun calon séjén kawas Allison, tapi taya nu bisa nandingan kakuatan béntang anyar Parté Républik, [[Theodore Roosevelt]].<ref>{{sfn|Phillips|pp=120–22}}</ref> Roosevelt, saurang pahlawan Perang Kuba anu keur ngajabat Gubernur New York, dipikaresep ku réa jalma pikeun jadi wakil présidén, sanajan Mark Hanna kurang satuju sabab nganggap Roosevelt téh jalma anu gurunggusuh (impulsif).{{sfn|Horner|pp=260–66}}
Nalika konvénsi Républik dimimitian di Philadelphia dina bulan Juni, McKinley negeskeun yén pilihan wakil présidén téh dipasrahkeun sagemblengna ka konvénsi.{{sfn|Gould|p=218}} Dina 21 Juni, McKinley dicalonkeun deui kalayan sora buleud, sarta kalayan rasa beurat ti Hanna, Roosevelt dicalonkeun jadi wakil présidén dina sora kahiji.{{sfn|Leech|pp=540–42}} Démokrat, dina konvénsina di Kansas City, nyalonkeun deui William Jennings Bryan, sahingga jadi tanding ulang ti kajadian taun 1896.{{sfn|Gould|pp=219–20}}
Sarat calon mah angget sarua, tapi pasualaan kampanye geus robah: "pérak bébas" masih jadi bahan sawala, tapi urang Républik leuwih museur kana kameunangan perang sarta kamakmuran ékonomi di jero nagri.{{sfnm|1a=Gould||1pp=226–27|2a=Leech||2pp=543–44}} Urang Démokrat nyoba nyerang McKinley ku jalan ngagambarkeun manéhna salaku badéga modal jeung pausahaan badag (trusts).{{sfnm|1a=Gould||1pp=227–28|2a=Leech||2pp=544–46}} Bryan ngayakeun tur biantara ka sakuliah nagara, samentara McKinley angget matuh di imah sarta ngan sakali biantara pikeun narima pencalonanana.{{sfn|Leech|pp=549–57}} Dina 6 Nopémber 1900, McKinley hasil meunangkeun kameunangan pangbadagna pikeun urang Républik saprak taun 1872, malah manéhna unggul di nagara bagian asal Bryan, Nebraska.{{sfn|Leech|p=559}}
===Mangsa jabatan kadua===
[[File:President William McKinley Taking The Oath (1901) by Thomas A. Edison.webm|thumb|Pelantikan kadua McKinley anu dirékam ku Thomas Edison. Ieu téh sumpah jabatan présidén munggaran anu dirékam dina sajarah AS.]]
Teu lila sabada pelantikan kaduana dina 4 Maret 1901, William jeung Ida McKinley ngayakeun tur genep minggu ka sakuliah nagara. Maranéhna sakuduna indit ka wilayah Kidul nepi ka basisir Pasifik, sarta ditungtungan ku kunjungan ka [[Pan-American Exposition]] di Buffalo, New York, dina 13 Juni 1901.{{sfn|Miller|pp=289–90}} Sanajan kitu, Sang Ibu Nagara (Ida) gering di California, sahingga McKinley kudu ngabatalkeun sababaraha acara biantara sarta nunda kunjungan ka paméran di Buffalo nepi ka bulan Séptémber.{{sfn|Gould|pp=247–49}} Manéhna méakkeun sabulan di Washington sarta dua bulan di Canton méméh indit ka Buffalo.{sfn|Gould|pp=247–49}}
==Dirogahala==
{{Main|Assassination of William McKinley}}
[[File:McKinley last photo.jpg|thumb|McKinley nuju ka [[Temple of Music]] dina 6 Séptémber 1901, sakedap méméh kajadian némbak.]]
Sanajan McKinley resep tepung jeung rahayat, Cortelyou (sekretarisna) ngarasa hariwang ku kaamanan présidén alatan loba kajadian rajapati ku kaum anarkis di Éropa.<ref>{{sfn|Miller|p=294}}</ref> Dina 5 Séptémber, McKinley méré biantara di hareupeun 50.000 jalma di tempat paméran, anu eusina ngajurung kana perjangjian dagang mancanagara. Ieu biantara téh sabenerna jadi dadasar rancana manéhna pikeun mangsa jabatan kadua.<ref>{{sfn|Miller|pp=298–300}}{{sfn|Gould|pp=250–51}}</ref>
Saurang lalaki dina éta balaréa, [[Leon Czolgosz]], boga niat jahat hayang maéhan McKinley. Czolgosz, anu kabaud ku paham anarkismeu, nunggu kasempetan di wangunan [[Temple of Music]] dina poé saterusna nalika présidén keur narima rahayat. Czolgosz nyumputkeun pestolna dina saputangan, sarta nalika gilirannana salaman, manéhna némbak McKinley dua kali dina bagian beuteung tina jarak deukeut.<ref>{{sfn|Miller|pp=300–01}}</ref>
Dina kaayaan kitu, McKinley masih kénéh ménta ka ajudanna sangkan méré nyaho éta béja kalayan ati-ati ka pamajikanana, sarta ménta sangkan balaréa ulah neunggeulan Czolgosz.<ref>{{sfn|Miller|pp=301–02}}</ref> McKinley dibawa ka imah John G. Milburn. Dina sababaraha poé awal, kaayaan McKinley kacirina mimiti hadé sahingga para menteri sarta Wakil Présidén Roosevelt bubar ti Buffalo.{{sfnm|1a=Miller||1pp=315–17|2a=Morgan||2pp=401–02}}
Nanging, dina isuk-isuk 13 Séptémber, kaayaanana ngadadak drop. Anu teu kanyahoan ku dokter harita, jaringan sakuliah tatu beuteungna geus ruksak sarta jadi racun kana getihna (gangrene).<ref>{{sfn|Leech|p=600}}</ref> Nalika peuting, McKinley geus sadar yén ajjalna geus deukeut, manéhna ngomong, "Geus taya gunana, Juragan. Jigana urang kudu ngadoa."<ref>{{sfn|Miller|pp=318–319}}</ref> Pamajikanana, Ida, ceurik bari ngomong hayang milu maot, tapi McKinley ngajawab kalayan tenang, "Urang kabéh ogé bakal indit. Kersaning Gusti anu bakal kajadian, lain kahayang urang."<ref>{{sfn|Miller|p=320}}</ref>
Dina jam 02:15 subuh, 14 Séptémber 1901, McKinley tilar dunya. Theodore Roosevelt rurusuhan mulang ka Buffalo sarta disumpah jadi présidén. Czolgosz ahirna dihukum pati maké korsi listrik dina 29 Oktober 1901.<ref>{{sfn|Miller|pp=321–30}}</ref>
==Pamakaman, tugu peringatan, sarta warisan==
===Pamakaman sarta tempat reureuh pamungkas===
[[File:President McKinley's funeral, 1901.webm|thumb|Iring-iringan layon McKinley dina 17 Séptémber 1901.]]
Nurutkeun Gould, "Sakuliah nagara ngarasa sedih anu kacida nalika ngadéngé béja pupusna McKinley."<ref>{{sfn|Gould|p=252}}</ref> Layon McKinley mimitina disimpen di East Room di Gedong Bodas, tuluy dibawa ka Capitol méméh dibawa ka Canton maké karéta api. Kira-kira 100.000 urang ngantri dina hujan pikeun méré panghurmatan pamungkas ka présidénna.<ref>{{sfn|Morgan|pp=402–03}}</ref>
Loba nu nyangka Ida McKinley moal kuat lila ditinggal maot ku salaki anu dipikacintana. Ida mémang teu milu kana upacara di Washington alatan teu kiat, tapi manéhna matuh di Canton sapanjang sésa hirupna bari terus nganjang ka tempat neundeun layon McKinley.<ref>{{sfn|Leech|p=602}}</ref> Ida tilar dunya dina umur 59 taun, 26 Méi 1907, ngan sababaraha bulan méméh tugu peringatan [[McKinley National Memorial]] réngsé diwangun.<ref>{{sfn|Miller|p=348}}</ref> William jeung Ida McKinley ayeuna dimakamkeun babarengan jeung dua putri maranéhna dina tugu marmer anu agréng di luhur pasir di kota Canton.{{sfnm|1a=McElroy||1pp=189–93|2a=Morgan||2p=406}}
===Tugu peringatan séjénna===
Salian ti makamna di Canton, loba kénéh tugu séjén pikeun ngahormat McKinley. Tugu William McKinley nangtung di hareupeun Gedong Nagara Ohio di Columbus, sarta patung marmer McKinley anu badag aya di tempat weton (gumelarna) di Niles. Aya dua puluh sakola di Ohio sarta loba deui sakola di sakuliah Amérika anu maké ngaran McKinley.<ref>{{sfn|McElroy|p=189}}</ref> Kurang leuwih sajuta dollar dikumpulkeun ti masarakat sarta dana publik pikeun ngawangun tugu-tugu peringatan dina taun mimitina sabada manéhna pupus.<ref>{{sfn|Olcott|p=388}}</ref> Kevin Phillips, nu nulis biografina, nyebutkeun yén lobana tugu peringatan di Ohio nembongkeun harepan urang Ohio harita yén McKinley bakal dijajarkeun jeung présidén-présidén agung Amérika.{{sfn|Phillips|p=161}}
Patung McKinley bisa kapanggih di leuwih ti dua belas nagara bagian. Dina taun 1896, saurang tukang neangan emas méré ngaran "Mount McKinley" pikeun [[Denali]], gunung pangluhurna di Amérika Kalér. Ngaran éta gunung dibalikkeun deui jadi Denali ku pamaréntah dina taun 1975 sarta 2015.<ref>{{cite news|last1=Hirschfeld Davis|first1=Julie|title=Mount McKinley Will Be Renamed Denali|url=https://www.nytimes.com/2015/08/31/us/mount-mckinley-will-be-renamed-denali.html?smid=fb-nytimes&smtyp=cur&_r=0|website=The New York Times |date=August 30, 2015}}</ref> Nanging, dina 20 Januari 2025, Présidén Donald Trump ngaluarkeun paréntah éksékutif pikeun mulikkeun deui ngaran McKinley kana éta gunung.<ref>{{Cite web |date=January 20, 2025 |title=Restoring names that honor American greatness|url=https://www.whitehouse.gov/presidential-actions/2025/01/restoring-names-that-honor-american-greatness/ |website=White House}}</ref>
===Warisan sarta citra sajarah===
[[File:Harriet Anderson Stubbs Murphy - William McKinley - Google Art Project.jpg|thumb|Potrét resmi Présidén William McKinley.]]
Phillips yakin yén bagian tina warisan McKinley nyaéta jalma-jalma anu aya dina pamaréntahanana anu jagana ngawasaan Parté Républik salila saparapat abad sabada manéhna tilar dunya. Para pajabat ieu kaasup Cortelyou, anu ngajabat dina tilu posisi Kabinét di handapeun Roosevelt, sarta Dawes, anu jadi wakil présidén di handapeun Coolidge. Jalma-jalma anu diangkat ku McKinley séjénna anu jagana jadi inohong utama nyaéta Day, anu diangkat ku Roosevelt ka Mahkamah Agung Amérika Sarikat, sarta William Howard Taft, anu dijadikeun Gubernur Jenderal Filipina ku McKinley sarta saterusna ngagantikeun Roosevelt jadi présidén.{{sfn|Phillips|pp=163–64}}
H. Wayne Morgan nyatet yén McKinley pupus salaku présidén anu paling dipikaasih dina sajarah.{{sfn|Morgan|p=404}} Nanging, sosok Roosevelt anu ngora sarta pinuh sumanget geuwat narik perhatian masarakat sarta nyieun kenangan ngeunaan McKinley lalaunan laleur. Nepi ka taun 1920, pamaréntahan McKinley ngan dianggap salaku "bubuka anu biasa-biasa baé méméh jagana datang Theodore Roosevelt anu pinuh énergi."{{sfn|Gould|p=252}} Ti mimiti taun 1950-an, McKinley mimiti meunang peunteun anu leuwih hadé, sanajan dina survéy-survéy présidén, manéhna biasana aya di tengah-tengah.{{sfn|Gould|p=252}}
Aya kasapukan di antara para ahli sajarah yén kapilihna McKinley nandaan parobahan jaman pulitik Amérika kana mangsa anu leuwih modern sarta pro-industri.<ref>{{cite book|author=Kenneth F. Warren|title=Encyclopedia of U.S. Campaigns, Elections, and Electoral Behavior|url=https://archive.org/details/encyclopediaofus0002unse_j8z1|year=2008|publisher=SAGE|page=211}}</ref> Kevin Phillips boga pamanggih yén peunteun rendah pikeun McKinley téh sabenerna teu pantes, sarta manéhna kudu dijajarkeun sanggeus présidén hébat kawas Washington jeung Lincoln.{{sfn|Phillips|pp=156–57}} Salah sahiji warisan McKinley anu nyata nyaéta jalma-jalma anu diangkat ku manéhna di pamaréntahan jagana jadi inohong penting, kawas William Howard Taft anu ahirna jadi présidén.{{sfn|Phillips|pp=163–64}} Salian ti éta, alatan kajadian rajapati McKinley, Kongrés ahirna mutuskeun yén nangtayungan kasalametan présidén kudu jadi tugas resmi Secret Service Amérika Sarikat.<ref>{{Cite web|url=https://www.smithsonianmag.com/smart-news/how-president-william-mckinleys-assassination-led-modern-secret-service-180964868/|title=How President William McKinley's Assassination Led to the Modern Secret Service|website=Smithsonianmag.com}}</ref>
Pasualan anu masih kontrovérsial nyaéta ngeunaan panyalapan wilayah (imperialismeu); iwal ti Filipina anu dibéré kamerdikaan dina taun 1946, Amérika Sarikat masih kénéh nyekel wilayah-wilayah anu dicangking di jaman McKinley.{{sfn|Phillips|p=154}} Ékspansi wilayah taun 1898 mindeng dianggap ku ahli sajarah salaku mimitina "Emperium Amérika".{{sfn|Phillips|p=99}} Morgan ningali yén sawala sajarah ieu mangrupa bagian tina perdebatan ngeunaan naékna Amérika salaku kakuatan dunya; manéhna ngarepkeun yén perdebatan ngeunaan tindakan McKinley bakal terus lumangsung salawasna tanpa aya kacindekan, sarta nyatet yén naon baé penilaian batur, salah sahiji motivasi McKinley téh nyaéta hayang ngarobah kahirupan rahayat Filipina jeung Kuba sangkan leuwih hadé.{{sfn|Morgan|p=468}}
Salaku saurang anu boga faham pulitik konservatif,<ref>[https://www.google.co.uk/books/edition/Presidential_Performance/EmLZb6uoo78C?hl=en&gbpv=1&dq=Cleveland+saw+conservative+Republican+William+McKinley%E2%80%99s+success+as+a+victory+for+his+own+policies.&pg=PA170&printsec=frontcover Presidential Performance A Comprehensive Review By Max J. Skidmore, 2014, P.170]</ref> McKinley mingpin pamaréntahan anu ngajalankeun kawijakan konservatif boh dina urusan luar nagri, hak sipil, ogé ékonomi. Ngeunaan ékonomi, salah saurang ahli sajarah nyebutkeun yén, "Ngeunaan pipilueun pamaréntah dina ékonomi di jaman McKinley, pendekatanna angget kana faham ''laissez-faire'' (pasar bébas)."<ref>[https://www.google.co.uk/books/edition/American_Conservatism/2dymBgAAQBAJ?hl=en&gbpv=1&dq=Concerning+government+intervention+in+the+economy+under+McKinley,+the+approach+remained+decidedly+laissez-faire&pg=PA189&printsec=frontcover American Conservatism History, Theory and Practice By Brian Farmer, 2005, P.189]</ref>
Morgan nyindekkeun yén naékna minat masarakat ka McKinley téh mangrupa bagian tina sawala ngeunaan kawijakan luar nagri Amérika anu beuki teges dina sababaraha dékade pamungkas:
{{blockquote|McKinley téh inohong utama dina sababaraha kajadian pangpentingna dina sajarah Amérika. Putusan-putusanana mangaruhan kawijakan sarta sikep publik di mangsa hareup. Peunteun para sarjana ka manéhna biasana beuki hadé nalika maranéhna nalungtik lalakon hirupna sacara jero. Malah jalma anu teu sapuk kana kawijakanana ogé ningali McKinley salaku pamingpin anu aktip, tanggung jawab, sarta weruh kana sagala putusan anu dicokotna. Sikepna anu tenang sarta cara gawéna anu rancingeus ngajadikeun manéhna rada jauh tina tetempoan publik. Tapi ayeuna, manéhna geus balik deui ka puseur kajadian, tempat dimana manéhna ngamimitian sagalana.{{sfn|Morgan|p=473}}}}
=== Galéri tugu peringatan ===
<gallery widths="200px" heights="200px">
File:McKinley Birthplace Memorial uncirculated dollar (common obverse).jpg |Duit koin emas "McKinley Birthplace Memorial" anu dicitak dina taun 1916 sarta 1917.
File:William McKinley Presidential Library & Museum.jpg|[[McKinley National Memorial|Makam McKinley]] di [[Canton, Ohio]].
File:McKinley Memorial Ohio Statehouse.JPG|''[[William McKinley Monument]]'' karya Hermon MacNeil di hareupeun Gedong Nagara Ohio, Columbus.
File:McKinley Monument, Buffalo, NY - IMG 3693.JPG|''[[McKinley Monument]]'' karya Alexander Phimister Proctor di hareupeun Balai Kota Buffalo.
File:McKinley Monument, Toledo, O. - DPLA - a6478c0228d574a7add483ba5b86bfb8 (page 1) (cropped).jpg|Tugu McKinley di hareupeun Pangadilan Lucas County, [[Toledo, Ohio]].
</gallery>
{| style="margin:auto"
| [[File:Mckinley stamp issues.jpg|thumb|upright=1.5|Perangko William McKinley tina citakan taun 1901 sarta 1927.]]
| [[File:500 USD note; series of 1934; obverse.jpg|thumb|upright=1.5|Gambar McKinley dina [[Large denominations of United States currency|duit kertas $500]].]]
|}
==Tempo ogé==
* [[List of heads of state and government who were assassinated or executed|Daptar kapala nagara anu dimaotkeun]]
* [[List of presidents of the United States|Daptar présidén Amérika Sarikat]]
* ''[[McKinley at Home, Canton, Ohio]]'' (film taun 1896)
== Catetan ==
{{notelist}}
== Rujukan ==
{{Reflist}}
* {{cite book
| last = Leech
| first = Margaret
| year = 1959
| title = In the Days of McKinley
| url = https://archive.org/details/indaysofmckinley00leec
| url-access = registration
| publisher = Harper and Brothers
| location = New York
| oclc = 456809
| ref = {{sfnRef|Leech}}
}} sajarah populer.
* {{cite book
| last = McCullough
| first = David
| author-link = David McCullough
| year = 1977
| title = The Path Between the Seas: The Creation of the Panama Canal 1870–1914
| publisher = Touchstone
| location = New York
| isbn = 978-0-671-24409-5
| ref = {{sfnRef|McCullough}}
| title-link = The Path Between the Seas: The Creation of the Panama Canal 1870–1914
}}
* {{cite book
| last = McElroy
| first = Richard L.
| year = 1996
| title = William McKinley and Our America
| url = https://archive.org/details/williammckinleyo0000mcel
| publisher = Stark County Historical Society
| location = Canton, Ohio
| isbn = 978-0-9634712-1-5
| ref = {{sfnRef|McElroy}}
}} sajarah populer.
* {{cite book
|title=President McKinley: Architect of the American Century
|url=https://archive.org/details/presidentmckinle0000merr
|first=Robert W.
|last=Merry
|author-link=Robert W. Merry
|place=New York
|publisher=[[Simon & Schuster]]
|year=2017
|isbn=9781451625448
}} sajarah populer.
* {{cite book
| last = Miller
| first = Scott
| year = 2011
| title = The President and the Assassin
| publisher = Random House
| location = New York
| isbn = 978-1-4000-6752-7
| ref = {{sfnRef|Miller}}
| url = https://archive.org/details/presidentassassi00mill
}}
* {{cite book
| last = Morgan
| first = H. Wayne
| year = 2003
| title = William McKinley and His America | url = https://archive.org/details/williammckinleyh0000morg
|edition=revised
| publisher = The Kent State University Press
| location = Kent, Ohio
| isbn = 978-0-87338-765-1
| ref = {{sfnRef|Morgan}}
}}
* Morgan, H. Wayne (1969). ''From Hayes to McKinley: National Party Politics, 1877–1896'', panalungtikan akademis.
* Oberholtzer, Ellis Paxson (1937). ''A History of the United States since the Civil War''. Volume V: ''1888–1901''. Macmillan. 791 pp.
* {{cite book
| last = Olcott
| first = Charles
| author-link = Charles S. Olcott
| year = 1916
| title = The Life of William McKinley, 2 vol.
| publisher = Houghton Mifflin
| location = Boston
| url = https://archive.org/details/williammckinley02olcogoog
| quote = the life of william mckinley olcutt.
| access-date = March 23, 2012
| ref = {{sfnRef|Olcott}}
}} sumber heubeul tapi detil pisan.
* {{cite book
| last = Phillips
| first = Kevin
| author-link = Kevin Phillips (political commentator)
| year = 2003
| title = William McKinley
| publisher = Times Books
| location = New York
| isbn = 978-0-8050-6953-2
| ref = {{sfnRef|Phillips}}
| url = https://archive.org/details/williammckinley00phil
}} museur kana sisi pamilih.
* {{cite book
| last = Pratt
| first = Walter F.
| year = 1999
| title = The Supreme Court under Edward Douglass White, 1910–1921
| publisher = University of South Carolina Press
| location = Columbia, South Carolina
| url = https://archive.org/details/supremecourtunde00prat_0
| url-access = registration
| isbn = 978-1-57003-309-4
| ref = {{sfnRef|Pratt}}
}}
* {{cite book
|title=The Triumph of William McKinley: Why the Election of 1896 Still Matters
|url=https://archive.org/details/triumphofwilliam0000rove
|first=Karl
|last=Rove
|author-link=Karl Rove
|ref={{sfnRef|Rove}}
|place=New York
|publisher=[[Simon & Schuster]]
|year=2015
|isbn=9781476752952
}} museur kana sisi pamilih.
* {{cite book
| last = Williams
| first = R. Hal
| year = 2010
| title = Realigning America: McKinley, Bryan and the Remarkable Election of 1896
| url = https://archive.org/details/realigningameric00unse
| publisher = University Press of Kansas
| location = Lawrence, Kansas
| isbn = 978-0-7006-1721-0
| ref = {{sfnRef|Williams}}
}}
* {{cite journal
| last = Nice
| first = David C.
|date=September 1984
| title = The Influence of War and Party System Aging on the Ranking of Presidents
| journal = The Western Political Quarterly
| volume = 37
| issue = 3
| pages = 443–55
| jstor = 448445
| doi = 10.2307/448445
| ref = {{sfnRef|Nice}}
}}
* {{cite journal
| last = Nichols
| first = Jeannette P.
|date=December 1933
| title = Silver Diplomacy
| url = https://archive.org/details/sim_political-science-quarterly_1933-12_48_4/page/565
| journal = Political Science Quarterly
| volume = 48
| issue = 4
| pages = 565–88
| jstor = 2142930
| doi = 10.2307/2142930
| ref = {{sfnRef|Nichols}}
}}
* Offner, John L. "McKinley and the Spanish‐American War." ''Presidential Studies Quarterly'' 34.1 (2004): 50-61. [https://cooperative-individualism.org/offner-john_mckinley-and-the-spanish-american-war-2004-mar.pdf online]
* {{cite journal
| last = Rauchway
| first = Eric
| author-link = Eric Rauchway
|date=July 2005
| title = William McKinley and Us
| journal = The Journal of the Gilded Age and Progressive Era
| volume = 4
| issue = 3
| pages = 235–53
| jstor = 25144402
| ref = {{sfnRef|Rauchway}}
| doi = 10.1017/S1537781400002644
| s2cid = 162552066
}}
* {{cite journal
| last = Weisenburger
| first = Francis P.
| date=June 1934
| title = The Time of Mark Hanna's First Acquaintance with McKinley
| journal = The Mississippi Valley Historical Review
| volume = 21
| issue = 1
| pages = 78–80
| jstor = 1896406
| doi = 10.2307/1896406
| ref = {{sfnRef|Weisenburger}}
}}
* {{cite book
| last = Phillips
| first = Kevin
| author-link = Kevin Phillips (political commentator)
| year = 2003
| title = William McKinley
| publisher = Times Books
| location = New York
| isbn = 978-0-8050-6953-2
| ref = {{sfnRef|Phillips}}
| url = https://archive.org/details/williammckinley00phil
}}
* {{cite book
| last = Morgan
| first = H. Wayne
| year = 2003
| title = William McKinley and His America | url = https://archive.org/details/williammckinleyh0000morg
|edition=revised
| publisher = The Kent State University Press
| location = Kent, Ohio
| isbn = 978-0-87338-765-1
| ref = {{sfnRef|Morgan}}
}}
{{refend}}
===Disertasi PhD===
Téks lengkep sayogi sacara online ngalangkungan perpustakaan akademik.
* Brady, David William. "A Congressional Response to a Stress Situation: Party Voting in the Mckinley Era" (The University of Iowa; Proquest Dissertations Publishing, 1970. 7023867).
* Damiani, Brian Paul. "Advocates of Empire: William Mckinley, The Senate and American Expansion, 1898-1899" (University of Delaware; Proquest Dissertations Publishing, 1978. 7816908).
* Labinski, Nicholas Winter. "A Transitional Moment: William McKinley's Foreign Policy Rhetoric and America's Outward Turn" (University of Kansas; ProQuest Dissertations Publishing, 2017. 10687965).
* Matlosz, Gregory. "The Political Symbiosis of Rutherford B. Hayes & William McKinley" (Drew University; ProQuest Dissertations Publishing, 2015. 3700842).
* Ofek, Hillel. "A Just Peace: Grover Cleveland, William McKinley, and the Moral Basis of American Foreign Policy" (University of Texas at Austin; ProQuest Dissertations Publishing, 2018. 28166006).
* Waksmundski, John. "Mckinley Politics and the Changing Attitudes Toward American Labor, 1870-1900" (The Ohio State University; Proquest Dissertations Publishing, 1972. 7311599).
==Tumbu luar==
{{Sister project links |wikt=no |commons=William McKinley |b=no |n=no |q=William McKinley |s=William McKinley |v=no |species=no |author=yes}}
===Resmi===
* [http://www.mckinleymuseum.org/ Perpustakaan sarta Musieum Présidén William McKinley]
* [https://bidenwhitehouse.archives.gov/about-the-white-house/presidents/william-mckinley/ Biografi Gedong Bodas]
===Biantara===
* [http://millercenter.org/president/speeches#mckinley Téks sababaraha biantara McKinley], [[Miller Center of Public Affairs]]
===Lalampahan média===
* {{NYT topic|people/m/william_mckinley}}
===Séjénna===
{{CongBio|M000522}}
* [https://www.loc.gov/rr/program/bib/presidents/mckinley/index.html William McKinley: Pituduh Sumber Daya], [[Library of Congress]]
* [https://web.archive.org/web/20120426175456/http://millercenter.org/index.php/academic/americanpresident/mckinley Ésay lengkep ngeunaan William McKinley] sarta ésay pondok ngeunaan unggal anggota kabinétna ti [[Miller Center of Public Affairs]]
* [http://mckinleydeath.com/ McKinley Assassination Ink], sajarah dokuménter ngeunaan rajapati William McKinley
* [http://www.c-span.org/video/?151617-1/life-portrait-william-mckinley "Life Portrait of William McKinley"], ti [[C-SPAN]]'s ''[[American Presidents: Life Portraits]]'', 23 Agustus 1999
* {{Gutenberg author|id=1656}}
* {{Internet Archive author |sname=William McKinley}}
* {{Librivox author |id=11295}}
* {{IMDb name}}
{{William McKinley}}
{{Navboxes
|title=Jabatan sarta panghormatan
|list1=
{{s-start}}
{{s-ppo}}
{{s-bef|before=[[Joseph B. Foraker]]}}
{{s-ttl|title=Calon [[Republican Party (United States)|Républik]] pikeun [[Gubernur Ohio]]|years=[[1891 Ohio gubernatorial election|1891]], [[1893 Ohio gubernatorial election|1893]]}}
{{s-aft|after=[[Asa S. Bushnell]]}}
{{s-bef|before=[[Benjamin Harrison]]}}
{{s-ttl|title=Calon [[Republican Party (United States)|Républik]] pikeun Présidén Amérika Sarikat|years=[[1896 United States presidential election|1896]], [[1900 United States presidential election|1900]]}}
{{s-aft|after=[[Theodore Roosevelt]]}}
{{s-par|us-hs}}
{{s-bef|before=[[Laurin D. Woodworth]]}}
{{s-ttl|title=Anggota [[List of United States representatives from Ohio|DPR AS]]<br />ti [[Ohio's 17th congressional district|distrik ka-17 Ohio]]|years=1877–1879}}
{{s-aft|after=[[James Monroe (congressman)|James Monroe]]}}
{{s-bef|before=[[Lorenzo Danford]]}}
{{s-ttl|title=Anggota [[List of United States representatives from Ohio|DPR AS]]<br />ti [[Ohio's 16th congressional district|distrik ka-16 Ohio]]|years=1879–1881}}
{{s-aft|after=[[Jonathan T. Updegraff]]}}
{{s-bef|before=[[James Monroe (congressman)|James Monroe]]}}
{{s-ttl|title=Anggota [[List of United States representatives from Ohio|DPR AS]]<br />ti [[Ohio's 17th congressional district|distrik ka-17 Ohio]]|years=1881–1883}}
{{s-aft|after=[[Joseph D. Taylor]]}}
{{s-bef|before=[[Addison S. McClure]]}}
{{s-ttl|title=Anggota [[List of United States representatives from Ohio|DPR AS]]<br />ti [[Ohio's 18th congressional district|distrik ka-18 Ohio]]|years=1883–1884}}
{{s-aft|after=[[Jonathan Wallace]]}}
{{s-bef|before=[[David R. Paige]]}}
{{s-ttl|title=Anggota [[List of United States representatives from Ohio|DPR AS]]<br />ti [[Ohio's 20th congressional district|distrik ka-20 Ohio]]|years=1885–1887}}
{{s-aft|after=[[George W. Crouse]]}}
{{s-bef|before=[[Isaac H. Taylor]]}}
{{s-ttl|title=Anggota [[List of United States representatives from Ohio|DPR AS]]<br />ti [[Ohio's 18th congressional district|distrik ka-18 Ohio]]|years=1887–1891}}
{{s-aft|after=[[Joseph D. Taylor]]}}
{{s-bef|before=[[Roger Q. Mills]]}}
{{s-ttl|title=Ketua [[United States House Committee on Ways and Means|Komite Ways and Means DPR AS]]|years=1889–1891}}
{{s-aft|after=[[William McKendree Springer]]}}
{{s-off}}
{{s-bef|before=[[James E. Campbell]]}}
{{s-ttl|title=[[Governor of Ohio|Gubernur Ohio]]|years=1892–1896}}
{{s-aft|after=[[Asa S. Bushnell (Governor)|Asa Bushnell]]}}
{{s-bef|before=[[Grover Cleveland]]}}
{{s-ttl|title=Présidén Amérika Sarikat|years=1897–1901}}
{{s-aft|after=[[Theodore Roosevelt]]}}
{{s-end}}
}}
[[Kategori:Présidén Amérika Sarikat]]
8jr3eo8nuyc9tirtkks4de11hd7m0i6
Végétasi
0
109225
731553
730298
2026-11-07T03:23:28Z
Akbar Soepadhi
23282
+
731553
wikitext
text/x-wiki
'''Végétasi''' (tina basa inggris; ''vegetation'') nyaéta istilah pikeun sagemblengna [[komunitas tutuwuhan]] di hiji tempat nu tangtu, anu ngawengku pirang-pirang tina jinis [[flora]] anu ngawangunna jeung ''ground cover'' anu kabentukna.<ref>{{Cite book|title=Processes of vegetation change|url=http://archive.org/details/processesofveget0000burr|publisher=London ; Boston : Unwin Hyman|date=1990|isbn=978-0-04-580012-4|last=Internet Archive|first=C. J. (Colin James)}}</ref>
== Rujukan ==
<references />
{{pondok}}
sozqat0p61ev5ke4q1hjru6p4jhoc9v
Jalan Merdéka
0
109532
731550
2026-11-07T02:55:23Z
Pijri Paijar
27067
Nyieun kaca anyar '''Jalan Merdéka''' mangrupakeun salah sahiji ruas jalan nu basajarah di puseur [[Kota Bandung]] nu ngampar manjang kurang leuwih 1 kilométer. Ieu jalan boga ajén historis nu luhur lantaran jadi saksi bisu kamekaran awal tata kota Bandung sarta lalampahan sajarah bangsa [[Indonésia]]. Kiwari, koridor Jalan Merdéka mangrupa kawasan sibuk nu pinuh ku gedong pakantoran pamaréntahan, fasilitas atikan, puseur balanja (mal), perhotélan, nepi ka tempat ibadah.<ref>{{Cite w...
731550
wikitext
text/x-wiki
'''Jalan Merdéka''' mangrupakeun salah sahiji ruas jalan nu basajarah di puseur [[Kota Bandung]] nu ngampar manjang kurang leuwih 1 kilométer. Ieu jalan boga ajén historis nu luhur lantaran jadi saksi bisu kamekaran awal tata kota Bandung sarta lalampahan sajarah bangsa [[Indonésia]]. Kiwari, koridor Jalan Merdéka mangrupa kawasan sibuk nu pinuh ku gedong pakantoran pamaréntahan, fasilitas atikan, puseur balanja (mal), perhotélan, nepi ka tempat ibadah.<ref>{{Cite web|url=https://kumparan.com/sejarah-dan-sosial/sejarah-jalan-merdeka-bandung-dan-fakta-menariknya-24GjrLJHHlA|title=Sejarah Jalan Merdeka Bandung dan Fakta Menariknya|website=kumparan|language=id-ID|accessdate=2026-11-07}}</ref>
== Référénsi ==
q5p346iojhxe1h7rscxn82a64clds52
731551
731550
2026-11-07T03:00:39Z
Pijri Paijar
27067
731551
wikitext
text/x-wiki
'''Jalan Merdéka''' mangrupakeun salah sahiji ruas jalan nu basajarah di puseur [[Kota Bandung]] nu ngampar manjang kurang leuwih 1 kilométer. Ieu jalan boga ajén historis nu luhur lantaran jadi saksi bisu kamekaran awal tata kota Bandung sarta lalampahan sajarah bangsa [[Indonésia]]. Kiwari, koridor Jalan Merdéka mangrupa kawasan sibuk nu pinuh ku gedong pakantoran pamaréntahan, fasilitas atikan, puseur balanja (mal), perhotélan, nepi ka tempat ibadah.<ref name=":1">{{Cite web|url=https://kumparan.com/sejarah-dan-sosial/sejarah-jalan-merdeka-bandung-dan-fakta-menariknya-24GjrLJHHlA|title=Sejarah Jalan Merdeka Bandung dan Fakta Menariknya|website=kumparan|language=id-ID|accessdate=2026-11-07}}</ref>
== Sajarah ==
Dina mangsa kolonial [[Hindia-Walanda]], ieu jalan mimitina dipikawanoh ku ngaran ''Schoolweg'' atawa ''Merdekaweg''.<ref name=":1" /> Ieu wewengkon ogé pasinggungan langsung jeung pangwangunan ''Groote Postweg'' ([[Jalan Raya Pos]]) nu diwangun dina mangsa Gubernur Jénderal [[Herman Willem Daendels]] di awal abad ka-19.<ref name=":2">{{Cite web|url=https://www.detik.com/jabar/berita/d-6238055/menyusuri-jalan-bertema-kemerdekaan-di-kota-bandung|title=Menyusuri Jalan Bertema Kemerdekaan di Kota Bandung|last=Alhamidi|first=Rifat|website=detikjabar|language=id-ID|accessdate=2026-11-07}}</ref>
Ngaran "Merdéka" sorangan teu ujug-ujug napel sanggeus proklamasi kamerdékaan Républik Indonésia, tapi boga akar sajarah lokal nu leuwih kolot. Dina ahir abad ka-19, aya hiji aréa tempat nyieun kenténg jeung bata (lio) di éta kawasan. Bupati Bandung nu ngajabat harita, [[R.A.A. Martanagara]], méré kabébasan atawa pambébasan pajeg pikeun pangeusi di éta kampung lio salaku wujud panghargaan kana kontribusi maranéhna pikeun pangwangunan kota.<ref name=":1" /><ref name=":2" /> Kusabab kawijakan pambébasan pajeg éta, kampungna dingaranan Kampung Merdéka Lio, jeung jalanna ogé kungsi dibéré ngaran Jalan Merdéka Lio. Luyu jeung jalanna waktu, panyebutan éta jalan dipondokkeun jadi Jalan Merdéka.<ref name=":2" />
== Référénsi ==
k318cygc8jbe2y39c1n4soudd7dkkxy
Jalan Naripan
0
109533
731552
2026-11-07T03:15:02Z
Pijri Paijar
27067
Nyieun kaca anyar '''Jalan Naripan''' mangrupakeun salah sahiji ruas jalan légendaris jeung basajarah di puseur [[Kota Bandung]], [[Jawa Kulon|Jawa]] [[Jawa Kulon|Kulon]].<ref name=":1">{{Cite web|url=https://www.detik.com/jabar/berita/d-8290125/asal-usul-nama-jalan-naripan-di-bandung-ternyata-dari-sosok-ini|title=Asal-usul Nama Jalan Naripan di Bandung, Ternyata dari Sosok Ini|last=Alhamidi|first=Rifat|website=detikjabar|language=id-ID|accessdate=2026-11-07}}</ref><ref>{{Cite web|url=http...
731552
wikitext
text/x-wiki
'''Jalan Naripan''' mangrupakeun salah sahiji ruas jalan légendaris jeung basajarah di puseur [[Kota Bandung]], [[Jawa Kulon|Jawa]] [[Jawa Kulon|Kulon]].<ref name=":1">{{Cite web|url=https://www.detik.com/jabar/berita/d-8290125/asal-usul-nama-jalan-naripan-di-bandung-ternyata-dari-sosok-ini|title=Asal-usul Nama Jalan Naripan di Bandung, Ternyata dari Sosok Ini|last=Alhamidi|first=Rifat|website=detikjabar|language=id-ID|accessdate=2026-11-07}}</ref><ref>{{Cite web|url=https://www.ayobandung.com/bandung-raya/7910710611/berada-di-kota-bandung-asal-usul-jalan-legendaris-ini-justru-berasal-dari-nama-orang-betawi|title=Berada di Kota Bandung, Asal Usul Jalan Legendaris ini Justru Berasal dari Nama Orang Betawi|website=Ayo Bandung}}</ref> Ngampar manjang kurang leuwih 1,1 kilométer, ieu jalan nyambungkeun simpangan [[Jalan ABC]] jeung [[Jalan Cikapundung]] Kulon di beulah kulon, nepi ka lekasan di simpangan [[Jalan Vétéran]] jeung Jalan Jénderal Ahmad Yani (kawasan [[Simpang Lima]]) di beulah wétan. Perenahna nu padeukeut jeung urat nadi wisata katut sajarah kawas [[Jalan Asia Afrika]] sarta [[Jalan Braga]] ngajadikeun Jalan Naripan salaku salah sahiji puseur aktivitas perékonomian, sosial, jeung pariwisata ti jaman kolonial [[Hindia-Walanda]] nepi ka kiwari.<ref name=":1" />
== Référénsi ==
rnarliyfecdkr2siltkbw3wfu855u79
731554
731552
2026-11-07T03:28:11Z
Pijri Paijar
27067
731554
wikitext
text/x-wiki
'''Jalan Naripan''' mangrupakeun salah sahiji ruas jalan légendaris jeung basajarah di puseur [[Kota Bandung]], [[Jawa Kulon|Jawa]] [[Jawa Kulon|Kulon]].<ref name=":1">{{Cite web|url=https://www.detik.com/jabar/berita/d-8290125/asal-usul-nama-jalan-naripan-di-bandung-ternyata-dari-sosok-ini|title=Asal-usul Nama Jalan Naripan di Bandung, Ternyata dari Sosok Ini|last=Alhamidi|first=Rifat|website=detikjabar|language=id-ID|accessdate=2026-11-07}}</ref><ref name=":2">{{Cite web|url=https://www.ayobandung.com/bandung-raya/7910710611/berada-di-kota-bandung-asal-usul-jalan-legendaris-ini-justru-berasal-dari-nama-orang-betawi|title=Berada di Kota Bandung, Asal Usul Jalan Legendaris ini Justru Berasal dari Nama Orang Betawi|website=Ayo Bandung}}</ref> Ngampar manjang kurang leuwih 1,1 kilométer, ieu jalan nyambungkeun simpangan [[Jalan ABC]] jeung [[Jalan Cikapundung]] Kulon di beulah kulon, nepi ka lekasan di simpangan [[Jalan Vétéran]] jeung Jalan Jénderal Ahmad Yani (kawasan [[Simpang Lima]]) di beulah wétan. Perenahna nu padeukeut jeung urat nadi wisata katut sajarah kawas [[Jalan Asia Afrika]] sarta [[Jalan Braga]] ngajadikeun Jalan Naripan salaku salah sahiji puseur aktivitas perékonomian, sosial, jeung pariwisata ti jaman kolonial [[Hindia-Walanda]] nepi ka kiwari.<ref name=":1" />
== Asal usul ngaran ==
Saméméh dipikawanoh ku ngaran nu ayeuna, dina mangsa kolonial ieu jalan kungsi ngaranna ''Kawedananweg''.<ref name=":1" /> Béda jeung loba jalan badag séjén di [[Indonésia]] nu dingaranan maké ngaran pahlawan nasional, ngaran "Naripan" (baheula ''Naripanweg'') justru dicokot tina ngaran warga biasa kalangan handap. Naripan dipikanyaho mangrupa saurang lalaki asal [[Betawi]] nu ngarintis jeung boga usaha panyéwaan transportasi béndi atawa délman di éta jalan.<ref name=":2" /><ref name=":3">{{Cite web|url=https://mediaini.com/bisnis/2020/07/08/35294/mengintip-sejarah-penamaan-jalan-di-kota-bandung/|title=Mengintip Sejarah Penamaan Jalan di Kota Bandung - MEDIAINI|language=id|accessdate=2026-11-07}}</ref>Dina mangsa harita, kalangan élit ménak biasana nyéwa béndi méwah ti Firma Hallerman, samentara warga biasa jeung ménak kelas menengah nyéwa béndi ti Naripan.<ref name=":3" />
Aya ogé vérsi séjén tina sajarah panamaan ieu, kawas nu dicatet ku M. Ryzki Wiryawan dina bukuna ''Pesona Sejarah Bandung''. Éta vérsi nyebutkeun yén inohong panyadia jasa délman éta ngaranna Ivan, saurang lalaki getih campuran Walanda-Betawi. Kusabab Ivan téh hiji-hijina jalma nu nyéwakeun délman di wewengkon éta dina mangsana, warga nu mikabutuh jasana mindeng ngucapkeun frasa basa Walanda "Naar Ivan" nu hartina "Indit ka (tempat) Ivan". Luyu jeung jalanna waktu, ngucapkeun "Naar Ivan" léméh dina létah masarakat lokal jadi "Naripan", nu ahirna diabadikeun jadi ngaran kawasan jeung jalan éta.<ref name=":1" />
== Référénsi ==
0somhr8zfryzll208h8oo6rn38weemd
Obrolan pamaké:KCN-NCDHNCPATPS
3
109534
731555
2026-11-07T10:06:29Z
Rafka Aditia
29183
Wilujeng sumping di [[Wikipédia basa Sunda]]
731555
wikitext
text/x-wiki
{{Wilujeng sumping|Oktober|2026}}
sqbgu0yduvg5b9afx4uhn1ovnx3id7b
731556
731555
2026-11-07T10:06:47Z
Rafka Aditia
29183
731556
wikitext
text/x-wiki
{{Wilujeng sumping|Oktober|2026}} [[Pamaké:Rafka Aditia|Rafka Aditia]] ([[Obrolan pamaké:Rafka Aditia|sawala]]) 7 Nopémber 2026 10.06 (UTC)
ctsutpmkb0lkj5x0bufcj2szr5063ft
Jalan Tamblong
0
109535
731557
2026-11-07T10:16:24Z
Mirani Pramitasari
26631
Nyieun kaca anyar '''Jalan Tamblong''' nyaéta salah sahiji jalan nu miboga sajarah di Bandung anu ngaranna dicokot tina saurang jalma Tionghoa nu ngaranna Tam Long salaku tukang nyieun parabot. Jalan ieu manjang ti tungtung Jalan Sumatra dugi ka Jalan Lengkong Besar.<ref>{{Cite news|url=https://www.jatimnetwork.com/nusantara/4312703050/kenapa-dinamakan-tamblong-jalan-di-bandung-itu-ternyata-diambil-dari|title=Kenapa Dinamakan Tamblong? Jalan di Bandung Itu Ternyata Diambil dari...|last=Dar...
731557
wikitext
text/x-wiki
'''Jalan Tamblong''' nyaéta salah sahiji jalan nu miboga sajarah di Bandung anu ngaranna dicokot tina saurang jalma Tionghoa nu ngaranna Tam Long salaku tukang nyieun parabot. Jalan ieu manjang ti tungtung Jalan Sumatra dugi ka Jalan Lengkong Besar.<ref>{{Cite news|url=https://www.jatimnetwork.com/nusantara/4312703050/kenapa-dinamakan-tamblong-jalan-di-bandung-itu-ternyata-diambil-dari|title=Kenapa Dinamakan Tamblong? Jalan di Bandung Itu Ternyata Diambil dari...|last=Daroini|first=Ike Khasanatut|date=19 Mei 2024|access-date=7 November 2026}}</ref>
== Sajarah ==
Jalan ieu dingaranan Jalan Tamblong dimimitian dina taun 1874, baheulana dingaranan Jalan Akip Prawira Suganda.<ref>{{Cite news|url=https://www.ayobandung.com/bandung-baheula/7910250825/jadi-mega-proyek-daendels-jalan-di-bandung-ini-diambil-dari-nama-tukang-mebel-begini-sejarahnya?page=2|title=Jadi Mega Proyek Daendels, Jalan di Bandung Ini Diambil dari Nama Tukang Mebel, Begini Sejarahnya|last=Khasanah|first=Uswatun|date=22 September 2023|access-date=7 November 2026}}</ref>
== Wangunan di Jalan Tamblong ==
=== Puskesmas Tamblong ===
=== Rasa Bakery ===
== Rujukan ==
<references />
275lh4g0ruaq6s12t5l3gyq8xjo3q7x
Jalan ABC
0
109536
731558
2026-11-07T10:52:17Z
Pijri Paijar
27067
Nyieun kaca anyar '''Jalan ABC''' mangrupaken salah sahiji ruas jalan basajarah di puseur [[Kota Bandung]] nu dipikawanoh salaku séntra perdagangan jaman baheula. Perenahna deukeut kawasan [[Pasar Baru Trade Center]] jeung nyambung ka [[Jalan Banceuy]] sarta [[Jalan Otto Iskandar Dinata (Otista)|Jalan Otto Iskandar]] [[Jalan Otto Iskandar Dinata (Otista)|Dinata (Otista)]], ieu jalan geus jadi puseur ékonomi warga saprak éra kolonial [[Hindia-Walanda|Hindia Walanda]]. Kiwari, Jalan ABC po...
731558
wikitext
text/x-wiki
'''Jalan ABC''' mangrupaken salah sahiji ruas jalan basajarah di puseur [[Kota Bandung]] nu dipikawanoh salaku séntra perdagangan jaman baheula. Perenahna deukeut kawasan [[Pasar Baru Trade Center]] jeung nyambung ka [[Jalan Banceuy]] sarta [[Jalan Otto Iskandar Dinata (Otista)|Jalan Otto Iskandar]] [[Jalan Otto Iskandar Dinata (Otista)|Dinata (Otista)]], ieu jalan geus jadi puseur ékonomi warga saprak éra kolonial [[Hindia-Walanda|Hindia Walanda]]. Kiwari, Jalan ABC populér salaku puseur perdagangan barang éléktronik, lampu hias, karpét, nepi ka barang-barang antik jeung ''vintage'' kawas kaméra analog heubeul, kacamata, jeung jam leungeun nu didagangkeun di rupa-rupa étalase toko atawa lapak kaki lima.<ref name=":1">{{Cite web|date=2020-07-04|title=Jalan ABC, Memasuki Sudut Sejarah Perdagangan Kota Bandung|url=https://kelananusantara.com/jalan-abc-memasuki-sudut-sejarah-perdagangan-kota-bandung/|website=Kelana Nusantara|language=id-ID|access-date=2026-11-07}}</ref><ref name=":2">{{Cite web|last=Atthoriq|first=Istawa Faqih|title=Asal-usul Penamaan Jalan ABC, Surga Bangunan Tua Bandung|url=https://www.detik.com/jabar/berita/d-6620371/asal-usul-penamaan-jalan-abc-surga-bangunan-tua-bandung|website=detikjabar|language=id-ID|access-date=2026-11-07}}</ref><ref name=":3">{{Cite web|title=Jejak Legenda Jalan ABC dan Banceuy, Napak Tilas Bandung Tempo Doeloe|url=https://sonata.bandung.go.id/artikel/jejak-legenda-jalan-abc-dan-banceuy-napak-tilas-bandung-tempo-doeloe|website=Radio Sonata}}</ref>
== Référénsi ==
h7q7hly7fzheynaxwgr83xp4lsnofdf