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&nbsp;cm nepika 6&nbsp;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&nbsp;cm - 10&nbsp;cm, rubakna 1,5&nbsp;cm - 3&nbsp;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&nbsp;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&nbsp;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"&nbsp;<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 &ndash; 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]', { ['['] = '&#91;', [']'] = '&#93;', ['\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 .. '&nbsp;', '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("&mdash;", 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 ('&nbsp;',''); -- count nbsps -- There is exactly 1 semicolon per &nbsp; 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&#035') or url:match ('%.pdf&#035'); 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 .. '&nbsp;', '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 .. '&nbsp;', '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 .. '&nbsp;', '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 .. '&nbsp;', '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 .. '&nbsp;', '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]', { ['['] = '&#91;', [']'] = '&#93;', ['\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 .. '&nbsp;', '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("&mdash;", 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 ('&nbsp;',''); -- count nbsps -- There is exactly 1 semicolon per &nbsp; 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&#035') or url:match ('%.pdf&#035'); 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 .. '&nbsp;', '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 .. '&nbsp;', '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 .. '&nbsp;', '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 .. '&nbsp;', '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 .. '&nbsp;', '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&nbsp;al.', ['in'] = 'Dina', -- edited works ['inactive'] = 'teu aktip', ['inset'] = 'insét $1', ['interview'] = 'Diwawancara ku $1', ['mismatch'] = '<code class="cs1-code">&#124;$1=</code> / <code class="cs1-code">&#124;$2=</code> mismatch', -- $1 is year param name; $2 is date param name ['newsgroup'] = '[[Usenet newsgroup|Newsgroup]]:&nbsp;$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'] = '§&nbsp;$1', ['sections'] = '§§&nbsp;$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.&nbsp;$2', -- $1 is sepc; bold journal style volume is in presentation{} ['vol-no'] = '$1 Vol.&nbsp;$2, no.&nbsp;$3', -- sepc, volume, issue (alternatively insert $1 after $2, but then we'd also have to change capitalization) ['issue'] = '$1 No.&nbsp;$2', -- $1 is sepc ['art'] = '$1 Art.&nbsp;$2', -- $1 is sepc; for {{cite conference}} only ['vol-art'] = '$1 Vol.&nbsp;$2, art.&nbsp;$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.&nbsp;$2", -- $1 is sepc ['pp-prefix'] = "$1 hlm.&nbsp;$2", -- $1 is sepc ['j-page(s)'] = ': $1', -- same for page and pages ['sheet'] = '$1 Lembar&nbsp;$2', -- $1 is sepc ['sheets'] = '$1 Lembar&nbsp;$2', -- $1 is sepc ['j-sheet'] = ': Lembar&nbsp;$1', ['j-sheets'] = ': Lembar&nbsp;$1', ['language'] = '(Dina basa $1)', ['via'] = " &ndash; 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;">&#123;{$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;">&#123;{$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 &#8239; 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">&#124;$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'] = "&#91;''$1''&#93;", ['trans-quoted-title'] = "&#91;$1&#93;", -- 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]?%.&nbsp;', -- 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 = '&nbsp;', 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 = '&nbsp;', }, ['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 = '&nbsp;', }, ['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 = '&nbsp;', }, ['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 = '&nbsp;', }, ['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 = '&nbsp;', }, ['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 = '&nbsp;', }, ['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 = '&nbsp;', }, ['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 = '&nbsp;', 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 = '&nbsp;', }, ['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 = '&nbsp;', }, ['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 = '&nbsp;', }, ['OCLC'] = { parameters = {'oclc', 'OCLC'}, link = 'OCLC', redirect = 'OCLC ', q = 'Q190593', label = 'OCLC', prefix = 'https://search.worldcat.org/oclc/', COinS = 'info:oclcnum', encode = true, separator = '&nbsp;', 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 = '&nbsp;', 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 = '&nbsp;', 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 = '&nbsp;', 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 = '&nbsp;', 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 = '&nbsp;', 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 = '&nbsp;', }, ['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 = '&nbsp;', 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 = '&nbsp;', 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 = '&nbsp;', }, ['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 = '&nbsp;', }, } --[[--------------------------< 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&nbsp;al.', ['in'] = 'Dina', -- edited works ['inactive'] = 'teu aktip', ['inset'] = 'insét $1', ['interview'] = 'Diwawancara ku $1', ['mismatch'] = '<code class="cs1-code">&#124;$1=</code> / <code class="cs1-code">&#124;$2=</code> mismatch', -- $1 is year param name; $2 is date param name ['newsgroup'] = '[[Usenet newsgroup|Newsgroup]]:&nbsp;$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'] = '§&nbsp;$1', ['sections'] = '§§&nbsp;$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.&nbsp;$2', -- $1 is sepc; bold journal style volume is in presentation{} ['vol-no'] = '$1 Vol.&nbsp;$2, no.&nbsp;$3', -- sepc, volume, issue (alternatively insert $1 after $2, but then we'd also have to change capitalization) ['issue'] = '$1 No.&nbsp;$2', -- $1 is sepc ['art'] = '$1 Art.&nbsp;$2', -- $1 is sepc; for {{cite conference}} only ['vol-art'] = '$1 Vol.&nbsp;$2, art.&nbsp;$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.&nbsp;$2", -- $1 is sepc ['pp-prefix'] = "$1 hlm.&nbsp;$2", -- $1 is sepc ['j-page(s)'] = ': $1', -- same for page and pages ['sheet'] = '$1 Lembar&nbsp;$2', -- $1 is sepc ['sheets'] = '$1 Lembar&nbsp;$2', -- $1 is sepc ['j-sheet'] = ': Lembar&nbsp;$1', ['j-sheets'] = ': Lembar&nbsp;$1', ['language'] = '(Dina basa $1)', ['via'] = " &ndash; 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;">&#123;{$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;">&#123;{$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 &#8239; 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">&#124;$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'] = "&#91;''$1''&#93;", ['trans-quoted-title'] = "&#91;$1&#93;", -- 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]?%.&nbsp;', -- 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 = '&nbsp;', 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 = '&nbsp;', }, ['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 = '&nbsp;', }, ['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 = '&nbsp;', }, ['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 = '&nbsp;', }, ['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 = '&nbsp;', }, ['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 = '&nbsp;', }, ['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 = '&nbsp;', }, ['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 = '&nbsp;', 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 = '&nbsp;', }, ['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 = '&nbsp;', }, ['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 = '&nbsp;', }, ['OCLC'] = { parameters = {'oclc', 'OCLC'}, link = 'OCLC', redirect = 'OCLC ', q = 'Q190593', label = 'OCLC', prefix = 'https://search.worldcat.org/oclc/', COinS = 'info:oclcnum', encode = true, separator = '&nbsp;', 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 = '&nbsp;', 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 = '&nbsp;', 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 = '&nbsp;', 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 = '&nbsp;', 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 = '&nbsp;', 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 = '&nbsp;', }, ['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 = '&nbsp;', 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 = '&nbsp;', 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 = '&nbsp;', }, ['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 = '&nbsp;', }, } --[[--------------------------< 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.&nbsp;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.&nbsp;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&nbsp;– MTV changed the music industry on August 1, 1981&nbsp;– 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