વિકિપીડિયા
guwiki
https://gu.wikipedia.org/wiki/%E0%AA%AE%E0%AB%81%E0%AA%96%E0%AA%AA%E0%AB%83%E0%AA%B7%E0%AB%8D%E0%AA%A0
MediaWiki 1.45.0-wmf.4
first-letter
દ્રશ્ય-શ્રાવ્ય (મિડિયા)
વિશેષ
ચર્ચા
સભ્ય
સભ્યની ચર્ચા
વિકિપીડિયા
વિકિપીડિયા ચર્ચા
ચિત્ર
ચિત્રની ચર્ચા
મીડિયાવિકિ
મીડિયાવિકિ ચર્ચા
ઢાંચો
ઢાંચાની ચર્ચા
મદદ
મદદની ચર્ચા
શ્રેણી
શ્રેણીની ચર્ચા
TimedText
TimedText talk
વિભાગ
વિભાગ ચર્ચા
વિકિપીડિયા:ચોતરો
4
774
886418
886207
2025-06-13T23:00:51Z
MediaWiki message delivery
18151
/* Vote now in the 2025 U4C Election */ નવો વિભાગ
886418
wikitext
text/x-wiki
<!-- -->{{/શીર્ષક}}<!-- -->
<!-- મહેરબાની કરી આ લીટી અને તેની ઉપરનું લખાણ હટાવવું નહી -->
== Invitation to Participate in the Wikimedia SAARC Conference Community Engagement Survey ==
Dear Community Members,
I hope this message finds you well. Please excuse the use of English; we encourage translations into your local languages to ensure inclusivity.
We are conducting a Community Engagement Survey to assess the sentiments, needs, and interests of South Asian Wikimedia communities in organizing the inaugural Wikimedia SAARC Regional Conference, proposed to be held in Kathmandu, Nepal.
This initiative aims to bring together participants from eight nations to collaborate towards shared goals. Your insights will play a vital role in shaping the event's focus, identifying priorities, and guiding the strategic planning for this landmark conference.
Survey Link: https://forms.gle/en8qSuCvaSxQVD7K6
We kindly request you to dedicate a few moments to complete the survey. Your feedback will significantly contribute to ensuring this conference addresses the community's needs and aspirations.
Deadline to Submit the Survey: 20 January 2025
Your participation is crucial in shaping the future of the Wikimedia SAARC community and fostering regional collaboration. Thank you for your time and valuable input.
Warm regards,<br>
[[:m:User:Biplab Anand|Biplab Anand]]
<!-- Message sent by User:Biplab Anand@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Biplab_Anand/lists&oldid=28074658 -->
== Open Community Call - [[:m:Expressions of Interest to host Wikimania 2027 in India: Initial conversation|Expressions of Interest to host Wikimania 2027 in India]] ==
<div lang="en" dir="ltr">
''{{int:please-translate}}''
Dear Wikimedians,
Happy 2025.. 😊
As you must have seen, members from Wikimedians of Kerala and Odia Wikimedia User Groups initiated preliminary discussions around submitting an Expression of Interest (EoI) to have Wikimania 2027 in India. You can find out more on the [[:m:Expressions of Interest to host Wikimania 2027 in India: Initial conversation|Meta Page]].
Our aim is to seek input and assess the overall community sentiment and thoughts from the Indian community before we proceed further with the steps involved in submitting the formal EOI.
As part of the same, we are hosting an '''open community call regarding India's Expression of Interest (EOI) to host Wikimania 2027'''. This is an opportunity to gather your valuable feedback, opinions, and suggestions to shape a strong and inclusive proposal.
* 📅 Date: Wednesday, January 15th 2025
* ⏰ Time: 7pm-8pm IST
* 📍 Platform: https://meet.google.com/sns-qebp-hck
Your participation is key to ensuring the EOI reflects the collective aspirations and potential of the vibrant South Asian community.
Let’s join together to make this a milestone event for the Wikimedia movement in South Asia.
We look forward to your presence!
<br>
Warm regards,
<br>
[[:m:Wikimedians of Kerala|Wikimedians of Kerala]] and [[:m:Odia Wikimedians User Group|Odia Wikimedians]] User Group's
<br>
This message was sent with [[સભ્ય:MediaWiki message delivery|MediaWiki message delivery]] ([[સભ્યની ચર્ચા:MediaWiki message delivery|ચર્ચા]]) by [[m:User:Gnoeee|Gnoeee]] ([[m:User_talk:Gnoeee|talk]]) at ૧૧:૨૫, ૧૪ જાન્યુઆરી ૨૦૨૫ (IST)
</div>
<!-- Message sent by User:Gnoeee@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Indic_VPs&oldid=28100038 -->
== શું મુખપૃષ્ઠ પર કુંભ મેળા વિશે મૂકી શકાય? ==
અત્યારે [[૨૦૨૫ પ્રયાગ કુંભ મેળો]] ચાલી રહ્યો છે તો શું મુખપૃષ્ઠ પર વર્તમાન ઘટનાઓનું નાનું સેક્શન બનાવી કે પછી હંગામી ધોરણે ઉમદાલેખમાં પૃષ્ઠ ઉમેરી કુંભ મેળા વિશે મૂકી શકાય? --[[સભ્ય:Brihaspati|બૃહસ્પતિ]] [[સભ્યની ચર્ચા:Brihaspati|<sup>મારી સાથે વાત કરો</sup>]] ૧૧:૪૫, ૧૬ જાન્યુઆરી ૨૦૨૫ (IST)
:ફેબ્રુઆરીમાં આ માસના ઉમદા લેખ તરીકે મૂકીએ. ત્યાં સુધી લેખમાં જરુરી સુધારા કરી રાખીએ. લગભગ આખો ફેબ્રુઆરી મહિનો કુંભમેળો ચાલવાનો છે એટલે તે સમયે મુખપૃષ્ઠ પર રહે તો યાદો તાજી રહે. [[:User:Dsvyas|ધવલ]]<sup>[[:User_talk:Dsvyas|ચર્ચા]]/[[:Special:Contributions/Dsvyas|યોગદાન]]</sup> ૧૬:૫૨, ૧૬ જાન્યુઆરી ૨૦૨૫ (IST)
::જી. ચોક્કસ. માહિતી ઉમેરવામાં મદદ કરવા વિનંતી. [[સભ્ય:Brihaspati|બૃહસ્પતિ]] [[સભ્યની ચર્ચા:Brihaspati|<sup>મારી સાથે વાત કરો</sup>]] ૧૫:૨૧, ૧૭ જાન્યુઆરી ૨૦૨૫ (IST)
== Launching! Join Us for Wiki Loves Ramadan 2025! ==
Dear All,
We’re happy to announce the launch of [[m:Wiki Loves Ramadan 2025|Wiki Loves Ramadan 2025]], an annual international campaign dedicated to celebrating and preserving Islamic cultures and history through the power of Wikipedia. As an active contributor to the Local Wikipedia, you are specially invited to participate in the launch.
This year’s campaign will be launched for you to join us write, edit, and improve articles that showcase the richness and diversity of Islamic traditions, history, and culture.
* Topic: [[m:Event:Wiki Loves Ramadan 2025 Campaign Launch|Wiki Loves Ramadan 2025 Campaign Launch]]
* When: Jan 19, 2025
* Time: 16:00 Universal Time UTC and runs throughout Ramadan (starting February 25, 2025).
* Join Zoom Meeting: https://us02web.zoom.us/j/88420056597?pwd=NdrpqIhrwAVPeWB8FNb258n7qngqqo.1
* Zoom meeting hosted by [[m:Wikimedia Bangladesh|Wikimedia Bangladesh]]
To get started, visit the [[m:Wiki Loves Ramadan 2025|campaign page]] for details, resources, and guidelines: Wiki Loves Ramadan 2025.
Add [[m:Wiki Loves Ramadan 2025/Participant|your community here]], and organized Wiki Loves Ramadan 2025 in your local language.
Whether you’re a first-time editor or an experienced Wikipedian, your contributions matter. Together, we can ensure Islamic cultures and traditions are well-represented and accessible to all.
Feel free to invite your community and friends too. Kindly reach out if you have any questions or need support as you prepare to participate.
Let’s make Wiki Loves Ramadan 2025 a success!
For the [[m:Wiki Loves Ramadan 2025/Team|International Team]] ૧૭:૩૮, ૧૬ જાન્યુઆરી ૨૦૨૫ (IST)
<!-- Message sent by User:ZI Jony@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Non-Technical_Village_Pumps_distribution_list&oldid=27568454 -->
== New Wikimedia Campaign Launching Tomorrow: Indic Writing Systems Campaign 2025 ==
Dear Wikimedians,
We are excited to announce the launch of the [[:d:Wikidata:WikiProject Writing Systems/Indic writing systems campaign 2025|Indic writing systems campaign 2025]], which will take place from 23 January 2025 (World Endangered Writing Day) to 21 February 2025 (International Mother Language Day). This initiative is part of the ongoing efforts of [[:d:Wikidata:WikiProject Writing Systems|WikiProject writing Systems]] to raise awareness about the documentation and revitalization of writing systems, many of which are currently underrepresented or endangered.
Representatives from important organizations that work with writing systems, such as Endangered Alphabets and the Script Encoding Initiative, support the campaign. The campaign will feature two primary activities focused on the [[:d:Wikidata:WikiProject Writing Systems/Indic writing systems campaign 2025/Lists|list of target scripts]]:
* '''Wikidata Labelathon''': A focused effort to improve and expand the information related to South Asian scripts on Wikidata.
* '''Wikipedia Translatathon''': A collaborative activity aimed at enhancing the coverage of South Asian writing systems and their cultural significance on Wikipedia.
We are looking for local organizers to engage their respective communities. If you are interested in organizing, kindly sign-up [[:d:Wikidata:WikiProject Writing Systems/Indic writing systems campaign 2025/Local Organizers|here]]. We also encourage all Indic Wikimedians to [[:d:Wikidata:WikiProject Writing Systems/Indic writing systems campaign 2025/Participate|join us]] in this important campaign to help document and celebrate the diverse writing systems of South Asia.
Thank you for your support, and we look forward to your active participation.
Regards [[સભ્ય:MediaWiki message delivery|MediaWiki message delivery]] ([[સભ્યની ચર્ચા:MediaWiki message delivery|ચર્ચા]]) ૨૨:૫૯, ૨૨ જાન્યુઆરી ૨૦૨૫ (IST)
Navya sri Kalli
<!-- Message sent by User:Nitesh (CIS-A2K)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Titodutta/lists/Indic_VPs&oldid=22433435 -->
== Universal Code of Conduct annual review: provide your comments on the UCoC and Enforcement Guidelines ==
<div lang="en" dir="ltr" class="mw-content-ltr">
My apologies for writing in English.
{{Int:Please-translate}}.
I am writing to you to let you know the annual review period for the Universal Code of Conduct and Enforcement Guidelines is open now. You can make suggestions for changes through 3 February 2025. This is the first step of several to be taken for the annual review.
[[m:Special:MyLanguage/Universal_Code_of_Conduct/Annual_review|Read more information and find a conversation to join on the UCoC page on Meta]].
The [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee|Universal Code of Conduct Coordinating Committee]] (U4C) is a global group dedicated to providing an equitable and consistent implementation of the UCoC. This annual review was planned and implemented by the U4C. For more information and the responsibilities of the U4C, [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Charter|you may review the U4C Charter]].
Please share this information with other members in your community wherever else might be appropriate.
-- In cooperation with the U4C, [[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|talk]]) ૦૬:૪૧, ૨૪ જાન્યુઆરી ૨૦૨૫ (IST)
</div>
<!-- Message sent by User:Keegan (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=27746256 -->
== Feminism and Folklore 2025 starts soon ==
<div style="border:8px maroon ridge;padding:6px;>
[[File:Feminism and Folklore 2025 logo.svg|centre|550px|frameless]]
::<div lang="en" dir="ltr" class="mw-content-ltr">
<center>''{{int:please-translate}}''</center>
Dear Wiki Community,
You are humbly invited to organize the '''[[:m:Feminism and Folklore 2025|Feminism and Folklore 2025]]''' writing competition from February 1, 2025, to March 31, 2025 on your local Wikipedia. This year, Feminism and Folklore will focus on feminism, women's issues, and gender-focused topics for the project, with a [[:c:Commons:Wiki Loves Folklore 2025|Wiki Loves Folklore]] gender gap focus and a folk culture theme on Wikipedia.
You can help Wikipedia's coverage of folklore from your area by writing or improving articles about things like folk festivals, folk dances, folk music, women and queer folklore figures, folk game athletes, women in mythology, women warriors in folklore, witches and witch hunting, fairy tales, and more. Users can help create new articles, expand or translate from a generated list of suggested articles.
Organisers are requested to work on the following action items to sign up their communities for the project:
# Create a page for the contest on the local wiki.
# Set up a campaign on '''CampWiz''' tool.
# Create the local list and mention the timeline and local and international prizes.
# Request local admins for site notice.
# Link the local page and the CampWiz link on the [[:m:Feminism and Folklore 2025/Project Page|meta project page]].
This year, the Wiki Loves Folklore Tech Team has introduced two new tools to enhance support for the campaign. These tools include the '''Article List Generator by Topic''' and '''CampWiz'''. The Article List Generator by Topic enables users to identify articles on the English Wikipedia that are not present in their native language Wikipedia. Users can customize their selection criteria, and the tool will present a table showcasing the missing articles along with suggested titles. Additionally, users have the option to download the list in both CSV and wikitable formats. Notably, the CampWiz tool will be employed for the project for the first time, empowering users to effectively host the project with a jury. Both tools are now available for use in the campaign. [https://tools.wikilovesfolklore.org/ '''Click here to access these tools''']
Learn more about the contest and prizes on our [[:m:Feminism and Folklore 2025|project page]]. Feel free to contact us on our [[:m:Talk:Feminism and Folklore 2025/Project Page|meta talk page]] or by email us if you need any assistance.
We look forward to your immense coordination.
Thank you and Best wishes,
'''[[:m:Feminism and Folklore 2025|Feminism and Folklore 2025 International Team]]'''
::::Stay connected [[File:B&W Facebook icon.png|link=https://www.facebook.com/feminismandfolklore/|30x30px]] [[File:B&W Twitter icon.png|link=https://twitter.com/wikifolklore|30x30px]]
</div></div>
--[[સભ્ય:MediaWiki message delivery|MediaWiki message delivery]] ([[સભ્યની ચર્ચા:MediaWiki message delivery|ચર્ચા]]) ૦૮:૦૫, ૨૯ જાન્યુઆરી ૨૦૨૫ (IST)
== Wiki Loves Folklore is back! ==
<div lang="en" dir="ltr" class="mw-content-ltr">
{{int:please-translate}}
[[File:Wiki Loves Folklore Logo.svg|right|150px|frameless]]
Dear Wiki Community,
You are humbly invited to participate in the '''[[:c:Commons:Wiki Loves Folklore 2025|Wiki Loves Folklore 2025]]''' an international media contest organized on Wikimedia Commons to document folklore and intangible cultural heritage from different regions, including, folk creative activities and many more. It is held every year from the '''1st till the 31st''' of March.
You can help in enriching the folklore documentation on Commons from your region by taking photos, audios, videos, and [https://commons.wikimedia.org/w/index.php?title=Special:UploadWizard&campaign=wlf_2025 submitting] them in this commons contest.
You can also [[:c:Commons:Wiki Loves Folklore 2025/Organize|organize a local contest]] in your country and support us in translating the [[:c:Commons:Wiki Loves Folklore 2025/Translations|project pages]] to help us spread the word in your native language.
Feel free to contact us on our [[:c:Commons talk:Wiki Loves Folklore 2025|project Talk page]] if you need any assistance.
'''Kind regards,'''
'''Wiki loves Folklore International Team'''
--[[સભ્ય:MediaWiki message delivery|MediaWiki message delivery]] ([[સભ્યની ચર્ચા:MediaWiki message delivery|ચર્ચા]]) ૦૮:૦૫, ૨૯ જાન્યુઆરી ૨૦૨૫ (IST)
</div>
<!-- Message sent by User:Tiven2240@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery/Wikipedia&oldid=26503019 -->
== Reminder: first part of the annual UCoC review closes soon ==
<div lang="en" dir="ltr" class="mw-content-ltr">
My apologies for writing in English.
{{Int:Please-translate}}.
This is a reminder that the first phase of the annual review period for the Universal Code of Conduct and Enforcement Guidelines will be closing soon. You can make suggestions for changes through [[d:Q614092|the end of day]], 3 February 2025. This is the first step of several to be taken for the annual review.
[[m:Special:MyLanguage/Universal_Code_of_Conduct/Annual_review|Read more information and find a conversation to join on the UCoC page on Meta]]. After review of the feedback, proposals for updated text will be published on Meta in March for another round of community review.
Please share this information with other members in your community wherever else might be appropriate.
-- In cooperation with the U4C, [[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|talk]]) ૦૬:૧૮, ૩ ફેબ્રુઆરી ૨૦૨૫ (IST)
</div>
<!-- Message sent by User:Keegan (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28198931 -->
== <span lang="en" dir="ltr"> Upcoming Language Community Meeting (Feb 28th, 14:00 UTC) and Newsletter</span> ==
<div lang="en" dir="ltr">
<section begin="message"/>
Hello everyone!
[[File:WP20Symbols WIKI INCUBATOR.svg|right|frameless|150x150px|alt=An image symbolising multiple languages]]
We’re excited to announce that the next '''Language Community Meeting''' is happening soon, '''February 28th at 14:00 UTC'''! If you’d like to join, simply sign up on the '''[[mw:Wikimedia_Language_and_Product_Localization/Community_meetings#28_February_2025|wiki page]]'''.
This is a participant-driven meeting where we share updates on language-related projects, discuss technical challenges in language wikis, and collaborate on solutions. In our last meeting, we covered topics like developing language keyboards, creating the Moore Wikipedia, and updates from the language support track at Wiki Indaba.
'''Got a topic to share?''' Whether it’s a technical update from your project, a challenge you need help with, or a request for interpretation support, we’d love to hear from you! Feel free to '''reply to this message''' or add agenda items to the document '''[[etherpad:p/language-community-meeting-feb-2025|here]]'''.
Also, we wanted to highlight that the sixth edition of the Language & Internationalization newsletter (January 2025) is available here: [[:mw:Special:MyLanguage/Wikimedia Language and Product Localization/Newsletter/2025/January|Wikimedia Language and Product Localization/Newsletter/2025/January]]. This newsletter provides updates from the October–December 2024 quarter on new feature development, improvements in various language-related technical projects and support efforts, details about community meetings, and ideas for contributing to projects. To stay updated, you can subscribe to the newsletter on its wiki page: [[:mw:Wikimedia Language and Product Localization/Newsletter|Wikimedia Language and Product Localization/Newsletter]].
We look forward to your ideas and participation at the language community meeting, see you there!
<section end="message"/>
</div>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> ૧૩:૫૯, ૨૨ ફેબ્રુઆરી ૨૦૨૫ (IST)
<!-- Message sent by User:SSethi (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28217779 -->
== Universal Code of Conduct annual review: proposed changes are available for comment ==
<div lang="en" dir="ltr" class="mw-content-ltr">
My apologies for writing in English.
{{Int:Please-translate}}.
I am writing to you to let you know that [[m:Special:MyLanguage/Universal_Code_of_Conduct/Annual_review/Proposed_Changes|proposed changes]] to the [[foundation:Special:MyLanguage/Policy:Universal_Code_of_Conduct/Enforcement_guidelines|Universal Code of Conduct (UCoC) Enforcement Guidelines]] and [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Charter|Universal Code of Conduct Coordinating Committee (U4C) Charter]] are open for review. '''[[m:Special:MyLanguage/Universal_Code_of_Conduct/Annual_review/Proposed_Changes|You can provide feedback on suggested changes]]''' through the [[d:Q614092|end of day]] on Tuesday, 18 March 2025. This is the second step in the annual review process, the final step will be community voting on the proposed changes.
[[m:Special:MyLanguage/Universal_Code_of_Conduct/Annual_review|Read more information and find relevant links about the process on the UCoC annual review page on Meta]].
The [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee|Universal Code of Conduct Coordinating Committee]] (U4C) is a global group dedicated to providing an equitable and consistent implementation of the UCoC. This annual review was planned and implemented by the U4C. For more information and the responsibilities of the U4C, [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Charter|you may review the U4C Charter]].
Please share this information with other members in your community wherever else might be appropriate.
-- In cooperation with the U4C, [[m:User:Keegan (WMF)|Keegan (WMF)]] ૦૦:૨૧, ૮ માર્ચ ૨૦૨૫ (IST)
</div>
<!-- Message sent by User:Keegan (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28307738 -->
== An improved dashboard for the Content Translation tool ==
<div lang="en" dir="ltr">
{{Int:hello}} Wikipedians,
Apologies as this message is not in your language, {{Int:please-translate}}.
The [[mediawikiwiki:Special:MyLanguage/Wikimedia_Language_and_Product_Localization|Language and Product Localization team]] has improved the [https://test.wikipedia.org/w/index.php?title=Special:ContentTranslation&filter-type=automatic&filter-id=previous-edits&active-list=suggestions&from=en&to=es Content Translation dashboard] to create a consistent experience for all contributors using mobile and desktop devices. The improved translation dashboard allows all logged-in users of the tool to enjoy a consistent experience regardless of their type of device.
With a harmonized experience, logged-in desktop users now have access to the capabilities shown in the image below.
[[file:Content_Translation_new-dashboard.png|alt=|center|thumb|576x576px|Notice that in this screenshot, the new dashboard allows: Users to adjust suggestions with the "For you" and "...More" buttons to select general topics or community-created collections (like the example of Climate topic). Also, users can use translation to create new articles (as before) and expand existing articles section by section. You can see how suggestions are provided in the new dashboard in two groups ("Create new pages" and "Expand with new sections")-one for each activity.]]
[[File:Content_Translation_dashboard_on_desktop.png|alt=|center|thumb|577x577px|In the current dashboard, you will notice that you can't adjust suggestions to select topics or community-created collections. Also, you can't expand on existing articles by translating new sections.]]
We will implement [[mw:Special:MyLanguage/Content translation#Improved translation experience|this improvement]] on your wiki '''on Monday, March 17th, 2025''' and remove the current dashboard '''by May 2025'''.
Please reach out with any questions concerning the dashboard in this thread.
Thank you!
On behalf of the Language and Product Localization team.
</div>
<bdi lang="en" dir="ltr">[[User:UOzurumba (WMF)|UOzurumba (WMF)]]</bdi> ૦૮:૨૫, ૧૩ માર્ચ ૨૦૨૫ (IST)
<!-- Message sent by User:UOzurumba (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:UOzurumba_(WMF)/sandbox_CX_Unified_dashboard_announcement_list_1&oldid=28382282 -->
== Your wiki will be in read-only soon ==
<section begin="server-switch"/><div class="plainlinks">
[[:m:Special:MyLanguage/Tech/Server switch|આ સંદેશો અન્ય ભાષામાં વાંચો]] • [https://meta.wikimedia.org/w/index.php?title=Special:Translate&group=page-Tech%2FServer+switch&language=&action=page&filter= {{int:please-translate}}]
<span class="mw-translate-fuzzy">[[foundation:|વિકિમીડિયા ફાઉન્ડેશન]] તેના પ્રાથમિક અને સહાયક ડેટા સેન્ટરની અદલાબદલીનું પરિક્ષણ કરવા જઈ રહ્યું છે.</span> આમ કરવાથી એ વાતની ખાતરી થશે કે આપત્તિના સમયે પણ વિકિપીડિયા અને અન્ય વિકિમીડિયા જાળસ્થળો (સાઇટો) ઉપલબ્ધ હોય.
તમામ ટ્રાફિક '''{{#time:j xg|2025-03-19|gu}}''' પર સ્વિચ થશે. ભારતીય સમય મુજબ ૧૯:૩૦ કલાકે '''[https://zonestamp.toolforge.org/{{#time:U|2025-03-19T14:00|en}} {{#time:H:i e|2025-03-19T14:00}}]''' પરિક્ષણની શરુઆત થશે.
દુર્ભાગ્યવશ, [[mw:Special:MyLanguage/Manual:What is MediaWiki?|MediaWiki]]ની અમુક મર્યાદાઓને કારણે આ પરિક્ષણના સમય દરમ્યાન બધું જ સંપાદન કાર્ય અટકાવવું પડે તેમ છે. ખલેલ બદલ અમે દિલગીર છીએ અને ભવિષ્યમાં આવી ખલેલ ઓછામાં ઓછી પડે તે દિશામાં અમે કાર્યરત છીએ.
પરિક્ષણ શરુ થવાની ૩૦ મિનિટ પહેલાથી બધાં જ વિકિ સંસ્કરણોમાં બેનર મૂકવામાં આવશે. <span lang="en" dir="ltr" class="mw-content-ltr">This banner will remain visible until the end of the operation.</span>
'''ટૂંકા સમયગાળા માટે તમે બધાં જ વિકિપીડિયા સંસ્કરણો ફક્ત વાંચી શકશો, તેમાં ફેરફાર નહિ કરી શકો.'''
*{{#time:l j xg Y|2025-03-19|gu}} ના રોજ, તમે લગભગ એક કલાક કે તેથી વધુ સમય સુધી વિકિપીડિયામાં કોઈ ફેરફાર કરી શકશો નહીં.
*જો આ સમયગાળામાં તમે કોઈ સંપાદન કરવાનો અને તે ફેરફારો સાચવવાનો પ્રયત્ન કરશો તો તમને ત્રુટિ સંદેશો મળશે. અમે આશા રાખીએ છીએ કે આ પરિક્ષણ દરમ્યાન કોઈ પણ સંપાદન ખોવાશે નહિ પરંતુ એ વાતની બાંહેધરી આપી શકાય એમ નથી. જો તમને ત્રુટિ સંદેશો મળે તો બધું જ પૂર્વવત થાય ત્યાં સુધી રાહ જોશો. પછી તમે કરેલા ફેરફારો સાચવી શકાશે. પણ અમારું સુચન છે કે તમે આવા કોઈ પણ ફેરફારોને કોપી કરી ને રાખી લો જેથી કાંઈ અનિચ્છનિય બને તો તમારી મહેનત બાતલ ન જાય.
''અન્ય અસરો'':
*પૃષ્ઠભૂમિમાં ચાલતાં કામો ધીમાં પડશે કે પછી અટકી જશે. લાલ (મૃત) કડીઓ સામન્ય ઝડપથી અપડેટ ન પણ થાય. તમે એવો કોઈ નવો લેખ બનાવો જે અન્યત્ર જોડાતો હોય તો પહેલાનાં તે પાના પર આ લેખની કડી થોડા વધુ સમય માટે હજુ લાલ જ બતાવે તેમ બને. અમુક લાંબા સમયથી ચાલતી સ્ક્રિપ્ટ અટકાવવી પડશે.
* <span lang="en" dir="ltr" class="mw-content-ltr">We expect the code deployments to happen as any other week.</span> <span lang="en" dir="ltr" class="mw-content-ltr">However, some case-by-case code freezes could punctually happen if the operation require them afterwards.</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">[[mw:Special:MyLanguage/GitLab|GitLab]] will be unavailable for about 90 minutes.</span>
જરુર પડે તો આ પરિક્ષણ પાછું પણ ઠેલવામાં આવે. [[wikitech:Switch_Datacenter|wikitech.wikimedia.org પર જઈ ને તમે સમયપત્રક]] જોઈ શકો છો. જો કોઈ પણ ફેરફાર હશે તો સમયપત્રકમાં જાણ કરવામાં આવશે.
'''આ માહિતીને તમારા સમુદાય સાથે વહેંચવા વિનંતી છે.'''</div><section end="server-switch"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> ૦૪:૪૫, ૧૫ માર્ચ ૨૦૨૫ (IST)
<!-- Message sent by User:Quiddity (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Non-Technical_Village_Pumps_distribution_list&oldid=28307742 -->
== Final proposed modifications to the Universal Code of Conduct Enforcement Guidelines and U4C Charter now posted ==
<div lang="en" dir="ltr" class="mw-content-ltr">
The proposed modifications to the [[foundation:Special:MyLanguage/Policy:Universal_Code_of_Conduct/Enforcement_guidelines|Universal Code of Conduct Enforcement Guidelines]] and the U4C Charter [[m:Universal_Code_of_Conduct/Annual_review/2025/Proposed_Changes|are now on Meta-wiki for community notice]] in advance of the voting period. This final draft was developed from the previous two rounds of community review. Community members will be able to vote on these modifications starting on 17 April 2025. The vote will close on 1 May 2025, and results will be announced no later than 12 May 2025. The U4C election period, starting with a call for candidates, will open immediately following the announcement of the review results. More information will be posted on [[m:Special:MyLanguage//Universal_Code_of_Conduct/Coordinating_Committee/Election|the wiki page for the election]] soon.
Please be advised that this process will require more messages to be sent here over the next two months.
The [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee|Universal Code of Conduct Coordinating Committee (U4C)]] is a global group dedicated to providing an equitable and consistent implementation of the UCoC. This annual review was planned and implemented by the U4C. For more information and the responsibilities of the U4C, you may [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Charter|review the U4C Charter]].
Please share this message with members of your community so they can participate as well.
-- In cooperation with the U4C, [[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User_talk:Keegan (WMF)|talk]]) ૦૭:૩૪, ૪ એપ્રિલ ૨૦૨૫ (IST)
</div>
<!-- Message sent by User:Keegan (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28469465 -->
== Editing contest about Norway ==
Hello! Please excuse me from writing in English. If this post should be posted on a different page instead, please feel free to move it (or tell me to move it).
I am Jon Harald Søby from the Norwegian Wikimedia chapter, [[wmno:|Wikimedia Norge]]. During the month of April, we are holding [[:no:Wikipedia:Konkurranser/Månedens konkurranse/2025-04|an editing contest]] about India on the Wikipedias in [[:nb:|Norwegian Bokmål]], [[:nn:|Norwegian Nynorsk]], [[:se:|Northern Sámi]] and [[:smn:|Inari Sámi]]̩, and we had the idea to also organize an "inverse" contest where contributors to Indian-language Wikipedias can write about Norway and Sápmi.
Therefore, I would like to invite interested participants from the Gujarati-language Wikipedia (it doesn't matter if you're from India or not) to join the contest by visiting [[:no:Wikipedia:Konkurranser/Månedens konkurranse/2025-04/For Indians|this page in the Norwegian Bokmål Wikipedia]] and following the instructions that are there.
Hope to see you there! [[સભ્ય:Jon Harald Søby (WMNO)|Jon Harald Søby (WMNO)]] ([[સભ્યની ચર્ચા:Jon Harald Søby (WMNO)|ચર્ચા]]) ૧૫:૫૦, ૪ એપ્રિલ ૨૦૨૫ (IST)
== ચિત્ર મુકવામાં સમસ્યા ==
નમસ્તે, [https://gu.wikipedia.org/wiki/%E0%AA%A2%E0%AA%BE%E0%AA%82%E0%AA%9A%E0%AB%8B:%E0%AA%AE%E0%AA%BE%E0%AA%B9%E0%AA%BF%E0%AA%A4%E0%AB%80%E0%AA%9A%E0%AB%8B%E0%AA%95%E0%AA%A0%E0%AB%81%E0%AA%82_%E0%AA%B5%E0%AA%BF%E0%AA%B6%E0%AB%8D%E0%AA%B5%E0%AA%B5%E0%AA%BF%E0%AA%A6%E0%AB%8D%E0%AA%AF%E0%AA%BE%E0%AA%B2%E0%AA%AF%E2%80%8C આ ઢાંચા]માં ઇમેજ કામ નથી કરતી. કૃપયા સહયતા કરવા વિનંતિ. <b><span style="border:1px dashed DarkOrange;padding:0.25em;margin:0.1em;text-shadow:2px 2px 3px Gray;font-size=75%;">લિ., [[સભ્ય:NehalDaveND|<font color="Red">નેહલ</font>]] [[User talk:NehalDaveND|<font color="DodgerBlue">દવે</font>]]</span></b> ૧૦:૧૨, ૧૪ એપ્રિલ ૨૦૨૫ (IST)
:@[[સભ્ય:Dsvyas|Dsvyas]] @[[સભ્ય:KartikMistry|KartikMistry]] please help. <b><span style="border:1px dashed DarkOrange;padding:0.25em;margin:0.1em;text-shadow:2px 2px 3px Gray;font-size=75%;">લિ., [[સભ્ય:NehalDaveND|<font color="Red">નેહલ</font>]] [[User talk:NehalDaveND|<font color="DodgerBlue">દવે</font>]]</span></b> ૦૭:૧૫, ૧૭ એપ્રિલ ૨૦૨૫ (IST)
::{{ping|NehalDaveND}} સૌ પ્રથમ તો જવાબ આપવામાં મોડું થયું તે બદલ ક્ષમાપ્રાર્થી છું. મેં હમણાં જ આ માહિતીચોકઠામાં ચિત્ર ઉમેરવાનો પ્રયત્ન કર્યો તો એણે સફળતાથી કામ કર્યું. તમારે '''image = ''' પરિમાણમાં '''='''ની નિશાનીની સામે ફક્ત ચિત્રનું નામ જ લખવાનું છે, દા.ત. '''image = વિશ્વવિદ્યાલય.PNG'''. ચિત્રના નામની આગળ File, Image, વગેરે જેવું કશું લખવાનું નથી. જો આમ કરતા પણ તમારું કામ ન થાય તો મને જણાવો કે તમે કયા લેખમાં આ માહિતીચોકઠામાં ચિત્ર ઉમેરવાનો પ્રયત્ન કરો છો અને એ ચિત્ર કયું છે. હું પ્રયત્ન કરી જોઈશ. [[:User:Dsvyas|ધવલ]]<sup>[[:User_talk:Dsvyas|ચર્ચા]]/[[:Special:Contributions/Dsvyas|યોગદાન]]</sup> ૨૨:૩૦, ૨૨ એપ્રિલ ૨૦૨૫ (IST)
== Invitation for the next South Asia Open Community Call (SAOCC) with a focus on WMF's Annual Plans (27th April, 2025) ==
Dear All,
The [[:m:South Asia Open Community Call|South Asia Open Community Call (SAOCC)]] is a monthly call where South Asian communities come together to participate, share community activities, receive important updates and ask questions in the moderated discussions.
The next SAOCC is scheduled for 27th April, 6:00 PM-7:00 PM (1230-1330 UTC) and will have a section with representatives from WMF who will be sharing more about their [[:m:Wikimedia Foundation Annual Plan/2025-2026/Global Trends|Annual Plans]] for the next year, in addition to Open Community Updates.
We request you all to please attend the call and you can find the joining details [https://meta.wikimedia.org/wiki/South_Asia_Open_Community_Call#27_April_2025 here].
Thank you! [[સભ્ય:MediaWiki message delivery|MediaWiki message delivery]] ([[સભ્યની ચર્ચા:MediaWiki message delivery|ચર્ચા]]) ૧૩:૫૫, ૧૪ એપ્રિલ ૨૦૨૫ (IST)
<!-- Message sent by User:Nitesh (CIS-A2K)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Nitesh_Gill/lists/Indic_VPs&oldid=28543211 -->
== Ukraine's Cultural Diplomacy Month 2025: Invitation ==
<div lang="en" dir="ltr">
[[File:UCDM 2025 general.png|180px|right]]
{{int:please-translate}}
Hello, dear Wikipedians!<br/>
[[:m:Special:MyLanguage/Wikimedia Ukraine|Wikimedia Ukraine]], in cooperation with the [[:en:Ministry of Foreign Affairs of Ukraine|MFA of Ukraine]] and [[:en:Ukrainian Institute|Ukrainian Institute]], has launched the fifth edition of writing challenge "'''[[:m:Special:MyLanguage/Ukraine's Cultural Diplomacy Month 2025|Ukraine's Cultural Diplomacy Month]]'''", which lasts from '''14th April''' until '''16th May 2025'''. The campaign is dedicated to famous Ukrainian artists of cinema, music, literature, architecture, design, and cultural phenomena of Ukraine that are now part of world heritage. We accept contributions in every language!
The most active contesters will receive prizes.
If you are interested in coordinating long-term community engagement for the campaign and becoming a local ambassador, we would love to hear from you! Please let us know your interest.
<br/>
We invite you to take part and help us improve the coverage of Ukrainian culture on Wikipedia in your language! Also, we plan to set up a [[:m:CentralNotice/Request/Ukraine's Cultural Diplomacy Month 2025|banner]] to notify users of the possibility to participate in such a challenge! [[:m:User:OlesiaLukaniuk (WMUA)|OlesiaLukaniuk (WMUA)]] ([[:m:User talk:OlesiaLukaniuk (WMUA)|talk]])
</div>
૨૧:૪૧, ૧૬ એપ્રિલ ૨૦૨૫ (IST)
<!-- Message sent by User:Hide on Rosé@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:OlesiaLukaniuk_(WMUA)/list_of_wikis&oldid=28552112 -->
== Vote now on the revised UCoC Enforcement Guidelines and U4C Charter ==
<div lang="en" dir="ltr" class="mw-content-ltr">
The voting period for the revisions to the Universal Code of Conduct Enforcement Guidelines ("UCoC EG") and the UCoC's Coordinating Committee Charter is open now through the end of 1 May (UTC) ([https://zonestamp.toolforge.org/1746162000 find in your time zone]). [[m:Special:MyLanguage/Universal_Code_of_Conduct/Annual_review/2025/Voter_information|Read the information on how to participate and read over the proposal before voting]] on the UCoC page on Meta-wiki.
The [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee|Universal Code of Conduct Coordinating Committee (U4C)]] is a global group dedicated to providing an equitable and consistent implementation of the UCoC. This annual review of the EG and Charter was planned and implemented by the U4C. Further information will be provided in the coming months about the review of the UCoC itself. For more information and the responsibilities of the U4C, you may [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Charter|review the U4C Charter]].
Please share this message with members of your community so they can participate as well.
In cooperation with the U4C -- [[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User_talk:Keegan (WMF)|talk]]) ૦૬:૦૪, ૧૭ એપ્રિલ ૨૦૨૫ (IST)
</div>
<!-- Message sent by User:Keegan (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28469465 -->
== Sub-referencing: User testing ==
<div lang="en" dir="ltr">
[[File:Sub-referencing reuse visual.png|400px|right]]
<small>''Apologies for writing in English, please help us by providing a translation below''</small>
Hi I’m Johannes from [[:m:Wikimedia Deutschland|Wikimedia Deutschland]]'s [[:m:WMDE Technical Wishes|Technical Wishes team]]. We are making great strides with the new [[:m:WMDE Technical Wishes/Sub-referencing|sub-referencing feature]] and we’d love to invite you to take part in two activities to help us move this work further:
#'''Try it out and share your feedback'''
#:[[:m:WMDE Technical Wishes/Sub-referencing# Test the prototype|Please try]] the updated ''wikitext'' feature [https://en.wikipedia.beta.wmflabs.org/wiki/Sub-referencing on the beta wiki] and let us know what you think, either [[:m:Talk:WMDE Technical Wishes/Sub-referencing|on our talk page]] or by [https://greatquestion.co/wikimediadeutschland/talktotechwish booking a call] with our UX researcher.
#'''Get a sneak peak and help shape the ''Visual Editor'' user designs'''
#:Help us test the new design prototypes by participating in user sessions – [https://greatquestion.co/wikimediadeutschland/gxk0taud/apply sign up here to receive an invite]. We're especially hoping to speak with people from underrepresented and diverse groups. If that's you, please consider signing up! No prior or extensive editing experience is required. User sessions will start ''May 14th''.
We plan to bring this feature to Wikimedia wikis later this year. We’ll reach out to wikis for piloting in time for deployments. Creators and maintainers of reference-related tools and templates will be contacted beforehand as well.
Thank you very much for your support and encouragement so far in helping bring this feature to life! </div> <bdi lang="en" dir="ltr">[[User:Johannes Richter (WMDE)|Johannes Richter (WMDE)]] ([[User talk:Johannes Richter (WMDE)|talk]])</bdi> ૨૦:૩૪, ૨૮ એપ્રિલ ૨૦૨૫ (IST)
<!-- Message sent by User:Johannes Richter (WMDE)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Johannes_Richter_(WMDE)/Sub-referencing/massmessage_list&oldid=28628657 -->
== <span lang="en" dir="ltr">Vote on proposed modifications to the UCoC Enforcement Guidelines and U4C Charter</span> ==
<div lang="en" dir="ltr">
<section begin="announcement-content" />
The voting period for the revisions to the Universal Code of Conduct Enforcement Guidelines and U4C Charter closes on 1 May 2025 at 23:59 UTC ([https://zonestamp.toolforge.org/1746162000 find in your time zone]). [[m:Special:MyLanguage/Universal Code of Conduct/Annual review/2025/Voter information|Read the information on how to participate and read over the proposal before voting]] on the UCoC page on Meta-wiki.
The [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee|Universal Code of Conduct Coordinating Committee (U4C)]] is a global group dedicated to providing an equitable and consistent implementation of the UCoC. This annual review was planned and implemented by the U4C. For more information and the responsibilities of the U4C, you may [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Charter|review the U4C Charter]].
Please share this message with members of your community in your language, as appropriate, so they can participate as well.
In cooperation with the U4C -- <section end="announcement-content" />
</div>
<div lang="en" dir="ltr" class="mw-content-ltr">
[[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|talk]]) ૦૯:૧૧, ૨૯ એપ્રિલ ૨૦૨૫ (IST)</div>
<!-- Message sent by User:Keegan (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28618011 -->
== We will be enabling the new Charts extension on your wiki soon! ==
''(Apologies for posting in English)''
Hi all! We have good news to share regarding the ongoing problem with graphs and charts affecting all wikis that use them.
As you probably know, the [[:mw:Special:MyLanguage/Extension:Graph|old Graph extension]] was disabled in 2023 [[listarchive:list/wikitech-l@lists.wikimedia.org/thread/EWL4AGBEZEDMNNFTM4FRD4MHOU3CVESO/|due to security reasons]]. We’ve worked in these two years to find a solution that could replace the old extension, and provide a safer and better solution to users who wanted to showcase graphs and charts in their articles. We therefore developed the [[:mw:Special:MyLanguage/Extension:Chart|Charts extension]], which will be replacing the old Graph extension and potentially also the [[:mw:Extension:EasyTimeline|EasyTimeline extension]].
After successfully deploying the extension on Italian, Swedish, and Hebrew Wikipedia, as well as on MediaWiki.org, as part of a pilot phase, we are now happy to announce that we are moving forward with the next phase of deployment, which will also include your wiki.
The deployment will happen in batches, and will start from '''May 6'''. Please, consult [[:mw:Special:MyLanguage/Extension:Chart/Project#Deployment Timeline|our page on MediaWiki.org]] to discover when the new Charts extension will be deployed on your wiki. You can also [[:mw:Special:MyLanguage/Extension:Chart|consult the documentation]] about the extension on MediaWiki.org.
If you have questions, need clarifications, or just want to express your opinion about it, please refer to the [[:mw:Special:MyLanguage/Extension_talk:Chart/Project|project’s talk page on Mediawiki.org]], or ping me directly under this thread. If you encounter issues using Charts once it gets enabled on your wiki, please report it on the [[:mw:Extension_talk:Chart/Project|talk page]] or at [[phab:tag/charts|Phabricator]].
Thank you in advance! -- [[User:Sannita (WMF)|User:Sannita (WMF)]] ([[User talk:Sannita (WMF)|talk]]) ૨૦:૩૮, ૬ મે ૨૦૨૫ (IST)
<!-- Message sent by User:Sannita (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Sannita_(WMF)/Mass_sending_test&oldid=28663781 -->
== <span lang="en" dir="ltr">Call for Candidates for the Universal Code of Conduct Coordinating Committee (U4C)</span> ==
<div lang="en" dir="ltr">
<section begin="announcement-content" />
The results of voting on the Universal Code of Conduct Enforcement Guidelines and Universal Code of Conduct Coordinating Committee (U4C) Charter is [[m:Special:MyLanguage/Universal Code of Conduct/Annual review/2025#Results|available on Meta-wiki]].
You may now [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Election/2025/Candidates|submit your candidacy to serve on the U4C]] through 29 May 2025 at 12:00 UTC. Information about [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Election/2025|eligibility, process, and the timeline are on Meta-wiki]]. Voting on candidates will open on 1 June 2025 and run for two weeks, closing on 15 June 2025 at 12:00 UTC.
If you have any questions, you can ask on [[m:Talk:Universal Code of Conduct/Coordinating Committee/Election/2025|the discussion page for the election]]. -- in cooperation with the U4C, </div><section end="announcement-content" />
</div>
<bdi lang="en" dir="ltr">[[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User_talk:Keegan (WMF)|ચર્ચા]])</bdi> ૦૩:૩૭, ૧૬ મે ૨૦૨૫ (IST)
<!-- Message sent by User:Keegan (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28618011 -->
== RfC ongoing regarding Abstract Wikipedia (and your project) ==
<div lang="en" dir="ltr" class="mw-content-ltr">
''(Apologies for posting in English, if this is not your first language)''
Hello all! We opened a discussion on Meta about a very delicate issue for the development of [[:m:Special:MyLanguage/Abstract Wikipedia|Abstract Wikipedia]]: where to store the abstract content that will be developed through functions from Wikifunctions and data from Wikidata. Since some of the hypothesis involve your project, we wanted to hear your thoughts too.
We want to make the decision process clear: we do not yet know which option we want to use, which is why we are consulting here. We will take the arguments from the Wikimedia communities into account, and we want to consult with the different communities and hear arguments that will help us with the decision. The decision will be made and communicated after the consultation period by the Foundation.
You can read the various hypothesis and have your say at [[:m:Abstract Wikipedia/Location of Abstract Content|Abstract Wikipedia/Location of Abstract Content]]. Thank you in advance! -- [[User:Sannita (WMF)|Sannita (WMF)]] ([[User talk:Sannita (WMF)|<span class="signature-talk">{{int:Talkpagelinktext}}</span>]]) ૨૦:૫૬, ૨૨ મે ૨૦૨૫ (IST)
</div>
<!-- Message sent by User:Sannita (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Sannita_(WMF)/Mass_sending_test&oldid=28768453 -->
== <span lang="en" dir="ltr">Wikimedia Foundation Board of Trustees 2025 Selection & Call for Questions</span> ==
<div lang="en" dir="ltr">
<section begin="announcement-content" />
:''[[m:Special:MyLanguage/Wikimedia Foundation elections/2025/Announcement/Selection announcement|{{int:interlanguage-link-mul}}]] • [https://meta.wikimedia.org/w/index.php?title=Special:Translate&group=page-{{urlencode:Wikimedia Foundation elections/2025/Announcement/Selection announcement}}&language=&action=page&filter= {{int:please-translate}}]''
Dear all,
This year, the term of 2 (two) Community- and Affiliate-selected Trustees on the Wikimedia Foundation Board of Trustees will come to an end [1]. The Board invites the whole movement to participate in this year’s selection process and vote to fill those seats.
The Elections Committee will oversee this process with support from Foundation staff [2]. The Governance Committee, composed of trustees who are not candidates in the 2025 community-and-affiliate-selected trustee selection process (Raju Narisetti, Shani Evenstein Sigalov, Lorenzo Losa, Kathy Collins, Victoria Doronina and Esra’a Al Shafei) [3], is tasked with providing Board oversight for the 2025 trustee selection process and for keeping the Board informed. More details on the roles of the Elections Committee, Board, and staff are here [4].
Here are the key planned dates:
* May 22 – June 5: Announcement (this communication) and call for questions period [6]
* June 17 – July 1, 2025: Call for candidates
* July 2025: If needed, affiliates vote to shortlist candidates if more than 10 apply [5]
* August 2025: Campaign period
* August – September 2025: Two-week community voting period
* October – November 2025: Background check of selected candidates
* Board’s Meeting in December 2025: New trustees seated
Learn more about the 2025 selection process - including the detailed timeline, the candidacy process, the campaign rules, and the voter eligibility criteria - on this Meta-wiki page [[m:Special:MyLanguage/Wikimedia_Foundation_elections/2025|[link]]].
'''Call for Questions'''
In each selection process, the community has the opportunity to submit questions for the Board of Trustees candidates to answer. The Election Committee selects questions from the list developed by the community for the candidates to answer. Candidates must answer all the required questions in the application in order to be eligible; otherwise their application will be disqualified. This year, the Election Committee will select 5 questions for the candidates to answer. The selected questions may be a combination of what’s been submitted from the community, if they’re alike or related. [[m:Special:MyLanguage/Wikimedia_Foundation_elections/2025/Questions_for_candidates|[link]]]
'''Election Volunteers'''
Another way to be involved with the 2025 selection process is to be an Election Volunteer. Election Volunteers are a bridge between the Elections Committee and their respective community. They help ensure their community is represented and mobilize them to vote. Learn more about the program and how to join on this Meta-wiki page [[m:Wikimedia_Foundation_elections/2025/Election_volunteers|[link].]]
Thank you!
[1] https://meta.wikimedia.org/wiki/Wikimedia_Foundation_elections/2022/Results
[2] https://foundation.wikimedia.org/wiki/Committee:Elections_Committee_Charter
[3] https://foundation.wikimedia.org/wiki/Resolution:Committee_Membership,_December_2024
[4] https://meta.wikimedia.org/wiki/Wikimedia_Foundation_elections_committee/Roles
[5] https://meta.wikimedia.org/wiki/Wikimedia_Foundation_elections/2025/FAQ
[6] https://meta.wikimedia.org/wiki/Wikimedia_Foundation_elections/2025/Questions_for_candidates
Best regards,
Victoria Doronina
Board Liaison to the Elections Committee
Governance Committee<section end="announcement-content" />
</div>
[[સભ્ય:MediaWiki message delivery|MediaWiki message delivery]] ([[સભ્યની ચર્ચા:MediaWiki message delivery|ચર્ચા]]) ૦૮:૩૭, ૨૮ મે ૨૦૨૫ (IST)
<!-- Message sent by User:RamzyM (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28618011 -->
== Update from A2K team: May 2025 ==
Hello everyone,
We’re happy to share that the ''Access to Knowledge'' (A2K) program has now formally become part of the '''Raj Reddy Centre for Technology and Society''' at '''IIIT-Hyderabad'''. Going forward, our work will continue under the name [[:m:IIITH-OKI|Open Knowledge Initiatives]].
The new team includes most members from the former A2K team, along with colleagues from IIIT-H already involved in Wikimedia and Open Knowledge work. Through this integration, our commitment to partnering with Indic Wikimedia communities, the GLAM sector, and broader open knowledge networks remains strong and ongoing. Learn more at our Team’s page on Meta-Wiki.
We’ll also be hosting an open session during the upcoming [[:m:South Asia Open Community Call|South Asia Open Community Call]] on 6 - 7 pm, and we look forward to connecting with you there.
Thanks for your continued support! Thank you
Pavan Santhosh,
On behalf of the Open Knowledge Initiatives Team.
<!-- Message sent by User:Nitesh (CIS-A2K)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Nitesh_Gill/lists/Indic_VPs&oldid=28543211 -->
== <span lang="en" dir="ltr"> Upcoming Deployment of the CampaignEvents Extension</span> ==
<div lang="en" dir="ltr">
<section begin="message"/>
Hello everyone,
''(Apologies for posting in English if English is not your first language. Please help translate to your language.)''
The Campaigns Product Team is planning a global deployment of the '''[[:mw:Help:Extension:CampaignEvents|CampaignEvents extension]]''' to all Wikipedias, including this wiki, during the '''week of June 23rd'''.
This extension is designed to help organizers plan and manage events, WikiProjects, and other on-wiki collaborations - and to make these efforts more discoverable.
The three main features of this extension are:
* '''[[:m:Event_Center/Registration|Event Registration]]''': A simple way to sign up for events on the wiki.
* '''[[:m:CampaignEvents/Collaboration_list|Collaboration List]]''': A global list of events and a local list of WikiProjects, accessible at '''[[:m:Special:AllEvents|Special:AllEvents]]'''.
* '''[[:m:Campaigns/Foundation_Product_Team/Invitation_list|Invitation Lists]]''': A tool to help organizers find editors who might want to join, based on their past contributions.
'''Note''': The extension comes with a new user right called '''"Event Organizer"''', which will be managed by administrators on this wiki. Organizer tools like Event Registration and Invitation Lists will only work if someone is granted this right. The Collaboration List is available to everyone immediately after deployment.
The extension is already live on several wikis, including '''Meta, Wikidata, English Wikipedia''', and more ( [[m:CampaignEvents/Deployment_status#Current_Deployment_Status_for_CampaignEvents_extension| See the full deployment list]])
If you have any questions, concerns, or feedback, please feel free to share them on the [[m:Talk:CampaignEvents| extension talkpage]]. We’d love to hear from you before the rollout.
Thank you! <section end="message"/>
</div>
<bdi lang="en" dir="ltr">[[User:Udehb-WMF|Udehb-WMF]] ([[User talk:Udehb-WMF|ચર્ચા]]) ૨૨:૧૭, ૨૯ મે ૨૦૨૫ (IST)</bdi>
<!-- Message sent by User:Udehb-WMF@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Udehb-WMF/sandbox/deployment_audience&oldid=28803829 -->
== મુખપૃષ્ઠ પર આજનું ચિત્ર બદલવું જોઈએ? ==
ગયા વર્ષે મે મહિનામાં આપણે '''આજનું ચિત્ર'''માં દર્શાવાતાં ચિત્રો બદલ્યાં હતાં. શું હવે ફરીથી તેને પૂર્ણ કે આંશિક રીતે બદલવા જોઈએ? સૌ કોઈ પોતાનો મત વ્યક્ત કરશો.
—[[સભ્ય:Brihaspati|બૃહસ્પતિ]] [[સભ્યની ચર્ચા:Brihaspati|<sup>મારી સાથે વાત કરો</sup>]] ૦૦:૩૯, ૩ જૂન ૨૦૨૫ (IST)
:હા. સહમત. સમયે સમયે નવા ચિત્રો મૂકવા જોઈએ.- [[સભ્ય:Nizil Shah|Nizil Shah]] ([[સભ્યની ચર્ચા:Nizil Shah|ચર્ચા]]) ૧૨:૫૪, ૯ જૂન ૨૦૨૫ (IST)
== 📣 Announcing the South Asia Newsletter – Get Involved! 🌏 ==
<div lang="en" dir="ltr">
''{{int:please-translate}}''
Hello Wikimedians of South Asia! 👋
We’re excited to launch the planning phase for the '''South Asia Newsletter''' – a bi-monthly, community-driven publication that brings news, updates, and original stories from across our vibrant region, to one page!
We’re looking for passionate contributors to join us in shaping this initiative:
* Editors/Reviewers – Craft and curate impactful content
* Technical Contributors – Build and maintain templates, modules, and other magic on meta.
* Community Representatives – Represent your Wikimedia Affiliate or community
If you're excited to contribute and help build a strong regional voice, we’d love to have you on board!
👉 Express your interest though [https://docs.google.com/forms/d/e/1FAIpQLSfhk4NIe3YwbX88SG5hJzcF3GjEeh5B1dMgKE3JGSFZ1vtrZw/viewform this link].
Please share this with your community members.. Let’s build this together! 💬
This message was sent with [[સભ્ય:MediaWiki message delivery|MediaWiki message delivery]] ([[સભ્યની ચર્ચા:MediaWiki message delivery|ચર્ચા]]) by [[m:User:Gnoeee|Gnoeee]] ([[m:User_talk:Gnoeee|talk]]) at ૨૧:૧૨, ૬ જૂન ૨૦૨૫ (IST)
</div>
<!-- Message sent by User:Gnoeee@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/South_Asia_Village_Pumps&oldid=25720607 -->
== Vote now in the 2025 U4C Election ==
<div lang="en" dir="ltr" class="mw-content-ltr">
Apologies for writing in English.
{{Int:Please-translate}}
Eligible voters are asked to participate in the 2025 [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee|Universal Code of Conduct Coordinating Committee]] election. More information–including an eligibility check, voting process information, candidate information, and a link to the vote–are available on Meta at the [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Election/2025|2025 Election information page]]. The vote closes on 17 June 2025 at [https://zonestamp.toolforge.org/1750161600 12:00 UTC].
Please vote if your account is eligible. Results will be available by 1 July 2025. -- In cooperation with the U4C, [[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|talk]]) ૦૪:૩૦, ૧૪ જૂન ૨૦૨૫ (IST) </div>
<!-- Message sent by User:Keegan (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28848819 -->
ivgnizkz1mj2lxu7lepel9rw8hovwdi
ઢાંચો:Coord
10
7880
886388
759864
2023-04-30T08:14:15Z
en>Galobtter
0
Changed protection settings for "[[Template:Coord]]": Only purpose is to call fully-protected module ([Edit=Require administrator access] (indefinite) [Move=Require administrator access] (indefinite))
759863
wikitext
text/x-wiki
<includeonly>{{#invoke:Coordinates|coord}}</includeonly><noinclude>
{{Documentation}}
<!-- Add categories to the /doc subpage, interwikis to Wikidata, not here -->
</noinclude>
0o7lasvhdxe29mlczlsmvzkvka0kscy
886389
886388
2025-06-13T17:03:28Z
KartikMistry
10383
[[:en:Template:Coord]] માંથી આયાત કરેલ ૧ પુનરાવર્તન
759863
wikitext
text/x-wiki
<includeonly>{{#invoke:Coordinates|coord}}</includeonly><noinclude>
{{Documentation}}
<!-- Add categories to the /doc subpage, interwikis to Wikidata, not here -->
</noinclude>
0o7lasvhdxe29mlczlsmvzkvka0kscy
ઍલન ટ્યુરિંગ
0
31006
886330
886314
2025-06-13T14:42:55Z
InternetArchiveBot
63183
Rescuing 2 sources and tagging 0 as dead.) #IABot (v2.0.9.5
886330
wikitext
text/x-wiki
{{cleanup}}
{{Infobox scientist
| name = ઍલન ટ્યુરિંગ
| image =Alan Turing az 1930-as években.jpg| image_width =
| caption =
| birth_date = {{Birth date|1912|6|23|df=yes}}
| birth_place = [[Maida Vale]], London, England, United Kingdom
| death_date = {{Death date and age|1954|6|7|1912|6|23|df=yes}}
| death_place = [[Wilmslow]], [[Cheshire]], England, United Kingdom
| nationality = British
| field = [[Mathematician]], [[logician]], [[cryptanalyst]], [[computer scientist]]
| work_institutions = [[University of Cambridge]]<br />[[Government Code and Cypher School]]<br />[[National Physical Laboratory, UK|National Physical Laboratory]]<br />[[University of Manchester]]
| alma_mater = [[King's College, Cambridge]]<br />[[Princeton University]]
| doctoral_advisor = [[Alonzo Church]]
| doctoral_students = [[Robin Gandy]]
| known_for = [[Halting problem]]<br />[[Turing machine]]<br />[[Cryptanalysis of the Enigma]]<br />[[Automatic Computing Engine]]<br />[[Turing Award]]<br />[[Turing Test]]<br />[[Turing pattern]]s
| prizes = [[Officer of the Order of the British Empire]]<br />[[Fellow of the Royal Society]]
}}
'''ઍલન મેથીસન ટ્યુરિંગ''', ઓબીઈ, એફઆરએસ; 23 જૂન 1912 – 7 જૂન 1954), અંગ્રેજી ગણિતશાસ્ત્રી, તર્કશાસ્ત્રી,સંકેતલિપિના વિશ્લેષક અને કમ્પ્યૂટર વૈજ્ઞાનિક હતા. તેમણે ટ્યુરિંગ મશિન સાથે ગાણિતિક નિયમો અને ગણતરીની વિભાવનાનું નિર્દિષ્ટીકરણ પૂરું પાડીને કમ્પ્યૂટર વિજ્ઞાનના વિકાસમાં અત્યંત પ્રભાવશાળી યોગદાન આપ્યું હતું, જેણે આધુનિક [[કમ્પ્યૂટર]]ના સર્જનમાં મહત્તવપૂર્ણ ભૂમિકા ભજવી હતી.<ref name="AFP"/> [[બીજું વિશ્વ યુદ્ધ|બીજા વિશ્વ યુદ્ધ]] દરમિયાન, ટ્યુરિંગે ગવર્મેન્ટ કોડ અને સાયફર સ્કૂલ બ્લેત્ચલેય પાર્ક, બ્રિટનના કોડબ્રેકીંગ સેન્ટર માટે કામ કર્યું. થોડા સમય માટે તેઓ હટ 8 વિભાગના મુખ્યાધિકારી હતા, આ વિભાગ જર્મન નૌકા સૈન્યને સંબંધિત સંકેતલિપિના વિશ્લેષણમાટે જવાબદાર હતું. તેમણે જર્મનસંકેતલિપિને તોડતી પદ્ધતિઓમાંની એક યોજના નક્કી કરી રાખી હતી, જેમાં બોમ્બે પદ્ધતિ, ઈલેક્ટ્રોમીકેનીકલમશીન ઈનીગ્મા મશીન માટે ગોઠવણી શોધી શકે છે, તેનો સમાવેશ થાય છે. યુદ્ધ પછી તેમણે નેશનલ ફિઝીકલ લેબોરેટરી ખાતે કામ કર્યું, જ્યાં એસીઈ(ACE), એક સંગ્રહ કરી શકાય એવા કમ્પ્યૂટર પ્રોગ્રામની સૌ પ્રથમ ડિઝાઈન તૈયાર કરી. તેમના જીવનના અંત ભાગમાં ટ્યુરિંગને ગાણિતિક જીવવિજ્ઞાનમાં રસ પડ્યો. તેમણે આકાર વિકાસ(મૉર્ફોજિનેસિસ)નું રસાયણશાસ્ત્ર આધારિત પેપર લખ્યું,<ref>{{Cite journal| last= Turing | first= A. M. | title = The Chemical Basis of Morphogenesis | journal = Philosophical Transactions of The Royal Society of London, series B | volume = 237 | pages = 37–72 | year = 1952 }}</ref> અને તેમણે ઓસીલેટીંગકેમિકલ રીએક્શન જેમ કે બીલોઅસોય- ઝાબોટીન્સ્કાય રીએક્શનનું અનુમાન કર્યું, જેનું 1960ના દાયકાઓમાં સૌ પ્રથમ વખત અવલોકિત બન્યું.
ટ્યુરિંગની સજાતીયતા 1952માં ફોજદારી ફરિયાદમાં પરિણમી- તે સમયે યુનાઈટેડ કિંગડ્મમાં સજાતીય વર્તણૂક ગેરકાયદેસર હતી- અને તેમણે જેલના વિકલ્પ તરીકે સ્ત્રી હોર્મોન(રાસાયણિક ખસીકરણ)ની સાથે સારવાર સ્વીકારી હતી. તેઓ 1954માં તેમના 42મા જન્મદિવસનાં કેટલાંક અઠવાડિયા પહેલાં, સાઈનાઈડ ઝેરથી મૃત્યુ પામ્યા. કાયદેસરની તપાસમાં તે આત્મહત્યા હોવાનું શોધાયું હતું, તેમની માતા અને કેટલાક અન્ય લોકો તેમના મોતને એક અકસ્માત હોવાનું માની રહ્યાં હતાં. 10 સપ્ટેમ્બર 2009ના રોજ, ઈન્ટરનેટ ઝુંબેશને અનુસરતાં, યુદ્ધ પછી ટ્યુરિંગની સાથે જે રીતનો વ્યવહાર થયો હતો, તે માટે બ્રિટિશ સરકાર વતી બ્રિટિશ વડાપ્રધાન ગોર્ડોન બ્રાઉને સત્તાવાર જાહેર માફી માંગી હતી. .<ref name="PM-apology">{{Cite news | url = http://news.bbc.co.uk/2/hi/technology/8249792.stm | title = PM apology after Turing petition | date = 11 September 2009 | work = BBC News}}</ref>
== બાળપણ અને યુવાની ==
ઍલન ટ્યુરિંગનું ગર્ભધાન [[છત્રપુર]],[[ઓરિસ્સા]], ભારતમાં થયું હતું.<ref name="Hodges1983P5">{{Harvnb|Hodges|1983|p=5}}</ref> તેમના પિતા, જુલિયસ મેથીસન ટ્યુરિંગભારતીય નાગરિક સેવાના સભ્ય હતા. જુલિયસ અને તેની પત્ની સારા (પૂર્વાશ્રમમાં સ્ટોનેય; 1881–1976, એડવર્ડ વોલ્લર સ્ટોનેય, મદ્રાસ રેલ્વેના મુખ્ય એન્જિનીયરની પુત્રી હતાં) ઇચ્છતાં હતાં કે ઍલનનો ઉછેર ઈંગ્લેન્ડમાં થાય, તેથી તેઓ મૈડા વેલે<ref name="englishheritaget">{{Cite web | url = http://www.english-heritage.org.uk/server/show/nav.001002006005/chooseLetter/T | title = London Blue Plaques | access-date = 10 February 2007 | work = English Heritage}}</ref>, લંડનમાં પાછાં આવ્યાં, જ્યાં ઍલન ટ્યુરિંગનો જન્મ 23 જૂન 1912ના રોજ થયો, જે પાછળથી કોલોન્નાડે હોટલપર બિલ્ડીંગની બહારની બાજુ<ref>{{openplaque|381}}</ref>એ એક વાદળી તકતી દ્વારા નોંધાયેલું હતું.<ref name="Hodges1983P5"/><ref name="turingorguk">{{Cite web| url=http://www.turing.org.uk/turing/scrapbook/memorial.html | title=The Alan Turing Internet Scrapbook | access-date=26 September 2006}}</ref> તેમને જ્હોન નામનો મોટો ભાઈ હતો. તેમના પિતાનું નાગરિક સેવા કમિશન હજી પણ સક્રિય હતું અને ટ્યુરિંગના બાળપણનાં વર્ષો દરમિયાન તેમનાં માતા-પિતા તેમના બે પુત્રોને નિવૃત્ત આર્મી દંપતી પાસે મૂકી, હેસ્ટીંગ્સ, ઈંગ્લેન્ડ<ref>{{Harvnb|Hodges|1983|p=6}}</ref> અને ભારત વચ્ચે આવ-જા કરતાં હતાં. જીવનમાં ખૂબ જ જલદી, ટ્યુરિંગે પાછળથી વધુ પ્રમુખતાઓ દર્શાવી હતી જે પ્રતિભાસંપન્ન હોવાની નિશાની દર્શાવતી હતી.<ref name="toolbox">{{Cite web |title=Alan Turing – Towards a Digital Mind: Part 1 |first=G. James |last=Jones |date=11 December 2001 |url=http://www.systemtoolbox.com/article.php?history_id=3 |access-date=27 July 2007 |work=System Toolbox |archive-date=3 ઑગસ્ટ 2007 |archive-url=https://web.archive.org/web/20070803163318/http://www.systemtoolbox.com/article.php?history_id=3 |url-status=dead }}</ref>
છ વર્ષની ઉંમરે તેમના વાલીએ સેન્ટ મિશેલ, દિવસની સ્કૂલમાં તેમનું નામ નોંધાવ્યું હતું. તેમની પ્રતિભા શરૂઆતમાં જ મુખ્ય શિક્ષિકાએ ઓળખી કાઢી, એવી રીતે તેમના ક્રમશઃ ઘણા શિક્ષકોએ તેમની પ્રતિભા ઓળખી હતી. 1924માં, 14 વર્ષની ઉંમરે, તેઓ ડૂરસેટમાં બજાર ભરાય એવા શહેર શેરબોર્નમાં જાણીતી સ્વતંત્ર્ય શાળાશેરબોર્ન શાળામાં ગયા. સત્રના પ્રથમ દિવસે બ્રિટનમાં સામાન્ય હડતાળ હતી, પણ તેને પ્રથમ દિવસે હાજર રહેવું હતું તે નિર્ધારિત હતું, તેથી તેણે તેની સાઈકલ સાઉથએમ્પટોનથી શાળા સુધી {{convert|60|mi|km}}થી વધુ દોરીને લઈ ગયો, આખી રાત પ્રવાસી માટેની વીશીમાં રોકાયો.<ref name="metamagical">{{Cite book|title=Metamagical Themas: Questing for the Essence of Mind and Pattern |first=Douglas R. |last=Hofstadter |year=1985 |publisher=Basic Books |isbn=0-465-04566-9 |oclc=230812136}}</ref>
[[ચિત્ર:KingsCollegeChapel.jpg|thumb|કિંગ્સ કોલેજ, કેમ્બ્રીજ ખાતે કમ્પ્યૂટર ખંડનું નામ ટ્યુરિંગના નામથી રાખવામાં આવ્યું હતું, જ્યાં તેઓ 1931માં વિદ્યાર્થી અને 1935માં ફેલો બન્યા હતા.]]
ટ્યુરિંગની કુદરતી રુચિ ગણિત અને વિજ્ઞાન હોવાથી શેરબોર્નમાં કેટલાક શિક્ષકો, જેમની શિક્ષણની વ્યાખ્યામાં સાહિત્યપર વધુ ભાર આપતા હતા, પરિણામે તેઓ પ્રત્યેથી તેમને આદર ન મળ્યો. તેમના મુખ્ય શિક્ષકે તેમના વાલીને લખ્યું હતું- "હું આશા રાખું છું કે તે બે સ્ટૂલની વચ્ચે ન પડે. જો તેને જાહેર શાળામાં રહેવું હશે, તો તેણે ચોક્કસપણે ''શિક્ષિત'' થવાનું ધ્યેય રાખવું જ પડશે. જો તેણે માત્ર''વિજ્ઞાનના વિશેષજ્ઞ'' બનવું હશે, તો તે જાહેર શાળામાં તેનો સમય બરબાદ કરી રહ્યો છે. "<ref>{{Harvnb|Hodges|1983|p=26}}</ref> આ બધું થવા છતાં, ટ્યુરિંગે અભ્યાસમાં નોંધનીય ક્ષમતા દર્શાવવાની ચાલુ રાખી. 1927માં પ્રાથમિક કલનશાસ્ત્રનો અભ્યાસ કર્યાં વિના જ તેને જટિલ સમસ્યાઓ ઉકેલવામાં મજા આવતી હતી. 1928માં, 16 વર્ષની ઉંમરે, ટ્યુરિંગને [[આલ્બર્ટ આઇન્સ્ટાઇન|આર્બલ્ટ આઈન્સ્ટાઈન]]ના કાર્યનો ભેટો થયો, તેણે માત્ર તેને સમજી લીધું એટલું જ નહીં, પણ તેણે એક લખાણમાંથી, કે જેમાં ક્યારેય એવું સ્પષ્ટપણે દર્શાવાયું નહોતું, તેમાંથી ન્યુટનના ગતિના નિયમોઅંગે આઈન્ટાઈનના પ્રશ્નો અંગે અનુમાન લગાવ્યું.<ref>{{Harvnb|Hodges|1983|p=34}}</ref>
ટ્યુરિંગની આશાઓ અને આકાંક્ષાઓ તેણે શાળામાં પોતાનાથી સહેજ મોટા વિદ્યાર્થી, ક્રિસ્ટોફર મોર્કોમ સાથે વિકસાવેલી નજીકની મિત્રતા દ્વારા વધુ ઊભરી. મોર્કોમ ટ્યુરિંગની પ્રથમ પ્રેમ જિજ્ઞાસા હતી. મોર્કોમ તેઓની શેરબોર્ન ખાતેની છેલ્લા સત્રના માત્ર થોડાં અઠવાડિયાં પહેલાં, ગાયનું ચેપી દૂધ પીધા પછી સંકોચાઈને, ગાયનાં ફેફસાંના ક્ષય રોગની જટિલતાથી મૃત્યુ પામ્યો.<ref name="teuscher">** {{Cite book|last=Teuscher |first=Christof (ed.) |authorlink=Christof Teuscher |title=Alan Turing: Life and Legacy of a Great Thinker |year=2004 |publisher=[[Springer Science+Business Media|Springer-Verlag]] |isbn=3-540-20020-7 |oclc=53434737 62339998}}</ref> ટ્યુરિંગનો ધાર્મિક વિશ્વાસ કકડભૂસ થઈ ગયો અને તે નાસ્તિક બની ગયો. તેણે તમામ વસ્તુઓ જડવાદજ છે, એવી હકીકત ખાતરીપૂર્વક સ્વીકારી લીધી, જેમાં જીવિત માનવીય મગજનો પણ સમાવેશ થાય છે,<ref>પાઉલ ગ્રે, [http://www.time.com/time/time100/scientist/profile/turing.html ઍલન ટ્યુરિંગ] {{Webarchive|url=https://web.archive.org/web/20080822093918/http://www.time.com/time/time100/scientist/profile/turing.html |date=2008-08-22 }} સદીના સૌથી મહત્વના ટાઈમ સામાયિકના લોકો, પાન નં ૨</ref> પણ તે હજી પણ એવું માનતા હતાં કે મૃત્યુ પછી આત્માનું અસ્તિત્વ હોય છે.<ref>[http://www.turing.org.uk/turing/scrapbook/spirit.html ધી ઈન્સ્પીરેશન ઓફ લાઈફ એન્ડ ડેથ, 1928–1932] ઍલન ટ્યુરિંગ સ્કેપબુક </ref>
== યુનિવર્સિટી અને ગણનક્ષમતા (કમ્પ્યૂટેબિલિટી) અંગેનું કાર્ય ==
[[ચિત્ર:Alan Turing Memorial Closer.jpg|thumb|સેકવિલે પાર્ક, માન્ચેસ્ટરમાં ઍલન ટ્યુરિંગનું યાદગાર પૂતળું]]
શેરબોર્ન પછી, ટ્યુરિંગ કેમ્બ્રિજ, કિંગસ્ કોલેજમાં અભ્યાસ કરવા ગયો. ત્યાં તેણે તેના ગ્રેજ્યુએશન પહેલાંના ત્રણ વર્ષો 1931થી 1934 પસાર કર્યાં, [[ગણિત]]માં પ્રથમ વર્ગ ઓનર્સ સાથે ગ્રેજ્યુએટ થયો અને 1935માં સેન્ટ્રલ લિમિટ થિયરમ પરના મહાનિબંધની ક્ષમતા પર કિંગ કોલેજ ખાતે ફેલોતરીકે ચૂંટાઈ આવ્યો.<ref>જોહ્ન ઓલ્ડરીચનો ત્રીજો વિભાગ જુઓ, "ઈંગ્લેન્ડ અને કોન્ટીનેન્ટલ પ્રોબેબલીટી ઈન ઈન્ટ વોર યર્સ", જર્નલ ઈલેક્ટોનીક d'Histoire des Probabilités et de la Statistique, ભાગ 5/2 [http://www.jehps.net/decembre2009.html ડિસેમ્બર 2009] જર્નલ ઈલેક્ટ્રોનિક d'Histoire des Probabilités et de la Statistique</ref>
તેમના અતિમહત્ત્વના પેપર "ઓન કમ્યૂટેબલ નંબરસ્, વીથ એન એપ્લીકેશન ટુ ધી ''એન્ટ્સેઈડંગસપ્રોબ્લેમ (Entscheidungsproblem)'' ",<ref>{{Cite journal | last= Turing | first= A. M. |year=1936 | publication-date = 1936–37 | title = On Computable Numbers, with an Application to the Entscheidungsproblem | periodical = Proceedings of the London Mathematical Society | series = 2 | volume = 42 | pages = 230–65 | doi= 10.1112/plms/s2-42.1.230 | url = http://www.comlab.ox.ac.uk/activities/ieg/e-library/sources/tp2-ie.pdf}} (અને {{Cite news| last = Turing | first = A.M. | publication-date = 1937 | title = On Computable Numbers, with an Application to the Entscheidungsproblem: A correction | periodical = Proceedings of the London Mathematical Society | series = 2 | volume = 43 | pages = 544–6 | doi = 10.1112/plms/s2-43.6.544 | year = 1938 }})</ref> ટ્યુરિંગે ગણતરી અને પ્રૂફની મર્યાદાઓ પરના 1931ના કુર્ટ ગોડેલનાં પરિણામો પર પુનઃસૂત્રો તારવી, ગોડેલના વૈશ્વિક ગાણિતિક આધારિત યાંત્રિક ભાષા સાથે ફેરબદલી કરીને યાંત્રિક અને સરળ ડિવાઈસો મૂક્યા, જે ટ્યુરિંગ મશીનો તરીકે જાણીતાં બન્યાં. તેમણે સાબિત કર્યું કે કેટલાંક આવા મશીનો કોઈપણ કલ્પના જો અલ્ગોરિધમ પ્રમાણે રજૂ કરવામાં આવે તો તેની ગાણિતિક ગણતરી કરવા માટે તે સક્ષમ બનશે. તેઓ સાબિત કરતાં ગયા કે ટ્યુરિંગના મશીન માટે અચકાવવાની સમસ્યા અનિશ્ચિત છે જે સૌ પ્રથમ વખતે દર્શાવતાં ''એન્ટ્સેઈડંગસપ્રોબ્લેમ (Entscheidungsproblem)'' નો કોઈ ઉકેલ નથી કે એવું સાબિત કરતા ગયા હતા. તે નક્કી કરવું શક્ય નથી, વાસ્તવમાં, જ્યારે પણ ગાણિકીત ક્રિયા ટ્યુરિંગ મશીનને આપવામાં આવશે ત્યારે તે હંમેશા અચકાશે કે કેમ. જો કે તેમના લેમ્બડા કલનને આદર આપવામાં એલોન્ઝો ચર્ચને સમકક્ષ પ્રુફ પથી તેમનું પ્રુફ પ્રકાશિત થયું હતું, તે વખતે ટ્યુરિંગ ચર્ચના કામથી અજાણ હતા.
ટ્યુરિંગે તેમની યાદશક્તિમાં લખ્યું હતું કે તેઓ આ 1936 પેપર સ્વીકારવા અંગે નિરાશ થયા હતા અને તે અંગે માત્ર બે વ્યક્તિઓએ પ્રતિક્રિયા આપી- તેઓ હતા [[Wikipedia talk:Articles for creation/Heinrich Scholz|હૈન્રીચ સ્કૂલઝ]]અને રિચાર્ડ બેવેન બ્રેઈથવેઈટ. ટ્યુરિંગનો અભિગમ નોંધનીય પણે ઘણો ખુલ્લો અને સ્વયંસ્ફૂર્ત છે. અન્ય કોઈ પણ મશીનની જે એક આવું મશીન પણ કાર્ય કરી શકે એવા વિચારને પણ એક યુનિવર્સલ (ટ્યુરિંગ) મશીનની તેની ધારણામાં નવીનતાથી ઉતાર્યો હતો. અથવા બીજા શબ્દોમાં, કોઈની પણ ગણતરી કરવાની ક્ષમતા સાબિત થાય છે તે ગણતરી કરે છે, તેનો અડસટ્ટો લગાવાય છે. ટ્યુરિંગ મશીનો આ દિવસોમાં ગણતરી કરવાના સિદ્ધાંતમાં અભ્યાસ માટે કેન્દ્રિય ભાગ છે, સ્ટીફન વોલ્ફ્રામ દ્વારા 2 રાજ્ય 3 ચિહ્નો ટ્યુરિંગ મશીન શોધ એક સૌથી સરળ ઉદાહરણબને છે.<ref>[http://www.wired.com/wiredscience/2007/10/college-kid-pro/ કોલેજ કીડ પ્રૂવ ધેટ વોલ્ફ્રામસ ટ્યુરિંગ મશિન યુનિવર્સલ કમ્પ્યૂટરોમાંનું સૌથી સરળ છે] વાયર્ડ 24 ઓક્ટોબર 2007</ref> પેપરવ્યાખ્યાયિત સંખ્યાઓની કલ્પના પણ રજૂ કરે છે.
સપ્ટેમ્બર 1936થી જુલાઈ 1938 સુધી તેમણે ઍલોન્ઝો ચર્ચ હેઠળ અભ્યાસ કરવા માટે ઇન્સ્ટિટયૂટ ફોર એડવાન્સડ સ્ટડી, પ્રીન્સેટન, ન્યુ જર્સી ખાતે તેમનો મોટા ભાગનો સમય પસાર કર્યો. સાથો સાથ પોતાના માત્ર ગાણિતને લગતા કામમાં તેમણે સાંકેતિક લિપિનો અભ્યાસ કર્યો અને એક વિદ્યુત યાંત્રિક દ્વિગુણ ગુણકના ચોથા તબક્કામાંથી ત્રણનું નિર્માણ પણ કર્યું.<ref>{{Harvnb|Hodges|1983|p=138}}</ref> જૂન 1938માં તેમણે પ્રીન્સેટનમાંથી તેમની Ph.D.ની ડીગ્રી મેળવીઃ તેમનો મહાનિબંધ સાપેક્ષ ગણતરીની કલ્પનાને રજૂ કરતો હતો, જ્યાં ટ્યુરિંગ મશીનો કહેવાતી ભાવિ આગાહી સાથે દલીલ કરે છે, સમસ્યાઓના અભ્યાસને મંજૂરી આપતાં, તે એક ટ્યુરિંગ મશીન દ્વારા ઉકેલી શકાતું નથી. કેમ્બ્રીજ ખાતે પાછા આવતાં, તેમણે લુડ્વીગ વિટ્ટુજેનસ્ટીન દ્વારા ગણિતની સ્થાપનાઅંગેના વ્યાખ્યામાં હાજરી આપી હતી.<ref>{{Harvnb|Hodges|1983|p=152}}</ref> શિષ્ટાચારના કડક પાલનથી ટ્યુરિંગના પ્રતિકાર સાથે બે વ્યક્તિઓએ દલીલ કરી અને અસહમતિ દાખવી અને વિટ્ટજેનસ્ટીનની દલીલ એ હતી કે ગણિતશાસ્ત્રી કશા પણ તદ્દન સત્યો શોધી કાઢતા નથી પણ તેના બદલે તેઓ તેનું નવનિર્માણ કરે છે.<ref>{{Harvnb|Hodges|1983|pp=153–154}}</ref> તેમણે અંશકાલીન સમય માટે ગવર્મેન્ટ કોડ અને સિફર સ્કૂલ (GCCS) સાથે કામ પણ કર્યું.
== સંકેતલિપિ વિશ્લેષણ ==
[[ચિત્ર:Turing flat.jpg|thumb|બ્લેત્ચલેય પાર્ક ખાતે તબેલામાં બે કોટેજો.જ્યારે તેઓ હટ 8માં ગયા ત્યારે તેમણે અહીં 1939થી ૧૯૪૦ સુધી કામ કર્યું. ]]
બીજા વિશ્વ યુદ્ધ દરમિયાન, ટ્યુરિંગ બ્લેત્ચલેય પાર્ક ખાતે જર્મન સંકેતોને તોડવામાંના પ્રયત્નોમાં એક મુખ્ય સહભાગી હતા. યુદ્ધ પહેલાં સીફર બ્યુરોમાંથી પોલેન્ડમાં મેરિન રેજેવ્સ્કી,જેર્ઝી રોઝીસ્કીઅને હેન્રીક ઝીગાલ્સ્કી દ્વારા સંકેતલિપિ વિશ્લેષણના કામની શરૂઆત પર, તેમણેઈનીગ્મા મશીન અને લોરેન્ઝ એસઝેડ 40/42 (એક ટેલીપ્રિન્ટર(ટેલીટાઈપ) બ્રિટિશ દ્વારા ''ટ્યુની'' કોડવાળું નામ ધરાવનાર સાંકેતિક જોડાણ) એ બંનેને તોડવા માટે ઊંડા જ્ઞાન દ્વારા ફાળો આપ્યો હતો, અને થોડા સમય માટે, જર્મન નૌકાદળના સિગ્નલો વાંચવા માટે જવાબદાર વિભાગ હટ 8ના મુખ્ય વ્યક્તિ હતા.
સપ્ટેમ્બર 1938થી ટ્યુરિંગ કોડ તોડતી બ્રિટિશ સંસ્થા ગવર્મેન્ટ કોડ અને સાઈફર સ્કૂલ(GCCS), સાથે ખંડ-સમય માટે (અનુમાન અનુસાર બ્રિટિશ ફોરેન ઓફિસમાટે) કામ કર્યું હતું. તેમણે જર્મન ઈનીગ્મા મશીનની સમસ્યા પર કામ કર્યું અને GCCSના સિનિયર કોડબ્રેકર ડીલ્લી ક્નોક્ષસાથે મળીને કામ કર્યું.<ref>જેક કોપલેન્ડ, "કોલોસ્સસ અને કમ્પ્યૂટરની ઉંમરનું ચિત્ર, પાના નં 352 ''એક્શન ધી ડે'' , 2001</ref> 4 સપ્ટેમ્બર 1939એ જર્મની પર યુકે(UK)એ યુદ્ધનું એલાન કર્યું, ત્યારે ટ્યુરિંગ બ્લેત્ચલેય પાર્ક, જીસીસીએસના યુદ્ધ સમયના સ્ટેશન પર હાજર થયા હતા.<ref name="Copeland2006p378">કોપલેન્ડ, 2006 પાન નં. 378</ref> 1945માં, ટ્યુરિંગને તેમની યુદ્ધ સમયની સેવાઓ બદલ ઓબીઈ દ્વારા સન્માન આપવામાં આવ્યું, પણ તેમનું કાર્ય ઘણાં વર્ષો સુધી ખાનગી રહ્યું. ટ્યુરિંગ પાસે બ્લેત્ચલેય પાર્ક ખાતે કંઈક પ્રતિષ્ઠિત વિચિત્રતા હતી. જેક ગુડ, સંકેતલિપિના વિશ્લેષક, જેમણે તેમની સાથે કામ કર્યું હતું, તેમણે ટ્યુરિંગ વિશે રોનાલ્ડ લેવિને ટાંકીને કહ્યું: <blockquote> દરેક વર્ષે જૂનના પ્રથમ અઠવાડિયામાં તેને હે ફિવર (પરાગને લીધે થતી ઉધરસ અને ક્યારેક દમનો વિકાર)નો ખરાબ હુમલો લાગુ પડતો હતો, અને તેઓ પરાગરજને દૂર રાખવા માટે ઓફિસમાં પહેરવાનો સર્વિસ ગેસ માસ્ક સાયકલ ચલાવતી વખતે પહેરી લેતા. તેમની સાયકલમાં ખરાબી હતીઃ ચેન નિયમિત અંતરાલે ઉતરી જતી. તેને સમી કરાવવાને બદલે તેઓ ગોળ ફરતાં પેડલની ગણતરી કરતાં અને ચેન સરખી કરવાના સમયે સાયકલ પરથી ઉતરી હાથ દ્વારા ચેનને સરખી કરતા. તેમની અન્ય વિચિત્રતા એ હતી કે તેઓ તેમના મગ (પ્યાલા)ને ચોરી થતો અટકાવવા માટે રેડિયેટરની પાઈપ સાથે બાંધી દેતા.<ref>{{Harvnb|Lewin|1978|p=57}}</ref></blockquote>
બ્લેત્ચલેય ખાતે કામ કરતી વખતે, ટ્યુરિંગ, એક પ્રતિભાસંપન્ન લાંબુ અંતર દોડનારા હતા, પ્રસંગોપાત્ત જ્યારે તેમની જરૂરિયાતત ઉચ્ચ-સ્તરી બેઠકો માટે પડતી ત્યારે તેઓ {{convert|40|mi}} થી લંડન સુધી દોડતા.<ref>''બોડીગાર્ડ ઓફ લીઝ'' , એન્થોની કેવ બ્રાઉન દ્વારા, 1975.</ref>
=== ટ્યુરિંગ- વેલ્ચમેન બોમ્બી ===
બ્લેત્ચલેય પાર્કમાં આવ્યાં પછી થોડા અઠવાડિયામાં,<ref name="Copeland2006p378"/> ટ્યુરિંગ ચોક્કસ પ્રકારના ઈલેક્ટ્રોમીકેનીકલ મશીન વિશે જણાવ્યું, જે ઈનીગ્માને તોડવા માટે બોમ્બા કરતાં વધુ ઝડપી મદદ કરી શકે છે, 1938 પછી મૂળ પોલીશ-ડિઝાઈન બોમ્બામાં સુધારાવધારા થયા બાદ તેનું નામ બોમ્બી થયું. ગણિતશાસ્ત્રી ગોર્ડોન વેલ્ચમેન દ્વારા વધુ તીવ્ર બનાવવાના સૂચન સાથે બોમ્બી એક પ્રાથમિક સાધનોમાંનું એક બન્યું અને ઈનીગ્મા પર પ્રહાર કરવા-સંદેશાઓના વિનિમયમાં રક્ષણ મેળવવાના ઉપયોગમાં યંત્ર પાસે કામ લેવામાં મુખ્ય બન્યું.
[[ચિત્ર:Bombe-rebuild.jpg|thumbnail|બ્લેત્ચલેય પાર્ક ખાતે બોમ્બીની એક સંપૂર્ણ અને કાર્યશીલ પ્રતિકૃતિ]]
જેક ગુડનો મત: <blockquote>મારા ''મતે'' ટ્યુરિંગનો અત્યંત મહત્તવનો ફાળો બોમ્બી, સંકેતલિપિનું વિશ્લેષણ કરતાં મશીનની ડિઝાઈન કરવામાં હતો. તેમની પાસે એવો વિચાર હતો કે જેનો તમે ઉપયોગ કરી શકો, પરિણામ રૂપે, તર્ક શાસ્ત્રનો એક પ્રમેયમાં વાહિયાતની બદલે બિનતાલીમી કાન લાગે છે, તેનાથી વિરોધાભાસી તમે ''દરેક વસ્તુનું'' અનુમાન લગાવી શકો છો.<ref>[http://www.imdb.com/title/tt1155383/episodes "ધી મેન હુ ક્રેક્ડ ઈનીગ્મા"], UKTV ઔતિહાસિક ચેનલની દસ્તાવેજી શ્રેણીઓનો ચોથો ભાગ [http://www.imdb.com/title/tt1157073/ "હિરોઝ ઓફ વર્લ્ડ વોર 2"]</ref></blockquote>
બોમ્બી ઈનીગ્મા સંદેશા (એટલે કે રોટરનો ક્રમ, રોટરની ગોઠવણી, વગેરે)માટે શક્ય એટલી સાચી ગોઠવણોના ઉપયોગ માટે શોધ કરતું હતું, અને યોગ્ય'' ભાષાંતરઃ'' સંભાવ્ય વાક્યનો એક ટુકડાનો ઉપયોગ કરતું. રોટરો(જેને 10<sup>19</sup> દરજ્જાનો ઓર્ડર અથવા યુ બોટ ચાર-રોટર માટે 10<sup>22</sup>થી ભિન્ન હોય છે)<ref>"ધી મેન હુ ક્રેક્ડ ઈનીગ્મા"માં પ્રોફેસર જેક ગુડ, 2003: "જો મારી યાદદાસ્ત સાચી છે", તેમની ચેતવણી સાથે </ref>ના દરેક શક્ય ગોઠવણીઓ માટે બોમ્બી ભાષાંતરના આધાર પર તાર્કીક અનુમાનોની સાંકળની ભજવણી કરી, ઈલેક્ટ્રીક રીતે તેનું અમલીકરણ કરતું. જ્યારે વિરોધાભાસ ઉદ્ભવે ત્યારે બોમ્બીને શોધી કાઢવામાં આવે છે, અને તે ગોઠવણીને કાઢી નાખવામાં આવે છે અને પછી બીજા પર આગળ વધવામાં આવે છે. મોટાભાગની શક્ય ગોઠવણીઓ વિરોધાભાસો સર્જે છે અને તેને નાશ કરવામાં આવે છે, માત્ર ખૂબ ઓછા ઊંડાણપૂર્વક સંશોધન કરવા માટે બાકી રહે છે. ટ્યુરિંગનું બોમ્બી 18 માર્ચ 1940માં પ્રથમ વખત ઈન્સ્ટોલ થયું.<ref>{{Harvnb|Hodges|1983|p=191}}</ref> યુદ્ધ પૂર્ણ થતાં પહેલાં 200 થી પણ વધારે બોમ્બીઓ કાર્યાન્વિત કરવામાં આવ્યાં હતાં.<ref name="codebreaker">{{Cite web|title=Alan Turing, Codebreaker and Computer Pioneer |last=Copeland |first=Jack |coauthors=Diane Proudfoot |month=May | year=2004 |url=http://www.alanturing.net/turing_archive/pages/Reference%20Articles/codebreaker.html |access-date=27 July 2007}}</ref>
=== હટ 8 and નોકાદળનું ઈનીગ્મા ===
[[ચિત્ર:AlanTuring-Bletchley.jpg|thumbnail|સ્ટેફન કેટ્ટલ દ્વારા બ્લેત્ચલેય પાર્ક ખાતે ટ્યુરિંગનું પૂતળું, અમેરિકાના પરોપકારી સીડની ઈ ફ્રેન્ક દ્વારા સોંપાયેલું કાર્ય.<ref>[58]</ref>]]
ટ્યુરિંગે જર્મન નોકા દળના ઈનીગ્માની ખાસ મુશ્કેલ સમસ્યાને ઉકેલવાનું નક્કી કર્યું "કારણ કે તેના માટે કોઈ અન્ય કંઈ પણ કરી રહ્યા નથી અને હું મારી જાતે તે કરી શકું છું".<ref name="MahonP14">{{Harvnb|Mahon|1945|p=14}}</ref> ડિસેમ્બર 1939માં, ટ્યુરિંગે નોકાદળની સૂચના આપતાં તંત્રના જરૂરી ભાગનો ઉકેલી આપ્યો, જે અન્ય સેવાઓ દ્વારા ઉપયોગમાં લેવામાં આવતાં સૂચકોના તંત્ર કરતાં વધુ જટિલ હતો.<ref name="MahonP14"/><ref>{{Harvnb|Leavitt|2007|pp=184–186}}</ref> જે રાત્રિએ તેમણે નોકાદળના સૂચક તંત્રનો ઉકેલ લાવ્યા તે જ રાત્રિએ તેમને ''બન્બુરીસ્મુસ'' નો વિચાર આવ્યો, આ એક પરિણામરૂપ આંકડાકીય પદ્ધતિ, જે નૌકાદળના ઈનીગ્માને તોડવામાં મદદરૂપ બનવા માટે હતી. (જેને અબ્રાહમ વાલ્ડેપાછળથી સિક્વેન્શ્યલ એનાલિસીસ) "જોકે હું ચોક્કસ ન હતો કે વાસ્તવમાં તે કામ કરી જશે, અને જ્યાં સુધી વાસ્તવમાં કેટલાંક દિવસો સુધી ઈનીગ્માને તોડ્યા ત્યાં સુધી હું ચોક્કસ ન હતો."<ref name="MahonP14"/> આ માટે તેમણે પુરાવાઓનું વજન માપવાની શોધ કરી, જેને તેઓ''બન'' કહેતા હતા. બાનબુરીસ્મઅસ ઈનીગ્મા રોટોરસના કેટલાક આદેશોને ધ્યાન બહાર મૂકી શકતાં, પરિણામરૂપે બોમ્બી પરનો પરીક્ષણ ગોઠવણીનો જરૂરી સમય ઓછો થઈ ગયો.
1941માં, ટ્યુરિંગે હટ 8ના સહકાર્યકર જોઅન ક્લાર્કે, એક સાથી ગણિતશાસ્ત્રી સમક્ષ લગ્નનો પ્રસ્તાવ મૂક્યો, પણ તેઓના વિવાહ ખૂબ જ ઓછા સમય માટે રહ્યો. પોતાની સમલૈગિકતા અંગે તેમની વાગ્દત્તાને જણાવ્યાં બાદ, નવાઈ પમાડે તેવી વાત બહાર આવવાથી તેણી અસ્વસ્થ હતી, તેથી ટ્યુરિંગે નક્કી કર્યું કે લગ્ન સાથે આગળ નહીં વધી શકે.<ref>{{Harvnb|Leavitt|2007|pp=176–178}}</ref>
જૂલાઈ 1942માં, જર્મનીના નવા જેહૈમસ્ચ્રૈબર મશિન (''ખાનગી લેખક'' ) દ્વારા લોરેન્ઝ સીફર વિરૂદ્ધ તૈયાર કરવામાં આવેલાં સંદેશાઓના<ref>{{Harvnb|Copeland|2006|p=380}}</ref> ઉપયોગ માટે ટ્યુરિંગે ''ટ્યુરિંગ્રેરી'' (અથવા મજાકમાં ''ટ્યુરિંગીસમસ'' )નામની પદ્ધતિની યોજના બનાવી. જેનું બ્લેત્ચલેય પાર્ક ખાતે કોડનું નામ ''ટ્યુની'' હતું. તેમણે મેક્સ ન્યુમેનના માર્ગદર્શન હેઠળ ટોમ્મી ફ્લાવરસ સાથે ટ્યુની ટીમની પણ શરૂઆત કરી, જે કોલોસ્સઅસ કમ્પ્યૂટર, દુનિયાનું સૌ પ્રથમ ડિઝીટલ ઈલેક્ટ્રોનિક કમ્પ્યૂટરના નિર્માણમાં પડ્યાં હતાં, જે સરળ એવું પહેલાનું મશીન(હીથ રોબિન્સન)ના બદલે મૂકવાનું હતું અને તેની અત્યંત ઝડપી કામ કરવાની ગતિ દરરોજ બદલાતી સાઈફરની ઉપયોગીતાને લાગુ કરવા માટેની ડિક્રિપ્શન તકનીકના ભૌતિક-બળને મંજૂરી આપતું હતું.<ref>{{Harvnb|Copeland|2006|p=72}}</ref> એક સતત ખોટી ધારણા એ છે કે ટ્યુરિંગ કોલોસ્સઅસના ડિઝાઈનમાં એક મુખ્ય વ્યક્તિ હતી, પણ તે કિસ્સો એવો ન હતો.<ref>{{Harvnb|Copeland|2006|pp=382,383}}</ref>
ટ્યુરિંગએ નવેમ્બર 1942માં સંયુક્ત રાજ્ય અમેરિકાનો પ્રવાસ કર્યો અને નોકાદળના ઈનીગ્મા પર સંકેતલિપિ વિશ્લેષક તરીકે અને વોશીંગ્ટનમાં બોમ્બીના નિર્માણમાં યુએસ નૌકાદળ સાથે કામ કર્યું, અને સલામત ભાષા ઉપકરણના વિકાસ સાથે બેલ લેબ્સખાતે સહાયક ભૂમિકા ભજવી. તેઓ બ્લેત્ચલેય પાર્ક ખાતે માર્ચ 1943માં પાછા આવ્યા. તેમની ગેરહાજરી દરમિયાન, હ્યુગ એલેક્ઝેન્ડરેઅધિકૃત રીતે હટ 8ના પ્રમુખ તરીકેનું સ્થાન ગ્રહણ કર્યું હતું, જોકે એલેક્ઝાન્ડર થોડા સમય માટે ''ડે ફાક્ટો'' ના પ્રમુખ તરીકે હતા- ટ્યુરિંગને રોજ-બ-રોજની ભાગાદોડી વાળા વિભાગમાં થોડો રસ હતો. ટ્યુરિંગ બ્લેત્ચલેય પાર્ક ખાતે સંકેતલિપિ વિશ્લેષણના એક સામાન્ય સલાહકાર બન્યા.
એલેક્ઝાન્ડરે તેમના યોગદાન અંગે આ પ્રમાણે લખ્યું: <blockquote>હટ 8ની સફળતામાં ટ્યુરિંગનું કામ સોથી મોટું પરિબળ હતું તે બાબતે કોઈના મનમાં પ્રશ્ન ન થવો જોઈએ. શરૂઆતના દિવસોમાં તે માત્ર સંકેતલિપિનો વિશ્લેષક હતો, જે ઉકેલવા લાયક સમસ્યાને વિચારતો અને તે માત્ર હટની અંદરના મુખ્ય સૈદ્ધાંતિક કામ માટે પ્રાથમિક પણ જવાબદાર ન હતો પણ તે વેલ્ચમેન સાથે દરેક મુદ્દાની રજૂઆત કરતો અને બોન્બીની શોધ માટે મુખ્ય શ્રેય માટે ઉત્સુક હતો. તે હંમેશા કહેવું મુશ્કેલ છે કે દરેક વ્યક્તિ તદ્-ન અનિવાર્ય છે પણ જો હટ 8 માટે કોઈ અનિવાર્ય હતું તો તે ટ્યુરિંગ હતો. જ્યારે અનુભવ અને રોજિંદું કાર્ય પાછળથી સહેલું લાગે છે ત્યારે શરૂઆતનું કામ હંમેશા ભૂલી જવાય છે અને હટ 8માંના ઘણાં એવું અનુભવે છે કે બહારની દુનિયાને ટ્યુરિંગના યોગદાનનું મહત્તવ ક્યારેય સંપૂર્ણ પણે સમજાઈ શકશે નહીં.<ref>{{ Harvnb | Alexander | circa 1945 }}</ref></blockquote>
યુદ્ધના પછીના ભાગમાં તેઓ હાન્સ્લોપ પાર્ક ખાતે કામ કરવા ગયા, જ્યાં તેઓ એન્જિનીયર ડોનાલ્ડ બેયલેયની મદદથી ઈલેક્ટ્રોનિકસના જ્ઞાનમાં વધુ વિકાસ કર્યો. તેઓએ સાથે એક પોર્ટેબલ સુરક્ષિત આવાજ સંચાર જેનું કોડનું નામ ''ડેલીલાહ'' હતું, તેનું ડિઝાઈન તૈયાર કરી અને નિર્માણ કર્યું.<ref>{{Harvnb|Hodges|1983|p=270}}</ref> તેનો હેતુ લાંબા અંતરાલ માટે રેડિયો ટ્રાન્સમીશન સાથેના ઉપયોગ માટેની ક્ષમતાની ઉણપ અને કોઈ પણ કિસ્સામાં તેની વિવિધ ઉપયોગીતા માટેનો હતો, ડેલીલાહનું નિર્માણ યુદ્ધ દરમિયાન ઉપયોગમાં લેવા માટે મોડુ પૂર્ણ થયું હતું. જોકે ટ્યુરિંગે અધિકારીઓને વિંસ્ટન ચર્ચિલનું ભાષણનું રેકોર્ડીંગ એનક્રિપ્ટીંગ/ડિક્રિપ્ટીંગ દ્વારા નિર્દેશિત કર્યું હોવા છતાં, ડેલીલાહનો ઉપયોગ કરવા માટે સ્વીકારવામાં ન આવ્યું. ટ્યુરિંગે SIGSALY, એક સલામત અવાજ તંત્રના વિકાસ માટે બેલ લેબ્સ સાથે સલાહ પણ લીધી, જેનો ઉપયોગ યુદ્ધના પાછળના વર્ષોમાં થયો હતો.
== શરૂઆતના કમ્પ્યૂટરો અને ટ્યુરિંગ પરીક્ષણ ==
તેઓ 1945થી 1947 સુધી ચર્ચ સ્ટ્રીટ, હેમ્પટન<ref>{{openplaque|1619}}</ref> ખાતે રહેતા હતા અને નેશનલ ફિઝીક્સ લેબોરેટરીમાં હતા, જ્યાં તેઓ એસીઈ (ઓટોમેટીક કમ્પ્યૂટીંગ એન્જિન)ની ડિઝાઈન પર કામ કરતા હતા. તેમણે 19 ફેબ્રુઆરી 1946ના રોજ એક પેપરની રજૂઆત કરી, જે એક પ્રોગ્રામનો સંગ્રહ કરી શકાય તેવા કમ્પ્યૂટરની સૌ પ્રથમ વિગત આપતી ડિઝાઈન હતી.<ref>{{Harvnb|Copeland|2006|p=108}}</ref> જોકે એસીઈ એ શક્ય કરી શકાય એવી ડિઝાઈન હતી, તેમ છતાં યુદ્ધના સમયે બ્લેત્ચલેય પાર્ક ખાતે ગોપનીય વાતાવરણ હોવાથી પ્રોજેક્ટ શરૂ કરવામાં વિલંબ થયો અને તેમનો ભ્રમ દૂર થયો. 1947ના વર્ષમાં પાછળથી એક સેબિટીકલ વર્ષ (અભ્યાસ અને પ્રયાસ માટે અપાતી રજાઓ) માટે કેમ્બ્રીજ પાછા આવ્યા. જ્યારે તેઓ કેમ્બ્રીજ પાછા આવ્યાં ત્યારે પાયલોટ એસીઈ તેમની ગેરહાજરીમાં તૈયાર થઈ ગયો હતો. તેનો પ્રથમ પ્રોગ્રામ 10 મે 1950ના રોજ અમલમાં મૂકવામાં આવ્યો.
1948માં તેમને માન્ચેસ્ટર ખાતે ગણિત વિભાગમાં રીડર તરીકે નિયુક્ત કરવામાં આવ્યા. 1949માં, તેઓ યુનિવર્સિટી ઓફ માન્ચેસ્ટર ખાતે કમ્પ્યૂટીંગ લેબોરેટરીના ઉપ નિયામક બન્યા, અને સોથી પહેલાં પ્રોગ્રામનો સંગ્રહ કરી શકાય એવા કમ્પ્યૂટરોના એક માન્ચેસ્ટર માર્ક 1ના સોફ્ટવેર અંગે કામ કર્યું. આ સમય દરમિયાન તેમણે વધુ એબસ્ટ્રેક કામ ચાલુ રાખ્યું અને "કમ્પ્યુટીંગ મશીનરી અને ઈન્ટલીજન્સ"માં (માઈન્ડ, ઓક્ટોબર 1950), ટ્યુરિંગ કૃત્રિમ બુદ્ધિમત્તાની સમસ્યાને સંબોધતાં હતા, અને એક સૂચિત પ્રયોગ જે ટ્યુરિંગ પરીક્ષણ તરીકે જાણીતું બન્યું, મશીન માટે સ્ટાન્ડર્ડ નિર્ધારિત કરવાનો પ્રયત્ન "બુદ્ધિમત્તા" કહેવાઈ. વિચાર એ હતો કે જો કમ્પ્યૂટરમાં વિચારવામાં ઝીણવટથી તપાસ કરનારની કરામત મૂકી શકાય જે એક વ્યક્તિ સાથે વાર્તાલાપ કરતો હતો તો કમ્પ્યૂટરને "વિચારવાનું" કહી શકાય. પેપરમાં ટ્યુરિંગે સૂચવ્યું હતું કે વયસ્કના મનનું અનુકરણ કરે એવા પ્રોગ્રામને બનાવવા કરતાં એક સરળ પ્રોગ્રામ બનાવવો બાળકોના મનનું અનુકરણ કરે અને પછી શિક્ષણના એક કોર્ષ તરીકેનો ઉદ્દેશ્ય બનાવવો. ટ્યુરિંગ પરીક્ષણથી વિરોધાભાસી સ્વરૂપ ઈન્ટરનેટ પર વ્યાપકપણ ઉપયોગમાં લેવાય છે- વપરાશકાર વ્યક્તિ છે કે કમ્પ્યૂટર એ નક્કી કરવા માટેનો હેતુપૂર્વકનું CAPTCHA (કેપ્ચા) પરીક્ષણ છે.
1948માં, ટ્યુરિંગ તેમના પૂર્વ ઉપસ્નાતક સાથીકાર્યકર ડી.જી.ચેમ્પરનોવ્ની સાથે કામ કરતાં, કમ્પ્યૂટર માટે [[ચેસ]]નો પ્રોગ્રામ લખવાની શરૂઆત કરી, જે હાલમાં ઉપલબ્ધ નથી. 1952માં, પ્રોગ્રામને અમલી બનાવવા માટે પૂરતા પ્રમાણમાં શક્તિશાળી એવા કમ્પ્યૂટરની ઉણપને કારણે, ટ્યુરિંગે રમત રમી જેમાં તેમણે કમ્પ્યૂટરનું અનુકરણ કર્યું, એક ચાલ રમવા માટે લગભગ અડધો કલાક લીધો. રમતની નોંધ લેવાઈ હતી.<ref>[http://www.chessgames.com/perl/chessgame?gid=1356927 ઍલન ટ્યુરિંગ વિરુદ્ધ એલીક ગ્લેન્ની(1952) "ટ્યુરિંગ પરીક્ષણ"] Chessgames.com</ref> પ્રોગ્રામ ટ્યુરિંગના સહકાર્યકર એલીક ગ્લેન્નીના માટે ખોઈ દીધો છે, જો કે એવું કહેવામાં આવે છે કે પ્રોગ્રામે ચેમ્પરનોવ્નીની પત્ની વિરુદ્ધ રમત જીતી લીધી હતી. તેમનું ટ્યુરિંગ પરીક્ષણએક મહત્ત્વપૂર્ણ અને ઉત્તેજક વિશેષતા ધરાવતું હતું અને કૃત્રિમ બુદ્ધિમત્તા સંદર્ભેની ચર્ચમાં છેલ્લું યોગદાન હતું, જે અડધી સદી કરતાં વધુ ચાલુ રહ્યું.<ref>સેજીન, એ.પી.., સીકેક્લી, આઈ., અને એકમેન, વી. (2000) ટ્યુરિંગનું પરીક્ષણ: 50 વર્ષો પછી. માઈન્ડસ એન્ડ મશીનસ, Vol. 10, પાનું 463–518.</ref> તેમણે લુ વિઘટનની પદ્ધતિ પણ 1948માં શોધી હતી, જેનો વર્તમાન સમયમાં મેટ્રીક્સ સમીકરણના ઉકેલમાં ઉપયોગ થાય છે.<ref>[http://www.intusoft.com/nlhtm/nl71.htm સ્પાઈસ 1 2 3 અને બીયોન્ડ][http://www.intusoft.com/nlhtm/nl71.htm ઈન્ટુસોફ્ટ ન્યુઝલેટર, ઓગસ્ટ 2003]</ref>
== પેટર્નનું બંધારણ અને ગાણિતીય જીવવિજ્ઞાન ==
ટ્યુરિંગે 1952થી તેમના 1954માં થયેલા મૃત્યુ સુધી ગાણિતીય જીવવિજ્ઞાન ખાસ કરીને મોર્ફોજીનેસીસ પર કામ કર્યું હતું. તેમણે 1952માં પેટર્ન બંધારણની ટ્યરિંગની પૂર્વધારણા રજૂ કરતું હતું, ધી ''કેમિકલ બેઝીઝ ઓફ મોર્ફોજીનેસીસ'' વિષય પર એક પેપર પ્રકાશિત કર્યું હતું.<ref>[http://www.sciencedaily.com/releases/2006/11/061128093244.htm "કંટ્રોલ મિકેનીઝમ ફોર બાયોલોડીકલ પેટર્ન ફોર્મેશન ડેકોડેડ"] ''સાયન્સ ડેઈલી'' , 30 નવેમ્બર 2006</ref> તેમના રસનો કેન્દ્ર વિસ્તાર ફિબોનાકી ફિલ્લોટેક્સીઝ, ગ્રહ માળખામાં ફિબોનાકી આંકડાઓનું અસ્તિત્વ સમજવાનું હતું. તેઓ રીએક્શન-ડીફ્યુઝન સમીકરણોનો ઉપયોગ કરતાં જે પેટર્ન બંધારણના ક્ષેત્રના મધ્યસ્થાને છે. જ્યારે 1992માં ''કલેક્ટેડ વર્ક ઓફ એ.એમ.ટ્યુરિંગ'' પ્રકાશિત થયું ત્યાં સુધી પછીના પેપરો અપ્રકાશિત રહ્યાં. આ ક્ષેત્રમાં તેમનું યોગદાન આધારભૂત ભાગ ગણવામાં આવે છે.<ref>[http://www.swintons.net/deodands/archives/000087.html ટ્યુરિંગઝ લાસ્ટ, લોસ્ટ વર્ક ] {{Webarchive|url=https://web.archive.org/web/20030823032620/http://www.swintons.net/deodands/archives/000087.html |date=2003-08-23 }} સ્વીનટનસ</ref>
== અનુચિતતા માટે ગુનેગાર ઠરવું ==
જાન્યુઆરી 1952માં, ટ્યુરિંગ માન્ચેસ્ટરમાં સિનેમાની બહાર આર્નોલ્ડ મૂર્રેને મળ્યાં હતાં. એક બપોરના ભોજના પછી ટ્યુરિંગે મૂર્રેને તેમના ઘરે સપ્તાહાંત પસાર કરવા માટે આમંત્રણ આપ્યું, મૂર્રેએ આમંત્રણ સ્વીકાર્યુ હોવા છતાં ટ્યુરિંગના ઘરે દેખાયા નહીં. આ જોડી માન્ચેસ્ટરમાં પછીના સોમવારે ફરીવખત મળી, ત્યારે મૂર્રેએ ટ્યુરિંગને તેમના ઘરે આવવાનું નિમંત્રણ સ્વીકાર્યું. થોડા અઠવાડિયા પછી મૂર્રેએ ટ્યુરિંગના ઘરની ફરી વખત મુલાકાત લીધી અને તે દેખીતું હતું કે રાત પણ ત્યાંજ વીતાવી.<ref>{{Harvnb|Leavitt|2007|p=266}}</ref>તેમના ઘરમાં મળતિયાઓને તોડફોડમાં મૂર્રેએ મદદ કર્યાં બાદ, ટ્યુરિંગે પોલીસમાં ગુનો નોંધાવ્યો. તપાસ દરમિયાન, ટ્યુરિંગે મૂર્રે સાથેના જાતીય સંબંધ સ્વીકાર્યો. તે સમયે યુનાઈટેડ કિંગડ્મમાં સમલૈગિંક કૃત્યો ગેરકાનૂની હતાં<ref>{{Harvnb|Hodges|1983|p=458}}</ref> અને તેથી ક્રિમીનલ લો અમેન્ડન્ટ એક્ટ 1885ના સેકશન 11 હેઠળ અનુચિતતા બદલ તે બંને પર આરોપ લગાવવામાં આવ્યો, 15 વર્ષથી વધુ વર્ષ પહેલાં ઓસ્કાર વાઈલ્ડ માટે આ જ ગુના હેઠળ આરોપ લગાવવામાં આવ્યો હતો.<ref name="LeavittP268">{{Harvnb|Leavitt|2007|p=268}}</ref>
ટ્યુરિંગને કેદમાં પૂરાવું અથવા પોતાની કામવાસનાને ઓછી કરવા માટે હોર્મોનની સારવાર સ્વીકારવી એ બે વિકલ્પ આપવામાં આવ્યા હતા. તેમણે ઈસ્ટ્રોજન હોર્મોન ઈન્જેક્શન દ્વારા રાસાયણિક કૅસ્ટ્રેશન(ખસીકરણ)નો સ્વીકાર કર્યો.<ref>{{Cite web |url=http://www.glbtq.com/social-sciences/turing_a,2.html |title=ટ્યુરિંગ, ઍલન(1912–1954) |access-date=2011-03-21 |archive-date=2009-09-01 |archive-url=https://web.archive.org/web/20090901020647/http://www.glbtq.com/social-sciences/turing_a%2C2.html |url-status=dead }}</ref> ટ્યુરિંગ ગુનેગાર ઠરતાં તેમની સુરક્ષા મંજૂરીને દૂર કરવામાં આવી અને GCHQ માટે તેમની સંકેતલિપિ વિશ્લેષક સલાહકાર તરીકેનું કામ ચાલુ રાખવા પર પ્રતિબંધ મૂકવામાં આવ્યો. તેમના બ્રિટિશ પાસપોર્ટ રદ કરી નાખવામાં આવ્યો, જો કે તેમનુ ગુનેગાર ઠરવા પછી યુનાઈટેડ સ્ટેટમાં તેનો પ્રવેશ નિષેધ કરવામાં આવ્યો.{{Citation needed|date=December 2010}} તે સમયે, સોવિએટ એજન્ટો દ્વારા સમલૈગિંકો અને જાસૂસોને ઝાંસામાં લેવાની ઉત્કૃત જીજ્ઞાસા લોકોમાં હતી,<ref>{{Harvnb|Leavitt|2007|p=269}}</ref> કારણ કે તાજેતરમાં કેમ્બ્રીજ પાંચના પ્રથમ બે સભ્યો ગાય બુર્ગીસ્સ અને ડોનાલ્ડ મેક્લીન KGBના બે તરફ એજન્ટ તરીકે બહાર આવ્યા હતા. ટ્યુરિંગ પર જાસૂસ તરીકેનો ક્યારેય આરોપ લાગ્યો ન હતો, પણ બ્લેત્ચલેય પાર્ક ખાતે જેટલા પણ લોકોએ કામ કર્યું હતું, તેમને તેમની યુદ્ધ સમયની કામગીરી અંગે ચર્ચા કરતાં અટકાવવામાં આવ્યા હતા.<ref>{{Harvnb|Copeland|2006|p=143}}</ref>
== મૃત્યુ ==
8 જૂન 1954એ ટ્યુરિંગના સફાઈ કરનારને જણાયું કે તેમનું મૃત્યુ થયું છે. તેમનું મૃત્યુ આગળના દિવસે થયું હતું. પોસ્ટમોર્ટમ રિપોર્ટમાં જણાયું હતું કે તેમના મૃત્યુનું કારણ સાઈનાઈડનું ઝેર હતું. જ્યારે એવું જાણવા મળ્યું કે અડધું ખવાયેલું સફરજન તેમની પથારીની બાજુમાં પડ્યું હતું,<ref>{{Harvnb|Hodges|1983|p=488}}</ref> અને તેમ છતાં તે સફરજનનો સાઈનાઈડ પરીક્ષણ કરવામાં ન આવ્યું, સફરજનનો ઉપયોગ કરીને તેમને ઘાતક માત્રા સાઈનાઈડ આપવામાં આવ્યું હોઈ શકે એવું અનુમાન કરવામાં આવે છે. એક કાયદાકીય તપાસે એ નક્કી કર્યું હતું કે તેમણે આત્મહત્યા કરી હતી અને તેમને 12 જૂન 1954ના રોજ સ્મશાનમાં અગ્નિદાહ આપવામાં આવ્યો.<ref>{{Harvnb|Hodges|1983|p=529}}</ref> ટ્યુરિંગની માતાએ ઘણી તર્ક પૂર્ણ દલીલો કરી કે લેબોરેટરીનાં રસાયણોની લાપરવાહી ભર્યા સંગ્રહના કારણે સાઈનાઈડ ગળી જવું તે આકસ્મિક હતું. પોતાની માતાને કંઈક સત્યાભાસી અસ્વીકાર લાગે, તે માટે ટ્યુરિંગે જાણી જોઈને પોતાની જાતને સંદિગ્ધ રીતે નાખી હોય તેમ બની શકે એવું તેમના ચરિત્ર લેખક એન્ડ્રુવ હોજેસએ સૂચવ્યું હતું.<ref>{{Harvnb|Hodges|1983|pp=488, 489}}</ref> કેટલાક અન્યોએ એવું સૂચવ્યું હતું કે ટ્યુરિંગ ફરીથી 1937ની ફિલ્મ ''સ્નો વ્હાઈટ'' માંથી પોતાની પસંદગીના પરીકથાના એક દ્રશ્યનું પુનઃઅભિનય કરી રહ્યો હોય તેમ બની શકે, "જેમાં તેને ખાસ કરીને એક ચૂડેલ ઝેરી દારૂમાં પોતાનું સફરજન ડૂબાડે છે એ દૃશ્યમાં ખૂબ મજા આવતી હતી."<ref>{{Harvnb|Leavitt|2007|p=140}}</ref>
=== સમાધિલેખ ===
{{quote|
Hyperboloids of wondrous Light<br />
Rolling for aye through Space and Time<br />
Harbour those Waves which somehow Might<br />
Play out God's holy pantomime
<ref>{{Cite book|last=Turing |first=A. M. |title=Postcard to [[Robin Gandy]] |year=1954 |publisher=Turing Digital Archive, AMT/D/4 image 16, [http://www.turingarchive.org/ The Turing Digital Archive]}}</ref>}}
== માન્યતા અને શ્રદ્ધાંજલિઓ ==
[[ચિત્ર:Turing Plaque.jpg|thumbnail|વિલ્મસ્લોવ, ચેશીર ખાતે ટ્યુરિંગના ઘર પર તકતીથી કરવામાં આવેલું ચિહ્ન ]]
ટ્યુરિંગના મૃત્યુ (અને તેમનું યુદ્ધ સમયનું કાર્ય હજી પણ ઓફિશ્યલ સીક્રેટસ એક્ટનો વિષય હતો) પછી થોડા સમયમાં રોયલ સોસાયટી દ્વારા એક જીવનચરિત્ર પ્રકાશિત થયું.{{quote|Three remarkable papers written just before the war, on three diverse mathematical subjects, show the quality of the work that might have been produced if he had settled down to work on some big problem at that critical time. For his work at the Foreign Office he was awarded the OBE.|{{Cite book|last=Newman |first=M. H. A. |title=Alan Mathison Turing |year=1955 |publisher=The Royal Society |isbn =|series=Biographical Memoirs of Fellows of the Royal Society, 1955, Volume 1}}}1966થી એસોશિયેશન ફોર કમ્પ્યૂટીંગ મશીનરીદ્વારા એવી વ્યક્તિને ટ્યુરિંગ એવોર્ડ આપવામાં આવે છે, જેણે કમ્પ્યુટીંગ સમુદાયમાં ટેક્નીકલ યોગદાન આપ્યું હોય. તે કમ્પ્યૂટરની દુનિયામાં તેને [[નોબૅલ પારિતોષિક|નોબલ પ્રાઈઝ]]ને સમકક્ષ સર્વોચ્ચતમ સન્માન ગણવામાં આવે છે.<ref>{{Cite web|url=http://www.acm.org/press-room/news-releases-2007/turingaward/|title=ACM'S Turing Award Prize Raised To $250,000|publisher=[[Association for Computing Machinery|ACM]] press release|date=27 July 2007|access-date=16 October 2008|author=Steven Geringer|archive-date=30 ડિસેમ્બર 2008|archive-url=https://web.archive.org/web/20081230233653/http://www.acm.org/press-room/news-releases-2007/turingaward/|url-status=dead}}</ref>એલન ટ્યુરિંગ અંગેનો હુગ વ્હાઈટમોર દ્વારા ''બ્રેકીંગ ધી કોડ'' 1986નું નાટક છે. આ નાટકના શો લંડનના વેસ્ટ એન્ડમાં નવેમ્બર 1986થી અને બ્રોડવેમાં 15 નવેમ્બર 1987થી શરૂ થયા અને 10 એપ્રિલ 1988માં પૂર્ણ થયા. 1996માં બીબીસી ટેલીવિઝનનું પણ નિર્માણ થયું હતું. દરેક કિસ્સાઓમાં ડેરેક જોકાબી ટ્યુરિંગનું પાત્ર ભજવતા. ટોની એવોર્ડ માટે બ્રોડવે નિર્માણનું ત્રણ વખત નામ નિયુક્ત કરવામાં આવ્યો જેમાં નાટકમાં શ્રેષ્ઠ અભિનેતા, નાટકમાં શ્રેષ્ઠ અભિનિત અભિનેતા અને નાટક માટે શ્રેષ્ઠ માર્ગદર્શનનો સમાવેશ થાય છે. અને ડ્રામા ડેસ્ક એવોર્ડ માટે શ્રેષ્ઠ અભિનેતા અને શ્રેષ્ઠ અભિનિત અભિનેતાનો સમાવેશ થાય છે. 2008માં "ડેન્જરસ નોલેજ" નામની બીબીસી દસ્તાવેજીફિલ્મમાં તપાસ કરવામાં આવેલાં ચાર ગણીતશાસ્ત્રીઓમાંના એક ટ્યુરિંગ હતું.<ref>{{Cite web|url=http://www.bbc.co.uk/bbcfour/documentaries/features/dangerous-knowledge.shtml|title=Dangerous Knowledge|publisher=BBC Four|date=11 June 2008|access-date=25 September 2009}}</ref>
23 જૂન 1998ના રોજ, ટ્યુરિંગના 86ના જન્મદિવસે, એન્ડ્રુ હોજેસ, તેમના જીવનચરિત્રકે, તેમના જન્મસ્થાન અને તેમના બાળપણનું ઘર વોર્રીંગટ્ન ક્રીસેન્ટ, લંડન અને પછીથી કોલોન્નેડ હોટલ ખાતે અધિકૃતપણે ઈંગ્લીશ હેરિટેજવાદળી તકતીનું અનાવરણ કર્યું હતું.<ref>{{Cite web| url=http://www.turing.org.uk/bio/oration.html | title=Unveiling the official Blue Plaque on Alan Turing's Birthplace | access-date=26 September 2006}}</ref><ref>{{Cite web | url=http://www.blueplaque.com/detail.php?plaque_id=348 | archive-url=https://web.archive.org/web/20071013143212/http://www.blueplaque.com/detail.php?plaque_id=348 | archive-date=13 ઑક્ટોબર 2007 | title=About this Plaque – Alan Turing | access-date=25 September 2006 | url-status=dead }}</ref>
તેમના મૃત્યુની 50મી વર્ષગાંઠે, એક યાદગાર તકતીનું અનાવરણ તેમના પહેલાંના રહેઠાંણ હોલીમેડ,વિલ્મસ્લો, ચેરશીર ખાતે 7 જૂન 2004માં કરવામાં આવ્યું હતું.<ref>{{openplaque|3276}}</ref>
13 માર્ચ 2000ના રોજ, સેઈન્ટ વિન્સેટ અને ગ્રીનાડીન્સે દ્વારા 20મી સદીની શ્રેષ્ઠ સિદ્ધઓની ઉજવણી માટે ટિકિટો બહાર પાડવામાં આવી, જેમાંની એક શૂન્ય અને એક સંખ્યાના પુનરાવર્તિત પૂર્વભૂમિકા સાથે ટ્યુરિંગનું પોર્ટેટ મૂકવામાં આવ્યું છે અને તેની નીચે આ પ્રમાણેનું લખાણ લખવામાં આવ્યું છેઃ"1937: ઍલન ટ્યુરિંગસ થીયરી ઓફ ડીજિટલ કમ્પ્યુટીંગ".28 ઓક્ટોબર 2004ના રોજ,જોહ્ન ડબલ્યુ મીલ્સ દ્વારા સર્જિક ઍલન ટ્યુરિંગનું કાંસ્ય આધારિત શિલ્પ ગીલ્ડફોર્ડમાં યુનિવર્સિટી ઓફ સૂર્રેય ખાતે અનાવૃત કરવામાં આવ્યું હતું. જે ટ્યુરિંગના મૃત્યુના 50 વર્ષ સૂચવતું હતું- કેમ્પસમાં તેઓ તેમના પુસ્તકો લઈ જતાં રજૂ કરવામાં આવ્યાં છે.<ref name="univsurrey">{{Cite web |url=http://portal.surrey.ac.uk/press/oct2004/281004a/ |title=The Earl of Wessex unveils statue of Alan Turing |access-date=10 February 2007 |archive-date=23 ઑક્ટોબર 2007 |archive-url=https://web.archive.org/web/20071023193441/http://portal.surrey.ac.uk/portal/page?_pageid=799%2C277813&_dad=portal&_schema=PORTAL |url-status=dead }}</ref>2006માં, બોસ્ટન પ્રાઈડે તેમના માનદ્ ગ્રાન્ડ માર્શલનું નામ ટ્યુરિંગ આપ્યું હતું.<ref name="bostonpride">{{Cite web |url=http://www.bostonpride.org/honorarymarshal.php |title=Honorary Grand Marshal |access-date=10 February 2007 |archive-date=1 જાન્યુઆરી 2009 |archive-url=https://web.archive.org/web/20090101213356/http://www.bostonpride.org/honorarymarshal.php |url-status=dead }}</ref> પ્રિન્સટન એલ્યુમની વિકલીનું નામ ટ્યુરિંગ રાખવામાં આવ્યું, જે પ્રિન્સટન યુનિવર્સિટીના ઇતિહાસમાં અત્યંત મહત્તવપૂર્ણ વિદ્યાર્થીઓમાં બીજા હતા, બીજા અન્ય રાષ્ટ્રપતિ જેમ્સ મેડિસન હતાં.
બ્લેત્ચલેય પાર્ક ખાતે ટ્યુરિંગના જીવન આકાર 1.5 ટનની પ્રતિમાનું અનાવરણ 19 જૂન 2007ના રોજ કરવામાં આવ્યું. વેલ્શ સ્લેટના લગભગ અડધા મિલિયન ટુકડાઓથી તેનું નિર્માણ કરવામાં આવ્યું, સ્ટેપ્હન કેટ્ટલ દ્વારા શિલ્પનું સર્જન કરવામાં આવ્યું હતું, આ કામ સ્વર્ગસ્થ અમેરિકાના અબજોપતિ સિડની ફ્રેન્ક દ્વારા સોંપવામાં આવ્યું હતું.<ref>[http://www.bletchleypark.org.uk/news/docview.rhtm/454075/article.html બ્લેત્ચલેય પાર્ક અનવેઈલ્સ સ્ટેટ્યુ કમેમરેટીંગ ઍલન ટ્યુરિંગ,] {{Webarchive|url=https://web.archive.org/web/20120227171427/http://www.bletchleypark.org.uk/news/docview.rhtm/454075/article.html |date=2012-02-27 }} , બ્લેત્ચલેય પાર્ક પ્રેસ રીલીઝ, 20 જૂન 2007</ref>ટ્યુરિંગને માન્ચેસ્ટર શહેર, જેમાં તેઓ તેમની જીવનના અંત સુધી કામ કર્યાં રહ્યાં, ત્યાં તેમને વિવિધ રીતે માનસન્માન મળ્યું હતું. 1994માં A6010 રોડ (માન્ચેસ્ટર શહેરનો આંતરિક રિંગ રોડ) બનાવવામાં આવ્યો, જેનું નામ ઍલન ટ્યુરિંગ વે રાખવામાં આવ્યું હતું. આ રસ્તો આગળ જતાં વધારે પહોળા પુલમાં લઈ જતો હતો અને તેનું નામ ઍલન ટ્યુરિંગ બ્રીજ રાખવામાં આવ્યું છે. 23 જૂન 2001માં માન્ચેસ્ટરશહેરમાં ટ્યુરિંગનું પુતળું અનાવૃત કરવામાં આવ્યું હતું. તે સેકવિલે પાર્કમાં, વિટવર્થ સ્ટીટની ઉપર યુનિવર્સિટી ઓફ માન્ચેસ્ટરઅને કેનલ સ્ટ્રીટ ગે વિલેજની વચ્ચે આવેલું છે. યાદગાર પૂતળું, "ફાધર ઓફ કમ્પ્યૂટર સાયન્સ"ને વર્ણવતું ટ્યુરિંગનું પૂતળું બગીચામાં કેન્દ્ર સ્થાને એક બાંકડાની ઉપર બેઠું છે. આ પૂતળાનું અનાવરણ ટ્યુરિંગના જન્મદિવસે થયું હતું.
[[ચિત્ર:Sackville Park Turing plaque.jpg|left|thumbnail|200px|ટ્યુરિંગનું યાદગાર પૂતળાની તકતી ]]
ટ્યુરિંગને એક સફરજન પકડીને દર્શાવવામાં આવ્યાં છે. – એક શિષ્ટ પ્રતીક વર્જિત પ્રેમની રજૂઆત કરવામાં વપરાય છે, સફરજન [[આઇઝેક ન્યુટન|આઈઝેક ન્યુટન]]ના ગુરુત્વાકર્ષણ બળના સિદ્ધાંતના વિચારને રજૂ કરે છે અને ટ્યુરિંગના પોતાના મૃત્યુનો અર્થ પણ સૂચવે છે. બાંકડા પર રાહતમાં બેઠેલાં કાંસામાં પૂતળા હેઠળ આ વાક્ય મૂકવામાં આવ્યું છે 'ઍલન મેથીસન ટ્યુરિંગ 1912–1954', અને જો ઈનીગ્મા મશીન: 'IEKYF ROMSI ADXUO KVKZC GUBJ'નું વિશ્લેષણ કરવામાં આવે તો તેમનું જીવનસૂત્ર 'કમ્પ્યૂટર વિજ્ઞાનના સ્થાપક' એવું બનશે.
પૂતળાના પગ પાસેનું એક તકતી કહે છે 'કમ્પ્યૂટર વિજ્ઞાનના પિતા, ગણિતશાસ્ત્રી, તર્કશાસ્ત્રી, યુદ્ધ સમયના કોડ તોડનારા, પૂર્વાગ્રહના શિકાર બનેલા'. બેર્ટ્રાન્ડ રસેલનું વાક્ય પણ આ પ્રમાણે કહે છે 'ગણિત, સાચી રીતે જોવાયેલું, સત્યના માત્ર સ્વામિ નહીં, પણ સર્વોચ્ચ સુંદરતાના સ્વામી- શિલ્પની જેમ ઠંડી અને તપસ્યાની સુંદરતા.' શિલ્પકારે તેના જૂના એમસ્ટ્રાડ કમ્પ્યૂટરને દફનાવી દીધું, જે એક પહેલાંનું જાણીતું કમ્પ્યૂટર હતું, એક શ્રદ્ધાંજલિ તરીકે તકતીની નીચે લખ્યું હતું, " ધી ગોડ ફાધર ઓફ ઓલ મોર્ડન કમ્પ્યૂટર્સ".<ref name="computerburied">^ જુઓ{{Cite news | title = Computer buried in tribute to genius | publisher = Manchester Evening News| date = 15 June 2001 | url = http://www.manchestereveningnews.co.uk/news/s/27/27595_computer_buried_in_tribute_to_genius.html | access-date = 23 June 2009 }}</ref>
આધુનિક કમ્પ્યૂટરના સર્જનમાં ટ્યુરિંગની ભૂમિકા માટે 1999માં ''ટાઈમ સામાયિકે'' [[Time 100: The Most Important People of the Century|20 સદીના 100 અત્યંત મહત્ત્વના લોકો]]માંના એક તરીકે ટ્યુરિંગનું નામ મૂક્યું હતું, અને કહ્યું હતું: "હકીકત એ છે કે દરેક વ્યક્તિ જે કીબોર્ડ થપાટ લગાવે છે, સ્પ્રેડશીટ અથવા વર્ડ પ્રોસેસીંગ પ્રોગ્રામ ચાલું કરે છે, તે ટ્યુરિંગ મશીનના મૂર્ત સ્વરૂપ પર કામ કરે છે."<ref name="AFP">{{Cite web |title=Alan Turing – Time 100 People of the Century |url=http://205.188.238.181/time/time100/scientist/profile/turing.html |publisher=''[[Time Magazine]]'' |quote=The fact remains that everyone who taps at a keyboard, opening a spreadsheet or a word-processing program, is working on an incarnation of a Turing machine. |access-date=2011-03-21 |archive-date=2011-02-26 |archive-url=https://web.archive.org/web/20110226144919/http://205.188.238.181/time/time100/scientist/profile/turing.html |url-status=dead }}</ref>
2002માં,''બીબીસી'' એ વૈશ્વિક સ્તરે કરવામાં આવેલાં 100 શ્રેષ્ઠ બ્રિટીશરોના મતદાનમાં ટ્યુરિંગને 21મો ક્રમાંક મળ્યો હતો.<ref>{{Cite news | url = http://news.bbc.co.uk/2/hi/entertainment/2208671.stm | title = 100 great British heroes | date = 21 August 2002 | work = BBC News }}</ref>
એપલ કમ્પ્યૂટરનો લોગો મોટા ભાગે ઍલન ટ્યુરિંગને શ્રદ્ધાંજલિ આપવા માટે આત્મહત્યાની તેમની પદ્ધતિને એક કટકાના સાથેના સંદર્ભમાં ખોટી રીતે લેવામાં આવે છે.<ref>{{Cite news|url=http://www.independent.co.uk/news/media/logos-that-became-legends-icons-from-the-world-of-advertising-768077.html |title=Logos that became legends: Icons from the world of advertising|work= The Independent |publisher=www.independent.co.uk |access-date=14 September 2009 | location=London | date=4 January 2008}}</ref> લોગોના રચયિતા<ref>{{Cite web | url = http://creativebits.org/interview/interview_rob_janoff_designer_apple_logo | title = Interview with Rob Janoff, designer of the Apple logo | publisher = creativebits| access-date = 14 September 2009 }}</ref> અને કંપનીએ લોગોની ડિઝાઈનમાં ટ્યુરિંગને કોઈ પણ અંજલિ આપ્યાનો ઈન્કાર કર્યો છે.<ref>{{Harvnb|Leavitt|2007|p=280}}</ref> 2010માં, અભિનેતા/નાટ્યલેખક જેડ ઈસ્ટેબેનએ ટ્યુરિંગને સોલો સંગીત "આઈકોન્સઃ ધી લેસ્બીયન ઍન્ડ ગે હિસ્ટરી ઓફ ધી વર્લ્ડ, ભાગ 4" માં વર્ણવ્યાં છે.
=== સરકારનું માફીનામું ===
ઓગસ્ટ 2009માં, જોહ્ન ગ્રેહામ-ક્યુમીનએ ઍલન ટ્યુરિંહની સામે સમલૈગિંક તરીકે કાયદેસરના પગલાં ભરવા બદલે બ્રિટિશ ગવર્મેન્ટને મરણોત્તર થયેલાં ઍલન ટ્યુરિંગની માફી માંગવાની એક અરજી દાખલ કરવાની શરૂઆત કરી હતી.<ref>{{Cite book|title=Thousands call for Turing apology |url=http://news.bbc.co.uk/2/hi/technology/8226509.stm |publisher=BBC News |date=31 August 2009 |access-date=31 August 2009}}</ref><ref>{{Cite book | title = Petition seeks apology for Enigma code-breaker Turing | url = http://www.cnn.com/2009/WORLD/europe/09/01/alan.turing.petition/index.html | publisher = CNN | date = 01 September 2009 | access-date = 1 September 2009}}</ref> આ અરજીને ટેકો કરતી હજારો લોકોની સહી મળી.<ref name="PMapology"/><ref>યુકેના નાગરિકો માટે જ અરજી ખુલી હતી. </ref> વડાપ્રધાન ગોર્ડન બ્રાઉને આ અરજીને સ્વીકારી, 10 સપ્ટેમ્બર 2009ના રોજ માફી માંગતું વિધાન રજૂ કર્યું અને ટ્યુરિંગ સાથેના વર્તાવ અંગે આઘાતની લાગણી વ્યક્ત કરી.<ref name="PM-apology"/><ref name="PMapology">{{Cite web | title = Treatment of Alan Turing was "appalling" | url = http://www.number10.gov.uk/Page20571 | publisher = Prime Minister's Office | date = 10 September 2009 | access-date = 21 માર્ચ 2011 | archive-date = 12 સપ્ટેમ્બર 2009 | archive-url = https://web.archive.org/web/20090912142412/http://www.number10.gov.uk/Page20571 | url-status = dead }}</ref>
<blockquote>
ઍલન ટ્યુરિંગ માટે ન્યાયની માંગણી માટે હજારો લોકો એકઠા થયા અને તેમની સાથે જે રીતે વર્તન કરવામાં આવ્યું હતું તે માટે આઘાતની લાગણી વ્યક્ત કરી. જ્યારે ટ્યુરિંગને તે સમયે કાયદા હેઠળ લાવવામાં આવ્યા હતા અને આપણે ઘડિયાળને પાછી ફેરવી શકતા નથી, તેમની સાથે કરવામાં આવેલો વ્યવહાર એકદમ અયોગ્ય હતો અને હું અને આપણે તેમની સાથે જે કંઈ થયું તે બદલ હૃદયના ઊંડાણ પૂર્વક માફી માંગવાની મળેલી તક બદલ હું ખુશ છું.... તેથી બ્રિટિશ સરકારના બદલે, અને ઍલનના કામને કારણે એ તમામ જેઓ મુક્તપણે રહે છે, તેમના વતી મને એ કહેતાં અત્યંત ગર્વ થાય છેઃ અમે માફી માંગીએ છીએ, તમે આના કરતાં ઘણી સારી લાયકાત ધરાવો છો.<ref name="PMapology"/>
</blockquote>
=== યુનિવર્સિટીઓ દ્વારા મળેલો આદર ===
[[ચિત્ર:Alan Turing Building 1.jpg|thumbnail|યુનિવર્સિટી ઓફ માન્ચેસ્ટર ખાતે ઍલન ટ્યુરિંગનું બિલ્ડીંગ]]
બ્રિટીશ સોસાયટી ફોર ધી હિસ્ટ્રી ઓફ મેથેમેટીક્સઅને બ્રિટિશ લોજીક કોલોક્વીયમ દ્વારા ટ્યુરિંગના જીવન અને સિદ્ધિઓની ગોઠવણી કરી પ્રસંગની ઉજવણી 5 જૂન 2005ના રોજ કરવામાં આવી હતી.
*સુર્રેય યુનિવર્સિટીએ તેના મુખ્ય ચોકમાં ટ્યુરિંગનું પૂતળું મૂક્યું છે.
*"ટ્યુરિંગ ડેઈઝ" તરીકે કહેવાતી ગણતરીની થીયરી પર ઈસ્તાનબુલ બીલ્જી યુનિવર્સિટીએ વાર્ષિક કોન્ફરન્સનું આયોજન થયું.<ref name="bilgiuniv">{{Cite web | url = http://cs.bilgi.edu.tr/pages/turing_days/ | title = Turing Days @ İstanbul Bilgi University | access-date = 10 February 2007 | archive-date = 1 ઑગસ્ટ 2013 | archive-url = https://web.archive.org/web/20130801193650/http://cs.bilgi.edu.tr/pages/turing_days/ | url-status = dead }}</ref>
*ઓસ્ટ્રીન ખાતે ટેક્સાસ યુનિવર્સિટીએ કમ્પ્યૂટર સાયન્સ પ્રોગ્રામનું નામ ટ્યુરિંગ સ્કોલરસ રાખી આદર આપ્યો છે.<ref name="texturingschol">{{Cite web |url=http://www.cs.utexas.edu/academics/undergraduate/honors/turing/ |title=Turing Scholars Program at the University of Texas at Austin |access-date=16 August 2009 |archive-date=17 ફેબ્રુઆરી 2010 |archive-url=https://web.archive.org/web/20100217072341/http://www.cs.utexas.edu/academics/undergraduate/honors/turing/ |url-status=dead }}</ref>
*ઉતર ફ્રાન્સમાં આવેલી લીલી યુનિવર્સિટી ખાતે આવેલા કમ્પ્યૂટર સાયન્સ વિભાગ (LIFL<ref name="lifl">{{Cite web |url=http://www.lifl.fr/ |title=Laboratoire d'Informatique Fondamentale de Lille |access-date=3 December 2010 |archive-date=22 જુલાઈ 2010 |archive-url=https://web.archive.org/web/20100722014047/http://www.lifl.fr/ |url-status=dead }}</ref>) ના એક પ્રયોગશાળાનું નામ ઍલન એમ. ટ્યુરિંગના સન્માનમાં ટ્યુરિંગ રાખવામાં આવ્યું (કુર્ટ ગોડેલ પછી અન્ય પ્રયોગશાળાનું નામ રાખવામાં આવ્યું હતું).
*ચિલિની પોન્ટીફીકલ કેથોલિક યુનિવર્સિટી, પ્યુરટો રીકોની પોલીટેક્નીક યુનિવર્સિટી, [[કોલમ્બીયા|કોલંબિયા]] બોગોટામાં લોસ એન્ડેસ યુનિવર્સિટી, કિંગસ કોલેજ, વેલ્સમાં કેમ્બ્રીજઅને બેનગોર યુનીવર્સીટીના કમ્પ્યૂટર સાયન્સના વિભાગનું નામ ટ્યુરિંગ પાછળ રાખવામાં આવ્યું હતું.
*યુનિવર્સિટી ઓફ માન્ચેસ્ટર, ધી ઓપન યુનિવર્સિટી, ઓક્સફર્ડ બ્રુકસ યુનિવર્સિટી અને આર્હુસ યુનિવર્સિટી (ડેનમાર્ક, અર્હુસમાં) તમામના મકાનનું નામ ટ્યુરિંગના નામથી રાખવામાં આવ્યું છે.
*સર્રેય રીસર્ચ પાર્કમાં ઍલન ટ્યુરિંગ રોડનું નામ ઍલન ટ્યુરિંગના નામ પરથી રાખવામાં આવ્યું છે.
*હોર્નબોસ્ટેલ મોલમાં આવેલી કાર્નેગી મેલોન યુનિવર્સિટીમાં ગ્રેનાઈટનો બાંકડો છે, જેની પર એ.એમ.ટ્યુરિંગ નામ કોતરાયેલું છે.
*તાજેતરમાં ઈકોલ ઈન્ટરનેશનલ ડેસ સાયન્સ ડુ ટ્રાઈટેમેન્ટ ડે ઈન્ફોર્મેશનના બનેલાં ત્રીજા બીલ્ડીંગનું નામ "ટ્યુરિંગ" રાખવામાં આવ્યું છે.
== આ પણ જુઓ ==
{{Portal box|Biography|Logic|LGBT}}
*ટ્યુરિંગ ડીગ્રી
*ટ્યુરિંગ સ્વીચ
*વણ ગોઠવેલું મશીન
*ઍલન ટ્યુરિંગ વર્ષ
*ગુડ-ટ્યુરિંગ પુનરાવર્તનની સંભાવનાઓ
*ટુરિંહ મશીનના ઉદાહરણો
*ટ્યુરિંગ પરીક્ષણ
== નોંધ ==
{{Reflist|colwidth=30em}}
== સંદર્ભો ==
{{Refbegin|colwidth=30em}}
* {{ Cite book | last = Agar | first = Jon | title = The government machine: a revolutionary history of the computer | publisher = MIT Press | year = 2003 | location = Cambridge, Massachusetts | isbn = 978-0-262-01202-7 }}
* {{ Cite book | last = Alexander | first = C. Hugh O'D. | author-link = Conel Hugh O'Donel Alexander | date = ''circa'' 1945 | title = Cryptographic History of Work on the German Naval Enigma | url = http://www.ellsbury.com/gne/gne-000.htm | publisher=The National Archives, Kew, Reference HW 25/1}}
* {{ Cite book | last = Beniger | first = James | title = The control revolution: technological and economic origins of the information society | publisher = Harvard University Press | year = 1986 | location = Cambridge, Massachusetts | isbn = 0-674-16986-7 }}
* {{Citation | last = Babbage | first = Charles | author-link = Charles Babbage | origyear = 1864
| publication-date = 2008 | editor-last = Campbell-Kelly | editor-first = Martin | editor-link = Martin Campbell-Kelly | title = Passages from the life of a philosopher | publisher = Rough Draft Printing | isbn = 978-1-60386-092-5 }}
* {{ Cite book | last = Bodanis | first = David | author-link = David Bodanis | title = Electric Universe: How Electricity Switched on the Modern World | year = 2005 |publisher = Three Rivers Press | location = New York | isbn = 0-307-33598-4 | oclc = 61684223 }}
* {{Cite book | last = Campbell-Kelly | first = Martin | authorlink = Martin Campbell-Kelly | last2 = Aspray | first2 = William | title = Computer: A History of the Information Machine | publisher = Basic Books | year = 1996 | location = New York | isbn = 0-465-02989-2 }}
* {{Cite book | last = Ceruzzi | first = Paul | authorlink = Paul Ceruzzi | title = A History of Modern Computing | publisher = MIT Press | year = 1998 | location = Cambridge, Massachusetts, and London | isbn = 0-262-53169-0}}
* {{ Cite book | last = Chandler | first = Alfred | authorlink = Alfred Chandler | title = The Visible Hand: The Managerial Revolution in American Business | publisher = Belknap Press | year = 1977 | location = Cambridge, Massachusetts | isbn = 0-674-94052-0 }}
* {{ Cite journal | last = Copeland | first = B. Jack | authorlink = B. Jack Copeland | title = Colossus: Its Origins and Originators | journal = [[IEEE Annals of the History of Computing]] | volume = 26 | issue = 4 | pages = 38–45 | year = 2004 |doi = 10.1109/MAHC.2004.26 | ref = harv }}
* {{ Cite book | last = Copeland | first = B. Jack (ed.) | authorlink = B. Jack Copeland | title = The Essential Turing | year = 2004 | publisher = Oxford University Press | location = Oxford | isbn = 0-19-825079-7 | oclc = 156728127 224173329 48931664 57434580 57530137 59399569 }}
* {{ Cite book | last = Copeland (ed.) | first = B. Jack | authorlink = B. Jack Copeland
| title = Alan Turing's Automatic Computing Engine | year = 2005 | publisher = Oxford University Press | location = Oxford | isbn = 0-19-856593-3 | oclc = 224640979 56539230 }}
* {{ Cite book | last = Copeland | first = B. Jack | authorlink = B. Jack Copeland | title = Colossus: The secrets of Bletchley Park's code-breaking computers | year = 2006 | publisher = Oxford University Press | isbn = 978-0-19-284055-4 | ref = harv }}
* {{ Cite book | last = Edwards | first = Paul N | title = The closed world: computers and the politics of discourse in Cold War America | publisher = MIT Press | year = 1996 | location = Cambridge, Massachusetts | isbn = 0-262-55028-8 }}
* {{ Cite book | last = Hodges | first = Andrew | authorlink = Hodges, Andrew | year = 1983 | title = Alan Turing: the enigma |location = London | publisher = Burnett Books | isbn = 0-04-510060-8 | ref = harv }}
* {{ Cite book | last = Hochhuth | first = Rolf | authorlink = Rolf Hochhuth | title = Alan Turing: en berättelse | publisher = Symposion | year = 1988 | isbn = 978-91-7868-109-9 }}
* {{ Cite book | last = Leavitt | first = David | authorlink = David Leavitt | year = 2007 | title = The man who knew too much: Alan Turing and the invention of the computer | publisher = Phoenix | isbn = 978-0-7538-2200-5 | ref = harv }}
* {{ Cite book | last = Levin | first = Janna | authorlink = Janna Levin | title = A Madman Dreams Of Turing Machines | publisher = Knopf | year = 2006 | location = New York | isbn = 978-1-4000-3240-2 }}
* {{ Cite book | last = Lewin | first = Ronald | authorlink = Ronald Lewin | title = Ultra Goes to War: The Secret Story | edition = Classic Penguin | series = Classic Military History | year = 1978 | publication-date = 2001 | publisher = Hutchinson & Co | location = London, England | isbn = 978-1-56649-231-7 | ref = harv }}
* {{Cite book| last = Lubar | first = Steven | year = 1993 | title = Infoculture | location = Boston, Massachusetts and New York | publisher = Houghton Mifflin | isbn = 0-395-57042-5}}
* {{ Cite document | last = Mahon | first = A.P. | title = The History of Hut Eight 1939–1945 | publisher = UK National Archives Reference HW 25/2 | year = 1945 | url = http://www.ellsbury.com/hut8/hut8-000.htm | access-date = 10 December 2009 | ref = harv }}
*{{MacTutor Biography|id=Turing|title=Alan Mathison Turing}}
*પેટઝોલ્ડ, ચાર્લેસ (2008). "ધી એનોટાટેડ ટ્યુરિંગ: અ ગાઈડેડ ટુર થ્રુ ઍલન ટ્યુરિંગસ હિસ્ટોરીક પેપર ઓન કમ્યુટેબીલીટી એન્ડ ધી ટ્યુરિંગ મશીન". ઈન્ડિયાનાપોલીસ: વિલેય પબ્લીશીંગ. આઈએસબીએન 978-0-470-22905-7
*સ્મીથ, રોજર (1997). ''ફોન્ટાના હિસ્ટ્રી ઓફ ધી હ્યુમન સાયન્સીસ'' . લંડન: ફોન્ટાના.
*વૈઝેનબૌમ, જોસેફ (1976). ''કમ્પ્યૂટર પાવર એન્ડ હ્યુમન રીઝન'' . લંડન: ડબ્લ્યુ.એચ. ફ્રીમેન. આઈએસબીએન 0-7167-167-0463-3
* {{Cite book | last = Turing | first = Sara Stoney | title = Alan M Turing | publisher = W Heffer | year = 1959 }} ટ્યુરિંગની માતા, જેણે ગ્લોરિફાઈંગ હીઝ લાઈફ નામની ૧૫૭ પાનાનું જીવનચરિત્ર લખી, ઘણાં વર્ષો સુધી તેને જીવિત રાખ્યો. તે 1959માં પ્રકાશિત થઈ હતી, અને તેથી તેનું યુદ્ધનું કાર્ય આવરી ન લઈ શકાયું. ભાગ્યેજ ૩૦૦ પ્રતો વેચાઈ હતી (સારા ટ્યુરિંગ થી લીન ન્યમેન, 1967, સેન્ટ જ્હોન કોલેજ, કેમ્બ્રીજની લાઈબ્રેરી). પ્રસ્તાવનાના ૬ પૃષ્ઠો લીન ઈરવીન દ્વારા લખવામાં આવ્યા હતા, સંભારણાંઓ અને તેના વારંવાર બોલાયેલાં વાક્યોનો સમાવેશ થયો છે.
* {{Cite book | last = Whitemore | first = Hugh | authorlink = Hugh Whitemore | last2 = Hodges | first2 = Andrew | authorlink2 = Andrew Hodges | title = Breaking the code | publisher = S. French | year = 1988 }} આ 1986 હ્યુગ વ્હાઈટમોર પ્લે ટ્યુરિંગના જીવન અને મૃત્યુની વાર્તા કહે છે. મૂળ વેસ્ટ એન્ડ અને બ્રોડવેમાં ડેરેક જાકોબીએ ટ્યુરિંગનો અભિનય કર્યો હતો અને તેણે 1997માં નાટક આધારિત ટેલિવિઝન ફિલ્મમાં અભિનયનું પુનસર્જન કર્યું હતું, જે સંયુક્તપણે બીબીસી અને ડબલ્યુજીબીએચ, બોસ્ટન દ્વારા બનાવાઈ હતી. નાટકનું પ્રકાશન અંબર લેન પ્રેસ, ઓક્સફર્ડ દ્વારા કરવામાં આવ્યું છે. - એએસઆઈએન: B000B7TM0Q
*વિલિયમસ, મિશેલ આર. (1985) ''એ હિસ્ટ્રી ઓફ ક્મ્યુટીંગ ટેક્નોલોજી'' , ઈન્ગલેવુડ ક્લીફ્ફસ, ન્યુ જર્સી: પ્રેન્ટીસ-હોલ, આઈએસબીએન 0-8186-7739-2
*{{Cite book|last=Yates |first=David M. |title=Turing's Legacy: A history of computing at the National Physical Laboratory 1945–1995 |year=1997 |publisher=[[Science Museum, London|London Science Museum]] |location=London |isbn=0-901805-94-7 |oclc=123794619 40624091 }}
{{Refend}}
== બાહ્ય લિંક્સ ==
{{External links|date=August 2010}}
{{Wikiquote}}
{{Commons category|Alan Turing}}
*[http://www.turing.org.uk/ ઍલન ટ્યુરિંગ] એક [http://www.turing.org.uk/bio/part1.html ટૂંકી જીવનકથા] સાથે એન્ડ્રુ હોજેસ દ્વારા ચલાવવામાં આવતી સાઈટ
*[http://www.alanturing.net/ AlanTuring.net – જેક કોપલેન્ડ દ્વારા ][http://www.alanturing.net/ ટ્યુરિંગ એચીવ ફોર ધી હિસ્ટ્રી ઓફ કમ્પ્યુટીંગ]
*[http://www.turingarchive.org/ ધી ટ્યુરિંગ એચીવ]{{Dead link|date=ડિસેમ્બર 2021 |bot=InternetArchiveBot |fix-attempted=yes }} – કિંગ્સ કોલેજ, કેમ્બીજ આર્ચીવ દ્વારા કેટલાંક અપ્રકાશિત દસ્તાવેજોની સ્કેન કરેલી પ્રતો
*{{MathGenealogy|id=8014}}
*[http://www.systemtoolbox.com/article.php?history_id=3 ઍલન ટ્યુરિંગ- ટુવર્ડઝ એ ડીઝીટલ માઈન્ડઃ ભાગ 1] {{Webarchive|url=https://web.archive.org/web/20070803163318/http://www.systemtoolbox.com/article.php?history_id=3 |date=2007-08-03 }} સીસ્ટમ ટુલબોક્સ, 11 ડિસેમ્બર 2001
*[http://plato.stanford.edu/entries/turing/ ઍલન ટ્યુરિંગ] ફિલોસોફીનું સ્ટેનફઓર્ટ એનસાઈક્રોપીડિયા. 3 જૂન ૨૦૦૨.
*[http://www.time.com/time/time100/scientist/profile/turing.html ઍલન ટ્યુરિંગ] {{Webarchive|url=https://web.archive.org/web/20080822093918/http://www.time.com/time/time100/scientist/profile/turing.html |date=2008-08-22 }} ''સમય'' 100
* [http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} Alan Turing] {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} {{Webarchive|url=https://web.archive.org/web/20080828060019/http://www.rkbexplorer.com/explorer/#display=person%2D%7Bhttp://dblp.rkbexplorer.com/id/people-a27f18ebafc0d76ddb05173ce7b9873d-e0b388b7c1e0985b1371d73ee1fae8b5} |date=2008-08-28 }} RKBExplorer
* [http://www.rutherfordjournal.org/article010111.html ''ધી માઈન્ડ એન્ડ ધી કમ્પ્યુટીંગ મશીન'' ] ''ધી રુથફોર્ડ જર્નલ'' - એક 1949 ઍલન ટ્યુરિંગ અને અન્યો પર ચર્ચા
* [http://www.turingcentenary.eu/ ઍલન ટ્યુરિંગ વર્ષ] {{Webarchive|url=https://web.archive.org/web/20190818065459/http://turingcentenary.eu/ |date=2019-08-18 }} {{Webarchive|url=https://web.archive.org/web/20190818065459/http://turingcentenary.eu/ |date=2019-08-18 }} {{Webarchive|url=https://web.archive.org/web/20190818065459/http://turingcentenary.eu/ |date=2019-08-18 }} {{Webarchive|url=https://web.archive.org/web/20190818065459/http://turingcentenary.eu/ |date=2019-08-18 }} {{Webarchive|url=https://web.archive.org/web/20190818065459/http://turingcentenary.eu/ |date=2019-08-18 }} {{Webarchive|url=https://web.archive.org/web/20190818065459/http://turingcentenary.eu/ |date=2019-08-18 }} {{Webarchive|url=https://web.archive.org/web/20190818065459/http://turingcentenary.eu/ |date=2019-08-18 }} {{Webarchive|url=https://web.archive.org/web/20190818065459/http://turingcentenary.eu/ |date=2019-08-18 }} {{Webarchive|url=https://web.archive.org/web/20190818065459/http://turingcentenary.eu/ |date=2019-08-18 }} {{Webarchive|url=https://web.archive.org/web/20190818065459/http://turingcentenary.eu/ |date=2019-08-18 }}
* [http://cs.swan.ac.uk/cie12/ CiE 2012: ટ્યુરિંગ સેન્ટીનરિ કોન્ફેરન્સ ] {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }} {{Webarchive|url=https://web.archive.org/web/20110716163131/http://cs.swan.ac.uk/cie12/ |date=2011-07-16 }}
* [http://www.visualturing.org/ વિઝ્યુઅલ ટ્યુરિંગ] {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }} {{Webarchive|url=https://web.archive.org/web/20110728163234/http://www.visualturing.org/ |date=2011-07-28 }}{{Dead link|date=જુલાઈ 2021 |bot=InternetArchiveBot |fix-attempted=yes }}
* [http://www.wolframalpha.com/examples/TuringMachines.html ટ્યુરિંગ મશીન કેલક્યુલેટરસ] વોલ્ફ્રામઆલ્ફા
=== પેપર્સ ===
* [http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper ટ્યુરિંગના પેપરો, અહેવાલો અને વ્યાખ્યાનો ઉપરાંત અનુવાદિત આવૃત્તિઓ અને સંગ્રહોની વિસ્તૃત યાદી] {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120827022642/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-08-27 }} {{Webarchive|url=https://web.archive.org/web/20120827022642/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-08-27 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120827022642/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-08-27 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120827022642/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-08-27 }} {{Webarchive|url=https://web.archive.org/web/20120827022642/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-08-27 }} {{Webarchive|url=https://web.archive.org/web/20120827022642/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-08-27 }} {{Webarchive|url=https://web.archive.org/web/20120827022642/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-08-27 }} {{Webarchive|url=https://web.archive.org/web/20120827022642/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-08-27 }} {{Webarchive|url=https://web.archive.org/web/20120827022642/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-08-27 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} {{Webarchive|url=https://web.archive.org/web/20120224231856/http://bibnetwiki.org/wiki/Category:Alan_M._Turing_Paper |date=2012-02-24 }} BibNetWiki
* [http://www.cbi.umn.edu/oh/display.phtml?id=116 ડોનાલ્ડ ડબલ્યુ. ડેવિસ સાથે મૌખિક ઇતિહાસ મુલાકાત], ચાર્લેસ બાબ્બેજ ઈન્સ્ટીટ્યુટ, યુનિવર્સિટી ઓફ મિન્નેસોટા; યુ.કે. ખાતે ક્મપ્યુટર પ્રોજેક્ટોને વર્ણવતાં ડેવિઝ નેશનલ ફિઝિક્સ લેબોરેટરી, 1947થી ટ્યુરિંગના ડિઝાઈનીંગ કામની સાથે બે એસીઈ કમ્પ્યૂટરોના વિકાસ સુધી
* [http://www.cbi.umn.edu/oh/display.phtml?id=81 નિકોલસ સી. મેટ્રોપોલીસ સાથે મૌખિક ઇતિહાસ મુલાકાત], ચાર્લેસ બાબ્બેજ ઈન્સ્ટીટ્યુટ, યુનિવર્સિટી ઓફ મીન્નેસોટી. લોસ ઍલામોસ નેશનલ લેબોરેટરી ખાતે, કમ્પ્યૂટર સેવાઓ માટેના પ્રથમ નિયામક મેટ્રોપોલીસ હતાં- ઍલન ટ્યુરિંગ અને જ્હોન વોન ન્યુમનવચ્ચેના સંબંધો સહિતના મુદ્દાઓનો સમાવેશ
[[શ્રેણી:વિસંગત પ્રશિસ્ત સ્વરૂપની સાથે લેખો]]
[[શ્રેણી:ઍલન ટ્યુરિંગ]]
[[શ્રેણી:કમ્પ્યૂટર ડિઝાઈનરો]]
[[શ્રેણી:કમ્પ્યૂટરનો પાયો નાખનારા]]
[[શ્રેણી:અંગ્રેજ નાસ્તિકતાવાદીઓ]]
[[શ્રેણી:અંગ્રેજ કમ્પ્યૂટર વિજ્ઞાનિઓ]]
[[શ્રેણી:અંગ્રેજ સંશોધકો]]
[[શ્રેણી:અંગ્રેજ તર્કશાસ્ત્રી]]
[[શ્રેણી:અંગ્રેજ ગણિતશાસ્ત્રીઓ]]
[[શ્રેણી:અંગ્રેજ ફિલસૂફીઓ]]
[[શ્રેણી:દૂરના અંતર સુધી દોડનારાઓ]]
[[શ્રેણી:ગણિતશાસ્ત્રી]]
[[શ્રેણી:૧૯૧૨માં જન્મ]]
[[શ્રેણી:૧૯૫૪માં મૃત્યુ]]
shqxvjfzgreupy7i13v99ingnao0opg
ઢાંચો:Clear
10
37997
886370
716895
2024-02-13T14:32:36Z
en>Redrose64
0
pass class through
886370
wikitext
text/x-wiki
<div style="clear:{{{1|both}}};" class={{{class|}}}></div><noinclude>
{{documentation}}
</noinclude>
an9yinekx1cqo1nkoi5u8nuqub9qbvg
886371
886370
2025-06-13T17:03:25Z
KartikMistry
10383
[[:en:Template:Clear]] માંથી આયાત કરેલ ૧ પુનરાવર્તન
886370
wikitext
text/x-wiki
<div style="clear:{{{1|both}}};" class={{{class|}}}></div><noinclude>
{{documentation}}
</noinclude>
an9yinekx1cqo1nkoi5u8nuqub9qbvg
ઢાંચો:See
10
38245
886396
197160
2017-12-09T06:09:36Z
en>Alex 21
0
Changed redirect target from [[Template:Further information]] to [[Template:Further]]
886396
wikitext
text/x-wiki
#REDIRECT [[Template:Further]]
av4m7u5e2k4xmrwdrm0zo3bi5s6lwhj
886397
886396
2025-06-13T17:03:29Z
KartikMistry
10383
[[:en:Template:See]] માંથી આયાત કરેલ ૧ પુનરાવર્તન
886396
wikitext
text/x-wiki
#REDIRECT [[Template:Further]]
av4m7u5e2k4xmrwdrm0zo3bi5s6lwhj
ઢાંચો:Further
10
43540
886386
257912
2018-07-27T17:29:22Z
en>Galobtter
0
fix so that don't need [[Module:Further]] anymore
886386
wikitext
text/x-wiki
<includeonly>{{#invoke:labelled list hatnote|labelledList|Further information{{#if:{{{topic|}}}| on {{{topic|}}}}}}}</includeonly><noinclude>
{{documentation}}
<!-- Categories go on the /doc subpage, and interwikis go on Wikidata. -->
</noinclude>
pu1ae8is2dmdurv2yovsfwxjajnhn2s
886387
756185
2025-06-13T17:03:27Z
KartikMistry
10383
[[:en:Template:Further]] માંથી આયાત કરેલ ૧ પુનરાવર્તન
756185
wikitext
text/x-wiki
{{rellink|વધુ માહિતી: [[{{{1<noinclude>|Example</noinclude>}}}]]{{#if: {{{3|}}}|,}}{{#if: {{{2{{{3|}}}|}}}| and}}{{#if: {{{2|}}}| [[{{{2|}}}]]}}{{#if: {{{3|}}}|,}}{{#if: {{{3{{{4|}}}|}}}| and}}{{#if: {{{3|}}}| [[{{{3|}}}]]}}{{#if: {{{4|}}}|,}}{{#if: {{{4{{{5|}}}|}}}| and}}{{#if: {{{4|}}}| [[{{{4|}}}]]}}{{#if: {{{5|}}}|,}}{{#if: {{{5{{{6|}}}|}}}| and}}{{#if: {{{5|}}}| [[{{{5|}}}]]}}{{#if: {{{6|}}}|,}}{{#if: {{{6{{{7|}}}|}}}| and}}{{#if: {{{6|}}}| [[{{{6|}}}]]}}{{#if: {{{7|}}}|,}}{{#if: {{{7{{{8|}}}|}}}| and}}{{#if: {{{7|}}}| [[{{{7|}}}]]}}{{#if: {{{8|}}}|,}}{{#if: {{{8{{{9|}}}|}}}| and}}{{#if: {{{8|}}}| [[{{{8|}}}]]}}{{#if: {{{9|}}}|, and [[{{{9|}}}]]}}}}<noinclude>
{{Documentation}}
<!-- Add categories and interwikis to the /doc subpage, not here! -->
</noinclude>
46rksomuys81au89fv2rzurfki959iq
વિભાગ:Coordinates
828
48446
886390
832411
2024-05-31T00:22:22Z
en>Pppery
0
Switch to template gadget category per request
886390
Scribunto
text/plain
--[[
This module is intended to replace the functionality of {{Coord}} and related
templates. It provides several methods, including
{{#invoke:Coordinates | coord }} : General function formatting and displaying
coordinate values.
{{#invoke:Coordinates | dec2dms }} : Simple function for converting decimal
degree values to DMS format.
{{#invoke:Coordinates | dms2dec }} : Simple function for converting DMS format
to decimal degree format.
{{#invoke:Coordinates | link }} : Export the link used to reach the tools
]]
require('strict')
local math_mod = require("Module:Math")
local coordinates = {};
local isSandbox = mw.getCurrentFrame():getTitle():find('sandbox', 1, true);
local current_page = mw.title.getCurrentTitle()
local page_name = mw.uri.encode( current_page.prefixedText, 'WIKI' );
local coord_link = 'https://geohack.toolforge.org/geohack.php?pagename=' .. page_name .. '¶ms='
--[[ Helper function, replacement for {{coord/display/title}} ]]
local function displaytitle(coords)
return mw.getCurrentFrame():extensionTag{
name = 'indicator',
args = { name = 'coordinates' },
content = '<span id="coordinates">[[Geographic coordinate system|Coordinates]]: ' .. coords .. '</span>'
}
end
--[[ Helper function, used in detecting DMS formatting ]]
local function dmsTest(first, second)
if type(first) ~= 'string' or type(second) ~= 'string' then
return nil
end
local s = (first .. second):upper()
return s:find('^[NS][EW]$') or s:find('^[EW][NS]$')
end
--[[ Wrapper function to grab args, see Module:Arguments for this function's documentation. ]]
local function makeInvokeFunc(funcName)
return function (frame)
local args = require('Module:Arguments').getArgs(frame, {
wrappers = 'Template:Coord'
})
return coordinates[funcName](args, frame)
end
end
--[[ Helper function, handle optional args. ]]
local function optionalArg(arg, supplement)
return arg and arg .. supplement or ''
end
--[[
Formats any error messages generated for display
]]
local function errorPrinter(errors)
local result = ""
for i,v in ipairs(errors) do
result = result .. '<strong class="error">Coordinates: ' .. v[2] .. '</strong><br />'
end
return result
end
--[[
Determine the required CSS class to display coordinates
Usually geo-nondefault is hidden by CSS, unless a user has overridden this for himself
default is the mode as specificied by the user when calling the {{coord}} template
mode is the display mode (dec or dms) that we will need to determine the css class for
]]
local function displayDefault(default, mode)
if default == "" then
default = "dec"
end
if default == mode then
return "geo-default"
else
return "geo-nondefault"
end
end
--[[
specPrinter
Output formatter. Takes the structure generated by either parseDec
or parseDMS and formats it for inclusion on Wikipedia.
]]
local function specPrinter(args, coordinateSpec)
local uriComponents = coordinateSpec["param"]
if uriComponents == "" then
-- RETURN error, should never be empty or nil
return "ERROR param was empty"
end
if args["name"] then
uriComponents = uriComponents .. "&title=" .. mw.uri.encode(coordinateSpec["name"])
end
local geodmshtml = '<span class="geo-dms" title="Maps, aerial photos, and other data for this location">'
.. '<span class="latitude">' .. coordinateSpec["dms-lat"] .. '</span> '
.. '<span class="longitude">' ..coordinateSpec["dms-long"] .. '</span>'
.. '</span>'
local lat = tonumber( coordinateSpec["dec-lat"] ) or 0
local geodeclat
if lat < 0 then
-- FIXME this breaks the pre-existing precision
geodeclat = tostring(coordinateSpec["dec-lat"]):sub(2) .. "°S"
else
geodeclat = (coordinateSpec["dec-lat"] or 0) .. "°N"
end
local long = tonumber( coordinateSpec["dec-long"] ) or 0
local geodeclong
if long < 0 then
-- FIXME does not handle unicode minus
geodeclong = tostring(coordinateSpec["dec-long"]):sub(2) .. "°W"
else
geodeclong = (coordinateSpec["dec-long"] or 0) .. "°E"
end
local geodechtml = '<span class="geo-dec" title="Maps, aerial photos, and other data for this location">'
.. geodeclat .. ' '
.. geodeclong
.. '</span>'
local geonumhtml = '<span class="geo">'
.. coordinateSpec["dec-lat"] .. '; '
.. coordinateSpec["dec-long"]
.. '</span>'
local inner = '<span class="' .. displayDefault(coordinateSpec["default"], "dms" ) .. '">' .. geodmshtml .. '</span>'
.. '<span class="geo-multi-punct"> / </span>'
.. '<span class="' .. displayDefault(coordinateSpec["default"], "dec" ) .. '">';
if not args["name"] then
inner = inner .. geodechtml
.. '<span style="display:none"> / ' .. geonumhtml .. '</span></span>'
else
inner = inner .. '<span class="vcard">' .. geodechtml
.. '<span style="display:none"> / ' .. geonumhtml .. '</span>'
.. '<span style="display:none"> (<span class="fn org">'
.. args["name"] .. '</span>)</span></span></span>'
end
local stylesheetLink = 'Module:Coordinates' .. ( isSandbox and '/sandbox' or '' ) .. '/styles.css'
return mw.getCurrentFrame():extensionTag{
name = 'templatestyles', args = { src = stylesheetLink }
} .. '<span class="plainlinks nourlexpansion">[' .. coord_link .. uriComponents ..
' ' .. inner .. ']</span>' .. '[[Category:Pages using gadget WikiMiniAtlas]]'
end
--[[ Helper function, convert decimal to degrees ]]
local function convert_dec2dms_d(coordinate)
local d = math_mod._round( coordinate, 0 ) .. "°"
return d .. ""
end
--[[ Helper function, convert decimal to degrees and minutes ]]
local function convert_dec2dms_dm(coordinate)
coordinate = math_mod._round( coordinate * 60, 0 );
local m = coordinate % 60;
coordinate = math.floor( (coordinate - m) / 60 );
local d = coordinate % 360 .."°"
return d .. string.format( "%02d′", m )
end
--[[ Helper function, convert decimal to degrees, minutes, and seconds ]]
local function convert_dec2dms_dms(coordinate)
coordinate = math_mod._round( coordinate * 60 * 60, 0 );
local s = coordinate % 60
coordinate = math.floor( (coordinate - s) / 60 );
local m = coordinate % 60
coordinate = math.floor( (coordinate - m) / 60 );
local d = coordinate % 360 .."°"
return d .. string.format( "%02d′", m ) .. string.format( "%02d″", s )
end
--[[
Helper function, convert decimal latitude or longitude to
degrees, minutes, and seconds format based on the specified precision.
]]
local function convert_dec2dms(coordinate, firstPostfix, secondPostfix, precision)
local coord = tonumber(coordinate)
local postfix
if coord >= 0 then
postfix = firstPostfix
else
postfix = secondPostfix
end
precision = precision:lower();
if precision == "dms" then
return convert_dec2dms_dms( math.abs( coord ) ) .. postfix;
elseif precision == "dm" then
return convert_dec2dms_dm( math.abs( coord ) ) .. postfix;
elseif precision == "d" then
return convert_dec2dms_d( math.abs( coord ) ) .. postfix;
end
end
--[[
Convert DMS format into a N or E decimal coordinate
]]
local function convert_dms2dec(direction, degrees_str, minutes_str, seconds_str)
local degrees = tonumber(degrees_str)
local minutes = tonumber(minutes_str) or 0
local seconds = tonumber(seconds_str) or 0
local factor = 1
if direction == "S" or direction == "W" then
factor = -1
end
local precision = 0
if seconds_str then
precision = 5 + math.max( math_mod._precision(seconds_str), 0 );
elseif minutes_str and minutes_str ~= '' then
precision = 3 + math.max( math_mod._precision(minutes_str), 0 );
else
precision = math.max( math_mod._precision(degrees_str), 0 );
end
local decimal = factor * (degrees+(minutes+seconds/60)/60)
return string.format( "%." .. precision .. "f", decimal ) -- not tonumber since this whole thing is string based.
end
--[[
Checks input values to for out of range errors.
]]
local function validate( lat_d, lat_m, lat_s, long_d, long_m, long_s, source, strong )
local errors = {};
lat_d = tonumber( lat_d ) or 0;
lat_m = tonumber( lat_m ) or 0;
lat_s = tonumber( lat_s ) or 0;
long_d = tonumber( long_d ) or 0;
long_m = tonumber( long_m ) or 0;
long_s = tonumber( long_s ) or 0;
if strong then
if lat_d < 0 then
table.insert(errors, {source, "latitude degrees < 0 with hemisphere flag"})
end
if long_d < 0 then
table.insert(errors, {source, "longitude degrees < 0 with hemisphere flag"})
end
--[[
#coordinates is inconsistent about whether this is an error. If globe: is
specified, it won't error on this condition, but otherwise it will.
For not simply disable this check.
if long_d > 180 then
table.insert(errors, {source, "longitude degrees > 180 with hemisphere flag"})
end
]]
end
if lat_d > 90 then
table.insert(errors, {source, "latitude degrees > 90"})
end
if lat_d < -90 then
table.insert(errors, {source, "latitude degrees < -90"})
end
if lat_m >= 60 then
table.insert(errors, {source, "latitude minutes >= 60"})
end
if lat_m < 0 then
table.insert(errors, {source, "latitude minutes < 0"})
end
if lat_s >= 60 then
table.insert(errors, {source, "latitude seconds >= 60"})
end
if lat_s < 0 then
table.insert(errors, {source, "latitude seconds < 0"})
end
if long_d >= 360 then
table.insert(errors, {source, "longitude degrees >= 360"})
end
if long_d <= -360 then
table.insert(errors, {source, "longitude degrees <= -360"})
end
if long_m >= 60 then
table.insert(errors, {source, "longitude minutes >= 60"})
end
if long_m < 0 then
table.insert(errors, {source, "longitude minutes < 0"})
end
if long_s >= 60 then
table.insert(errors, {source, "longitude seconds >= 60"})
end
if long_s < 0 then
table.insert(errors, {source, "longitude seconds < 0"})
end
return errors;
end
--[[
parseDec
Transforms decimal format latitude and longitude into the
structure to be used in displaying coordinates
]]
local function parseDec( lat, long, format )
local coordinateSpec = {}
local errors = {}
if not long then
return nil, {{"parseDec", "Missing longitude"}}
elseif not tonumber(long) then
return nil, {{"parseDec", "Longitude could not be parsed as a number: " .. long}}
end
errors = validate( lat, nil, nil, long, nil, nil, 'parseDec', false );
coordinateSpec["dec-lat"] = lat;
coordinateSpec["dec-long"] = long;
local mode = coordinates.determineMode( lat, long );
coordinateSpec["dms-lat"] = convert_dec2dms( lat, "N", "S", mode) -- {{coord/dec2dms|{{{1}}}|N|S|{{coord/prec dec|{{{1}}}|{{{2}}}}}}}
coordinateSpec["dms-long"] = convert_dec2dms( long, "E", "W", mode) -- {{coord/dec2dms|{{{2}}}|E|W|{{coord/prec dec|{{{1}}}|{{{2}}}}}}}
if format then
coordinateSpec.default = format
else
coordinateSpec.default = "dec"
end
return coordinateSpec, errors
end
--[[
parseDMS
Transforms degrees, minutes, seconds format latitude and longitude
into the a structure to be used in displaying coordinates
]]
local function parseDMS( lat_d, lat_m, lat_s, lat_f, long_d, long_m, long_s, long_f, format )
local coordinateSpec, errors, backward = {}, {}
lat_f = lat_f:upper();
long_f = long_f:upper();
-- Check if specified backward
if lat_f == 'E' or lat_f == 'W' then
lat_d, long_d, lat_m, long_m, lat_s, long_s, lat_f, long_f, backward = long_d, lat_d, long_m, lat_m, long_s, lat_s, long_f, lat_f, true;
end
errors = validate( lat_d, lat_m, lat_s, long_d, long_m, long_s, 'parseDMS', true );
if not long_d then
return nil, {{"parseDMS", "Missing longitude" }}
elseif not tonumber(long_d) then
return nil, {{"parseDMS", "Longitude could not be parsed as a number:" .. long_d }}
end
if not lat_m and not lat_s and not long_m and not long_s and #errors == 0 then
if math_mod._precision( lat_d ) > 0 or math_mod._precision( long_d ) > 0 then
if lat_f:upper() == 'S' then
lat_d = '-' .. lat_d;
end
if long_f:upper() == 'W' then
long_d = '-' .. long_d;
end
return parseDec( lat_d, long_d, format );
end
end
coordinateSpec["dms-lat"] = lat_d.."°"..optionalArg(lat_m,"′") .. optionalArg(lat_s,"″") .. lat_f
coordinateSpec["dms-long"] = long_d.."°"..optionalArg(long_m,"′") .. optionalArg(long_s,"″") .. long_f
coordinateSpec["dec-lat"] = convert_dms2dec(lat_f, lat_d, lat_m, lat_s) -- {{coord/dms2dec|{{{4}}}|{{{1}}}|0{{{2}}}|0{{{3}}}}}
coordinateSpec["dec-long"] = convert_dms2dec(long_f, long_d, long_m, long_s) -- {{coord/dms2dec|{{{8}}}|{{{5}}}|0{{{6}}}|0{{{7}}}}}
if format then
coordinateSpec.default = format
else
coordinateSpec.default = "dms"
end
return coordinateSpec, errors, backward
end
--[[
Check the input arguments for coord to determine the kind of data being provided
and then make the necessary processing.
]]
local function formatTest(args)
local result, errors
local backward, primary = false, false
local function getParam(args, lim)
local ret = {}
for i = 1, lim do
ret[i] = args[i] or ''
end
return table.concat(ret, '_')
end
if not args[1] then
-- no lat logic
return errorPrinter( {{"formatTest", "Missing latitude"}} )
elseif not tonumber(args[1]) then
-- bad lat logic
return errorPrinter( {{"formatTest", "Unable to parse latitude as a number:" .. args[1]}} )
elseif not args[4] and not args[5] and not args[6] then
-- dec logic
result, errors = parseDec(args[1], args[2], args.format)
if not result then
return errorPrinter(errors);
end
-- formatting for geohack: geohack expects D_N_D_E notation or D;D notation
-- wikiminiatlas doesn't support D;D notation
-- #coordinates parserfunction doesn't support negative decimals with NSWE
result.param = table.concat({
math.abs(tonumber(args[1])),
((tonumber(args[1]) or 0) < 0) and 'S' or 'N',
math.abs(tonumber(args[2])),
((tonumber(args[2]) or 0) < 0) and 'W' or 'E',
args[3] or ''}, '_')
elseif dmsTest(args[4], args[8]) then
-- dms logic
result, errors, backward = parseDMS(args[1], args[2], args[3], args[4],
args[5], args[6], args[7], args[8], args.format)
if args[10] then
table.insert(errors, {'formatTest', 'Extra unexpected parameters'})
end
if not result then
return errorPrinter(errors)
end
result.param = getParam(args, 9)
elseif dmsTest(args[3], args[6]) then
-- dm logic
result, errors, backward = parseDMS(args[1], args[2], nil, args[3],
args[4], args[5], nil, args[6], args['format'])
if args[8] then
table.insert(errors, {'formatTest', 'Extra unexpected parameters'})
end
if not result then
return errorPrinter(errors)
end
result.param = getParam(args, 7)
elseif dmsTest(args[2], args[4]) then
-- d logic
result, errors, backward = parseDMS(args[1], nil, nil, args[2],
args[3], nil, nil, args[4], args.format)
if args[6] then
table.insert(errors, {'formatTest', 'Extra unexpected parameters'})
end
if not result then
return errorPrinter(errors)
end
result.param = getParam(args, 5)
else
-- Error
return errorPrinter({{"formatTest", "Unknown argument format"}}) .. '[[Category:Pages with malformed coordinate tags]]'
end
result.name = args.name
local extra_param = {'dim', 'globe', 'scale', 'region', 'source', 'type'}
for _, v in ipairs(extra_param) do
if args[v] then
table.insert(errors, {'formatTest', 'Parameter: "' .. v .. '=" should be "' .. v .. ':"' })
end
end
local ret = specPrinter(args, result)
if #errors > 0 then
ret = ret .. ' ' .. errorPrinter(errors) .. '[[Category:Pages with malformed coordinate tags]]'
end
return ret, backward
end
--[[
Generate Wikidata tracking categories.
]]
local function makeWikidataCategories(qid)
local ret
local qid = qid or mw.wikibase.getEntityIdForCurrentPage()
if mw.wikibase and current_page.namespace == 0 then
if qid and mw.wikibase.entityExists(qid) and mw.wikibase.getBestStatements(qid, "P625") and mw.wikibase.getBestStatements(qid, "P625")[1] then
local snaktype = mw.wikibase.getBestStatements(qid, "P625")[1].mainsnak.snaktype
if snaktype == 'value' then
-- coordinates exist both here and on Wikidata, and can be compared.
ret = 'Coordinates on Wikidata'
elseif snaktype == 'somevalue' then
ret = 'Coordinates on Wikidata set to unknown value'
elseif snaktype == 'novalue' then
ret = 'Coordinates on Wikidata set to no value'
end
else
-- We have to either import the coordinates to Wikidata or remove them here.
ret = 'Coordinates not on Wikidata'
end
end
if ret then
return string.format('[[Category:%s]]', ret)
else
return ''
end
end
--[[
link
Simple function to export the coordinates link for other uses.
Usage:
{{#invoke:Coordinates | link }}
]]
function coordinates.link(frame)
return coord_link;
end
--[[
dec2dms
Wrapper to allow templates to call dec2dms directly.
Usage:
{{#invoke:Coordinates | dec2dms | decimal_coordinate | positive_suffix |
negative_suffix | precision }}
decimal_coordinate is converted to DMS format. If positive, the positive_suffix
is appended (typical N or E), if negative, the negative suffix is appended. The
specified precision is one of 'D', 'DM', or 'DMS' to specify the level of detail
to use.
]]
coordinates.dec2dms = makeInvokeFunc('_dec2dms')
function coordinates._dec2dms(args)
local coordinate = args[1]
local firstPostfix = args[2] or ''
local secondPostfix = args[3] or ''
local precision = args[4] or ''
return convert_dec2dms(coordinate, firstPostfix, secondPostfix, precision)
end
--[[
Helper function to determine whether to use D, DM, or DMS
format depending on the precision of the decimal input.
]]
function coordinates.determineMode( value1, value2 )
local precision = math.max( math_mod._precision( value1 ), math_mod._precision( value2 ) );
if precision <= 0 then
return 'd'
elseif precision <= 2 then
return 'dm';
else
return 'dms';
end
end
--[[
dms2dec
Wrapper to allow templates to call dms2dec directly.
Usage:
{{#invoke:Coordinates | dms2dec | direction_flag | degrees |
minutes | seconds }}
Converts DMS values specified as degrees, minutes, seconds too decimal format.
direction_flag is one of N, S, E, W, and determines whether the output is
positive (i.e. N and E) or negative (i.e. S and W).
]]
coordinates.dms2dec = makeInvokeFunc('_dms2dec')
function coordinates._dms2dec(args)
local direction = args[1]
local degrees = args[2]
local minutes = args[3]
local seconds = args[4]
return convert_dms2dec(direction, degrees, minutes, seconds)
end
--[[
coord
Main entry point for Lua function to replace {{coord}}
Usage:
{{#invoke:Coordinates | coord }}
{{#invoke:Coordinates | coord | lat | long }}
{{#invoke:Coordinates | coord | lat | lat_flag | long | long_flag }}
...
Refer to {{coord}} documentation page for many additional parameters and
configuration options.
Note: This function provides the visual display elements of {{coord}}. In
order to load coordinates into the database, the {{#coordinates:}} parser
function must also be called, this is done automatically in the Lua
version of {{coord}}.
]]
coordinates.coord = makeInvokeFunc('_coord')
function coordinates._coord(args)
if not tonumber(args[1]) and not args[2] then
args[3] = args[1]; args[1] = nil
local entity = mw.wikibase.getEntityObject(args.qid)
if entity
and entity.claims
and entity.claims.P625
and entity.claims.P625[1].mainsnak.snaktype == 'value'
then
local precision = entity.claims.P625[1].mainsnak.datavalue.value.precision
args[1] = entity.claims.P625[1].mainsnak.datavalue.value.latitude
args[2] = entity.claims.P625[1].mainsnak.datavalue.value.longitude
if precision then
precision = -math_mod._round(math.log(precision)/math.log(10),0)
args[1] = math_mod._round(args[1],precision)
args[2] = math_mod._round(args[2],precision)
end
end
end
local contents, backward = formatTest(args)
local Notes = args.notes or ''
local Display = args.display and args.display:lower() or 'inline'
-- it and ti are short for inline,title and title,inline
local function isInline(s)
-- Finds whether coordinates are displayed inline.
return s:find('inline') ~= nil or s == 'i' or s == 'it' or s == 'ti'
end
local function isInTitle(s)
-- Finds whether coordinates are displayed in the title.
return s:find('title') ~= nil or s == 't' or s == 'it' or s == 'ti'
end
local function coord_wrapper(in_args)
-- Calls the parser function {{#coordinates:}}.
return mw.getCurrentFrame():callParserFunction('#coordinates', in_args) or ''
end
local text = ''
if isInline(Display) then
text = text .. '<span class="geo-inline">' .. contents .. Notes .. '</span>'
end
if isInTitle(Display) then
-- Add to output since indicator content is invisible to Lua later on
if not isInline(Display) then
text = text .. '<span class="geo-inline-hidden noexcerpt">' .. contents .. Notes .. '</span>'
end
text = text .. displaytitle(contents .. Notes) .. makeWikidataCategories(args.qid)
end
if not args.nosave then
local page_title, count = mw.title.getCurrentTitle(), 1
if backward then
local tmp = {}
while not string.find((args[count-1] or ''), '[EW]') do tmp[count] = (args[count] or ''); count = count+1 end
tmp.count = count; count = 2*(count-1)
while count >= tmp.count do table.insert(tmp, 1, (args[count] or '')); count = count-1 end
for i, v in ipairs(tmp) do args[i] = v end
else
while count <= 9 do args[count] = (args[count] or ''); count = count+1 end
end
if isInTitle(Display) and not page_title.isTalkPage and page_title.subpageText ~= 'doc' and page_title.subpageText ~= 'testcases' then args[10] = 'primary' end
args.notes, args.format, args.display = nil
text = text .. coord_wrapper(args)
end
return text
end
--[[
coord2text
Extracts a single value from a transclusion of {{Coord}}.
IF THE GEOHACK LINK SYNTAX CHANGES THIS FUNCTION MUST BE MODIFIED.
Usage:
{{#invoke:Coordinates | coord2text | {{Coord}} | parameter }}
Valid values for the second parameter are: lat (signed integer), long (signed integer), type, scale, dim, region, globe, source
]]
function coordinates._coord2text(coord,type)
if coord == '' or type == '' or not type then return nil end
type = mw.text.trim(type)
if type == 'lat' or type == 'long' then
local result, negative = mw.text.split((mw.ustring.match(coord,'[%.%d]+°[NS] [%.%d]+°[EW]') or ''), ' ')
if type == 'lat' then
result, negative = result[1], 'S'
else
result, negative = result[2], 'W'
end
result = mw.text.split(result, '°')
if result[2] == negative then result[1] = '-'..result[1] end
return result[1]
else
return mw.ustring.match(coord, 'params=.-_' .. type .. ':(.-)[ _]')
end
end
function coordinates.coord2text(frame)
return coordinates._coord2text(frame.args[1],frame.args[2])
end
--[[
coordinsert
Injects some text into the Geohack link of a transclusion of {{Coord}} (if that text isn't already in the transclusion). Outputs the modified transclusion of {{Coord}}.
IF THE GEOHACK LINK SYNTAX CHANGES THIS FUNCTION MUST BE MODIFIED.
Usage:
{{#invoke:Coordinates | coordinsert | {{Coord}} | parameter:value | parameter:value | … }}
Do not make Geohack unhappy by inserting something which isn't mentioned in the {{Coord}} documentation.
]]
function coordinates.coordinsert(frame)
-- for the 2nd or later integer parameter (the first is the coord template, as above)
for i, v in ipairs(frame.args) do
if i ~= 1 then
-- if we cannot find in the coord_template the i_th coordinsert parameter e.g. region
if not mw.ustring.find(frame.args[1], (mw.ustring.match(frame.args[i], '^(.-:)') or '')) then
-- find from the params= up to the first possibly-present underscore
-- and append the i_th coordinsert parameter and a space
-- IDK why we're adding a space but it does seem somewhat convenient
frame.args[1] = mw.ustring.gsub(frame.args[1], '(params=.-)_? ', '%1_'..frame.args[i]..' ')
end
end
end
if frame.args.name then
-- if we can't find the vcard class
if not mw.ustring.find(frame.args[1], '<span class="vcard">') then
-- take something that looks like a coord template and add the vcard span with class and fn org class
local namestr = frame.args.name
frame.args[1] = mw.ustring.gsub(
frame.args[1],
'(<span class="geo%-default">)(<span[^<>]*>[^<>]*</span><span[^<>]*>[^<>]*<span[^<>]*>[^<>]*</span></span>)(</span>)',
'%1<span class="vcard">%2<span style="display:none"> (<span class="fn org">' .. namestr .. '</span>)</span></span>%3'
)
-- then find anything from coordinates parameters to the 'end' and attach the title parameter
frame.args[1] = mw.ustring.gsub(
frame.args[1],
'(¶ms=[^&"<>%[%] ]*) ',
'%1&title=' .. mw.uri.encode(namestr) .. ' '
)
end
end
-- replace the existing indicator with a new indicator using the modified content
frame.args[1] = mw.ustring.gsub(
frame.args[1],
'(<span class="geo%-inline[^"]*">(.+)</span>)\127[^\127]*UNIQ%-%-indicator%-%x+%-%-?QINU[^\127]*\127',
function (inline, coord) return inline .. displaytitle(coord) end
)
return frame.args[1]
end
return coordinates
g0kvlzvbowr8hudeh9ex9bf1dawsj3q
886391
886390
2025-06-13T17:03:29Z
KartikMistry
10383
[[:en:Module:Coordinates]] માંથી આયાત કરેલ ૧ પુનરાવર્તન
886390
Scribunto
text/plain
--[[
This module is intended to replace the functionality of {{Coord}} and related
templates. It provides several methods, including
{{#invoke:Coordinates | coord }} : General function formatting and displaying
coordinate values.
{{#invoke:Coordinates | dec2dms }} : Simple function for converting decimal
degree values to DMS format.
{{#invoke:Coordinates | dms2dec }} : Simple function for converting DMS format
to decimal degree format.
{{#invoke:Coordinates | link }} : Export the link used to reach the tools
]]
require('strict')
local math_mod = require("Module:Math")
local coordinates = {};
local isSandbox = mw.getCurrentFrame():getTitle():find('sandbox', 1, true);
local current_page = mw.title.getCurrentTitle()
local page_name = mw.uri.encode( current_page.prefixedText, 'WIKI' );
local coord_link = 'https://geohack.toolforge.org/geohack.php?pagename=' .. page_name .. '¶ms='
--[[ Helper function, replacement for {{coord/display/title}} ]]
local function displaytitle(coords)
return mw.getCurrentFrame():extensionTag{
name = 'indicator',
args = { name = 'coordinates' },
content = '<span id="coordinates">[[Geographic coordinate system|Coordinates]]: ' .. coords .. '</span>'
}
end
--[[ Helper function, used in detecting DMS formatting ]]
local function dmsTest(first, second)
if type(first) ~= 'string' or type(second) ~= 'string' then
return nil
end
local s = (first .. second):upper()
return s:find('^[NS][EW]$') or s:find('^[EW][NS]$')
end
--[[ Wrapper function to grab args, see Module:Arguments for this function's documentation. ]]
local function makeInvokeFunc(funcName)
return function (frame)
local args = require('Module:Arguments').getArgs(frame, {
wrappers = 'Template:Coord'
})
return coordinates[funcName](args, frame)
end
end
--[[ Helper function, handle optional args. ]]
local function optionalArg(arg, supplement)
return arg and arg .. supplement or ''
end
--[[
Formats any error messages generated for display
]]
local function errorPrinter(errors)
local result = ""
for i,v in ipairs(errors) do
result = result .. '<strong class="error">Coordinates: ' .. v[2] .. '</strong><br />'
end
return result
end
--[[
Determine the required CSS class to display coordinates
Usually geo-nondefault is hidden by CSS, unless a user has overridden this for himself
default is the mode as specificied by the user when calling the {{coord}} template
mode is the display mode (dec or dms) that we will need to determine the css class for
]]
local function displayDefault(default, mode)
if default == "" then
default = "dec"
end
if default == mode then
return "geo-default"
else
return "geo-nondefault"
end
end
--[[
specPrinter
Output formatter. Takes the structure generated by either parseDec
or parseDMS and formats it for inclusion on Wikipedia.
]]
local function specPrinter(args, coordinateSpec)
local uriComponents = coordinateSpec["param"]
if uriComponents == "" then
-- RETURN error, should never be empty or nil
return "ERROR param was empty"
end
if args["name"] then
uriComponents = uriComponents .. "&title=" .. mw.uri.encode(coordinateSpec["name"])
end
local geodmshtml = '<span class="geo-dms" title="Maps, aerial photos, and other data for this location">'
.. '<span class="latitude">' .. coordinateSpec["dms-lat"] .. '</span> '
.. '<span class="longitude">' ..coordinateSpec["dms-long"] .. '</span>'
.. '</span>'
local lat = tonumber( coordinateSpec["dec-lat"] ) or 0
local geodeclat
if lat < 0 then
-- FIXME this breaks the pre-existing precision
geodeclat = tostring(coordinateSpec["dec-lat"]):sub(2) .. "°S"
else
geodeclat = (coordinateSpec["dec-lat"] or 0) .. "°N"
end
local long = tonumber( coordinateSpec["dec-long"] ) or 0
local geodeclong
if long < 0 then
-- FIXME does not handle unicode minus
geodeclong = tostring(coordinateSpec["dec-long"]):sub(2) .. "°W"
else
geodeclong = (coordinateSpec["dec-long"] or 0) .. "°E"
end
local geodechtml = '<span class="geo-dec" title="Maps, aerial photos, and other data for this location">'
.. geodeclat .. ' '
.. geodeclong
.. '</span>'
local geonumhtml = '<span class="geo">'
.. coordinateSpec["dec-lat"] .. '; '
.. coordinateSpec["dec-long"]
.. '</span>'
local inner = '<span class="' .. displayDefault(coordinateSpec["default"], "dms" ) .. '">' .. geodmshtml .. '</span>'
.. '<span class="geo-multi-punct"> / </span>'
.. '<span class="' .. displayDefault(coordinateSpec["default"], "dec" ) .. '">';
if not args["name"] then
inner = inner .. geodechtml
.. '<span style="display:none"> / ' .. geonumhtml .. '</span></span>'
else
inner = inner .. '<span class="vcard">' .. geodechtml
.. '<span style="display:none"> / ' .. geonumhtml .. '</span>'
.. '<span style="display:none"> (<span class="fn org">'
.. args["name"] .. '</span>)</span></span></span>'
end
local stylesheetLink = 'Module:Coordinates' .. ( isSandbox and '/sandbox' or '' ) .. '/styles.css'
return mw.getCurrentFrame():extensionTag{
name = 'templatestyles', args = { src = stylesheetLink }
} .. '<span class="plainlinks nourlexpansion">[' .. coord_link .. uriComponents ..
' ' .. inner .. ']</span>' .. '[[Category:Pages using gadget WikiMiniAtlas]]'
end
--[[ Helper function, convert decimal to degrees ]]
local function convert_dec2dms_d(coordinate)
local d = math_mod._round( coordinate, 0 ) .. "°"
return d .. ""
end
--[[ Helper function, convert decimal to degrees and minutes ]]
local function convert_dec2dms_dm(coordinate)
coordinate = math_mod._round( coordinate * 60, 0 );
local m = coordinate % 60;
coordinate = math.floor( (coordinate - m) / 60 );
local d = coordinate % 360 .."°"
return d .. string.format( "%02d′", m )
end
--[[ Helper function, convert decimal to degrees, minutes, and seconds ]]
local function convert_dec2dms_dms(coordinate)
coordinate = math_mod._round( coordinate * 60 * 60, 0 );
local s = coordinate % 60
coordinate = math.floor( (coordinate - s) / 60 );
local m = coordinate % 60
coordinate = math.floor( (coordinate - m) / 60 );
local d = coordinate % 360 .."°"
return d .. string.format( "%02d′", m ) .. string.format( "%02d″", s )
end
--[[
Helper function, convert decimal latitude or longitude to
degrees, minutes, and seconds format based on the specified precision.
]]
local function convert_dec2dms(coordinate, firstPostfix, secondPostfix, precision)
local coord = tonumber(coordinate)
local postfix
if coord >= 0 then
postfix = firstPostfix
else
postfix = secondPostfix
end
precision = precision:lower();
if precision == "dms" then
return convert_dec2dms_dms( math.abs( coord ) ) .. postfix;
elseif precision == "dm" then
return convert_dec2dms_dm( math.abs( coord ) ) .. postfix;
elseif precision == "d" then
return convert_dec2dms_d( math.abs( coord ) ) .. postfix;
end
end
--[[
Convert DMS format into a N or E decimal coordinate
]]
local function convert_dms2dec(direction, degrees_str, minutes_str, seconds_str)
local degrees = tonumber(degrees_str)
local minutes = tonumber(minutes_str) or 0
local seconds = tonumber(seconds_str) or 0
local factor = 1
if direction == "S" or direction == "W" then
factor = -1
end
local precision = 0
if seconds_str then
precision = 5 + math.max( math_mod._precision(seconds_str), 0 );
elseif minutes_str and minutes_str ~= '' then
precision = 3 + math.max( math_mod._precision(minutes_str), 0 );
else
precision = math.max( math_mod._precision(degrees_str), 0 );
end
local decimal = factor * (degrees+(minutes+seconds/60)/60)
return string.format( "%." .. precision .. "f", decimal ) -- not tonumber since this whole thing is string based.
end
--[[
Checks input values to for out of range errors.
]]
local function validate( lat_d, lat_m, lat_s, long_d, long_m, long_s, source, strong )
local errors = {};
lat_d = tonumber( lat_d ) or 0;
lat_m = tonumber( lat_m ) or 0;
lat_s = tonumber( lat_s ) or 0;
long_d = tonumber( long_d ) or 0;
long_m = tonumber( long_m ) or 0;
long_s = tonumber( long_s ) or 0;
if strong then
if lat_d < 0 then
table.insert(errors, {source, "latitude degrees < 0 with hemisphere flag"})
end
if long_d < 0 then
table.insert(errors, {source, "longitude degrees < 0 with hemisphere flag"})
end
--[[
#coordinates is inconsistent about whether this is an error. If globe: is
specified, it won't error on this condition, but otherwise it will.
For not simply disable this check.
if long_d > 180 then
table.insert(errors, {source, "longitude degrees > 180 with hemisphere flag"})
end
]]
end
if lat_d > 90 then
table.insert(errors, {source, "latitude degrees > 90"})
end
if lat_d < -90 then
table.insert(errors, {source, "latitude degrees < -90"})
end
if lat_m >= 60 then
table.insert(errors, {source, "latitude minutes >= 60"})
end
if lat_m < 0 then
table.insert(errors, {source, "latitude minutes < 0"})
end
if lat_s >= 60 then
table.insert(errors, {source, "latitude seconds >= 60"})
end
if lat_s < 0 then
table.insert(errors, {source, "latitude seconds < 0"})
end
if long_d >= 360 then
table.insert(errors, {source, "longitude degrees >= 360"})
end
if long_d <= -360 then
table.insert(errors, {source, "longitude degrees <= -360"})
end
if long_m >= 60 then
table.insert(errors, {source, "longitude minutes >= 60"})
end
if long_m < 0 then
table.insert(errors, {source, "longitude minutes < 0"})
end
if long_s >= 60 then
table.insert(errors, {source, "longitude seconds >= 60"})
end
if long_s < 0 then
table.insert(errors, {source, "longitude seconds < 0"})
end
return errors;
end
--[[
parseDec
Transforms decimal format latitude and longitude into the
structure to be used in displaying coordinates
]]
local function parseDec( lat, long, format )
local coordinateSpec = {}
local errors = {}
if not long then
return nil, {{"parseDec", "Missing longitude"}}
elseif not tonumber(long) then
return nil, {{"parseDec", "Longitude could not be parsed as a number: " .. long}}
end
errors = validate( lat, nil, nil, long, nil, nil, 'parseDec', false );
coordinateSpec["dec-lat"] = lat;
coordinateSpec["dec-long"] = long;
local mode = coordinates.determineMode( lat, long );
coordinateSpec["dms-lat"] = convert_dec2dms( lat, "N", "S", mode) -- {{coord/dec2dms|{{{1}}}|N|S|{{coord/prec dec|{{{1}}}|{{{2}}}}}}}
coordinateSpec["dms-long"] = convert_dec2dms( long, "E", "W", mode) -- {{coord/dec2dms|{{{2}}}|E|W|{{coord/prec dec|{{{1}}}|{{{2}}}}}}}
if format then
coordinateSpec.default = format
else
coordinateSpec.default = "dec"
end
return coordinateSpec, errors
end
--[[
parseDMS
Transforms degrees, minutes, seconds format latitude and longitude
into the a structure to be used in displaying coordinates
]]
local function parseDMS( lat_d, lat_m, lat_s, lat_f, long_d, long_m, long_s, long_f, format )
local coordinateSpec, errors, backward = {}, {}
lat_f = lat_f:upper();
long_f = long_f:upper();
-- Check if specified backward
if lat_f == 'E' or lat_f == 'W' then
lat_d, long_d, lat_m, long_m, lat_s, long_s, lat_f, long_f, backward = long_d, lat_d, long_m, lat_m, long_s, lat_s, long_f, lat_f, true;
end
errors = validate( lat_d, lat_m, lat_s, long_d, long_m, long_s, 'parseDMS', true );
if not long_d then
return nil, {{"parseDMS", "Missing longitude" }}
elseif not tonumber(long_d) then
return nil, {{"parseDMS", "Longitude could not be parsed as a number:" .. long_d }}
end
if not lat_m and not lat_s and not long_m and not long_s and #errors == 0 then
if math_mod._precision( lat_d ) > 0 or math_mod._precision( long_d ) > 0 then
if lat_f:upper() == 'S' then
lat_d = '-' .. lat_d;
end
if long_f:upper() == 'W' then
long_d = '-' .. long_d;
end
return parseDec( lat_d, long_d, format );
end
end
coordinateSpec["dms-lat"] = lat_d.."°"..optionalArg(lat_m,"′") .. optionalArg(lat_s,"″") .. lat_f
coordinateSpec["dms-long"] = long_d.."°"..optionalArg(long_m,"′") .. optionalArg(long_s,"″") .. long_f
coordinateSpec["dec-lat"] = convert_dms2dec(lat_f, lat_d, lat_m, lat_s) -- {{coord/dms2dec|{{{4}}}|{{{1}}}|0{{{2}}}|0{{{3}}}}}
coordinateSpec["dec-long"] = convert_dms2dec(long_f, long_d, long_m, long_s) -- {{coord/dms2dec|{{{8}}}|{{{5}}}|0{{{6}}}|0{{{7}}}}}
if format then
coordinateSpec.default = format
else
coordinateSpec.default = "dms"
end
return coordinateSpec, errors, backward
end
--[[
Check the input arguments for coord to determine the kind of data being provided
and then make the necessary processing.
]]
local function formatTest(args)
local result, errors
local backward, primary = false, false
local function getParam(args, lim)
local ret = {}
for i = 1, lim do
ret[i] = args[i] or ''
end
return table.concat(ret, '_')
end
if not args[1] then
-- no lat logic
return errorPrinter( {{"formatTest", "Missing latitude"}} )
elseif not tonumber(args[1]) then
-- bad lat logic
return errorPrinter( {{"formatTest", "Unable to parse latitude as a number:" .. args[1]}} )
elseif not args[4] and not args[5] and not args[6] then
-- dec logic
result, errors = parseDec(args[1], args[2], args.format)
if not result then
return errorPrinter(errors);
end
-- formatting for geohack: geohack expects D_N_D_E notation or D;D notation
-- wikiminiatlas doesn't support D;D notation
-- #coordinates parserfunction doesn't support negative decimals with NSWE
result.param = table.concat({
math.abs(tonumber(args[1])),
((tonumber(args[1]) or 0) < 0) and 'S' or 'N',
math.abs(tonumber(args[2])),
((tonumber(args[2]) or 0) < 0) and 'W' or 'E',
args[3] or ''}, '_')
elseif dmsTest(args[4], args[8]) then
-- dms logic
result, errors, backward = parseDMS(args[1], args[2], args[3], args[4],
args[5], args[6], args[7], args[8], args.format)
if args[10] then
table.insert(errors, {'formatTest', 'Extra unexpected parameters'})
end
if not result then
return errorPrinter(errors)
end
result.param = getParam(args, 9)
elseif dmsTest(args[3], args[6]) then
-- dm logic
result, errors, backward = parseDMS(args[1], args[2], nil, args[3],
args[4], args[5], nil, args[6], args['format'])
if args[8] then
table.insert(errors, {'formatTest', 'Extra unexpected parameters'})
end
if not result then
return errorPrinter(errors)
end
result.param = getParam(args, 7)
elseif dmsTest(args[2], args[4]) then
-- d logic
result, errors, backward = parseDMS(args[1], nil, nil, args[2],
args[3], nil, nil, args[4], args.format)
if args[6] then
table.insert(errors, {'formatTest', 'Extra unexpected parameters'})
end
if not result then
return errorPrinter(errors)
end
result.param = getParam(args, 5)
else
-- Error
return errorPrinter({{"formatTest", "Unknown argument format"}}) .. '[[Category:Pages with malformed coordinate tags]]'
end
result.name = args.name
local extra_param = {'dim', 'globe', 'scale', 'region', 'source', 'type'}
for _, v in ipairs(extra_param) do
if args[v] then
table.insert(errors, {'formatTest', 'Parameter: "' .. v .. '=" should be "' .. v .. ':"' })
end
end
local ret = specPrinter(args, result)
if #errors > 0 then
ret = ret .. ' ' .. errorPrinter(errors) .. '[[Category:Pages with malformed coordinate tags]]'
end
return ret, backward
end
--[[
Generate Wikidata tracking categories.
]]
local function makeWikidataCategories(qid)
local ret
local qid = qid or mw.wikibase.getEntityIdForCurrentPage()
if mw.wikibase and current_page.namespace == 0 then
if qid and mw.wikibase.entityExists(qid) and mw.wikibase.getBestStatements(qid, "P625") and mw.wikibase.getBestStatements(qid, "P625")[1] then
local snaktype = mw.wikibase.getBestStatements(qid, "P625")[1].mainsnak.snaktype
if snaktype == 'value' then
-- coordinates exist both here and on Wikidata, and can be compared.
ret = 'Coordinates on Wikidata'
elseif snaktype == 'somevalue' then
ret = 'Coordinates on Wikidata set to unknown value'
elseif snaktype == 'novalue' then
ret = 'Coordinates on Wikidata set to no value'
end
else
-- We have to either import the coordinates to Wikidata or remove them here.
ret = 'Coordinates not on Wikidata'
end
end
if ret then
return string.format('[[Category:%s]]', ret)
else
return ''
end
end
--[[
link
Simple function to export the coordinates link for other uses.
Usage:
{{#invoke:Coordinates | link }}
]]
function coordinates.link(frame)
return coord_link;
end
--[[
dec2dms
Wrapper to allow templates to call dec2dms directly.
Usage:
{{#invoke:Coordinates | dec2dms | decimal_coordinate | positive_suffix |
negative_suffix | precision }}
decimal_coordinate is converted to DMS format. If positive, the positive_suffix
is appended (typical N or E), if negative, the negative suffix is appended. The
specified precision is one of 'D', 'DM', or 'DMS' to specify the level of detail
to use.
]]
coordinates.dec2dms = makeInvokeFunc('_dec2dms')
function coordinates._dec2dms(args)
local coordinate = args[1]
local firstPostfix = args[2] or ''
local secondPostfix = args[3] or ''
local precision = args[4] or ''
return convert_dec2dms(coordinate, firstPostfix, secondPostfix, precision)
end
--[[
Helper function to determine whether to use D, DM, or DMS
format depending on the precision of the decimal input.
]]
function coordinates.determineMode( value1, value2 )
local precision = math.max( math_mod._precision( value1 ), math_mod._precision( value2 ) );
if precision <= 0 then
return 'd'
elseif precision <= 2 then
return 'dm';
else
return 'dms';
end
end
--[[
dms2dec
Wrapper to allow templates to call dms2dec directly.
Usage:
{{#invoke:Coordinates | dms2dec | direction_flag | degrees |
minutes | seconds }}
Converts DMS values specified as degrees, minutes, seconds too decimal format.
direction_flag is one of N, S, E, W, and determines whether the output is
positive (i.e. N and E) or negative (i.e. S and W).
]]
coordinates.dms2dec = makeInvokeFunc('_dms2dec')
function coordinates._dms2dec(args)
local direction = args[1]
local degrees = args[2]
local minutes = args[3]
local seconds = args[4]
return convert_dms2dec(direction, degrees, minutes, seconds)
end
--[[
coord
Main entry point for Lua function to replace {{coord}}
Usage:
{{#invoke:Coordinates | coord }}
{{#invoke:Coordinates | coord | lat | long }}
{{#invoke:Coordinates | coord | lat | lat_flag | long | long_flag }}
...
Refer to {{coord}} documentation page for many additional parameters and
configuration options.
Note: This function provides the visual display elements of {{coord}}. In
order to load coordinates into the database, the {{#coordinates:}} parser
function must also be called, this is done automatically in the Lua
version of {{coord}}.
]]
coordinates.coord = makeInvokeFunc('_coord')
function coordinates._coord(args)
if not tonumber(args[1]) and not args[2] then
args[3] = args[1]; args[1] = nil
local entity = mw.wikibase.getEntityObject(args.qid)
if entity
and entity.claims
and entity.claims.P625
and entity.claims.P625[1].mainsnak.snaktype == 'value'
then
local precision = entity.claims.P625[1].mainsnak.datavalue.value.precision
args[1] = entity.claims.P625[1].mainsnak.datavalue.value.latitude
args[2] = entity.claims.P625[1].mainsnak.datavalue.value.longitude
if precision then
precision = -math_mod._round(math.log(precision)/math.log(10),0)
args[1] = math_mod._round(args[1],precision)
args[2] = math_mod._round(args[2],precision)
end
end
end
local contents, backward = formatTest(args)
local Notes = args.notes or ''
local Display = args.display and args.display:lower() or 'inline'
-- it and ti are short for inline,title and title,inline
local function isInline(s)
-- Finds whether coordinates are displayed inline.
return s:find('inline') ~= nil or s == 'i' or s == 'it' or s == 'ti'
end
local function isInTitle(s)
-- Finds whether coordinates are displayed in the title.
return s:find('title') ~= nil or s == 't' or s == 'it' or s == 'ti'
end
local function coord_wrapper(in_args)
-- Calls the parser function {{#coordinates:}}.
return mw.getCurrentFrame():callParserFunction('#coordinates', in_args) or ''
end
local text = ''
if isInline(Display) then
text = text .. '<span class="geo-inline">' .. contents .. Notes .. '</span>'
end
if isInTitle(Display) then
-- Add to output since indicator content is invisible to Lua later on
if not isInline(Display) then
text = text .. '<span class="geo-inline-hidden noexcerpt">' .. contents .. Notes .. '</span>'
end
text = text .. displaytitle(contents .. Notes) .. makeWikidataCategories(args.qid)
end
if not args.nosave then
local page_title, count = mw.title.getCurrentTitle(), 1
if backward then
local tmp = {}
while not string.find((args[count-1] or ''), '[EW]') do tmp[count] = (args[count] or ''); count = count+1 end
tmp.count = count; count = 2*(count-1)
while count >= tmp.count do table.insert(tmp, 1, (args[count] or '')); count = count-1 end
for i, v in ipairs(tmp) do args[i] = v end
else
while count <= 9 do args[count] = (args[count] or ''); count = count+1 end
end
if isInTitle(Display) and not page_title.isTalkPage and page_title.subpageText ~= 'doc' and page_title.subpageText ~= 'testcases' then args[10] = 'primary' end
args.notes, args.format, args.display = nil
text = text .. coord_wrapper(args)
end
return text
end
--[[
coord2text
Extracts a single value from a transclusion of {{Coord}}.
IF THE GEOHACK LINK SYNTAX CHANGES THIS FUNCTION MUST BE MODIFIED.
Usage:
{{#invoke:Coordinates | coord2text | {{Coord}} | parameter }}
Valid values for the second parameter are: lat (signed integer), long (signed integer), type, scale, dim, region, globe, source
]]
function coordinates._coord2text(coord,type)
if coord == '' or type == '' or not type then return nil end
type = mw.text.trim(type)
if type == 'lat' or type == 'long' then
local result, negative = mw.text.split((mw.ustring.match(coord,'[%.%d]+°[NS] [%.%d]+°[EW]') or ''), ' ')
if type == 'lat' then
result, negative = result[1], 'S'
else
result, negative = result[2], 'W'
end
result = mw.text.split(result, '°')
if result[2] == negative then result[1] = '-'..result[1] end
return result[1]
else
return mw.ustring.match(coord, 'params=.-_' .. type .. ':(.-)[ _]')
end
end
function coordinates.coord2text(frame)
return coordinates._coord2text(frame.args[1],frame.args[2])
end
--[[
coordinsert
Injects some text into the Geohack link of a transclusion of {{Coord}} (if that text isn't already in the transclusion). Outputs the modified transclusion of {{Coord}}.
IF THE GEOHACK LINK SYNTAX CHANGES THIS FUNCTION MUST BE MODIFIED.
Usage:
{{#invoke:Coordinates | coordinsert | {{Coord}} | parameter:value | parameter:value | … }}
Do not make Geohack unhappy by inserting something which isn't mentioned in the {{Coord}} documentation.
]]
function coordinates.coordinsert(frame)
-- for the 2nd or later integer parameter (the first is the coord template, as above)
for i, v in ipairs(frame.args) do
if i ~= 1 then
-- if we cannot find in the coord_template the i_th coordinsert parameter e.g. region
if not mw.ustring.find(frame.args[1], (mw.ustring.match(frame.args[i], '^(.-:)') or '')) then
-- find from the params= up to the first possibly-present underscore
-- and append the i_th coordinsert parameter and a space
-- IDK why we're adding a space but it does seem somewhat convenient
frame.args[1] = mw.ustring.gsub(frame.args[1], '(params=.-)_? ', '%1_'..frame.args[i]..' ')
end
end
end
if frame.args.name then
-- if we can't find the vcard class
if not mw.ustring.find(frame.args[1], '<span class="vcard">') then
-- take something that looks like a coord template and add the vcard span with class and fn org class
local namestr = frame.args.name
frame.args[1] = mw.ustring.gsub(
frame.args[1],
'(<span class="geo%-default">)(<span[^<>]*>[^<>]*</span><span[^<>]*>[^<>]*<span[^<>]*>[^<>]*</span></span>)(</span>)',
'%1<span class="vcard">%2<span style="display:none"> (<span class="fn org">' .. namestr .. '</span>)</span></span>%3'
)
-- then find anything from coordinates parameters to the 'end' and attach the title parameter
frame.args[1] = mw.ustring.gsub(
frame.args[1],
'(¶ms=[^&"<>%[%] ]*) ',
'%1&title=' .. mw.uri.encode(namestr) .. ' '
)
end
end
-- replace the existing indicator with a new indicator using the modified content
frame.args[1] = mw.ustring.gsub(
frame.args[1],
'(<span class="geo%-inline[^"]*">(.+)</span>)\127[^\127]*UNIQ%-%-indicator%-%x+%-%-?QINU[^\127]*\127',
function (inline, coord) return inline .. displaytitle(coord) end
)
return frame.args[1]
end
return coordinates
g0kvlzvbowr8hudeh9ex9bf1dawsj3q
વિભાગ:String
828
49509
886360
865021
2024-09-03T18:35:47Z
en>Trappist the monk
0
per [[Special:Permalink/1243837716#Protected_edit_request_on_3_September_2024|edit request (permalink)]];
886360
Scribunto
text/plain
--[[
This module is intended to provide access to basic string functions.
Most of the functions provided here can be invoked with named parameters,
unnamed parameters, or a mixture. If named parameters are used, Mediawiki will
automatically remove any leading or trailing whitespace from the parameter.
Depending on the intended use, it may be advantageous to either preserve or
remove such whitespace.
Global options
ignore_errors: If set to 'true' or 1, any error condition will result in
an empty string being returned rather than an error message.
error_category: If an error occurs, specifies the name of a category to
include with the error message. The default category is
[Category:Errors reported by Module String].
no_category: If set to 'true' or 1, no category will be added if an error
is generated.
Unit tests for this module are available at Module:String/tests.
]]
local str = {}
--[[
len
This function returns the length of the target string.
Usage:
{{#invoke:String|len|target_string|}}
OR
{{#invoke:String|len|s=target_string}}
Parameters
s: The string whose length to report
If invoked using named parameters, Mediawiki will automatically remove any leading or
trailing whitespace from the target string.
]]
function str.len( frame )
local new_args = str._getParameters( frame.args, {'s'} )
local s = new_args['s'] or ''
return mw.ustring.len( s )
end
--[[
sub
This function returns a substring of the target string at specified indices.
Usage:
{{#invoke:String|sub|target_string|start_index|end_index}}
OR
{{#invoke:String|sub|s=target_string|i=start_index|j=end_index}}
Parameters
s: The string to return a subset of
i: The first index of the substring to return, defaults to 1.
j: The last index of the string to return, defaults to the last character.
The first character of the string is assigned an index of 1. If either i or j
is a negative value, it is interpreted the same as selecting a character by
counting from the end of the string. Hence, a value of -1 is the same as
selecting the last character of the string.
If the requested indices are out of range for the given string, an error is
reported.
]]
function str.sub( frame )
local new_args = str._getParameters( frame.args, { 's', 'i', 'j' } )
local s = new_args['s'] or ''
local i = tonumber( new_args['i'] ) or 1
local j = tonumber( new_args['j'] ) or -1
local len = mw.ustring.len( s )
-- Convert negatives for range checking
if i < 0 then
i = len + i + 1
end
if j < 0 then
j = len + j + 1
end
if i > len or j > len or i < 1 or j < 1 then
return str._error( 'String subset index out of range' )
end
if j < i then
return str._error( 'String subset indices out of order' )
end
return mw.ustring.sub( s, i, j )
end
--[[
This function implements that features of {{str sub old}} and is kept in order
to maintain these older templates.
]]
function str.sublength( frame )
local i = tonumber( frame.args.i ) or 0
local len = tonumber( frame.args.len )
return mw.ustring.sub( frame.args.s, i + 1, len and ( i + len ) )
end
--[[
_match
This function returns a substring from the source string that matches a
specified pattern. It is exported for use in other modules
Usage:
strmatch = require("Module:String")._match
sresult = strmatch( s, pattern, start, match, plain, nomatch )
Parameters
s: The string to search
pattern: The pattern or string to find within the string
start: The index within the source string to start the search. The first
character of the string has index 1. Defaults to 1.
match: In some cases it may be possible to make multiple matches on a single
string. This specifies which match to return, where the first match is
match= 1. If a negative number is specified then a match is returned
counting from the last match. Hence match = -1 is the same as requesting
the last match. Defaults to 1.
plain: A flag indicating that the pattern should be understood as plain
text. Defaults to false.
nomatch: If no match is found, output the "nomatch" value rather than an error.
For information on constructing Lua patterns, a form of [regular expression], see:
* http://www.lua.org/manual/5.1/manual.html#5.4.1
* http://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#Patterns
* http://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#Ustring_patterns
]]
-- This sub-routine is exported for use in other modules
function str._match( s, pattern, start, match_index, plain_flag, nomatch )
if s == '' then
return str._error( 'Target string is empty' )
end
if pattern == '' then
return str._error( 'Pattern string is empty' )
end
start = tonumber(start) or 1
if math.abs(start) < 1 or math.abs(start) > mw.ustring.len( s ) then
return str._error( 'Requested start is out of range' )
end
if match_index == 0 then
return str._error( 'Match index is out of range' )
end
if plain_flag then
pattern = str._escapePattern( pattern )
end
local result
if match_index == 1 then
-- Find first match is simple case
result = mw.ustring.match( s, pattern, start )
else
if start > 1 then
s = mw.ustring.sub( s, start )
end
local iterator = mw.ustring.gmatch(s, pattern)
if match_index > 0 then
-- Forward search
for w in iterator do
match_index = match_index - 1
if match_index == 0 then
result = w
break
end
end
else
-- Reverse search
local result_table = {}
local count = 1
for w in iterator do
result_table[count] = w
count = count + 1
end
result = result_table[ count + match_index ]
end
end
if result == nil then
if nomatch == nil then
return str._error( 'Match not found' )
else
return nomatch
end
else
return result
end
end
--[[
match
This function returns a substring from the source string that matches a
specified pattern.
Usage:
{{#invoke:String|match|source_string|pattern_string|start_index|match_number|plain_flag|nomatch_output}}
OR
{{#invoke:String|match|s=source_string|pattern=pattern_string|start=start_index
|match=match_number|plain=plain_flag|nomatch=nomatch_output}}
Parameters
s: The string to search
pattern: The pattern or string to find within the string
start: The index within the source string to start the search. The first
character of the string has index 1. Defaults to 1.
match: In some cases it may be possible to make multiple matches on a single
string. This specifies which match to return, where the first match is
match= 1. If a negative number is specified then a match is returned
counting from the last match. Hence match = -1 is the same as requesting
the last match. Defaults to 1.
plain: A flag indicating that the pattern should be understood as plain
text. Defaults to false.
nomatch: If no match is found, output the "nomatch" value rather than an error.
If invoked using named parameters, Mediawiki will automatically remove any leading or
trailing whitespace from each string. In some circumstances this is desirable, in
other cases one may want to preserve the whitespace.
If the match_number or start_index are out of range for the string being queried, then
this function generates an error. An error is also generated if no match is found.
If one adds the parameter ignore_errors=true, then the error will be suppressed and
an empty string will be returned on any failure.
For information on constructing Lua patterns, a form of [regular expression], see:
* http://www.lua.org/manual/5.1/manual.html#5.4.1
* http://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#Patterns
* http://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#Ustring_patterns
]]
-- This is the entry point for #invoke:String|match
function str.match( frame )
local new_args = str._getParameters( frame.args, {'s', 'pattern', 'start', 'match', 'plain', 'nomatch'} )
local s = new_args['s'] or ''
local start = tonumber( new_args['start'] ) or 1
local plain_flag = str._getBoolean( new_args['plain'] or false )
local pattern = new_args['pattern'] or ''
local match_index = math.floor( tonumber(new_args['match']) or 1 )
local nomatch = new_args['nomatch']
return str._match( s, pattern, start, match_index, plain_flag, nomatch )
end
--[[
pos
This function returns a single character from the target string at position pos.
Usage:
{{#invoke:String|pos|target_string|index_value}}
OR
{{#invoke:String|pos|target=target_string|pos=index_value}}
Parameters
target: The string to search
pos: The index for the character to return
If invoked using named parameters, Mediawiki will automatically remove any leading or
trailing whitespace from the target string. In some circumstances this is desirable, in
other cases one may want to preserve the whitespace.
The first character has an index value of 1.
If one requests a negative value, this function will select a character by counting backwards
from the end of the string. In other words pos = -1 is the same as asking for the last character.
A requested value of zero, or a value greater than the length of the string returns an error.
]]
function str.pos( frame )
local new_args = str._getParameters( frame.args, {'target', 'pos'} )
local target_str = new_args['target'] or ''
local pos = tonumber( new_args['pos'] ) or 0
if pos == 0 or math.abs(pos) > mw.ustring.len( target_str ) then
return str._error( 'String index out of range' )
end
return mw.ustring.sub( target_str, pos, pos )
end
--[[
str_find
This function duplicates the behavior of {{str_find}}, including all of its quirks.
This is provided in order to support existing templates, but is NOT RECOMMENDED for
new code and templates. New code is recommended to use the "find" function instead.
Returns the first index in "source" that is a match to "target". Indexing is 1-based,
and the function returns -1 if the "target" string is not present in "source".
Important Note: If the "target" string is empty / missing, this function returns a
value of "1", which is generally unexpected behavior, and must be accounted for
separatetly.
]]
function str.str_find( frame )
local new_args = str._getParameters( frame.args, {'source', 'target'} )
local source_str = new_args['source'] or ''
local target_str = new_args['target'] or ''
if target_str == '' then
return 1
end
local start = mw.ustring.find( source_str, target_str, 1, true )
if start == nil then
start = -1
end
return start
end
--[[
find
This function allows one to search for a target string or pattern within another
string.
Usage:
{{#invoke:String|find|source_str|target_string|start_index|plain_flag}}
OR
{{#invoke:String|find|source=source_str|target=target_str|start=start_index|plain=plain_flag}}
Parameters
source: The string to search
target: The string or pattern to find within source
start: The index within the source string to start the search, defaults to 1
plain: Boolean flag indicating that target should be understood as plain
text and not as a Lua style regular expression, defaults to true
If invoked using named parameters, Mediawiki will automatically remove any leading or
trailing whitespace from the parameter. In some circumstances this is desirable, in
other cases one may want to preserve the whitespace.
This function returns the first index >= "start" where "target" can be found
within "source". Indices are 1-based. If "target" is not found, then this
function returns 0. If either "source" or "target" are missing / empty, this
function also returns 0.
This function should be safe for UTF-8 strings.
]]
function str.find( frame )
local new_args = str._getParameters( frame.args, {'source', 'target', 'start', 'plain' } )
local source_str = new_args['source'] or ''
local pattern = new_args['target'] or ''
local start_pos = tonumber(new_args['start']) or 1
local plain = new_args['plain'] or true
if source_str == '' or pattern == '' then
return 0
end
plain = str._getBoolean( plain )
local start = mw.ustring.find( source_str, pattern, start_pos, plain )
if start == nil then
start = 0
end
return start
end
--[[
replace
This function allows one to replace a target string or pattern within another
string.
Usage:
{{#invoke:String|replace|source_str|pattern_string|replace_string|replacement_count|plain_flag}}
OR
{{#invoke:String|replace|source=source_string|pattern=pattern_string|replace=replace_string|
count=replacement_count|plain=plain_flag}}
Parameters
source: The string to search
pattern: The string or pattern to find within source
replace: The replacement text
count: The number of occurences to replace, defaults to all.
plain: Boolean flag indicating that pattern should be understood as plain
text and not as a Lua style regular expression, defaults to true
]]
function str.replace( frame )
local new_args = str._getParameters( frame.args, {'source', 'pattern', 'replace', 'count', 'plain' } )
local source_str = new_args['source'] or ''
local pattern = new_args['pattern'] or ''
local replace = new_args['replace'] or ''
local count = tonumber( new_args['count'] )
local plain = new_args['plain'] or true
if source_str == '' or pattern == '' then
return source_str
end
plain = str._getBoolean( plain )
if plain then
pattern = str._escapePattern( pattern )
replace = string.gsub( replace, "%%", "%%%%" ) --Only need to escape replacement sequences.
end
local result
if count ~= nil then
result = mw.ustring.gsub( source_str, pattern, replace, count )
else
result = mw.ustring.gsub( source_str, pattern, replace )
end
return result
end
--[[
simple function to pipe string.rep to templates.
]]
function str.rep( frame )
local repetitions = tonumber( frame.args[2] )
if not repetitions then
return str._error( 'function rep expects a number as second parameter, received "' .. ( frame.args[2] or '' ) .. '"' )
end
return string.rep( frame.args[1] or '', repetitions )
end
--[[
escapePattern
This function escapes special characters from a Lua string pattern. See [1]
for details on how patterns work.
[1] https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#Patterns
Usage:
{{#invoke:String|escapePattern|pattern_string}}
Parameters
pattern_string: The pattern string to escape.
]]
function str.escapePattern( frame )
local pattern_str = frame.args[1]
if not pattern_str then
return str._error( 'No pattern string specified' )
end
local result = str._escapePattern( pattern_str )
return result
end
--[[
count
This function counts the number of occurrences of one string in another.
]]
function str.count(frame)
local args = str._getParameters(frame.args, {'source', 'pattern', 'plain'})
local source = args.source or ''
local pattern = args.pattern or ''
local plain = str._getBoolean(args.plain or true)
if plain then
pattern = str._escapePattern(pattern)
end
local _, count = mw.ustring.gsub(source, pattern, '')
return count
end
--[[
endswith
This function determines whether a string ends with another string.
]]
function str.endswith(frame)
local args = str._getParameters(frame.args, {'source', 'pattern'})
local source = args.source or ''
local pattern = args.pattern or ''
if pattern == '' then
-- All strings end with the empty string.
return "yes"
end
if mw.ustring.sub(source, -mw.ustring.len(pattern), -1) == pattern then
return "yes"
else
return ""
end
end
--[[
join
Join all non empty arguments together; the first argument is the separator.
Usage:
{{#invoke:String|join|sep|one|two|three}}
]]
function str.join(frame)
local args = {}
local sep
for _, v in ipairs( frame.args ) do
if sep then
if v ~= '' then
table.insert(args, v)
end
else
sep = v
end
end
return table.concat( args, sep or '' )
end
--[[
Helper function that populates the argument list given that user may need to use a mix of
named and unnamed parameters. This is relevant because named parameters are not
identical to unnamed parameters due to string trimming, and when dealing with strings
we sometimes want to either preserve or remove that whitespace depending on the application.
]]
function str._getParameters( frame_args, arg_list )
local new_args = {}
local index = 1
local value
for _, arg in ipairs( arg_list ) do
value = frame_args[arg]
if value == nil then
value = frame_args[index]
index = index + 1
end
new_args[arg] = value
end
return new_args
end
--[[
Helper function to handle error messages.
]]
function str._error( error_str )
local frame = mw.getCurrentFrame()
local error_category = frame.args.error_category or 'Errors reported by Module String'
local ignore_errors = frame.args.ignore_errors or false
local no_category = frame.args.no_category or false
if str._getBoolean(ignore_errors) then
return ''
end
local error_str = '<strong class="error">String Module Error: ' .. error_str .. '</strong>'
if error_category ~= '' and not str._getBoolean( no_category ) then
error_str = '[[Category:' .. error_category .. ']]' .. error_str
end
return error_str
end
--[[
Helper Function to interpret boolean strings
]]
function str._getBoolean( boolean_str )
local boolean_value
if type( boolean_str ) == 'string' then
boolean_str = boolean_str:lower()
if boolean_str == 'false' or boolean_str == 'no' or boolean_str == '0'
or boolean_str == '' then
boolean_value = false
else
boolean_value = true
end
elseif type( boolean_str ) == 'boolean' then
boolean_value = boolean_str
else
error( 'No boolean value found' )
end
return boolean_value
end
--[[
Helper function that escapes all pattern characters so that they will be treated
as plain text.
]]
function str._escapePattern( pattern_str )
return ( string.gsub( pattern_str, "[%(%)%.%%%+%-%*%?%[%^%$%]]", "%%%0" ) )
end
return str
j5gx8uvupr1pa2fyqbe6td6jr0hxq00
886361
886360
2025-06-13T17:03:25Z
KartikMistry
10383
[[:en:Module:String]] માંથી આયાત કરેલ ૧ પુનરાવર્તન
886360
Scribunto
text/plain
--[[
This module is intended to provide access to basic string functions.
Most of the functions provided here can be invoked with named parameters,
unnamed parameters, or a mixture. If named parameters are used, Mediawiki will
automatically remove any leading or trailing whitespace from the parameter.
Depending on the intended use, it may be advantageous to either preserve or
remove such whitespace.
Global options
ignore_errors: If set to 'true' or 1, any error condition will result in
an empty string being returned rather than an error message.
error_category: If an error occurs, specifies the name of a category to
include with the error message. The default category is
[Category:Errors reported by Module String].
no_category: If set to 'true' or 1, no category will be added if an error
is generated.
Unit tests for this module are available at Module:String/tests.
]]
local str = {}
--[[
len
This function returns the length of the target string.
Usage:
{{#invoke:String|len|target_string|}}
OR
{{#invoke:String|len|s=target_string}}
Parameters
s: The string whose length to report
If invoked using named parameters, Mediawiki will automatically remove any leading or
trailing whitespace from the target string.
]]
function str.len( frame )
local new_args = str._getParameters( frame.args, {'s'} )
local s = new_args['s'] or ''
return mw.ustring.len( s )
end
--[[
sub
This function returns a substring of the target string at specified indices.
Usage:
{{#invoke:String|sub|target_string|start_index|end_index}}
OR
{{#invoke:String|sub|s=target_string|i=start_index|j=end_index}}
Parameters
s: The string to return a subset of
i: The first index of the substring to return, defaults to 1.
j: The last index of the string to return, defaults to the last character.
The first character of the string is assigned an index of 1. If either i or j
is a negative value, it is interpreted the same as selecting a character by
counting from the end of the string. Hence, a value of -1 is the same as
selecting the last character of the string.
If the requested indices are out of range for the given string, an error is
reported.
]]
function str.sub( frame )
local new_args = str._getParameters( frame.args, { 's', 'i', 'j' } )
local s = new_args['s'] or ''
local i = tonumber( new_args['i'] ) or 1
local j = tonumber( new_args['j'] ) or -1
local len = mw.ustring.len( s )
-- Convert negatives for range checking
if i < 0 then
i = len + i + 1
end
if j < 0 then
j = len + j + 1
end
if i > len or j > len or i < 1 or j < 1 then
return str._error( 'String subset index out of range' )
end
if j < i then
return str._error( 'String subset indices out of order' )
end
return mw.ustring.sub( s, i, j )
end
--[[
This function implements that features of {{str sub old}} and is kept in order
to maintain these older templates.
]]
function str.sublength( frame )
local i = tonumber( frame.args.i ) or 0
local len = tonumber( frame.args.len )
return mw.ustring.sub( frame.args.s, i + 1, len and ( i + len ) )
end
--[[
_match
This function returns a substring from the source string that matches a
specified pattern. It is exported for use in other modules
Usage:
strmatch = require("Module:String")._match
sresult = strmatch( s, pattern, start, match, plain, nomatch )
Parameters
s: The string to search
pattern: The pattern or string to find within the string
start: The index within the source string to start the search. The first
character of the string has index 1. Defaults to 1.
match: In some cases it may be possible to make multiple matches on a single
string. This specifies which match to return, where the first match is
match= 1. If a negative number is specified then a match is returned
counting from the last match. Hence match = -1 is the same as requesting
the last match. Defaults to 1.
plain: A flag indicating that the pattern should be understood as plain
text. Defaults to false.
nomatch: If no match is found, output the "nomatch" value rather than an error.
For information on constructing Lua patterns, a form of [regular expression], see:
* http://www.lua.org/manual/5.1/manual.html#5.4.1
* http://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#Patterns
* http://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#Ustring_patterns
]]
-- This sub-routine is exported for use in other modules
function str._match( s, pattern, start, match_index, plain_flag, nomatch )
if s == '' then
return str._error( 'Target string is empty' )
end
if pattern == '' then
return str._error( 'Pattern string is empty' )
end
start = tonumber(start) or 1
if math.abs(start) < 1 or math.abs(start) > mw.ustring.len( s ) then
return str._error( 'Requested start is out of range' )
end
if match_index == 0 then
return str._error( 'Match index is out of range' )
end
if plain_flag then
pattern = str._escapePattern( pattern )
end
local result
if match_index == 1 then
-- Find first match is simple case
result = mw.ustring.match( s, pattern, start )
else
if start > 1 then
s = mw.ustring.sub( s, start )
end
local iterator = mw.ustring.gmatch(s, pattern)
if match_index > 0 then
-- Forward search
for w in iterator do
match_index = match_index - 1
if match_index == 0 then
result = w
break
end
end
else
-- Reverse search
local result_table = {}
local count = 1
for w in iterator do
result_table[count] = w
count = count + 1
end
result = result_table[ count + match_index ]
end
end
if result == nil then
if nomatch == nil then
return str._error( 'Match not found' )
else
return nomatch
end
else
return result
end
end
--[[
match
This function returns a substring from the source string that matches a
specified pattern.
Usage:
{{#invoke:String|match|source_string|pattern_string|start_index|match_number|plain_flag|nomatch_output}}
OR
{{#invoke:String|match|s=source_string|pattern=pattern_string|start=start_index
|match=match_number|plain=plain_flag|nomatch=nomatch_output}}
Parameters
s: The string to search
pattern: The pattern or string to find within the string
start: The index within the source string to start the search. The first
character of the string has index 1. Defaults to 1.
match: In some cases it may be possible to make multiple matches on a single
string. This specifies which match to return, where the first match is
match= 1. If a negative number is specified then a match is returned
counting from the last match. Hence match = -1 is the same as requesting
the last match. Defaults to 1.
plain: A flag indicating that the pattern should be understood as plain
text. Defaults to false.
nomatch: If no match is found, output the "nomatch" value rather than an error.
If invoked using named parameters, Mediawiki will automatically remove any leading or
trailing whitespace from each string. In some circumstances this is desirable, in
other cases one may want to preserve the whitespace.
If the match_number or start_index are out of range for the string being queried, then
this function generates an error. An error is also generated if no match is found.
If one adds the parameter ignore_errors=true, then the error will be suppressed and
an empty string will be returned on any failure.
For information on constructing Lua patterns, a form of [regular expression], see:
* http://www.lua.org/manual/5.1/manual.html#5.4.1
* http://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#Patterns
* http://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#Ustring_patterns
]]
-- This is the entry point for #invoke:String|match
function str.match( frame )
local new_args = str._getParameters( frame.args, {'s', 'pattern', 'start', 'match', 'plain', 'nomatch'} )
local s = new_args['s'] or ''
local start = tonumber( new_args['start'] ) or 1
local plain_flag = str._getBoolean( new_args['plain'] or false )
local pattern = new_args['pattern'] or ''
local match_index = math.floor( tonumber(new_args['match']) or 1 )
local nomatch = new_args['nomatch']
return str._match( s, pattern, start, match_index, plain_flag, nomatch )
end
--[[
pos
This function returns a single character from the target string at position pos.
Usage:
{{#invoke:String|pos|target_string|index_value}}
OR
{{#invoke:String|pos|target=target_string|pos=index_value}}
Parameters
target: The string to search
pos: The index for the character to return
If invoked using named parameters, Mediawiki will automatically remove any leading or
trailing whitespace from the target string. In some circumstances this is desirable, in
other cases one may want to preserve the whitespace.
The first character has an index value of 1.
If one requests a negative value, this function will select a character by counting backwards
from the end of the string. In other words pos = -1 is the same as asking for the last character.
A requested value of zero, or a value greater than the length of the string returns an error.
]]
function str.pos( frame )
local new_args = str._getParameters( frame.args, {'target', 'pos'} )
local target_str = new_args['target'] or ''
local pos = tonumber( new_args['pos'] ) or 0
if pos == 0 or math.abs(pos) > mw.ustring.len( target_str ) then
return str._error( 'String index out of range' )
end
return mw.ustring.sub( target_str, pos, pos )
end
--[[
str_find
This function duplicates the behavior of {{str_find}}, including all of its quirks.
This is provided in order to support existing templates, but is NOT RECOMMENDED for
new code and templates. New code is recommended to use the "find" function instead.
Returns the first index in "source" that is a match to "target". Indexing is 1-based,
and the function returns -1 if the "target" string is not present in "source".
Important Note: If the "target" string is empty / missing, this function returns a
value of "1", which is generally unexpected behavior, and must be accounted for
separatetly.
]]
function str.str_find( frame )
local new_args = str._getParameters( frame.args, {'source', 'target'} )
local source_str = new_args['source'] or ''
local target_str = new_args['target'] or ''
if target_str == '' then
return 1
end
local start = mw.ustring.find( source_str, target_str, 1, true )
if start == nil then
start = -1
end
return start
end
--[[
find
This function allows one to search for a target string or pattern within another
string.
Usage:
{{#invoke:String|find|source_str|target_string|start_index|plain_flag}}
OR
{{#invoke:String|find|source=source_str|target=target_str|start=start_index|plain=plain_flag}}
Parameters
source: The string to search
target: The string or pattern to find within source
start: The index within the source string to start the search, defaults to 1
plain: Boolean flag indicating that target should be understood as plain
text and not as a Lua style regular expression, defaults to true
If invoked using named parameters, Mediawiki will automatically remove any leading or
trailing whitespace from the parameter. In some circumstances this is desirable, in
other cases one may want to preserve the whitespace.
This function returns the first index >= "start" where "target" can be found
within "source". Indices are 1-based. If "target" is not found, then this
function returns 0. If either "source" or "target" are missing / empty, this
function also returns 0.
This function should be safe for UTF-8 strings.
]]
function str.find( frame )
local new_args = str._getParameters( frame.args, {'source', 'target', 'start', 'plain' } )
local source_str = new_args['source'] or ''
local pattern = new_args['target'] or ''
local start_pos = tonumber(new_args['start']) or 1
local plain = new_args['plain'] or true
if source_str == '' or pattern == '' then
return 0
end
plain = str._getBoolean( plain )
local start = mw.ustring.find( source_str, pattern, start_pos, plain )
if start == nil then
start = 0
end
return start
end
--[[
replace
This function allows one to replace a target string or pattern within another
string.
Usage:
{{#invoke:String|replace|source_str|pattern_string|replace_string|replacement_count|plain_flag}}
OR
{{#invoke:String|replace|source=source_string|pattern=pattern_string|replace=replace_string|
count=replacement_count|plain=plain_flag}}
Parameters
source: The string to search
pattern: The string or pattern to find within source
replace: The replacement text
count: The number of occurences to replace, defaults to all.
plain: Boolean flag indicating that pattern should be understood as plain
text and not as a Lua style regular expression, defaults to true
]]
function str.replace( frame )
local new_args = str._getParameters( frame.args, {'source', 'pattern', 'replace', 'count', 'plain' } )
local source_str = new_args['source'] or ''
local pattern = new_args['pattern'] or ''
local replace = new_args['replace'] or ''
local count = tonumber( new_args['count'] )
local plain = new_args['plain'] or true
if source_str == '' or pattern == '' then
return source_str
end
plain = str._getBoolean( plain )
if plain then
pattern = str._escapePattern( pattern )
replace = string.gsub( replace, "%%", "%%%%" ) --Only need to escape replacement sequences.
end
local result
if count ~= nil then
result = mw.ustring.gsub( source_str, pattern, replace, count )
else
result = mw.ustring.gsub( source_str, pattern, replace )
end
return result
end
--[[
simple function to pipe string.rep to templates.
]]
function str.rep( frame )
local repetitions = tonumber( frame.args[2] )
if not repetitions then
return str._error( 'function rep expects a number as second parameter, received "' .. ( frame.args[2] or '' ) .. '"' )
end
return string.rep( frame.args[1] or '', repetitions )
end
--[[
escapePattern
This function escapes special characters from a Lua string pattern. See [1]
for details on how patterns work.
[1] https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#Patterns
Usage:
{{#invoke:String|escapePattern|pattern_string}}
Parameters
pattern_string: The pattern string to escape.
]]
function str.escapePattern( frame )
local pattern_str = frame.args[1]
if not pattern_str then
return str._error( 'No pattern string specified' )
end
local result = str._escapePattern( pattern_str )
return result
end
--[[
count
This function counts the number of occurrences of one string in another.
]]
function str.count(frame)
local args = str._getParameters(frame.args, {'source', 'pattern', 'plain'})
local source = args.source or ''
local pattern = args.pattern or ''
local plain = str._getBoolean(args.plain or true)
if plain then
pattern = str._escapePattern(pattern)
end
local _, count = mw.ustring.gsub(source, pattern, '')
return count
end
--[[
endswith
This function determines whether a string ends with another string.
]]
function str.endswith(frame)
local args = str._getParameters(frame.args, {'source', 'pattern'})
local source = args.source or ''
local pattern = args.pattern or ''
if pattern == '' then
-- All strings end with the empty string.
return "yes"
end
if mw.ustring.sub(source, -mw.ustring.len(pattern), -1) == pattern then
return "yes"
else
return ""
end
end
--[[
join
Join all non empty arguments together; the first argument is the separator.
Usage:
{{#invoke:String|join|sep|one|two|three}}
]]
function str.join(frame)
local args = {}
local sep
for _, v in ipairs( frame.args ) do
if sep then
if v ~= '' then
table.insert(args, v)
end
else
sep = v
end
end
return table.concat( args, sep or '' )
end
--[[
Helper function that populates the argument list given that user may need to use a mix of
named and unnamed parameters. This is relevant because named parameters are not
identical to unnamed parameters due to string trimming, and when dealing with strings
we sometimes want to either preserve or remove that whitespace depending on the application.
]]
function str._getParameters( frame_args, arg_list )
local new_args = {}
local index = 1
local value
for _, arg in ipairs( arg_list ) do
value = frame_args[arg]
if value == nil then
value = frame_args[index]
index = index + 1
end
new_args[arg] = value
end
return new_args
end
--[[
Helper function to handle error messages.
]]
function str._error( error_str )
local frame = mw.getCurrentFrame()
local error_category = frame.args.error_category or 'Errors reported by Module String'
local ignore_errors = frame.args.ignore_errors or false
local no_category = frame.args.no_category or false
if str._getBoolean(ignore_errors) then
return ''
end
local error_str = '<strong class="error">String Module Error: ' .. error_str .. '</strong>'
if error_category ~= '' and not str._getBoolean( no_category ) then
error_str = '[[Category:' .. error_category .. ']]' .. error_str
end
return error_str
end
--[[
Helper Function to interpret boolean strings
]]
function str._getBoolean( boolean_str )
local boolean_value
if type( boolean_str ) == 'string' then
boolean_str = boolean_str:lower()
if boolean_str == 'false' or boolean_str == 'no' or boolean_str == '0'
or boolean_str == '' then
boolean_value = false
else
boolean_value = true
end
elseif type( boolean_str ) == 'boolean' then
boolean_value = boolean_str
else
error( 'No boolean value found' )
end
return boolean_value
end
--[[
Helper function that escapes all pattern characters so that they will be treated
as plain text.
]]
function str._escapePattern( pattern_str )
return ( string.gsub( pattern_str, "[%(%)%.%%%+%-%*%?%[%^%$%]]", "%%%0" ) )
end
return str
j5gx8uvupr1pa2fyqbe6td6jr0hxq00
વિભાગ:Citation/CS1
828
50994
886342
821284
2025-04-12T13:48:53Z
en>Trappist the monk
0
sync from sandbox;
886342
Scribunto
text/plain
require ('strict');
--[[--------------------------< F O R W A R D D E C L A R A T I O N S >--------------------------------------
each of these counts against the Lua upvalue limit
]]
local validation; -- functions in Module:Citation/CS1/Date_validation
local utilities; -- functions in Module:Citation/CS1/Utilities
local z = {}; -- table of tables in Module:Citation/CS1/Utilities
local identifiers; -- functions and tables in Module:Citation/CS1/Identifiers
local metadata; -- functions in Module:Citation/CS1/COinS
local cfg = {}; -- table of configuration tables that are defined in Module:Citation/CS1/Configuration
local whitelist = {}; -- table of tables listing valid template parameter names; defined in Module:Citation/CS1/Whitelist
--[[------------------< P A G E S C O P E V A R I A B L E S >---------------
declare variables here that have page-wide scope that are not brought in from
other modules; that are created here and used here
]]
local added_deprecated_cat; -- Boolean flag so that the category is added only once
local added_vanc_errs; -- Boolean flag so we only emit one Vancouver error / category
local added_generic_name_errs; -- Boolean flag so we only emit one generic name error / category and stop testing names once an error is encountered
local added_numeric_name_errs; -- Boolean flag so we only emit one numeric name error / category and stop testing names once an error is encountered
local added_numeric_name_maint; -- Boolean flag so we only emit one numeric name maint category and stop testing names once a category has been emitted
local is_preview_mode; -- true when article is in preview mode; false when using 'Preview page with this template' (previewing the module)
local is_sandbox; -- true when using sandbox modules to render citation
--[[--------------------------< F I R S T _ S E T >------------------------------------------------------------
Locates and returns the first set value in a table of values where the order established in the table,
left-to-right (or top-to-bottom), is the order in which the values are evaluated. Returns nil if none are set.
This version replaces the original 'for _, val in pairs do' and a similar version that used ipairs. With the pairs
version the order of evaluation could not be guaranteed. With the ipairs version, a nil value would terminate
the for-loop before it reached the actual end of the list.
]]
local function first_set (list, count)
local i = 1;
while i <= count do -- loop through all items in list
if utilities.is_set( list[i] ) then
return list[i]; -- return the first set list member
end
i = i + 1; -- point to next
end
end
--[[--------------------------< A D D _ V A N C _ E R R O R >----------------------------------------------------
Adds a single Vancouver system error message to the template's output regardless of how many error actually exist.
To prevent duplication, added_vanc_errs is nil until an error message is emitted.
added_vanc_errs is a Boolean declared in page scope variables above
]]
local function add_vanc_error (source, position)
if added_vanc_errs then return end
added_vanc_errs = true; -- note that we've added this category
utilities.set_message ('err_vancouver', {source, position});
end
--[[--------------------------< I S _ S C H E M E >------------------------------------------------------------
does this thing that purports to be a URI scheme seem to be a valid scheme? The scheme is checked to see if it
is in agreement with http://tools.ietf.org/html/std66#section-3.1 which says:
Scheme names consist of a sequence of characters beginning with a
letter and followed by any combination of letters, digits, plus
("+"), period ("."), or hyphen ("-").
returns true if it does, else false
]]
local function is_scheme (scheme)
return scheme and scheme:match ('^%a[%a%d%+%.%-]*:'); -- true if scheme is set and matches the pattern
end
--[=[-------------------------< I S _ D O M A I N _ N A M E >--------------------------------------------------
Does this thing that purports to be a domain name seem to be a valid domain name?
Syntax defined here: http://tools.ietf.org/html/rfc1034#section-3.5
BNF defined here: https://tools.ietf.org/html/rfc4234
Single character names are generally reserved; see https://tools.ietf.org/html/draft-ietf-dnsind-iana-dns-01#page-15;
see also [[Single-letter second-level domain]]
list of TLDs: https://www.iana.org/domains/root/db
RFC 952 (modified by RFC 1123) requires the first and last character of a hostname to be a letter or a digit. Between
the first and last characters the name may use letters, digits, and the hyphen.
Also allowed are IPv4 addresses. IPv6 not supported
domain is expected to be stripped of any path so that the last character in the last character of the TLD. tld
is two or more alpha characters. Any preceding '//' (from splitting a URL with a scheme) will be stripped
here. Perhaps not necessary but retained in case it is necessary for IPv4 dot decimal.
There are several tests:
the first character of the whole domain name including subdomains must be a letter or a digit
internationalized domain name (ASCII characters with .xn-- ASCII Compatible Encoding (ACE) prefix xn-- in the TLD) see https://tools.ietf.org/html/rfc3490
single-letter/digit second-level domains in the .org, .cash, and .today TLDs
q, x, and z SL domains in the .com TLD
i and q SL domains in the .net TLD
single-letter SL domains in the ccTLDs (where the ccTLD is two letters)
two-character SL domains in gTLDs (where the gTLD is two or more letters)
three-plus-character SL domains in gTLDs (where the gTLD is two or more letters)
IPv4 dot-decimal address format; TLD not allowed
returns true if domain appears to be a proper name and TLD or IPv4 address, else false
]=]
local function is_domain_name (domain)
if not domain then
return false; -- if not set, abandon
end
domain = domain:gsub ('^//', ''); -- strip '//' from domain name if present; done here so we only have to do it once
if not domain:match ('^[%w]') then -- first character must be letter or digit
return false;
end
if domain:match ('^%a+:') then -- hack to detect things that look like s:Page:Title where Page: is namespace at Wikisource
return false;
end
local patterns = { -- patterns that look like URLs
'%f[%w][%w][%w%-]+[%w]%.%a%a+$', -- three or more character hostname.hostname or hostname.tld
'%f[%w][%w][%w%-]+[%w]%.xn%-%-[%w]+$', -- internationalized domain name with ACE prefix
'%f[%a][qxz]%.com$', -- assigned one character .com hostname (x.com times out 2015-12-10)
'%f[%a][iq]%.net$', -- assigned one character .net hostname (q.net registered but not active 2015-12-10)
'%f[%w][%w]%.%a%a$', -- one character hostname and ccTLD (2 chars)
'%f[%w][%w][%w]%.%a%a+$', -- two character hostname and TLD
'^%d%d?%d?%.%d%d?%d?%.%d%d?%d?%.%d%d?%d?', -- IPv4 address
'[%a%d]+%:?' -- IPv6 address
}
for _, pattern in ipairs (patterns) do -- loop through the patterns list
if domain:match (pattern) then
return true; -- if a match then we think that this thing that purports to be a URL is a URL
end
end
for _, d in ipairs (cfg.single_letter_2nd_lvl_domains_t) do -- look for single letter second level domain names for these top level domains
if domain:match ('%f[%w][%w]%.' .. d) then
return true
end
end
return false; -- no matches, we don't know what this thing is
end
--[[--------------------------< I S _ U R L >------------------------------------------------------------------
returns true if the scheme and domain parts of a URL appear to be a valid URL; else false.
This function is the last step in the validation process. This function is separate because there are cases that
are not covered by split_url(), for example is_parameter_ext_wikilink() which is looking for bracketted external
wikilinks.
]]
local function is_url (scheme, domain)
if utilities.is_set (scheme) then -- if scheme is set check it and domain
return is_scheme (scheme) and is_domain_name (domain);
else
return is_domain_name (domain); -- scheme not set when URL is protocol-relative
end
end
--[[--------------------------< S P L I T _ U R L >------------------------------------------------------------
Split a URL into a scheme, authority indicator, and domain.
First remove Fully Qualified Domain Name terminator (a dot following TLD) (if any) and any path(/), query(?) or fragment(#).
If protocol-relative URL, return nil scheme and domain else return nil for both scheme and domain.
When not protocol-relative, get scheme, authority indicator, and domain. If there is an authority indicator (one
or more '/' characters immediately following the scheme's colon), make sure that there are only 2.
Any URL that does not have news: scheme must have authority indicator (//). TODO: are there other common schemes
like news: that don't use authority indicator?
Strip off any port and path;
]]
local function split_url (url_str)
local scheme, authority, domain;
url_str = url_str:gsub ('([%a%d])%.?[/%?#].*$', '%1'); -- strip FQDN terminator and path(/), query(?), fragment (#) (the capture prevents false replacement of '//')
if url_str:match ('^//%S*') then -- if there is what appears to be a protocol-relative URL
domain = url_str:match ('^//(%S*)')
elseif url_str:match ('%S-:/*%S+') then -- if there is what appears to be a scheme, optional authority indicator, and domain name
scheme, authority, domain = url_str:match ('(%S-:)(/*)(%S+)'); -- extract the scheme, authority indicator, and domain portions
if utilities.is_set (authority) then
authority = authority:gsub ('//', '', 1); -- replace place 1 pair of '/' with nothing;
if utilities.is_set(authority) then -- if anything left (1 or 3+ '/' where authority should be) then
return scheme; -- return scheme only making domain nil which will cause an error message
end
else
if not scheme:match ('^news:') then -- except for news:..., MediaWiki won't link URLs that do not have authority indicator; TODO: a better way to do this test?
return scheme; -- return scheme only making domain nil which will cause an error message
end
end
domain = domain:gsub ('(%a):%d+', '%1'); -- strip port number if present
end
return scheme, domain;
end
--[[--------------------------< L I N K _ P A R A M _ O K >---------------------------------------------------
checks the content of |title-link=, |series-link=, |author-link=, etc. for properly formatted content: no wikilinks, no URLs
Link parameters are to hold the title of a Wikipedia article, so none of the WP:TITLESPECIALCHARACTERS are allowed:
# < > [ ] | { } _
except the underscore which is used as a space in wiki URLs and # which is used for section links
returns false when the value contains any of these characters.
When there are no illegal characters, this function returns TRUE if value DOES NOT appear to be a valid URL (the
|<param>-link= parameter is ok); else false when value appears to be a valid URL (the |<param>-link= parameter is NOT ok).
]]
local function link_param_ok (value)
local scheme, domain;
if value:find ('[<>%[%]|{}]') then -- if any prohibited characters
return false;
end
scheme, domain = split_url (value); -- get scheme or nil and domain or nil from URL;
return not is_url (scheme, domain); -- return true if value DOES NOT appear to be a valid URL
end
--[[--------------------------< L I N K _ T I T L E _ O K >---------------------------------------------------
Use link_param_ok() to validate |<param>-link= value and its matching |<title>= value.
|<title>= may be wiki-linked but not when |<param>-link= has a value. This function emits an error message when
that condition exists
check <link> for inter-language interwiki-link prefix. prefix must be a MediaWiki-recognized language
code and must begin with a colon.
]]
local function link_title_ok (link, lorig, title, torig)
local orig;
if utilities.is_set (link) then -- don't bother if <param>-link doesn't have a value
if not link_param_ok (link) then -- check |<param>-link= markup
orig = lorig; -- identify the failing link parameter
elseif title:find ('%[%[') then -- check |title= for wikilink markup
orig = torig; -- identify the failing |title= parameter
elseif link:match ('^%a+:') then -- if the link is what looks like an interwiki
local prefix = link:match ('^(%a+):'):lower(); -- get the interwiki prefix
if cfg.inter_wiki_map[prefix] then -- if prefix is in the map, must have preceding colon
orig = lorig; -- flag as error
end
end
end
if utilities.is_set (orig) then
link = ''; -- unset
utilities.set_message ('err_bad_paramlink', orig); -- URL or wikilink in |title= with |title-link=;
end
return link; -- link if ok, empty string else
end
--[[--------------------------< C H E C K _ U R L >------------------------------------------------------------
Determines whether a URL string appears to be valid.
First we test for space characters. If any are found, return false. Then split the URL into scheme and domain
portions, or for protocol-relative (//example.com) URLs, just the domain. Use is_url() to validate the two
portions of the URL. If both are valid, or for protocol-relative if domain is valid, return true, else false.
Because it is different from a standard URL, and because this module used external_link() to make external links
that work for standard and news: links, we validate newsgroup names here. The specification for a newsgroup name
is at https://tools.ietf.org/html/rfc5536#section-3.1.4
]]
local function check_url( url_str )
if nil == url_str:match ("^%S+$") then -- if there are any spaces in |url=value it can't be a proper URL
return false;
end
local scheme, domain;
scheme, domain = split_url (url_str); -- get scheme or nil and domain or nil from URL;
if 'news:' == scheme then -- special case for newsgroups
return domain:match('^[%a%d%+%-_]+%.[%a%d%+%-_%.]*[%a%d%+%-_]$');
end
return is_url (scheme, domain); -- return true if value appears to be a valid URL
end
--[=[-------------------------< I S _ P A R A M E T E R _ E X T _ W I K I L I N K >----------------------------
Return true if a parameter value has a string that begins and ends with square brackets [ and ] and the first
non-space characters following the opening bracket appear to be a URL. The test will also find external wikilinks
that use protocol-relative URLs. Also finds bare URLs.
The frontier pattern prevents a match on interwiki-links which are similar to scheme:path URLs. The tests that
find bracketed URLs are required because the parameters that call this test (currently |title=, |chapter=, |work=,
and |publisher=) may have wikilinks and there are articles or redirects like '//Hus' so, while uncommon, |title=[[//Hus]]
is possible as might be [[en://Hus]].
]=]
local function is_parameter_ext_wikilink (value)
local scheme, domain;
if value:match ('%f[%[]%[%a%S*:%S+.*%]') then -- if ext. wikilink with scheme and domain: [xxxx://yyyyy.zzz]
scheme, domain = split_url (value:match ('%f[%[]%[(%a%S*:%S+).*%]'));
elseif value:match ('%f[%[]%[//%S+.*%]') then -- if protocol-relative ext. wikilink: [//yyyyy.zzz]
scheme, domain = split_url (value:match ('%f[%[]%[(//%S+).*%]'));
elseif value:match ('%a%S*:%S+') then -- if bare URL with scheme; may have leading or trailing plain text
scheme, domain = split_url (value:match ('(%a%S*:%S+)'));
elseif value:match ('^//%S+') or value:match ('%s//%S+') then -- if protocol-relative bare URL: //yyyyy.zzz; authority indicator (//) must be be preceded nothing or by whitespace
scheme, domain = split_url (value:match ('(//%S+)')); -- what is left should be the domain
else
return false; -- didn't find anything that is obviously a URL
end
return is_url (scheme, domain); -- return true if value appears to be a valid URL
end
--[[-------------------------< C H E C K _ F O R _ U R L >-----------------------------------------------------
loop through a list of parameters and their values. Look at the value and if it has an external link, emit an error message.
]]
local function check_for_url (parameter_list, error_list)
for k, v in pairs (parameter_list) do -- for each parameter in the list
if is_parameter_ext_wikilink (v) then -- look at the value; if there is a URL add an error message
table.insert (error_list, utilities.wrap_style ('parameter', k));
end
end
end
--[[--------------------------< S A F E _ F O R _ U R L >------------------------------------------------------
Escape sequences for content that will be used for URL descriptions
]]
local function safe_for_url( str )
if str:match( "%[%[.-%]%]" ) ~= nil then
utilities.set_message ('err_wikilink_in_url', {});
end
return str:gsub( '[%[%]\n]', {
['['] = '[',
[']'] = ']',
['\n'] = ' ' } );
end
--[[--------------------------< E X T E R N A L _ L I N K >----------------------------------------------------
Format an external link with error checking
]]
local function external_link (URL, label, source, access)
local err_msg = '';
local domain;
local path;
local base_url;
if not utilities.is_set (label) then
label = URL;
if utilities.is_set (source) then
utilities.set_message ('err_bare_url_missing_title', {utilities.wrap_style ('parameter', source)});
else
error (cfg.messages["bare_url_no_origin"]); -- programmer error; valid parameter name does not have matching meta-parameter
end
end
if not check_url (URL) then
utilities.set_message ('err_bad_url', {utilities.wrap_style ('parameter', source)});
end
domain, path = URL:match ('^([/%.%-%+:%a%d]+)([/%?#].*)$'); -- split the URL into scheme plus domain and path
if path then -- if there is a path portion
path = path:gsub ('[%[%]]', {['['] = '%5b', [']'] = '%5d'}); -- replace '[' and ']' with their percent-encoded values
URL = table.concat ({domain, path}); -- and reassemble
end
base_url = table.concat ({ "[", URL, " ", safe_for_url (label), "]" }); -- assemble a wiki-markup URL
if utilities.is_set (access) then -- access level (subscription, registration, limited)
base_url = utilities.substitute (cfg.presentation['ext-link-access-signal'], {cfg.presentation[access].class, cfg.presentation[access].title, base_url}); -- add the appropriate icon
end
return base_url;
end
--[[--------------------------< D E P R E C A T E D _ P A R A M E T E R >--------------------------------------
Categorize and emit an error message when the citation contains one or more deprecated parameters. The function includes the
offending parameter name to the error message. Only one error message is emitted regardless of the number of deprecated
parameters in the citation.
added_deprecated_cat is a Boolean declared in page scope variables above
]]
local function deprecated_parameter(name)
if not added_deprecated_cat then
added_deprecated_cat = true; -- note that we've added this category
utilities.set_message ('err_deprecated_params', {name}); -- add error message
end
end
--[=[-------------------------< K E R N _ Q U O T E S >--------------------------------------------------------
Apply kerning to open the space between the quote mark provided by the module and a leading or trailing quote
mark contained in a |title= or |chapter= parameter's value.
This function will positive kern either single or double quotes:
"'Unkerned title with leading and trailing single quote marks'"
" 'Kerned title with leading and trailing single quote marks' " (in real life the kerning isn't as wide as this example)
Double single quotes (italic or bold wiki-markup) are not kerned.
Replaces Unicode quote marks in plain text or in the label portion of a [[L|D]] style wikilink with typewriter
quote marks regardless of the need for kerning. Unicode quote marks are not replaced in simple [[D]] wikilinks.
Call this function for chapter titles, for website titles, etc.; not for book titles.
]=]
local function kern_quotes (str)
local cap = '';
local wl_type, label, link;
wl_type, label, link = utilities.is_wikilink (str); -- wl_type is: 0, no wl (text in label variable); 1, [[D]]; 2, [[L|D]]
if 1 == wl_type then -- [[D]] simple wikilink with or without quote marks
if mw.ustring.match (str, '%[%[[\"“”\'‘’].+[\"“”\'‘’]%]%]') then -- leading and trailing quote marks
str = utilities.substitute (cfg.presentation['kern-left'], str);
str = utilities.substitute (cfg.presentation['kern-right'], str);
elseif mw.ustring.match (str, '%[%[[\"“”\'‘’].+%]%]') then -- leading quote marks
str = utilities.substitute (cfg.presentation['kern-left'], str);
elseif mw.ustring.match (str, '%[%[.+[\"“”\'‘’]%]%]') then -- trailing quote marks
str = utilities.substitute (cfg.presentation['kern-right'], str);
end
else -- plain text or [[L|D]]; text in label variable
label = mw.ustring.gsub (label, '[“”]', '\"'); -- replace “” (U+201C & U+201D) with " (typewriter double quote mark)
label = mw.ustring.gsub (label, '[‘’]', '\''); -- replace ‘’ (U+2018 & U+2019) with ' (typewriter single quote mark)
cap = mw.ustring.match (label, "^([\"\'][^\'].+)"); -- match leading double or single quote but not doubled single quotes (italic markup)
if utilities.is_set (cap) then
label = utilities.substitute (cfg.presentation['kern-left'], cap);
end
cap = mw.ustring.match (label, "^(.+[^\'][\"\'])$") -- match trailing double or single quote but not doubled single quotes (italic markup)
if utilities.is_set (cap) then
label = utilities.substitute (cfg.presentation['kern-right'], cap);
end
if 2 == wl_type then
str = utilities.make_wikilink (link, label); -- reassemble the wikilink
else
str = label;
end
end
return str;
end
--[[--------------------------< F O R M A T _ S C R I P T _ V A L U E >----------------------------------------
|script-title= holds title parameters that are not written in Latin-based scripts: Chinese, Japanese, Arabic, Hebrew, etc. These scripts should
not be italicized and may be written right-to-left. The value supplied by |script-title= is concatenated onto Title after Title has been wrapped
in italic markup.
Regardless of language, all values provided by |script-title= are wrapped in <bdi>...</bdi> tags to isolate RTL languages from the English left to right.
|script-title= provides a unique feature. The value in |script-title= may be prefixed with a two-character ISO 639-1 language code and a colon:
|script-title=ja:*** *** (where * represents a Japanese character)
Spaces between the two-character code and the colon and the colon and the first script character are allowed:
|script-title=ja : *** ***
|script-title=ja: *** ***
|script-title=ja :*** ***
Spaces preceding the prefix are allowed: |script-title = ja:*** ***
The prefix is checked for validity. If it is a valid ISO 639-1 language code, the lang attribute (lang="ja") is added to the <bdi> tag so that browsers can
know the language the tag contains. This may help the browser render the script more correctly. If the prefix is invalid, the lang attribute
is not added. At this time there is no error message for this condition.
Supports |script-title=, |script-chapter=, |script-<periodical>=
]]
local function format_script_value (script_value, script_param)
local lang=''; -- initialize to empty string
local name;
if script_value:match('^%l%l%l?%s*:') then -- if first 3 or 4 non-space characters are script language prefix
lang = script_value:match('^(%l%l%l?)%s*:%s*%S.*'); -- get the language prefix or nil if there is no script
if not utilities.is_set (lang) then
utilities.set_message ('err_script_parameter', {script_param, cfg.err_msg_supl['missing title part']}); -- prefix without 'title'; add error message
return ''; -- script_value was just the prefix so return empty string
end
-- if we get this far we have prefix and script
name = cfg.lang_tag_remap[lang] or mw.language.fetchLanguageName( lang, cfg.this_wiki_code ); -- get language name so that we can use it to categorize
if utilities.is_set (name) then -- is prefix a proper ISO 639-1 language code?
script_value = script_value:gsub ('^%l+%s*:%s*', ''); -- strip prefix from script
-- is prefix one of these language codes?
if utilities.in_array (lang, cfg.script_lang_codes) then
utilities.add_prop_cat ('script', {name, lang})
else
utilities.set_message ('err_script_parameter', {script_param, cfg.err_msg_supl['unknown language code']}); -- unknown script-language; add error message
end
lang = ' lang="' .. lang .. '" '; -- convert prefix into a lang attribute
else
utilities.set_message ('err_script_parameter', {script_param, cfg.err_msg_supl['invalid language code']}); -- invalid language code; add error message
lang = ''; -- invalid so set lang to empty string
end
else
utilities.set_message ('err_script_parameter', {script_param, cfg.err_msg_supl['missing prefix']}); -- no language code prefix; add error message
end
script_value = utilities.substitute (cfg.presentation['bdi'], {lang, script_value}); -- isolate in case script is RTL
return script_value;
end
--[[--------------------------< S C R I P T _ C O N C A T E N A T E >------------------------------------------
Initially for |title= and |script-title=, this function concatenates those two parameter values after the script
value has been wrapped in <bdi> tags.
]]
local function script_concatenate (title, script, script_param)
if utilities.is_set (script) then
script = format_script_value (script, script_param); -- <bdi> tags, lang attribute, categorization, etc.; returns empty string on error
if utilities.is_set (script) then
title = title .. ' ' .. script; -- concatenate title and script title
end
end
return title;
end
--[[--------------------------< W R A P _ M S G >--------------------------------------------------------------
Applies additional message text to various parameter values. Supplied string is wrapped using a message_list
configuration taking one argument. Supports lower case text for {{citation}} templates. Additional text taken
from citation_config.messages - the reason this function is similar to but separate from wrap_style().
]]
local function wrap_msg (key, str, lower)
if not utilities.is_set ( str ) then
return "";
end
if true == lower then
local msg;
msg = cfg.messages[key]:lower(); -- set the message to lower case before
return utilities.substitute ( msg, str ); -- including template text
else
return utilities.substitute ( cfg.messages[key], str );
end
end
--[[----------------< W I K I S O U R C E _ U R L _ M A K E >-------------------
Makes a Wikisource URL from Wikisource interwiki-link. Returns the URL and appropriate
label; nil else.
str is the value assigned to |chapter= (or aliases) or |title= or |title-link=
]]
local function wikisource_url_make (str)
local wl_type, D, L;
local ws_url, ws_label;
local wikisource_prefix = table.concat ({'https://', cfg.this_wiki_code, '.wikisource.org/wiki/'});
wl_type, D, L = utilities.is_wikilink (str); -- wl_type is 0 (not a wikilink), 1 (simple wikilink), 2 (complex wikilink)
if 0 == wl_type then -- not a wikilink; might be from |title-link=
str = D:match ('^[Ww]ikisource:(.+)') or D:match ('^[Ss]:(.+)'); -- article title from interwiki link with long-form or short-form namespace
if utilities.is_set (str) then
ws_url = table.concat ({ -- build a Wikisource URL
wikisource_prefix, -- prefix
str, -- article title
});
ws_label = str; -- label for the URL
end
elseif 1 == wl_type then -- simple wikilink: [[Wikisource:ws article]]
str = D:match ('^[Ww]ikisource:(.+)') or D:match ('^[Ss]:(.+)'); -- article title from interwiki link with long-form or short-form namespace
if utilities.is_set (str) then
ws_url = table.concat ({ -- build a Wikisource URL
wikisource_prefix, -- prefix
str, -- article title
});
ws_label = str; -- label for the URL
end
elseif 2 == wl_type then -- non-so-simple wikilink: [[Wikisource:ws article|displayed text]] ([[L|D]])
str = L:match ('^[Ww]ikisource:(.+)') or L:match ('^[Ss]:(.+)'); -- article title from interwiki link with long-form or short-form namespace
if utilities.is_set (str) then
ws_label = D; -- get ws article name from display portion of interwiki link
ws_url = table.concat ({ -- build a Wikisource URL
wikisource_prefix, -- prefix
str, -- article title without namespace from link portion of wikilink
});
end
end
if ws_url then
ws_url = mw.uri.encode (ws_url, 'WIKI'); -- make a usable URL
ws_url = ws_url:gsub ('%%23', '#'); -- undo percent-encoding of fragment marker
end
return ws_url, ws_label, L or D; -- return proper URL or nil and a label or nil
end
--[[----------------< F O R M A T _ P E R I O D I C A L >-----------------------
Format the three periodical parameters: |script-<periodical>=, |<periodical>=,
and |trans-<periodical>= into a single Periodical meta-parameter.
]]
local function format_periodical (script_periodical, script_periodical_source, periodical, trans_periodical)
if not utilities.is_set (periodical) then
periodical = ''; -- to be safe for concatenation
else
periodical = utilities.wrap_style ('italic-title', periodical); -- style
end
periodical = script_concatenate (periodical, script_periodical, script_periodical_source); -- <bdi> tags, lang attribute, categorization, etc.; must be done after title is wrapped
if utilities.is_set (trans_periodical) then
trans_periodical = utilities.wrap_style ('trans-italic-title', trans_periodical);
if utilities.is_set (periodical) then
periodical = periodical .. ' ' .. trans_periodical;
else -- here when trans-periodical without periodical or script-periodical
periodical = trans_periodical;
utilities.set_message ('err_trans_missing_title', {'periodical'});
end
end
return periodical;
end
--[[------------------< F O R M A T _ C H A P T E R _ T I T L E >---------------
Format the four chapter parameters: |script-chapter=, |chapter=, |trans-chapter=,
and |chapter-url= into a single chapter meta- parameter (chapter_url_source used
for error messages).
]]
local function format_chapter_title (script_chapter, script_chapter_source, chapter, chapter_source, trans_chapter, trans_chapter_source, chapter_url, chapter_url_source, no_quotes, access)
local ws_url, ws_label, L = wikisource_url_make (chapter); -- make a wikisource URL and label from a wikisource interwiki link
if ws_url then
ws_label = ws_label:gsub ('_', ' '); -- replace underscore separators with space characters
chapter = ws_label;
end
if not utilities.is_set (chapter) then
chapter = ''; -- to be safe for concatenation
else
if false == no_quotes then
chapter = kern_quotes (chapter); -- if necessary, separate chapter title's leading and trailing quote marks from module provided quote marks
chapter = utilities.wrap_style ('quoted-title', chapter);
end
end
chapter = script_concatenate (chapter, script_chapter, script_chapter_source); -- <bdi> tags, lang attribute, categorization, etc.; must be done after title is wrapped
if utilities.is_set (chapter_url) then
chapter = external_link (chapter_url, chapter, chapter_url_source, access); -- adds bare_url_missing_title error if appropriate
elseif ws_url then
chapter = external_link (ws_url, chapter .. ' ', 'ws link in chapter'); -- adds bare_url_missing_title error if appropriate; space char to move icon away from chap text; TODO: better way to do this?
chapter = utilities.substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], L, chapter});
end
if utilities.is_set (trans_chapter) then
trans_chapter = utilities.wrap_style ('trans-quoted-title', trans_chapter);
if utilities.is_set (chapter) then
chapter = chapter .. ' ' .. trans_chapter;
else -- here when trans_chapter without chapter or script-chapter
chapter = trans_chapter;
chapter_source = trans_chapter_source:match ('trans%-?(.+)'); -- when no chapter, get matching name from trans-<param>
utilities.set_message ('err_trans_missing_title', {chapter_source});
end
end
return chapter;
end
--[[----------------< H A S _ I N V I S I B L E _ C H A R S >-------------------
This function searches a parameter's value for non-printable or invisible characters.
The search stops at the first match.
This function will detect the visible replacement character when it is part of the Wikisource.
Detects but ignores nowiki and math stripmarkers. Also detects other named stripmarkers
(gallery, math, pre, ref) and identifies them with a slightly different error message.
See also coins_cleanup().
Output of this function is an error message that identifies the character or the
Unicode group, or the stripmarker that was detected along with its position (or,
for multi-byte characters, the position of its first byte) in the parameter value.
]]
local function has_invisible_chars (param, v)
local position = ''; -- position of invisible char or starting position of stripmarker
local capture; -- used by stripmarker detection to hold name of the stripmarker
local stripmarker; -- boolean set true when a stripmarker is found
capture = string.match (v, '[%w%p ]*'); -- test for values that are simple ASCII text and bypass other tests if true
if capture == v then -- if same there are no Unicode characters
return;
end
for _, invisible_char in ipairs (cfg.invisible_chars) do
local char_name = invisible_char[1]; -- the character or group name
local pattern = invisible_char[2]; -- the pattern used to find it
position, _, capture = mw.ustring.find (v, pattern); -- see if the parameter value contains characters that match the pattern
if position and (cfg.invisible_defs.zwj == capture) then -- if we found a zero-width joiner character
if mw.ustring.find (v, cfg.indic_script) then -- it's ok if one of the Indic scripts
position = nil; -- unset position
elseif cfg.emoji_t[mw.ustring.codepoint (v, position+1)] then -- is zwj followed by a character listed in emoji{}?
position = nil; -- unset position
end
end
if position then
if 'nowiki' == capture or 'math' == capture or -- nowiki and math stripmarkers (not an error condition)
('templatestyles' == capture and utilities.in_array (param, {'id', 'quote'})) then -- templatestyles stripmarker allowed in these parameters
stripmarker = true; -- set a flag
elseif true == stripmarker and cfg.invisible_defs.del == capture then -- because stripmakers begin and end with the delete char, assume that we've found one end of a stripmarker
position = nil; -- unset
else
local err_msg;
if capture and not (cfg.invisible_defs.del == capture or cfg.invisible_defs.zwj == capture) then
err_msg = capture .. ' ' .. char_name;
else
err_msg = char_name .. ' ' .. 'character';
end
utilities.set_message ('err_invisible_char', {err_msg, utilities.wrap_style ('parameter', param), position}); -- add error message
return; -- and done with this parameter
end
end
end
end
--[[-------------------< A R G U M E N T _ W R A P P E R >----------------------
Argument wrapper. This function provides support for argument mapping defined
in the configuration file so that multiple names can be transparently aliased to
single internal variable.
]]
local function argument_wrapper ( args )
local origin = {};
return setmetatable({
ORIGIN = function ( self, k )
local dummy = self[k]; -- force the variable to be loaded.
return origin[k];
end
},
{
__index = function ( tbl, k )
if origin[k] ~= nil then
return nil;
end
local args, list, v = args, cfg.aliases[k];
if type( list ) == 'table' then
v, origin[k] = utilities.select_one ( args, list, 'err_redundant_parameters' );
if origin[k] == nil then
origin[k] = ''; -- Empty string, not nil
end
elseif list ~= nil then
v, origin[k] = args[list], list;
else
-- maybe let through instead of raising an error?
-- v, origin[k] = args[k], k;
error( cfg.messages['unknown_argument_map'] .. ': ' .. k);
end
-- Empty strings, not nil;
if v == nil then
v = '';
origin[k] = '';
end
tbl = rawset( tbl, k, v );
return v;
end,
});
end
--[[--------------------------< N O W R A P _ D A T E >-------------------------
When date is YYYY-MM-DD format wrap in nowrap span: <span ...>YYYY-MM-DD</span>.
When date is DD MMMM YYYY or is MMMM DD, YYYY then wrap in nowrap span:
<span ...>DD MMMM</span> YYYY or <span ...>MMMM DD,</span> YYYY
DOES NOT yet support MMMM YYYY or any of the date ranges.
]]
local function nowrap_date (date)
local cap = '';
local cap2 = '';
if date:match("^%d%d%d%d%-%d%d%-%d%d$") then
date = utilities.substitute (cfg.presentation['nowrap1'], date);
elseif date:match("^%a+%s*%d%d?,%s+%d%d%d%d$") or date:match ("^%d%d?%s*%a+%s+%d%d%d%d$") then
cap, cap2 = string.match (date, "^(.*)%s+(%d%d%d%d)$");
date = utilities.substitute (cfg.presentation['nowrap2'], {cap, cap2});
end
return date;
end
--[[--------------------------< S E T _ T I T L E T Y P E >---------------------
This function sets default title types (equivalent to the citation including
|type=<default value>) for those templates that have defaults. Also handles the
special case where it is desirable to omit the title type from the rendered citation
(|type=none).
]]
local function set_titletype (cite_class, title_type)
if utilities.is_set (title_type) then
if 'none' == cfg.keywords_xlate[title_type] then
title_type = ''; -- if |type=none then type parameter not displayed
end
return title_type; -- if |type= has been set to any other value use that value
end
return cfg.title_types [cite_class] or ''; -- set template's default title type; else empty string for concatenation
end
--[[--------------------------< S A F E _ J O I N >-----------------------------
Joins a sequence of strings together while checking for duplicate separation characters.
]]
local function safe_join( tbl, duplicate_char )
local f = {}; -- create a function table appropriate to type of 'duplicate character'
if 1 == #duplicate_char then -- for single byte ASCII characters use the string library functions
f.gsub = string.gsub
f.match = string.match
f.sub = string.sub
else -- for multi-byte characters use the ustring library functions
f.gsub = mw.ustring.gsub
f.match = mw.ustring.match
f.sub = mw.ustring.sub
end
local str = ''; -- the output string
local comp = ''; -- what does 'comp' mean?
local end_chr = '';
local trim;
for _, value in ipairs( tbl ) do
if value == nil then value = ''; end
if str == '' then -- if output string is empty
str = value; -- assign value to it (first time through the loop)
elseif value ~= '' then
if value:sub(1, 1) == '<' then -- special case of values enclosed in spans and other markup.
comp = value:gsub( "%b<>", "" ); -- remove HTML markup (<span>string</span> -> string)
else
comp = value;
end
-- typically duplicate_char is sepc
if f.sub(comp, 1, 1) == duplicate_char then -- is first character same as duplicate_char? why test first character?
-- Because individual string segments often (always?) begin with terminal punct for the
-- preceding segment: 'First element' .. 'sepc next element' .. etc.?
trim = false;
end_chr = f.sub(str, -1, -1); -- get the last character of the output string
-- str = str .. "<HERE(enchr=" .. end_chr .. ")" -- debug stuff?
if end_chr == duplicate_char then -- if same as separator
str = f.sub(str, 1, -2); -- remove it
elseif end_chr == "'" then -- if it might be wiki-markup
if f.sub(str, -3, -1) == duplicate_char .. "''" then -- if last three chars of str are sepc''
str = f.sub(str, 1, -4) .. "''"; -- remove them and add back ''
elseif f.sub(str, -5, -1) == duplicate_char .. "]]''" then -- if last five chars of str are sepc]]''
trim = true; -- why? why do this and next differently from previous?
elseif f.sub(str, -4, -1) == duplicate_char .. "]''" then -- if last four chars of str are sepc]''
trim = true; -- same question
end
elseif end_chr == "]" then -- if it might be wiki-markup
if f.sub(str, -3, -1) == duplicate_char .. "]]" then -- if last three chars of str are sepc]] wikilink
trim = true;
elseif f.sub(str, -3, -1) == duplicate_char .. '"]' then -- if last three chars of str are sepc"] quoted external link
trim = true;
elseif f.sub(str, -2, -1) == duplicate_char .. "]" then -- if last two chars of str are sepc] external link
trim = true;
elseif f.sub(str, -4, -1) == duplicate_char .. "'']" then -- normal case when |url=something & |title=Title.
trim = true;
end
elseif end_chr == " " then -- if last char of output string is a space
if f.sub(str, -2, -1) == duplicate_char .. " " then -- if last two chars of str are <sepc><space>
str = f.sub(str, 1, -3); -- remove them both
end
end
if trim then
if value ~= comp then -- value does not equal comp when value contains HTML markup
local dup2 = duplicate_char;
if f.match(dup2, "%A" ) then dup2 = "%" .. dup2; end -- if duplicate_char not a letter then escape it
value = f.gsub(value, "(%b<>)" .. dup2, "%1", 1 ) -- remove duplicate_char if it follows HTML markup
else
value = f.sub(value, 2, -1 ); -- remove duplicate_char when it is first character
end
end
end
str = str .. value; -- add it to the output string
end
end
return str;
end
--[[--------------------------< I S _ S U F F I X >-----------------------------
returns true if suffix is properly formed Jr, Sr, or ordinal in the range 1–9.
Puncutation not allowed.
]]
local function is_suffix (suffix)
if utilities.in_array (suffix, {'Jr', 'Sr', 'Jnr', 'Snr', '1st', '2nd', '3rd'}) or suffix:match ('^%dth$') then
return true;
end
return false;
end
--[[--------------------< I S _ G O O D _ V A N C _ N A M E >-------------------
For Vancouver style, author/editor names are supposed to be rendered in Latin
(read ASCII) characters. When a name uses characters that contain diacritical
marks, those characters are to be converted to the corresponding Latin
character. When a name is written using a non-Latin alphabet or logogram, that
name is to be transliterated into Latin characters. The module doesn't do this
so editors may/must.
This test allows |first= and |last= names to contain any of the letters defined
in the four Unicode Latin character sets
[http://www.unicode.org/charts/PDF/U0000.pdf C0 Controls and Basic Latin] 0041–005A, 0061–007A
[http://www.unicode.org/charts/PDF/U0080.pdf C1 Controls and Latin-1 Supplement] 00C0–00D6, 00D8–00F6, 00F8–00FF
[http://www.unicode.org/charts/PDF/U0100.pdf Latin Extended-A] 0100–017F
[http://www.unicode.org/charts/PDF/U0180.pdf Latin Extended-B] 0180–01BF, 01C4–024F
|lastn= also allowed to contain hyphens, spaces, and apostrophes.
(http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35029/)
|firstn= also allowed to contain hyphens, spaces, apostrophes, and periods
This original test:
if nil == mw.ustring.find (last, "^[A-Za-zÀ-ÖØ-öø-ƿDŽ-ɏ%-%s%']*$")
or nil == mw.ustring.find (first, "^[A-Za-zÀ-ÖØ-öø-ƿDŽ-ɏ%-%s%'%.]+[2-6%a]*$") then
was written outside of the code editor and pasted here because the code editor
gets confused between character insertion point and cursor position. The test has
been rewritten to use decimal character escape sequence for the individual bytes
of the Unicode characters so that it is not necessary to use an external editor
to maintain this code.
\195\128-\195\150 – À-Ö (U+00C0–U+00D6 – C0 controls)
\195\152-\195\182 – Ø-ö (U+00D8-U+00F6 – C0 controls)
\195\184-\198\191 – ø-ƿ (U+00F8-U+01BF – C0 controls, Latin extended A & B)
\199\132-\201\143 – DŽ-ɏ (U+01C4-U+024F – Latin extended B)
]]
local function is_good_vanc_name (last, first, suffix, position)
if not suffix then
if first:find ('[,%s]') then -- when there is a space or comma, might be first name/initials + generational suffix
first = first:match ('(.-)[,%s]+'); -- get name/initials
suffix = first:match ('[,%s]+(.+)$'); -- get generational suffix
end
end
if utilities.is_set (suffix) then
if not is_suffix (suffix) then
add_vanc_error (cfg.err_msg_supl.suffix, position);
return false; -- not a name with an appropriate suffix
end
end
if nil == mw.ustring.find (last, "^[A-Za-z\195\128-\195\150\195\152-\195\182\195\184-\198\191\199\132-\201\143\225\184\128-\225\187\191%-%s%']*$") or
nil == mw.ustring.find (first, "^[A-Za-z\195\128-\195\150\195\152-\195\182\195\184-\198\191\199\132-\201\143\225\184\128-\225\187\191%-%s%'%.]*$") then
add_vanc_error (cfg.err_msg_supl['non-Latin char'], position);
return false; -- not a string of Latin characters; Vancouver requires Romanization
end;
return true;
end
--[[--------------------------< R E D U C E _ T O _ I N I T I A L S >------------------------------------------
Attempts to convert names to initials in support of |name-list-style=vanc.
Names in |firstn= may be separated by spaces or hyphens, or for initials, a period.
See http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35062/.
Vancouver style requires family rank designations (Jr, II, III, etc.) to be rendered
as Jr, 2nd, 3rd, etc. See http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35085/.
This code only accepts and understands generational suffix in the Vancouver format
because Roman numerals look like, and can be mistaken for, initials.
This function uses ustring functions because firstname initials may be any of the
Unicode Latin characters accepted by is_good_vanc_name ().
]]
local function reduce_to_initials (first, position)
if first:find (',', 1, true) then
return first; -- commas not allowed; abandon
end
local name, suffix = mw.ustring.match (first, "^(%u+) ([%dJS][%drndth]+)$");
if not name then -- if not initials and a suffix
name = mw.ustring.match (first, "^(%u+)$"); -- is it just initials?
end
if name then -- if first is initials with or without suffix
if 3 > mw.ustring.len (name) then -- if one or two initials
if suffix then -- if there is a suffix
if is_suffix (suffix) then -- is it legitimate?
return first; -- one or two initials and a valid suffix so nothing to do
else
add_vanc_error (cfg.err_msg_supl.suffix, position); -- one or two initials with invalid suffix so error message
return first; -- and return first unmolested
end
else
return first; -- one or two initials without suffix; nothing to do
end
end
end -- if here then name has 3 or more uppercase letters so treat them as a word
local initials_t, names_t = {}, {}; -- tables to hold name parts and initials
local i = 1; -- counter for number of initials
names_t = mw.text.split (first, '[%s%-]+'); -- split into a sequence of names and possible suffix
while names_t[i] do -- loop through the sequence
if 1 < i and names_t[i]:match ('[%dJS][%drndth]+%.?$') then -- if not the first name, and looks like a suffix (may have trailing dot)
names_t[i] = names_t[i]:gsub ('%.', ''); -- remove terminal dot if present
if is_suffix (names_t[i]) then -- if a legitimate suffix
table.insert (initials_t, ' ' .. names_t[i]); -- add a separator space, insert at end of initials sequence
break; -- and done because suffix must fall at the end of a name
end -- no error message if not a suffix; possibly because of Romanization
end
if 3 > i then
table.insert (initials_t, mw.ustring.sub (names_t[i], 1, 1)); -- insert the initial at end of initials sequence
end
i = i + 1; -- bump the counter
end
return table.concat (initials_t); -- Vancouver format does not include spaces.
end
--[[--------------------------< I N T E R W I K I _ P R E F I X E N _ G E T >----------------------------------
extract interwiki prefixen from <value>. Returns two one or two values:
false – no prefixen
nil – prefix exists but not recognized
project prefix, language prefix – when value has either of:
:<project>:<language>:<article>
:<language>:<project>:<article>
project prefix, nil – when <value> has only a known single-letter prefix
nil, language prefix – when <value> has only a known language prefix
accepts single-letter project prefixen: 'd' (wikidata), 's' (wikisource), and 'w' (wikipedia) prefixes; at this
writing, the other single-letter prefixen (b (wikibook), c (commons), m (meta), n (wikinews), q (wikiquote), and
v (wikiversity)) are not supported.
]]
local function interwiki_prefixen_get (value, is_link)
if not value:find (':%l+:') then -- if no prefix
return false; -- abandon; boolean here to distinguish from nil fail returns later
end
local prefix_patterns_linked_t = { -- sequence of valid interwiki and inter project prefixen
'^%[%[:([dsw]):(%l%l+):', -- wikilinked; project and language prefixes
'^%[%[:(%l%l+):([dsw]):', -- wikilinked; language and project prefixes
'^%[%[:([dsw]):', -- wikilinked; project prefix
'^%[%[:(%l%l+):', -- wikilinked; language prefix
}
local prefix_patterns_unlinked_t = { -- sequence of valid interwiki and inter project prefixen
'^:([dsw]):(%l%l+):', -- project and language prefixes
'^:(%l%l+):([dsw]):', -- language and project prefixes
'^:([dsw]):', -- project prefix
'^:(%l%l+):', -- language prefix
}
local cap1, cap2;
for _, pattern in ipairs ((is_link and prefix_patterns_linked_t) or prefix_patterns_unlinked_t) do
cap1, cap2 = value:match (pattern);
if cap1 then
break; -- found a match so stop looking
end
end
if cap1 and cap2 then -- when both then :project:language: or :language:project: (both forms allowed)
if 1 == #cap1 then -- length == 1 then :project:language:
if cfg.inter_wiki_map[cap2] then -- is language prefix in the interwiki map?
return cap1, cap2; -- return interwiki project and interwiki language
end
else -- here when :language:project:
if cfg.inter_wiki_map[cap1] then -- is language prefix in the interwiki map?
return cap2, cap1; -- return interwiki project and interwiki language
end
end
return nil; -- unknown interwiki language
elseif not (cap1 or cap2) then -- both are nil?
return nil; -- we got something that looks like a project prefix but isn't; return fail
elseif 1 == #cap1 then -- here when one capture
return cap1, nil; -- length is 1 so return project, nil language
else -- here when one capture and its length it more than 1
if cfg.inter_wiki_map[cap1] then -- is language prefix in the interwiki map?
return nil, cap1; -- return nil project, language
end
end
end
--[[--------------------------< L I S T _ P E O P L E >--------------------------
Formats a list of people (authors, contributors, editors, interviewers, translators)
names in the list will be linked when
|<name>-link= has a value
|<name>-mask- does NOT have a value; masked names are presumed to have been
rendered previously so should have been linked there
when |<name>-mask=0, the associated name is not rendered
]]
local function list_people (control, people, etal)
local sep;
local namesep;
local format = control.format;
local maximum = control.maximum;
local name_list = {};
if 'vanc' == format then -- Vancouver-like name styling?
sep = cfg.presentation['sep_nl_vanc']; -- name-list separator between names is a comma
namesep = cfg.presentation['sep_name_vanc']; -- last/first separator is a space
else
sep = cfg.presentation['sep_nl']; -- name-list separator between names is a semicolon
namesep = cfg.presentation['sep_name']; -- last/first separator is <comma><space>
end
if sep:sub (-1, -1) ~= " " then sep = sep .. " " end
if utilities.is_set (maximum) and maximum < 1 then return "", 0; end -- returned 0 is for EditorCount; not used for other names
for i, person in ipairs (people) do
if utilities.is_set (person.last) then
local mask = person.mask;
local one;
local sep_one = sep;
if utilities.is_set (maximum) and i > maximum then
etal = true;
break;
end
if mask then
local n = tonumber (mask); -- convert to a number if it can be converted; nil else
if n then
one = 0 ~= n and string.rep("—", n) or nil; -- make a string of (n > 0) mdashes, nil else, to replace name
person.link = nil; -- don't create link to name if name is replaces with mdash string or has been set nil
else
one = mask; -- replace name with mask text (must include name-list separator)
sep_one = " "; -- modify name-list separator
end
else
one = person.last; -- get surname
local first = person.first -- get given name
if utilities.is_set (first) then
if ("vanc" == format) then -- if Vancouver format
one = one:gsub ('%.', ''); -- remove periods from surnames (http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35029/)
if not person.corporate and is_good_vanc_name (one, first, nil, i) then -- and name is all Latin characters; corporate authors not tested
first = reduce_to_initials (first, i); -- attempt to convert first name(s) to initials
end
end
one = one .. namesep .. first;
end
end
if utilities.is_set (person.link) then
one = utilities.make_wikilink (person.link, one); -- link author/editor
end
if one then -- if <one> has a value (name, mdash replacement, or mask text replacement)
local proj, tag = interwiki_prefixen_get (one, true); -- get the interwiki prefixen if present
if 'w' == proj and ('Wikipedia' == mw.site.namespaces.Project['name']) then
proj = nil; -- for stuff like :w:de:<article>, :w is unnecessary TODO: maint cat?
end
if proj then
local proj_name = ({['d'] = 'Wikidata', ['s'] = 'Wikisource', ['w'] = 'Wikipedia'})[proj]; -- :w (wikipedia) for linking from a non-wikipedia project
if proj_name then
one = one .. utilities.wrap_style ('interproj', proj_name); -- add resized leading space, brackets, static text, language name
utilities.add_prop_cat ('interproj-linked-name', proj); -- categorize it; <proj> is sort key
tag = nil; -- unset; don't do both project and language
end
end
if tag == cfg.this_wiki_code then
tag = nil; -- stuff like :en:<article> at en.wiki is pointless TODO: maint cat?
end
if tag then
local lang = cfg.lang_tag_remap[tag] or cfg.mw_languages_by_tag_t[tag];
if lang then -- error messaging done in extract_names() where we know parameter names
one = one .. utilities.wrap_style ('interwiki', lang); -- add resized leading space, brackets, static text, language name
utilities.add_prop_cat ('interwiki-linked-name', tag); -- categorize it; <tag> is sort key
end
end
table.insert (name_list, one); -- add it to the list of names
table.insert (name_list, sep_one); -- add the proper name-list separator
end
end
end
local count = #name_list / 2; -- (number of names + number of separators) divided by 2
if 0 < count then
if 1 < count and not etal then
if 'amp' == format then
name_list[#name_list-2] = " & "; -- replace last separator with ampersand text
elseif 'and' == format then
if 2 == count then
name_list[#name_list-2] = cfg.presentation.sep_nl_and; -- replace last separator with 'and' text
else
name_list[#name_list-2] = cfg.presentation.sep_nl_end; -- replace last separator with '(sep) and' text
end
end
end
name_list[#name_list] = nil; -- erase the last separator
end
local result = table.concat (name_list); -- construct list
if etal and utilities.is_set (result) then -- etal may be set by |display-authors=etal but we might not have a last-first list
result = result .. sep .. cfg.messages['et al']; -- we've got a last-first list and etal so add et al.
end
return result, count; -- return name-list string and count of number of names (count used for editor names only)
end
--[[--------------------< M A K E _ C I T E R E F _ I D >-----------------------
Generates a CITEREF anchor ID if we have at least one name or a date. Otherwise
returns an empty string.
namelist is one of the contributor-, author-, or editor-name lists chosen in that
order. year is Year or anchor_year.
]]
local function make_citeref_id (namelist, year)
local names={}; -- a table for the one to four names and year
for i,v in ipairs (namelist) do -- loop through the list and take up to the first four last names
names[i] = v.last
if i == 4 then break end -- if four then done
end
table.insert (names, year); -- add the year at the end
local id = table.concat(names); -- concatenate names and year for CITEREF id
if utilities.is_set (id) then -- if concatenation is not an empty string
return "CITEREF" .. id; -- add the CITEREF portion
else
return ''; -- return an empty string; no reason to include CITEREF id in this citation
end
end
--[[--------------------------< C I T E _ C L A S S _A T T R I B U T E _M A K E >------------------------------
construct <cite> tag class attribute for this citation.
<cite_class> – config.CitationClass from calling template
<mode> – value from |mode= parameter
]]
local function cite_class_attribute_make (cite_class, mode)
local class_t = {};
table.insert (class_t, 'citation'); -- required for blue highlight
if 'citation' ~= cite_class then
table.insert (class_t, cite_class); -- identify this template for user css
table.insert (class_t, utilities.is_set (mode) and mode or 'cs1'); -- identify the citation style for user css or javascript
else
table.insert (class_t, utilities.is_set (mode) and mode or 'cs2'); -- identify the citation style for user css or javascript
end
for _, prop_key in ipairs (z.prop_keys_t) do
table.insert (class_t, prop_key); -- identify various properties for user css or javascript
end
return table.concat (class_t, ' '); -- make a big string and done
end
--[[---------------------< N A M E _ H A S _ E T A L >--------------------------
Evaluates the content of name parameters (author, editor, etc.) for variations on
the theme of et al. If found, the et al. is removed, a flag is set to true and
the function returns the modified name and the flag.
This function never sets the flag to false but returns its previous state because
it may have been set by previous passes through this function or by the associated
|display-<names>=etal parameter
]]
local function name_has_etal (name, etal, nocat, param)
if utilities.is_set (name) then -- name can be nil in which case just return
local patterns = cfg.et_al_patterns; -- get patterns from configuration
for _, pattern in ipairs (patterns) do -- loop through all of the patterns
if name:match (pattern) then -- if this 'et al' pattern is found in name
name = name:gsub (pattern, ''); -- remove the offending text
etal = true; -- set flag (may have been set previously here or by |display-<names>=etal)
if not nocat then -- no categorization for |vauthors=
utilities.set_message ('err_etal', {param}); -- and set an error if not added
end
end
end
end
return name, etal;
end
--[[---------------------< N A M E _ I S _ N U M E R I C >----------------------
Add an error message and category when <name> parameter value does not contain letters.
Add a maintenance category when <name> parameter value has numeric characters mixed with characters that are
not numeric characters; could be letters and/or punctuation characters.
This function will only emit one error and one maint message for the current template. Does not emit both error
and maint messages/categories for the same parameter value.
returns nothing
]]
local function name_is_numeric (name, name_alias, list_name)
local patterns = {
'^%D+%d', -- <name> must have digits preceded by other characters
'^%D*%d+%D+', -- <name> must have digits followed by other characters
}
if not added_numeric_name_errs and mw.ustring.match (name, '^[%A]+$') then -- if we have not already set an error message and <name> does not have any alpha characters
utilities.set_message ('err_numeric_names', name_alias); -- add an error message
added_numeric_name_errs = true; -- set the flag so we emit only one error message
return; -- when here no point in further testing; abandon
end
if not added_numeric_name_maint then -- if we have already set a maint message
for _, pattern in ipairs (patterns) do -- spin through list of patterns
if mw.ustring.match (name, pattern) then -- digits preceded or followed by anything but digits; %D+ includes punctuation
utilities.set_message ('maint_numeric_names', cfg.special_case_translation [list_name]); -- add a maint cat for this template
added_numeric_name_maint = true; -- set the flag so we emit only one maint message
return; -- when here no point in further testing; abandon
end
end
end
end
--[[-----------------< N A M E _ H A S _ M U L T _ N A M E S >------------------
Evaluates the content of last/surname (authors etc.) parameters for multiple names.
Multiple names are indicated if there is more than one comma or any "unescaped"
semicolons. Escaped semicolons are ones used as part of selected HTML entities.
If the condition is met, the function adds the multiple name maintenance category.
Same test for first except that commas should not appear in given names (MOS:JR says
that the generational suffix does not take a separator character). Titles, degrees,
postnominals, affiliations, all normally comma separated don't belong in a citation.
<name> – name parameter value
<list_name> – AuthorList, EditorList, etc
<limit> – number of allowed commas; 1 (default) for surnames; 0 for given names
returns nothing
]]
local function name_has_mult_names (name, list_name, limit)
local _, commas, semicolons, nbsps;
limit = limit and limit or 1;
if utilities.is_set (name) then
_, commas = name:gsub (',', ''); -- count the number of commas
_, semicolons = name:gsub (';', ''); -- count the number of semicolons
-- nbsps probably should be its own separate count rather than merged in
-- some way with semicolons because Lua patterns do not support the
-- grouping operator that regex does, which means there is no way to add
-- more entities to escape except by adding more counts with the new
-- entities
_, nbsps = name:gsub (' ',''); -- count nbsps
-- There is exactly 1 semicolon per entity, so subtract nbsps
-- from semicolons to 'escape' them. If additional entities are added,
-- they also can be subtracted.
if limit < commas or 0 < (semicolons - nbsps) then
utilities.set_message ('maint_mult_names', cfg.special_case_translation [list_name]); -- add a maint message
end
end
end
--[=[-------------------------< I S _ G E N E R I C >----------------------------------------------------------
Compares values assigned to various parameters according to the string provided as <item> in the function call.
<item> can have on of two values:
'generic_names' – for name-holding parameters: |last=, |first=, |editor-last=, etc
'generic_titles' – for |title=
There are two types of generic tests. The 'accept' tests look for a pattern that should not be rejected by the
'reject' test. For example,
|author=[[John Smith (author)|Smith, John]]
would be rejected by the 'author' reject test. But piped wikilinks with 'author' disambiguation should not be
rejected so the 'accept' test prevents that from happening. Accept tests are always performed before reject
tests.
Each of the 'accept' and 'reject' sequence tables hold tables for en.wiki (['en']) and local.wiki (['local'])
that each can hold a test sequence table The sequence table holds, at index [1], a test pattern, and, at index
[2], a boolean control value. The control value tells string.find() or mw.ustring.find() to do plain-text search (true)
or a pattern search (false). The intent of all this complexity is to make these searches as fast as possible so
that we don't run out of processing time on very large articles.
Returns
true when a reject test finds the pattern or string
false when an accept test finds the pattern or string
nil else
]=]
local function is_generic (item, value, wiki)
local test_val;
local str_lower = { -- use string.lower() for en.wiki (['en']) and use mw.ustring.lower() or local.wiki (['local'])
['en'] = string.lower,
['local'] = mw.ustring.lower,
}
local str_find = { -- use string.find() for en.wiki (['en']) and use mw.ustring.find() or local.wiki (['local'])
['en'] = string.find,
['local'] = mw.ustring.find,
}
local function test (val, test_t, wiki) -- local function to do the testing; <wiki> selects lower() and find() functions
val = test_t[2] and str_lower[wiki](value) or val; -- when <test_t[2]> set to 'true', plaintext search using lowercase value
return str_find[wiki] (val, test_t[1], 1, test_t[2]); -- return nil when not found or matched
end
local test_types_t = {'accept', 'reject'}; -- test accept patterns first, then reject patterns
local wikis_t = {'en', 'local'}; -- do tests for each of these keys; en.wiki first, local.wiki second
for _, test_type in ipairs (test_types_t) do -- for each test type
for _, generic_value in pairs (cfg.special_case_translation[item][test_type]) do -- spin through the list of generic value fragments to accept or reject
for _, wiki in ipairs (wikis_t) do
if generic_value[wiki] then
if test (value, generic_value[wiki], wiki) then -- go do the test
return ('reject' == test_type); -- param value rejected, return true; false else
end
end
end
end
end
end
--[[--------------------------< N A M E _ I S _ G E N E R I C >------------------------------------------------
calls is_generic() to determine if <name> is a 'generic name' listed in cfg.generic_names; <name_alias> is the
parameter name used in error messaging
]]
local function name_is_generic (name, name_alias)
if not added_generic_name_errs and is_generic ('generic_names', name) then
utilities.set_message ('err_generic_name', name_alias); -- set an error message
added_generic_name_errs = true;
end
end
--[[--------------------------< N A M E _ C H E C K S >--------------------------------------------------------
This function calls various name checking functions used to validate the content of the various name-holding parameters.
]]
local function name_checks (last, first, list_name, last_alias, first_alias)
local accept_name;
if utilities.is_set (last) then
last, accept_name = utilities.has_accept_as_written (last); -- remove accept-this-as-written markup when it wraps all of <last>
if not accept_name then -- <last> not wrapped in accept-as-written markup
name_has_mult_names (last, list_name); -- check for multiple names in the parameter
name_is_numeric (last, last_alias, list_name); -- check for names that have no letters or are a mix of digits and other characters
name_is_generic (last, last_alias); -- check for names found in the generic names list
end
end
if utilities.is_set (first) then
first, accept_name = utilities.has_accept_as_written (first); -- remove accept-this-as-written markup when it wraps all of <first>
if not accept_name then -- <first> not wrapped in accept-as-written markup
name_has_mult_names (first, list_name, 0); -- check for multiple names in the parameter; 0 is number of allowed commas in a given name
name_is_numeric (first, first_alias, list_name); -- check for names that have no letters or are a mix of digits and other characters
name_is_generic (first, first_alias); -- check for names found in the generic names list
end
local wl_type, D = utilities.is_wikilink (first);
if 0 ~= wl_type then
first = D;
utilities.set_message ('err_bad_paramlink', first_alias);
end
end
return last, first; -- done
end
--[[----------------------< E X T R A C T _ N A M E S >-------------------------
Gets name list from the input arguments
Searches through args in sequential order to find |lastn= and |firstn= parameters
(or their aliases), and their matching link and mask parameters. Stops searching
when both |lastn= and |firstn= are not found in args after two sequential attempts:
found |last1=, |last2=, and |last3= but doesn't find |last4= and |last5= then the
search is done.
This function emits an error message when there is a |firstn= without a matching
|lastn=. When there are 'holes' in the list of last names, |last1= and |last3=
are present but |last2= is missing, an error message is emitted. |lastn= is not
required to have a matching |firstn=.
When an author or editor parameter contains some form of 'et al.', the 'et al.'
is stripped from the parameter and a flag (etal) returned that will cause list_people()
to add the static 'et al.' text from Module:Citation/CS1/Configuration. This keeps
'et al.' out of the template's metadata. When this occurs, an error is emitted.
]]
local function extract_names(args, list_name)
local names = {}; -- table of names
local last; -- individual name components
local first;
local link;
local mask;
local i = 1; -- loop counter/indexer
local n = 1; -- output table indexer
local count = 0; -- used to count the number of times we haven't found a |last= (or alias for authors, |editor-last or alias for editors)
local etal = false; -- return value set to true when we find some form of et al. in an author parameter
local last_alias, first_alias, link_alias; -- selected parameter aliases used in error messaging
while true do
last, last_alias = utilities.select_one ( args, cfg.aliases[list_name .. '-Last'], 'err_redundant_parameters', i ); -- search through args for name components beginning at 1
first, first_alias = utilities.select_one ( args, cfg.aliases[list_name .. '-First'], 'err_redundant_parameters', i );
link, link_alias = utilities.select_one ( args, cfg.aliases[list_name .. '-Link'], 'err_redundant_parameters', i );
mask = utilities.select_one ( args, cfg.aliases[list_name .. '-Mask'], 'err_redundant_parameters', i );
if last then -- error check |lastn= alias for unknown interwiki link prefix; done here because this is where we have the parameter name
local project, language = interwiki_prefixen_get (last, true); -- true because we expect interwiki links in |lastn= to be wikilinked
if nil == project and nil == language then -- when both are nil
utilities.set_message ('err_bad_paramlink', last_alias); -- not known, emit an error message -- TODO: err_bad_interwiki?
last = utilities.remove_wiki_link (last); -- remove wikilink markup; show display value only
end
end
if link then -- error check |linkn= alias for unknown interwiki link prefix
local project, language = interwiki_prefixen_get (link, false); -- false because wiki links in |author-linkn= is an error
if nil == project and nil == language then -- when both are nil
utilities.set_message ('err_bad_paramlink', link_alias); -- not known, emit an error message -- TODO: err_bad_interwiki?
link = nil; -- unset so we don't link
link_alias = nil;
end
end
last, etal = name_has_etal (last, etal, false, last_alias); -- find and remove variations on et al.
first, etal = name_has_etal (first, etal, false, first_alias); -- find and remove variations on et al.
last, first = name_checks (last, first, list_name, last_alias, first_alias); -- multiple names, extraneous annotation, etc. checks
if first and not last then -- if there is a firstn without a matching lastn
local alias = first_alias:find ('given', 1, true) and 'given' or 'first'; -- get first or given form of the alias
utilities.set_message ('err_first_missing_last', {
first_alias, -- param name of alias missing its mate
first_alias:gsub (alias, {['first'] = 'last', ['given'] = 'surname'}), -- make param name appropriate to the alias form
}); -- add this error message
elseif not first and not last then -- if both firstn and lastn aren't found, are we done?
count = count + 1; -- number of times we haven't found last and first
if 2 <= count then -- two missing names and we give up
break; -- normal exit or there is a two-name hole in the list; can't tell which
end
else -- we have last with or without a first
local result;
link = link_title_ok (link, link_alias, last, last_alias); -- check for improper wiki-markup
if first then
link = link_title_ok (link, link_alias, first, first_alias); -- check for improper wiki-markup
end
names[n] = {last = last, first = first, link = link, mask = mask, corporate = false}; -- add this name to our names list (corporate for |vauthors= only)
n = n + 1; -- point to next location in the names table
if 1 == count then -- if the previous name was missing
utilities.set_message ('err_missing_name', {list_name:match ("(%w+)List"):lower(), i - 1}); -- add this error message
end
count = 0; -- reset the counter, we're looking for two consecutive missing names
end
i = i + 1; -- point to next args location
end
return names, etal; -- all done, return our list of names and the etal flag
end
--[[--------------------------< N A M E _ T A G _ G E T >------------------------------------------------------
attempt to decode |language=<lang_param> and return language name and matching tag; nil else.
This function looks for:
<lang_param> as a tag in cfg.lang_tag_remap{}
<lang_param> as a name in cfg.lang_name_remap{}
<lang_param> as a name in cfg.mw_languages_by_name_t
<lang_param> as a tag in cfg.mw_languages_by_tag_t
when those fail, presume that <lang_param> is an IETF-like tag that MediaWiki does not recognize. Strip all
script, region, variant, whatever subtags from <lang_param> to leave just a two or three character language tag
and look for the new <lang_param> in cfg.mw_languages_by_tag_t{}
on success, returns name (in properly capitalized form) and matching tag (in lowercase); on failure returns nil
]]
local function name_tag_get (lang_param)
local lang_param_lc = mw.ustring.lower (lang_param); -- use lowercase as an index into the various tables
local name;
local tag;
name = cfg.lang_tag_remap[lang_param_lc]; -- assume <lang_param_lc> is a tag; attempt to get remapped language name
if name then -- when <name>, <lang_param> is a tag for a remapped language name
if cfg.lang_name_remap[name:lower()][2] ~= lang_param_lc then
utilities.set_message ('maint_unknown_lang'); -- add maint category if not already added
return name, cfg.lang_name_remap[name:lower()][2]; -- so return name and tag from lang_name_remap[name]; special case to xlate sr-ec and sr-el to sr-cyrl and sr-latn
end
return name, lang_param_lc; -- so return <name> from remap and <lang_param_lc>
end
tag = lang_param_lc:match ('^(%a%a%a?)%-.*'); -- still assuming that <lang_param_lc> is a tag; strip script, region, variant subtags
name = cfg.lang_tag_remap[tag]; -- attempt to get remapped language name with language subtag only
if name then -- when <name>, <tag> is a tag for a remapped language name
return name, tag; -- so return <name> from remap and <tag>
end
if cfg.lang_name_remap[lang_param_lc] then -- not a remapped tag, assume <lang_param_lc> is a name; attempt to get remapped language tag
return cfg.lang_name_remap[lang_param_lc][1], cfg.lang_name_remap[lang_param_lc][2]; -- for this <lang_param_lc>, return a (possibly) new name and appropriate tag
end
name = cfg.mw_languages_by_tag_t[lang_param_lc]; -- assume that <lang_param_lc> is a tag; attempt to get its matching language name
if name then
return name, lang_param_lc; -- <lang_param_lc> is a tag so return it and <name>
end
tag = cfg.mw_languages_by_name_t[lang_param_lc]; -- assume that <lang_param_lc> is a language name; attempt to get its matching tag
if tag then
return cfg.mw_languages_by_tag_t[tag], tag; -- <lang_param_lc> is a name so return the name from the table and <tag>
end
tag = lang_param_lc:match ('^(%a%a%a?)%-.*'); -- is <lang_param_lc> an IETF-like tag that MediaWiki doesn't recognize? <tag> gets the language subtag; nil else
if tag then
name = cfg.mw_languages_by_tag_t[tag]; -- attempt to get a language name using the shortened <tag>
if name then
return name, tag; -- <lang_param_lc> is an unrecognized IETF-like tag so return <name> and language subtag
end
end
end
--[[-------------------< L A N G U A G E _ P A R A M E T E R >------------------
Gets language name from a provided two- or three-character ISO 639 code. If a code
is recognized by MediaWiki, use the returned name; if not, then use the value that
was provided with the language parameter.
When |language= contains a recognized language (either code or name), the page is
assigned to the category for that code: Category:Norwegian-language sources (no).
For valid three-character code languages, the page is assigned to the single category
for '639-2' codes: Category:CS1 ISO 639-2 language sources.
Languages that are the same as the local wiki are not categorized. MediaWiki does
not recognize three-character equivalents of two-character codes: code 'ar' is
recognized but code 'ara' is not.
This function supports multiple languages in the form |language=nb, French, th
where the language names or codes are separated from each other by commas with
optional space characters.
]]
local function language_parameter (lang)
local tag; -- some form of IETF-like language tag; language subtag with optional region, sript, vatiant, etc subtags
local lang_subtag; -- ve populates |language= with mostly unecessary region subtags the MediaWiki does not recognize; this is the base language subtag
local name; -- the language name
local language_list = {}; -- table of language names to be rendered
local names_t = {}; -- table made from the value assigned to |language=
local this_wiki_name = mw.language.fetchLanguageName (cfg.this_wiki_code, cfg.this_wiki_code); -- get this wiki's language name
names_t = mw.text.split (lang, '%s*,%s*'); -- names should be a comma separated list
for _, lang in ipairs (names_t) do -- reuse lang here because we don't yet know if lang is a language name or a language tag
name, tag = name_tag_get (lang); -- attempt to get name/tag pair for <lang>; <name> has proper capitalization; <tag> is lowercase
if utilities.is_set (tag) then
lang_subtag = tag:gsub ('^(%a%a%a?)%-.*', '%1'); -- for categorization, strip any IETF-like tags from language tag
if cfg.this_wiki_code ~= lang_subtag then -- when the language is not the same as this wiki's language
if 2 == lang_subtag:len() then -- and is a two-character tag
utilities.add_prop_cat ('foreign-lang-source', {name, tag}, lang_subtag); -- categorize it; tag appended to allow for multiple language categorization
else -- or is a recognized language (but has a three-character tag)
utilities.add_prop_cat ('foreign-lang-source-2', {lang_subtag}, lang_subtag); -- categorize it differently TODO: support multiple three-character tag categories per cs1|2 template?
end
elseif cfg.local_lang_cat_enable then -- when the language and this wiki's language are the same and categorization is enabled
utilities.add_prop_cat ('local-lang-source', {name, lang_subtag}); -- categorize it
end
else
name = lang; -- return whatever <lang> has so that we show something
utilities.set_message ('maint_unknown_lang'); -- add maint category if not already added
end
table.insert (language_list, name);
name = ''; -- so we can reuse it
end
name = utilities.make_sep_list (#language_list, language_list);
if (1 == #language_list) and (lang_subtag == cfg.this_wiki_code) then -- when only one language, find lang name in this wiki lang name; for |language=en-us, 'English' in 'American English'
return ''; -- if one language and that language is this wiki's return an empty string (no annotation)
end
return (" " .. wrap_msg ('language', name)); -- otherwise wrap with '(in ...)'
--[[ TODO: should only return blank or name rather than full list
so we can clean up the bunched parenthetical elements Language, Type, Format
]]
end
--[[-----------------------< S E T _ C S _ S T Y L E >--------------------------
Gets the default CS style configuration for the given mode.
Returns default separator and either postscript as passed in or the default.
In CS1, the default postscript and separator are '.'.
In CS2, the default postscript is the empty string and the default separator is ','.
]]
local function set_cs_style (postscript, mode)
if utilities.is_set(postscript) then
-- emit a maintenance message if user postscript is the default cs1 postscript
-- we catch the opposite case for cs2 in set_style
if mode == 'cs1' and postscript == cfg.presentation['ps_' .. mode] then
utilities.set_message ('maint_postscript');
end
else
postscript = cfg.presentation['ps_' .. mode];
end
return cfg.presentation['sep_' .. mode], postscript;
end
--[[--------------------------< S E T _ S T Y L E >-----------------------------
Sets the separator and postscript styles. Checks the |mode= first and the
#invoke CitationClass second. Removes the postscript if postscript == none.
]]
local function set_style (mode, postscript, cite_class)
local sep;
if 'cs2' == mode then
sep, postscript = set_cs_style (postscript, 'cs2');
elseif 'cs1' == mode then
sep, postscript = set_cs_style (postscript, 'cs1');
elseif 'citation' == cite_class then
sep, postscript = set_cs_style (postscript, 'cs2');
else
sep, postscript = set_cs_style (postscript, 'cs1');
end
if cfg.keywords_xlate[postscript:lower()] == 'none' then
-- emit a maintenance message if user postscript is the default cs2 postscript
-- we catch the opposite case for cs1 in set_cs_style
if 'cs2' == mode or ('cs1' ~= mode and 'citation' == cite_class) then -- {{citation |title=Title |mode=cs1 |postscript=none}} should not emit maint message
utilities.set_message ('maint_postscript');
end
postscript = '';
end
return sep, postscript
end
--[=[-------------------------< I S _ P D F >-----------------------------------
Determines if a URL has the file extension that is one of the PDF file extensions
used by [[MediaWiki:Common.css]] when applying the PDF icon to external links.
returns true if file extension is one of the recognized extensions, else false
]=]
local function is_pdf (url)
return url:match ('%.pdf$') or url:match ('%.PDF$') or
url:match ('%.pdf[%?#]') or url:match ('%.PDF[%?#]') or
url:match ('%.PDF#') or url:match ('%.pdf#');
end
--[[--------------------------< S T Y L E _ F O R M A T >-----------------------
Applies CSS style to |format=, |chapter-format=, etc. Also emits an error message
if the format parameter does not have a matching URL parameter. If the format parameter
is not set and the URL contains a file extension that is recognized as a PDF document
by MediaWiki's commons.css, this code will set the format parameter to (PDF) with
the appropriate styling.
]]
local function style_format (format, url, fmt_param, url_param)
if utilities.is_set (format) then
format = utilities.wrap_style ('format', format); -- add leading space, parentheses, resize
if not utilities.is_set (url) then
utilities.set_message ('err_format_missing_url', {fmt_param, url_param}); -- add an error message
end
elseif is_pdf (url) then -- format is not set so if URL is a PDF file then
format = utilities.wrap_style ('format', 'PDF'); -- set format to PDF
else
format = ''; -- empty string for concatenation
end
return format;
end
--[[---------------------< G E T _ D I S P L A Y _ N A M E S >------------------
Returns a number that defines the number of names displayed for author and editor
name lists and a Boolean flag to indicate when et al. should be appended to the name list.
When the value assigned to |display-xxxxors= is a number greater than or equal to zero,
return the number and the previous state of the 'etal' flag (false by default
but may have been set to true if the name list contains some variant of the text 'et al.').
When the value assigned to |display-xxxxors= is the keyword 'etal', return a number
that is one greater than the number of authors in the list and set the 'etal' flag true.
This will cause the list_people() to display all of the names in the name list followed by 'et al.'
In all other cases, returns nil and the previous state of the 'etal' flag.
inputs:
max: A['DisplayAuthors'] or A['DisplayEditors'], etc; a number or some flavor of etal
count: #a or #e
list_name: 'authors' or 'editors'
etal: author_etal or editor_etal
This function sets an error message when |display-xxxxors= value greater than or equal to number of names but
not when <max> comes from {{cs1 config}} global settings. When using global settings, <param> is set to the
keyword 'cs1 config' which is used to supress the normal error. Error is suppressed because it is to be expected
that some citations in an article will have the same or fewer names that the limit specified in {{cs1 config}}.
]]
local function get_display_names (max, count, list_name, etal, param)
if utilities.is_set (max) then
if 'etal' == max:lower():gsub("[ '%.]", '') then -- the :gsub() portion makes 'etal' from a variety of 'et al.' spellings and stylings
max = count + 1; -- number of authors + 1 so display all author name plus et al.
etal = true; -- overrides value set by extract_names()
elseif max:match ('^%d+$') then -- if is a string of numbers
max = tonumber (max); -- make it a number
if (max >= count) and ('cs1 config' ~= param) then -- error when local |display-xxxxors= value greater than or equal to number of names; not an error when using global setting
utilities.set_message ('err_disp_name', {param, max}); -- add error message
max = nil;
end
else -- not a valid keyword or number
utilities.set_message ('err_disp_name', {param, max}); -- add error message
max = nil; -- unset; as if |display-xxxxors= had not been set
end
end
return max, etal;
end
--[[----------< E X T R A _ T E X T _ I N _ P A G E _ C H E C K >---------------
Adds error if |page=, |pages=, |quote-page=, |quote-pages= has what appears to be
some form of p. or pp. abbreviation in the first characters of the parameter content.
check page for extraneous p, p., pp, pp., pg, pg. at start of parameter value:
good pattern: '^P[^%.P%l]' matches when page begins PX or P# but not Px
where x and X are letters and # is a digit
bad pattern: '^[Pp][PpGg]' matches when page begins pp, pP, Pp, PP, pg, pG, Pg, PG
]]
local function extra_text_in_page_check (val, name)
if not val:match (cfg.vol_iss_pg_patterns.good_ppattern) then
for _, pattern in ipairs (cfg.vol_iss_pg_patterns.bad_ppatterns) do -- spin through the selected sequence table of patterns
if val:match (pattern) then -- when a match, error so
utilities.set_message ('err_extra_text_pages', name); -- add error message
return; -- and done
end
end
end
end
--[[--------------------------< E X T R A _ T E X T _ I N _ V O L _ I S S _ C H E C K >------------------------
Adds error if |volume= or |issue= has what appears to be some form of redundant 'type' indicator. Applies to
both; this function looks for issue text in both |issue= and |volume= and looks for volume-like text in |voluem=
and |issue=.
For |volume=:
'V.', or 'Vol.' (with or without the dot) abbreviations or 'Volume' in the first characters of the parameter
content (all case insensitive). 'V' and 'v' (without the dot) are presumed to be roman numerals so
are allowed.
For |issue=:
'No.', 'I.', 'Iss.' (with or without the dot) abbreviations, or 'Issue' in the first characters of the
parameter content (all case insensitive); numero styling: 'n°' with degree sign U+00B0, and № precomposed
numero sign U+2116.
Single character values ('v', 'i', 'n') allowed when not followed by separator character ('.', ':', '=', or
whitespace character) – param values are trimmed of whitespace by MediaWiki before delivered to the module.
<val> is |volume= or |issue= parameter value
<name> is |volume= or |issue= parameter name for error message
<selector> is 'v' for |volume=, 'i' for |issue=
sets error message on failure; returns nothing
]]
local function extra_text_in_vol_iss_check (val, name, selector)
if not utilities.is_set (val) then
return;
end
local handler = 'v' == selector and 'err_extra_text_volume' or 'err_extra_text_issue';
val = val:lower(); -- force parameter value to lower case
for _, pattern in ipairs (cfg.vol_iss_pg_patterns.vi_patterns_t) do -- spin through the sequence table of patterns
if val:match (pattern) then -- when a match, error so
utilities.set_message (handler, name); -- add error message
return; -- and done
end
end
end
--[=[-------------------------< G E T _ V _ N A M E _ T A B L E >----------------------------------------------
split apart a |vauthors= or |veditors= parameter. This function allows for corporate names, wrapped in doubled
parentheses to also have commas; in the old version of the code, the doubled parentheses were included in the
rendered citation and in the metadata. Individual author names may be wikilinked
|vauthors=Jones AB, [[E. B. White|White EB]], ((Black, Brown, and Co.))
]=]
local function get_v_name_table (vparam, output_table, output_link_table)
local _, accept = utilities.has_accept_as_written (vparam);
if accept then
utilities.add_prop_cat ('vanc-accept'); -- add properties category
end
local name_table = mw.text.split(vparam, "%s*,%s*"); -- names are separated by commas
local wl_type, label, link; -- wl_type not used here; just a placeholder
local i = 1;
while name_table[i] do
if name_table[i]:match ('^%(%(.*[^%)][^%)]$') then -- first segment of corporate with one or more commas; this segment has the opening doubled parentheses
local name = name_table[i];
i = i + 1; -- bump indexer to next segment
while name_table[i] do
name = name .. ', ' .. name_table[i]; -- concatenate with previous segments
if name_table[i]:match ('^.*%)%)$') then -- if this table member has the closing doubled parentheses
break; -- and done reassembling so
end
i = i + 1; -- bump indexer
end
table.insert (output_table, name); -- and add corporate name to the output table
table.insert (output_link_table, ''); -- no wikilink
else
wl_type, label, link = utilities.is_wikilink (name_table[i]); -- wl_type is: 0, no wl (text in label variable); 1, [[D]]; 2, [[L|D]]
table.insert (output_table, label); -- add this name
if 1 == wl_type then
table.insert (output_link_table, label); -- simple wikilink [[D]]
else
table.insert (output_link_table, link); -- no wikilink or [[L|D]]; add this link if there is one, else empty string
end
end
i = i + 1;
end
return output_table;
end
--[[--------------------------< P A R S E _ V A U T H O R S _ V E D I T O R S >--------------------------------
This function extracts author / editor names from |vauthors= or |veditors= and finds matching |xxxxor-maskn= and
|xxxxor-linkn= in args. It then returns a table of assembled names just as extract_names() does.
Author / editor names in |vauthors= or |veditors= must be in Vancouver system style. Corporate or institutional names
may sometimes be required and because such names will often fail the is_good_vanc_name() and other format compliance
tests, are wrapped in doubled parentheses ((corporate name)) to suppress the format tests.
Supports generational suffixes Jr, 2nd, 3rd, 4th–6th.
This function sets the Vancouver error when a required comma is missing and when there is a space between an author's initials.
]]
local function parse_vauthors_veditors (args, vparam, list_name)
local names = {}; -- table of names assembled from |vauthors=, |author-maskn=, |author-linkn=
local v_name_table = {};
local v_link_table = {}; -- when name is wikilinked, targets go in this table
local etal = false; -- return value set to true when we find some form of et al. vauthors parameter
local last, first, link, mask, suffix;
local corporate = false;
vparam, etal = name_has_etal (vparam, etal, true); -- find and remove variations on et al. do not categorize (do it here because et al. might have a period)
v_name_table = get_v_name_table (vparam, v_name_table, v_link_table); -- names are separated by commas
for i, v_name in ipairs(v_name_table) do
first = ''; -- set to empty string for concatenation and because it may have been set for previous author/editor
local accept_name;
v_name, accept_name = utilities.has_accept_as_written (v_name); -- remove accept-this-as-written markup when it wraps all of <v_name>
if accept_name then
last = v_name;
corporate = true; -- flag used in list_people()
elseif string.find(v_name, "%s") then
if v_name:find('[;%.]') then -- look for commonly occurring punctuation characters;
add_vanc_error (cfg.err_msg_supl.punctuation, i);
end
local lastfirstTable = {}
lastfirstTable = mw.text.split(v_name, "%s+")
first = table.remove(lastfirstTable); -- removes and returns value of last element in table which should be initials or generational suffix
if not mw.ustring.match (first, '^%u+$') then -- mw.ustring here so that later we will catch non-Latin characters
suffix = first; -- not initials so assume that whatever we got is a generational suffix
first = table.remove(lastfirstTable); -- get what should be the initials from the table
end
last = table.concat(lastfirstTable, ' ') -- returns a string that is the concatenation of all other names that are not initials and generational suffix
if not utilities.is_set (last) then
first = ''; -- unset
last = v_name; -- last empty because something wrong with first
add_vanc_error (cfg.err_msg_supl.name, i);
end
if mw.ustring.match (last, '%a+%s+%u+%s+%a+') then
add_vanc_error (cfg.err_msg_supl['missing comma'], i); -- matches last II last; the case when a comma is missing
end
if mw.ustring.match (v_name, ' %u %u$') then -- this test is in the wrong place TODO: move or replace with a more appropriate test
add_vanc_error (cfg.err_msg_supl.initials, i); -- matches a space between two initials
end
else
last = v_name; -- last name or single corporate name? Doesn't support multiword corporate names? do we need this?
end
if utilities.is_set (first) then
if not mw.ustring.match (first, "^%u?%u$") then -- first shall contain one or two upper-case letters, nothing else
add_vanc_error (cfg.err_msg_supl.initials, i); -- too many initials; mixed case initials (which may be ok Romanization); hyphenated initials
end
is_good_vanc_name (last, first, suffix, i); -- check first and last before restoring the suffix which may have a non-Latin digit
if utilities.is_set (suffix) then
first = first .. ' ' .. suffix; -- if there was a suffix concatenate with the initials
suffix = ''; -- unset so we don't add this suffix to all subsequent names
end
else
if not corporate then
is_good_vanc_name (last, '', nil, i);
end
end
link = utilities.select_one ( args, cfg.aliases[list_name .. '-Link'], 'err_redundant_parameters', i ) or v_link_table[i];
mask = utilities.select_one ( args, cfg.aliases[list_name .. '-Mask'], 'err_redundant_parameters', i );
names[i] = {last = last, first = first, link = link, mask = mask, corporate = corporate}; -- add this assembled name to our names list
end
return names, etal; -- all done, return our list of names
end
--[[--------------------------< S E L E C T _ A U T H O R _ E D I T O R _ S O U R C E >------------------------
Select one of |authors=, |authorn= / |lastn / firstn=, or |vauthors= as the source of the author name list or
select one of |editorn= / editor-lastn= / |editor-firstn= or |veditors= as the source of the editor name list.
Only one of these appropriate three will be used. The hierarchy is: |authorn= (and aliases) highest and |authors= lowest;
|editorn= (and aliases) highest and |veditors= lowest (support for |editors= withdrawn)
When looking for |authorn= / |editorn= parameters, test |xxxxor1= and |xxxxor2= (and all of their aliases); stops after the second
test which mimicks the test used in extract_names() when looking for a hole in the author name list. There may be a better
way to do this, I just haven't discovered what that way is.
Emits an error message when more than one xxxxor name source is provided.
In this function, vxxxxors = vauthors or veditors; xxxxors = authors as appropriate.
]]
local function select_author_editor_source (vxxxxors, xxxxors, args, list_name)
local lastfirst = false;
if utilities.select_one ( args, cfg.aliases[list_name .. '-Last'], 'none', 1 ) or -- do this twice in case we have a |first1= without a |last1=; this ...
utilities.select_one ( args, cfg.aliases[list_name .. '-First'], 'none', 1 ) or -- ... also catches the case where |first= is used with |vauthors=
utilities.select_one ( args, cfg.aliases[list_name .. '-Last'], 'none', 2 ) or
utilities.select_one ( args, cfg.aliases[list_name .. '-First'], 'none', 2 ) then
lastfirst = true;
end
if (utilities.is_set (vxxxxors) and true == lastfirst) or -- these are the three error conditions
(utilities.is_set (vxxxxors) and utilities.is_set (xxxxors)) or
(true == lastfirst and utilities.is_set (xxxxors)) then
local err_name;
if 'AuthorList' == list_name then -- figure out which name should be used in error message
err_name = 'author';
else
err_name = 'editor';
end
utilities.set_message ('err_redundant_parameters', err_name .. '-name-list parameters'); -- add error message
end
if true == lastfirst then return 1 end; -- return a number indicating which author name source to use
if utilities.is_set (vxxxxors) then return 2 end;
if utilities.is_set (xxxxors) then return 3 end;
return 1; -- no authors so return 1; this allows missing author name test to run in case there is a first without last
end
--[[--------------------------< I S _ V A L I D _ P A R A M E T E R _ V A L U E >------------------------------
This function is used to validate a parameter's assigned value for those parameters that have only a limited number
of allowable values (yes, y, true, live, dead, etc.). When the parameter value has not been assigned a value (missing
or empty in the source template) the function returns the value specified by ret_val. If the parameter value is one
of the list of allowed values returns the translated value; else, emits an error message and returns the value
specified by ret_val.
TODO: explain <invert>
]]
local function is_valid_parameter_value (value, name, possible, ret_val, invert)
if not utilities.is_set (value) then
return ret_val; -- an empty parameter is ok
end
if (not invert and utilities.in_array (value, possible)) then -- normal; <value> is in <possible> table
return cfg.keywords_xlate[value]; -- return translation of parameter keyword
elseif invert and not utilities.in_array (value, possible) then -- invert; <value> is not in <possible> table
return value; -- return <value> as it is
else
utilities.set_message ('err_invalid_param_val', {name, value}); -- not an allowed value so add error message
return ret_val;
end
end
--[[--------------------------< T E R M I N A T E _ N A M E _ L I S T >----------------------------------------
This function terminates a name list (author, contributor, editor) with a separator character (sepc) and a space
when the last character is not a sepc character or when the last three characters are not sepc followed by two
closing square brackets (close of a wikilink). When either of these is true, the name_list is terminated with a
single space character.
]]
local function terminate_name_list (name_list, sepc)
if (string.sub (name_list, -3, -1) == sepc .. '. ') then -- if already properly terminated
return name_list; -- just return the name list
elseif (string.sub (name_list, -1, -1) == sepc) or (string.sub (name_list, -3, -1) == sepc .. ']]') then -- if last name in list ends with sepc char
return name_list .. " "; -- don't add another
else
return name_list .. sepc .. ' '; -- otherwise terminate the name list
end
end
--[[-------------------------< F O R M A T _ V O L U M E _ I S S U E >-----------------------------------------
returns the concatenation of the formatted volume and issue (or journal article number) parameters as a single
string; or formatted volume or formatted issue, or an empty string if neither are set.
]]
local function format_volume_issue (volume, issue, article, cite_class, origin, sepc, lower)
if not utilities.is_set (volume) and not utilities.is_set (issue) and not utilities.is_set (article) then
return '';
end
-- same condition as in format_pages_sheets()
local is_journal = 'journal' == cite_class or (utilities.in_array (cite_class, {'citation', 'map', 'interview'}) and 'journal' == origin);
local is_numeric_vol = volume and (volume:match ('^[MDCLXVI]+$') or volume:match ('^%d+$')); -- is only uppercase roman numerals or only digits?
local is_long_vol = volume and (4 < mw.ustring.len(volume)); -- is |volume= value longer than 4 characters?
if volume and (not is_numeric_vol and is_long_vol) then -- when not all digits or Roman numerals, is |volume= longer than 4 characters?
utilities.add_prop_cat ('long-vol'); -- yes, add properties cat
end
if is_journal then -- journal-style formatting
local vol = '';
if utilities.is_set (volume) then
if is_numeric_vol then -- |volume= value all digits or all uppercase Roman numerals?
vol = utilities.substitute (cfg.presentation['vol-bold'], {sepc, volume}); -- render in bold face
elseif is_long_vol then -- not all digits or Roman numerals; longer than 4 characters?
vol = utilities.substitute (cfg.messages['j-vol'], {sepc, utilities.hyphen_to_dash (volume)}); -- not bold
else -- four or fewer characters
vol = utilities.substitute (cfg.presentation['vol-bold'], {sepc, utilities.hyphen_to_dash (volume)}); -- bold
end
end
vol = vol .. (utilities.is_set (issue) and utilities.substitute (cfg.messages['j-issue'], issue) or '')
vol = vol .. (utilities.is_set (article) and utilities.substitute (cfg.messages['j-article-num'], article) or '')
return vol;
end
if 'podcast' == cite_class and utilities.is_set (issue) then
return wrap_msg ('issue', {sepc, issue}, lower);
end
if 'conference' == cite_class and utilities.is_set (article) then -- |article-number= supported only in journal and conference cites
if utilities.is_set (volume) and utilities.is_set (article) then -- both volume and article number
return wrap_msg ('vol-art', {sepc, utilities.hyphen_to_dash (volume), article}, lower);
elseif utilities.is_set (article) then -- article number alone; when volume alone, handled below
return wrap_msg ('art', {sepc, article}, lower);
end
end
-- all other types of citation
if utilities.is_set (volume) and utilities.is_set (issue) then
return wrap_msg ('vol-no', {sepc, utilities.hyphen_to_dash (volume), issue}, lower);
elseif utilities.is_set (volume) then
return wrap_msg ('vol', {sepc, utilities.hyphen_to_dash (volume)}, lower);
else
return wrap_msg ('issue', {sepc, issue}, lower);
end
end
--[[-------------------------< F O R M A T _ P A G E S _ S H E E T S >-----------------------------------------
adds static text to one of |page(s)= or |sheet(s)= values and returns it with all of the others set to empty strings.
The return order is:
page, pages, sheet, sheets
Singular has priority over plural when both are provided.
]]
local function format_pages_sheets (page, pages, sheet, sheets, cite_class, origin, sepc, nopp, lower)
if 'map' == cite_class then -- only cite map supports sheet(s) as in-source locators
if utilities.is_set (sheet) then
if 'journal' == origin then
return '', '', wrap_msg ('j-sheet', sheet, lower), '';
else
return '', '', wrap_msg ('sheet', {sepc, sheet}, lower), '';
end
elseif utilities.is_set (sheets) then
if 'journal' == origin then
return '', '', '', wrap_msg ('j-sheets', sheets, lower);
else
return '', '', '', wrap_msg ('sheets', {sepc, sheets}, lower);
end
end
end
local is_journal = 'journal' == cite_class or (utilities.in_array (cite_class, {'citation', 'map', 'interview'}) and 'journal' == origin);
if utilities.is_set (page) then
if is_journal then
return utilities.substitute (cfg.messages['j-page(s)'], page), '', '', '';
elseif not nopp then
return utilities.substitute (cfg.messages['p-prefix'], {sepc, page}), '', '', '';
else
return utilities.substitute (cfg.messages['nopp'], {sepc, page}), '', '', '';
end
elseif utilities.is_set (pages) then
if is_journal then
return utilities.substitute (cfg.messages['j-page(s)'], pages), '', '', '';
elseif tonumber(pages) ~= nil and not nopp then -- if pages is only digits, assume a single page number
return '', utilities.substitute (cfg.messages['p-prefix'], {sepc, pages}), '', '';
elseif not nopp then
return '', utilities.substitute (cfg.messages['pp-prefix'], {sepc, pages}), '', '';
else
return '', utilities.substitute (cfg.messages['nopp'], {sepc, pages}), '', '';
end
end
return '', '', '', ''; -- return empty strings
end
--[[--------------------------< I N S O U R C E _ L O C _ G E T >----------------------------------------------
returns one of the in-source locators: page, pages, or at.
If any of these are interwiki links to Wikisource, returns the label portion of the interwiki-link as plain text
for use in COinS. This COinS thing is done because here we convert an interwiki-link to an external link and
add an icon span around that; get_coins_pages() doesn't know about the span. TODO: should it?
TODO: add support for sheet and sheets?; streamline;
TODO: make it so that this function returns only one of the three as the single in-source (the return value assigned
to a new name)?
]]
local function insource_loc_get (page, page_orig, pages, pages_orig, at)
local ws_url, ws_label, coins_pages, L; -- for Wikisource interwiki-links; TODO: this corrupts page metadata (span remains in place after cleanup; fix there?)
if utilities.is_set (page) then
if utilities.is_set (pages) or utilities.is_set (at) then
pages = ''; -- unset the others
at = '';
end
extra_text_in_page_check (page, page_orig); -- emit error message when |page= value begins with what looks like p., pp., etc.
ws_url, ws_label, L = wikisource_url_make (page); -- make ws URL from |page= interwiki link; link portion L becomes tooltip label
if ws_url then
page = external_link (ws_url, ws_label .. ' ', 'ws link in page'); -- space char after label to move icon away from in-source text; TODO: a better way to do this?
page = utilities.substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], L, page});
coins_pages = ws_label;
end
elseif utilities.is_set (pages) then
if utilities.is_set (at) then
at = ''; -- unset
end
extra_text_in_page_check (pages, pages_orig); -- emit error message when |page= value begins with what looks like p., pp., etc.
ws_url, ws_label, L = wikisource_url_make (pages); -- make ws URL from |pages= interwiki link; link portion L becomes tooltip label
if ws_url then
pages = external_link (ws_url, ws_label .. ' ', 'ws link in pages'); -- space char after label to move icon away from in-source text; TODO: a better way to do this?
pages = utilities.substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], L, pages});
coins_pages = ws_label;
end
elseif utilities.is_set (at) then
ws_url, ws_label, L = wikisource_url_make (at); -- make ws URL from |at= interwiki link; link portion L becomes tooltip label
if ws_url then
at = external_link (ws_url, ws_label .. ' ', 'ws link in at'); -- space char after label to move icon away from in-source text; TODO: a better way to do this?
at = utilities.substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], L, at});
coins_pages = ws_label;
end
end
return page, pages, at, coins_pages;
end
--[[--------------------------< I S _ U N I Q U E _ A R C H I V E _ U R L >------------------------------------
add error message when |archive-url= value is same as |url= or chapter-url= (or alias...) value
]]
local function is_unique_archive_url (archive, url, c_url, source, date)
if utilities.is_set (archive) then
if archive == url or archive == c_url then
utilities.set_message ('err_bad_url', {utilities.wrap_style ('parameter', source)}); -- add error message
return '', ''; -- unset |archive-url= and |archive-date= because same as |url= or |chapter-url=
end
end
return archive, date;
end
--[=[-------------------------< A R C H I V E _ U R L _ C H E C K >--------------------------------------------
Check archive.org URLs to make sure they at least look like they are pointing at valid archives and not to the
save snapshot URL or to calendar pages. When the archive URL is 'https://web.archive.org/save/' (or http://...)
archive.org saves a snapshot of the target page in the URL. That is something that Wikipedia should not allow
unwitting readers to do.
When the archive.org URL does not have a complete timestamp, archive.org chooses a snapshot according to its own
algorithm or provides a calendar 'search' result. [[WP:ELNO]] discourages links to search results.
This function looks at the value assigned to |archive-url= and returns empty strings for |archive-url= and
|archive-date= and an error message when:
|archive-url= holds an archive.org save command URL
|archive-url= is an archive.org URL that does not have a complete timestamp (YYYYMMDDhhmmss 14 digits) in the
correct place
otherwise returns |archive-url= and |archive-date=
There are two mostly compatible archive.org URLs:
//web.archive.org/<timestamp>... -- the old form
//web.archive.org/web/<timestamp>... -- the new form
The old form does not support or map to the new form when it contains a display flag. There are four identified flags
('id_', 'js_', 'cs_', 'im_') but since archive.org ignores others following the same form (two letters and an underscore)
we don't check for these specific flags but we do check the form.
This function supports a preview mode. When the article is rendered in preview mode, this function may return a modified
archive URL:
for save command errors, return undated wildcard (/*/)
for timestamp errors when the timestamp has a wildcard, return the URL unmodified
for timestamp errors when the timestamp does not have a wildcard, return with timestamp limited to six digits plus wildcard (/yyyymm*/)
A secondary function is to return an archive-url timestamp from those urls that have them (archive.org and
archive.today). The timestamp is used by validation.archive_date_check() to see if the value in |archive-date=
matches the timestamp in the archive url.
]=]
local function archive_url_check (url, date)
local err_msg = ''; -- start with the error message empty
local path, timestamp, flag; -- portions of the archive.org URL
timestamp = url:match ('//archive.today/(%d%d%d%d%d%d%d%d%d%d%d%d%d%d)/') or -- get timestamp from archive.today urls
url:match ('//archive.today/(%d%d%d%d%.%d%d%.%d%d%-%d%d%d%d%d%d)/'); -- this timestamp needs cleanup
if timestamp then -- if this was an archive.today url ...
return url, date, timestamp:gsub ('[%.%-]', ''); -- return ArchiveURL, ArchiveDate, and timestamp (dots and dashes removed) from |archive-url=, and done
end
-- here for archive.org urls
if (not url:match('//web%.archive%.org/')) and (not url:match('//liveweb%.archive%.org/')) then -- also deprecated liveweb Wayback machine URL
return url, date; -- not an archive.org archive, return ArchiveURL and ArchiveDate
end
if url:match('//web%.archive%.org/save/') then -- if a save command URL, we don't want to allow saving of the target page
err_msg = cfg.err_msg_supl.save;
url = url:gsub ('(//web%.archive%.org)/save/', '%1/*/', 1); -- for preview mode: modify ArchiveURL
elseif url:match('//liveweb%.archive%.org/') then
err_msg = cfg.err_msg_supl.liveweb;
else
path, timestamp, flag = url:match('//web%.archive%.org/([^%d]*)(%d+)([^/]*)/'); -- split out some of the URL parts for evaluation
if not path then -- malformed in some way; pattern did not match
err_msg = cfg.err_msg_supl.timestamp;
elseif 14 ~= timestamp:len() then -- path and flag optional, must have 14-digit timestamp here
err_msg = cfg.err_msg_supl.timestamp;
if '*' ~= flag then
local replacement = timestamp:match ('^%d%d%d%d%d%d') or timestamp:match ('^%d%d%d%d'); -- get the first 6 (YYYYMM) or first 4 digits (YYYY)
if replacement then -- nil if there aren't at least 4 digits (year)
replacement = replacement .. string.rep ('0', 14 - replacement:len()); -- year or yearmo (4 or 6 digits) zero-fill to make 14-digit timestamp
url=url:gsub ('(//web%.archive%.org/[^%d]*)%d[^/]*', '%1' .. replacement .. '*', 1) -- for preview, modify ts to 14 digits plus splat for calendar display
end
end
elseif utilities.is_set (path) and 'web/' ~= path then -- older archive URLs do not have the extra 'web/' path element
err_msg = cfg.err_msg_supl.path;
elseif utilities.is_set (flag) and not utilities.is_set (path) then -- flag not allowed with the old form URL (without the 'web/' path element)
err_msg = cfg.err_msg_supl.flag;
elseif utilities.is_set (flag) and not flag:match ('%a%a_') then -- flag if present must be two alpha characters and underscore (requires 'web/' path element)
err_msg = cfg.err_msg_supl.flag;
else
return url, date, timestamp; -- return ArchiveURL, ArchiveDate, and timestamp from |archive-url=
end
end
-- if here, something not right so
utilities.set_message ('err_archive_url', {err_msg}); -- add error message and
if is_preview_mode then
return url, date, timestamp; -- preview mode so return ArchiveURL, ArchiveDate, and timestamp from |archive-url=
else
return '', ''; -- return empty strings for ArchiveURL and ArchiveDate
end
end
--[[--------------------------< P L A C E _ C H E C K >--------------------------------------------------------
check |place=, |publication-place=, |location= to see if these params include digits. This function added because
many editors misuse location to specify the in-source location (|page(s)= and |at= are supposed to do that)
returns the original parameter value without modification; added maint cat when parameter value contains digits
]]
local function place_check (param_val)
if not utilities.is_set (param_val) then -- parameter empty or omitted
return param_val; -- return that empty state
end
if mw.ustring.find (param_val, '%d') then -- not empty, are there digits in the parameter value
utilities.set_message ('maint_location'); -- yep, add maint cat
end
return param_val; -- and done
end
--[[--------------------------< I S _ A R C H I V E D _ C O P Y >----------------------------------------------
compares |title= to 'Archived copy' (placeholder added by bots that can't find proper title); if matches, return true; nil else
]]
local function is_archived_copy (title)
title = mw.ustring.lower(title); -- switch title to lower case
if title:find (cfg.special_case_translation.archived_copy.en) then -- if title is 'Archived copy'
return true;
elseif cfg.special_case_translation.archived_copy['local'] then
if mw.ustring.find (title, cfg.special_case_translation.archived_copy['local']) then -- mw.ustring() because might not be Latin script
return true;
end
end
end
--[[--------------------------< D I S P L A Y _ N A M E S _ S E L E C T >--------------------------------------
for any of the |display-authors=, |display-editors=, etc parameters, select either the local or global setting.
When both are present, look at <local_display_names> value. When the value is some sort of 'et al.'string,
special handling is required.
When {{cs1 config}} has |display-<namelist>= AND this template has |display-<namelist>=etal AND:
the number of names specified by <number_of_names> is:
greater than the number specified in the global |display-<namelist>= parameter (<global_display_names>)
use global |display-<namelist>= parameter value
set overridden maint category
less than or equal to the number specified in the global |display-<namelist>= parameter
use local |display-<namelist>= parameter value
The purpose of this function is to prevent categorizing a template that has fewer names than the global setting
to keep the etal annotation specified by <local_display_names>.
]]
local function display_names_select (global_display_names, local_display_names, param_name, number_of_names, test)
if global_display_names and utilities.is_set (local_display_names) then -- when both
if 'etal' == local_display_names:lower():gsub("[ '%.]", '') then -- the :gsub() portion makes 'etal' from a variety of 'et al.' spellings and stylings
number_of_names = tonumber (number_of_names); -- convert these to numbers for comparison
local global_display_names_num = tonumber (global_display_names); -- <global_display_names> not set when parameter value is not digits
if number_of_names > global_display_names_num then -- template has more names than global config allows to be displayed?
utilities.set_message ('maint_overridden_setting'); -- set a maint message because global is overriding local |display-<namelist>=etal
return global_display_names, 'cs1 config'; -- return global with spoof parameter name (for get_display_names())
else
return local_display_names, param_name; -- return local because fewer names so let <local_display_names> control
end
end
-- here when <global_display_names> and <local_display_names> both numbers; <global_display_names> controls
utilities.set_message ('maint_overridden_setting'); -- set a maint message
return global_display_names, 'cs1 config'; -- return global with spoof parameter name (for get_display_names())
end
-- here when only one of <global_display_names> or <local_display_names> set
if global_display_names then
return global_display_names, 'cs1 config'; -- return global with spoof parameter name (for get_display_names())
else
return local_display_names, param_name; -- return local
end
end
--[[--------------------------< M O D E _ S E T >--------------------------------------------------------------
fetch global mode setting from {{cs1 config}} (if present) or from |mode= (if present); global setting overrides
local |mode= parameter value. When both are present, emit maintenance message
]]
local function mode_set (Mode, Mode_origin)
local mode;
if cfg.global_cs1_config_t['Mode'] then -- global setting in {{cs1 config}}; nil when empty or assigned value invalid
mode = is_valid_parameter_value (cfg.global_cs1_config_t['Mode'], 'cs1 config: mode', cfg.keywords_lists['mode'], ''); -- error messaging 'param' here is a hoax
else
mode = is_valid_parameter_value (Mode, Mode_origin, cfg.keywords_lists['mode'], '');
end
if cfg.global_cs1_config_t['Mode'] and utilities.is_set (Mode) then -- when template has |mode=<something> which global setting has overridden
utilities.set_message ('maint_overridden_setting'); -- set a maint message
end
return mode;
end
--[[--------------------------< Q U O T E _ M A K E >----------------------------------------------------------
create quotation from |quote=, |trans-quote=, and/or script-quote= with or without |quote-page= or |quote-pages=
when any of those three quote parameters are set, this function unsets <PostScript>. When none of those parameters
are set, |quote-page= and |quote-pages= are unset to nil so that they are not included in the template's metadata
]]
local function quote_make (quote, trans_quote, script_quote, quote_page, quote_pages, nopp, sepc, postscript)
if utilities.is_set (quote) or utilities.is_set (trans_quote) or utilities.is_set (script_quote) then
if utilities.is_set (quote) then
if quote:sub(1, 1) == '"' and quote:sub(-1, -1) == '"' then -- if first and last characters of quote are quote marks
quote = quote:sub(2, -2); -- strip them off
end
end
quote = kern_quotes (quote); -- kern if needed
quote = utilities.wrap_style ('quoted-text', quote ); -- wrap in <q>...</q> tags
if utilities.is_set (script_quote) then
quote = script_concatenate (quote, script_quote, 'script-quote'); -- <bdi> tags, lang attribute, categorization, etc.; must be done after quote is wrapped
end
if utilities.is_set (trans_quote) then
if trans_quote:sub(1, 1) == '"' and trans_quote:sub(-1, -1) == '"' then -- if first and last characters of |trans-quote are quote marks
trans_quote = trans_quote:sub(2, -2); -- strip them off
end
quote = quote .. " " .. utilities.wrap_style ('trans-quoted-title', trans_quote );
end
if utilities.is_set (quote_page) or utilities.is_set (quote_pages) then -- add page prefix
local quote_prefix = '';
if utilities.is_set (quote_page) then
extra_text_in_page_check (quote_page, 'quote-page'); -- add to maint cat if |quote-page= value begins with what looks like p., pp., etc.
if not nopp then
quote_prefix = utilities.substitute (cfg.messages['p-prefix'], {sepc, quote_page}), '', '', '';
else
quote_prefix = utilities.substitute (cfg.messages['nopp'], {sepc, quote_page}), '', '', '';
end
elseif utilities.is_set (quote_pages) then
extra_text_in_page_check (quote_pages, 'quote-pages'); -- add to maint cat if |quote-pages= value begins with what looks like p., pp., etc.
if tonumber(quote_pages) ~= nil and not nopp then -- if only digits, assume single page
quote_prefix = utilities.substitute (cfg.messages['p-prefix'], {sepc, quote_pages}), '', '';
elseif not nopp then
quote_prefix = utilities.substitute (cfg.messages['pp-prefix'], {sepc, quote_pages}), '', '';
else
quote_prefix = utilities.substitute (cfg.messages['nopp'], {sepc, quote_pages}), '', '';
end
end
quote = quote_prefix .. ": " .. quote;
else
quote = sepc .. " " .. quote;
end
postscript = ""; -- cs1|2 does not supply terminal punctuation when |quote= is set
elseif utilities.is_set (quote_page) or utilities.is_set (quote_pages) then
quote_page = nil; -- unset; these require |quote=; TODO: error message?
quote_pages = nil;
end
return quote, quote_page, quote_pages, postscript;
end
--[[--------------------------< C H E C K _ P U B L I S H E R _ N A M E >--------------------------------------
look for variations of '<text>: <text>' that might be '<location>: <publisher>' in |publisher= parameter value.
when found, emit a maintenance message; return nil else
<publisher> is the value assigned to |publisher= or |institution=
]]
local function check_publisher_name (publisher)
local patterns_t = {
'^[%w%s]+%s*:%s*[%w%s]+$', -- plain text <location>: <publisher>
'^%[+[%w%s:|]+%]+%s*:%s*[%w%s]+$', -- partially wikilinked [[<location>]]: <publisher>
'^[%w%s]+%s*:%s*%[+[%w%s:|]+%]+$', -- partially wikilinked <location>: [[<publisher>]]
'^%[+[%w%s:|]+%]+%s*:%s*%[+[%w%s:|]+%]+$', -- wikilinked [[<location>]]: [[<publisher>]]
}
for _, pattern in ipairs (patterns_t) do -- spin through the patterns_t sequence
if mw.ustring.match (publisher, pattern) then -- does this pattern match?
utilities.set_message ('maint_publisher_location'); -- set a maint message
return; -- and done
end
end
end
--[[--------------------------< C I T A T I O N 0 >------------------------------------------------------------
This is the main function doing the majority of the citation formatting.
]]
local function citation0( config, args )
--[[
Load Input Parameters
The argument_wrapper facilitates the mapping of multiple aliases to single internal variable.
]]
local A = argument_wrapper ( args );
local i
-- Pick out the relevant fields from the arguments. Different citation templates
-- define different field names for the same underlying things.
local author_etal;
local a = {}; -- authors list from |lastn= / |firstn= pairs or |vauthors=
local Authors;
local NameListStyle;
if cfg.global_cs1_config_t['NameListStyle'] then -- global setting in {{cs1 config}} overrides local |name-list-style= parameter value; nil when empty or assigned value invalid
NameListStyle = is_valid_parameter_value (cfg.global_cs1_config_t['NameListStyle'], 'cs1 config: name-list-style', cfg.keywords_lists['name-list-style'], ''); -- error messaging 'param' here is a hoax
else
NameListStyle = is_valid_parameter_value (A['NameListStyle'], A:ORIGIN('NameListStyle'), cfg.keywords_lists['name-list-style'], '');
end
if cfg.global_cs1_config_t['NameListStyle'] and utilities.is_set (A['NameListStyle']) then -- when template has |name-list-style=<something> which global setting has overridden
utilities.set_message ('maint_overridden_setting'); -- set a maint message
end
local Collaboration = A['Collaboration'];
do -- to limit scope of selected
local selected = select_author_editor_source (A['Vauthors'], A['Authors'], args, 'AuthorList');
if 1 == selected then
a, author_etal = extract_names (args, 'AuthorList'); -- fetch author list from |authorn= / |lastn= / |firstn=, |author-linkn=, and |author-maskn=
elseif 2 == selected then
NameListStyle = 'vanc'; -- override whatever |name-list-style= might be
a, author_etal = parse_vauthors_veditors (args, A['Vauthors'], 'AuthorList'); -- fetch author list from |vauthors=, |author-linkn=, and |author-maskn=
elseif 3 == selected then
Authors = A['Authors']; -- use content of |people= or |credits=; |authors= is deprecated; TODO: constrain |people= and |credits= to cite av media, episode, serial?
end
if utilities.is_set (Collaboration) then
author_etal = true; -- so that |display-authors=etal not required
end
end
local editor_etal;
local e = {}; -- editors list from |editor-lastn= / |editor-firstn= pairs or |veditors=
do -- to limit scope of selected
local selected = select_author_editor_source (A['Veditors'], nil, args, 'EditorList'); -- support for |editors= withdrawn
if 1 == selected then
e, editor_etal = extract_names (args, 'EditorList'); -- fetch editor list from |editorn= / |editor-lastn= / |editor-firstn=, |editor-linkn=, and |editor-maskn=
elseif 2 == selected then
NameListStyle = 'vanc'; -- override whatever |name-list-style= might be
e, editor_etal = parse_vauthors_veditors (args, args.veditors, 'EditorList'); -- fetch editor list from |veditors=, |editor-linkn=, and |editor-maskn=
end
end
local Chapter = A['Chapter']; -- done here so that we have access to |contribution= from |chapter= aliases
local Chapter_origin = A:ORIGIN ('Chapter');
local Contribution; -- because contribution is required for contributor(s)
if 'contribution' == Chapter_origin then
Contribution = Chapter; -- get the name of the contribution
end
local c = {}; -- contributors list from |contributor-lastn= / contributor-firstn= pairs
if utilities.in_array (config.CitationClass, {"book", "citation"}) and not utilities.is_set (A['Periodical']) then -- |contributor= and |contribution= only supported in book cites
c = extract_names (args, 'ContributorList'); -- fetch contributor list from |contributorn= / |contributor-lastn=, -firstn=, -linkn=, -maskn=
if 0 < #c then
if not utilities.is_set (Contribution) then -- |contributor= requires |contribution=
utilities.set_message ('err_contributor_missing_required_param', 'contribution'); -- add missing contribution error message
c = {}; -- blank the contributors' table; it is used as a flag later
end
if 0 == #a then -- |contributor= requires |author=
utilities.set_message ('err_contributor_missing_required_param', 'author'); -- add missing author error message
c = {}; -- blank the contributors' table; it is used as a flag later
end
end
else -- if not a book cite
if utilities.select_one (args, cfg.aliases['ContributorList-Last'], 'err_redundant_parameters', 1 ) then -- are there contributor name list parameters?
utilities.set_message ('err_contributor_ignored'); -- add contributor ignored error message
end
Contribution = nil; -- unset
end
local Title = A['Title'];
local TitleLink = A['TitleLink'];
local auto_select = ''; -- default is auto
local accept_link;
TitleLink, accept_link = utilities.has_accept_as_written (TitleLink, true); -- test for accept-this-as-written markup
if (not accept_link) and utilities.in_array (TitleLink, {'none', 'pmc', 'doi'}) then -- check for special keywords
auto_select = TitleLink; -- remember selection for later
TitleLink = ''; -- treat as if |title-link= would have been empty
end
TitleLink = link_title_ok (TitleLink, A:ORIGIN ('TitleLink'), Title, 'title'); -- check for wiki-markup in |title-link= or wiki-markup in |title= when |title-link= is set
local Section = ''; -- {{cite map}} only; preset to empty string for concatenation if not used
if 'map' == config.CitationClass and 'section' == Chapter_origin then
Section = A['Chapter']; -- get |section= from |chapter= alias list; |chapter= and the other aliases not supported in {{cite map}}
Chapter = ''; -- unset for now; will be reset later from |map= if present
end
local Periodical = A['Periodical'];
local Periodical_origin = A:ORIGIN('Periodical');
local ScriptPeriodical = A['ScriptPeriodical'];
local ScriptPeriodical_origin = A:ORIGIN('ScriptPeriodical');
local TransPeriodical = A['TransPeriodical'];
local TransPeriodical_origin = A:ORIGIN ('TransPeriodical');
if (utilities.in_array (config.CitationClass, {'book', 'encyclopaedia'}) and (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical) or utilities.is_set (TransPeriodical))) then
local param;
if utilities.is_set (Periodical) then -- get a parameter name from one of these periodical related meta-parameters
Periodical = ''; -- unset because not valid {{cite book}} or {{cite encyclopedia}} parameters
param = Periodical_origin -- get parameter name for error messaging
elseif utilities.is_set (TransPeriodical) then
TransPeriodical = ''; -- unset because not valid {{cite book}} or {{cite encyclopedia}} parameters
param = TransPeriodical_origin; -- get parameter name for error messaging
elseif utilities.is_set (ScriptPeriodical) then
ScriptPeriodical = ''; -- unset because not valid {{cite book}} or {{cite encyclopedia}} parameters
param = ScriptPeriodical_origin; -- get parameter name for error messaging
end
if utilities.is_set (param) then -- if we found one
utilities.set_message ('err_periodical_ignored', {param}); -- emit an error message
end
end
if utilities.is_set (Periodical) then
local i;
Periodical, i = utilities.strip_apostrophe_markup (Periodical); -- strip apostrophe markup so that metadata isn't contaminated
if i then -- non-zero when markup was stripped so emit an error message
utilities.set_message ('err_apostrophe_markup', {Periodical_origin});
end
end
if 'mailinglist' == config.CitationClass then -- special case for {{cite mailing list}}
if utilities.is_set (Periodical) and utilities.is_set (A ['MailingList']) then -- both set emit an error TODO: make a function for this and similar?
utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', Periodical_origin) .. cfg.presentation['sep_list_pair'] .. utilities.wrap_style ('parameter', 'mailinglist')});
end
Periodical = A ['MailingList']; -- error or no, set Periodical to |mailinglist= value because this template is {{cite mailing list}}
Periodical_origin = A:ORIGIN('MailingList');
end
-- web and news not tested for now because of
-- Wikipedia:Administrators%27_noticeboard#Is_there_a_semi-automated_tool_that_could_fix_these_annoying_"Cite_Web"_errors?
if not (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)) then -- 'periodical' templates require periodical parameter
-- local p = {['journal'] = 'journal', ['magazine'] = 'magazine', ['news'] = 'newspaper', ['web'] = 'website'}; -- for error message
local p = {['journal'] = 'journal', ['magazine'] = 'magazine'}; -- for error message
if p[config.CitationClass] then
utilities.set_message ('err_missing_periodical', {config.CitationClass, p[config.CitationClass]});
end
end
local Volume;
if 'citation' == config.CitationClass then
if utilities.is_set (Periodical) then
if not utilities.in_array (Periodical_origin, cfg.citation_no_volume_t) then -- {{citation}} does not render |volume= when these parameters are used
Volume = A['Volume']; -- but does for all other 'periodicals'
end
elseif utilities.is_set (ScriptPeriodical) then
if 'script-website' ~= ScriptPeriodical_origin then -- {{citation}} does not render volume for |script-website=
Volume = A['Volume']; -- but does for all other 'periodicals'
end
else
Volume = A['Volume']; -- and does for non-'periodical' cites
end
elseif utilities.in_array (config.CitationClass, cfg.templates_using_volume) then -- render |volume= for cs1 according to the configuration settings
Volume = A['Volume'];
end
extra_text_in_vol_iss_check (Volume, A:ORIGIN ('Volume'), 'v');
local Issue;
if 'citation' == config.CitationClass then
if utilities.is_set (Periodical) and utilities.in_array (Periodical_origin, cfg.citation_issue_t) then -- {{citation}} may render |issue= when these parameters are used
Issue = utilities.hyphen_to_dash (A['Issue']);
end
elseif utilities.in_array (config.CitationClass, cfg.templates_using_issue) then -- conference & map books do not support issue; {{citation}} listed here because included in settings table
if not (utilities.in_array (config.CitationClass, {'conference', 'map', 'citation'}) and not (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical))) then
Issue = utilities.hyphen_to_dash (A['Issue']);
end
end
local ArticleNumber;
if utilities.in_array (config.CitationClass, {'journal', 'conference'}) or ('citation' == config.CitationClass and utilities.is_set (Periodical) and 'journal' == Periodical_origin) then
ArticleNumber = A['ArticleNumber'];
end
extra_text_in_vol_iss_check (Issue, A:ORIGIN ('Issue'), 'i');
local Page;
local Pages;
local At;
local QuotePage;
local QuotePages;
if not utilities.in_array (config.CitationClass, cfg.templates_not_using_page) then -- TODO: rewrite to emit ignored parameter error message?
Page = A['Page'];
Pages = utilities.hyphen_to_dash (A['Pages']);
At = A['At'];
QuotePage = A['QuotePage'];
QuotePages = utilities.hyphen_to_dash (A['QuotePages']);
end
local NoPP = is_valid_parameter_value (A['NoPP'], A:ORIGIN('NoPP'), cfg.keywords_lists['yes_true_y'], nil);
local Mode = mode_set (A['Mode'], A:ORIGIN('Mode'));
-- separator character and postscript
local sepc, PostScript = set_style (Mode:lower(), A['PostScript'], config.CitationClass);
local Quote;
Quote, QuotePage, QuotePages, PostScript = quote_make (A['Quote'], A['TransQuote'], A['ScriptQuote'], QuotePage, QuotePages, NoPP, sepc, PostScript);
local Edition = A['Edition'];
local PublicationPlace = place_check (A['PublicationPlace'], A:ORIGIN('PublicationPlace'));
local Place = place_check (A['Place'], A:ORIGIN('Place'));
local PublisherName = A['PublisherName'];
local PublisherName_origin = A:ORIGIN('PublisherName');
if utilities.is_set (PublisherName) and (cfg.keywords_xlate['none'] ~= PublisherName) then
local i = 0;
PublisherName, i = utilities.strip_apostrophe_markup (PublisherName); -- strip apostrophe markup so that metadata isn't contaminated; publisher is never italicized
if i and (0 < i) then -- non-zero when markup was stripped so emit an error message
utilities.set_message ('err_apostrophe_markup', {PublisherName_origin});
end
end
if ('document' == config.CitationClass) and not utilities.is_set (PublisherName) then
utilities.set_message ('err_missing_publisher', {config.CitationClass, 'publisher'});
end
local Newsgroup = A['Newsgroup']; -- TODO: strip apostrophe markup?
local Newsgroup_origin = A:ORIGIN('Newsgroup');
if 'newsgroup' == config.CitationClass then
if utilities.is_set (PublisherName) and (cfg.keywords_xlate['none'] ~= PublisherName) then -- general use parameter |publisher= not allowed in cite newsgroup
utilities.set_message ('err_parameter_ignored', {PublisherName_origin});
end
PublisherName = nil; -- ensure that this parameter is unset for the time being; will be used again after COinS
end
if 'book' == config.CitationClass or 'encyclopaedia' == config.CitationClass or ('citation' == config.CitationClass and not utilities.is_set (Periodical)) then
local accept;
PublisherName, accept = utilities.has_accept_as_written (PublisherName); -- check for and remove accept-as-written markup from |publisher= wrapped
if not accept then -- when no accept-as-written markup
check_publisher_name (PublisherName); -- emit maint message when |publisher= might be prefixed with publisher's location
end
end
local URL = A['URL']; -- TODO: better way to do this for URL, ChapterURL, and MapURL?
local UrlAccess = is_valid_parameter_value (A['UrlAccess'], A:ORIGIN('UrlAccess'), cfg.keywords_lists['url-access'], nil);
if not utilities.is_set (URL) and utilities.is_set (UrlAccess) then
UrlAccess = nil;
utilities.set_message ('err_param_access_requires_param', 'url');
end
local ChapterURL = A['ChapterURL'];
local ChapterUrlAccess = is_valid_parameter_value (A['ChapterUrlAccess'], A:ORIGIN('ChapterUrlAccess'), cfg.keywords_lists['url-access'], nil);
if not utilities.is_set (ChapterURL) and utilities.is_set (ChapterUrlAccess) then
ChapterUrlAccess = nil;
utilities.set_message ('err_param_access_requires_param', {A:ORIGIN('ChapterUrlAccess'):gsub ('%-access', '')});
end
local MapUrlAccess = is_valid_parameter_value (A['MapUrlAccess'], A:ORIGIN('MapUrlAccess'), cfg.keywords_lists['url-access'], nil);
if not utilities.is_set (A['MapURL']) and utilities.is_set (MapUrlAccess) then
MapUrlAccess = nil;
utilities.set_message ('err_param_access_requires_param', {'map-url'});
end
local this_page = mw.title.getCurrentTitle(); -- also used for COinS and for language
local no_tracking_cats = is_valid_parameter_value (A['NoTracking'], A:ORIGIN('NoTracking'), cfg.keywords_lists['yes_true_y'], nil);
-- check this page to see if it is in one of the namespaces that cs1 is not supposed to add to the error categories
if not utilities.is_set (no_tracking_cats) then -- ignore if we are already not going to categorize this page
if cfg.uncategorized_namespaces[this_page.namespace] then -- is this page's namespace id one of the uncategorized namespace ids?
no_tracking_cats = "true"; -- set no_tracking_cats
end
for _, v in ipairs (cfg.uncategorized_subpages) do -- cycle through page name patterns
if this_page.text:match (v) then -- test page name against each pattern
no_tracking_cats = "true"; -- set no_tracking_cats
break; -- bail out if one is found
end
end
end
-- check for extra |page=, |pages= or |at= parameters. (also sheet and sheets while we're at it)
utilities.select_one (args, {'page', 'p', 'pp', 'pages', 'at', 'sheet', 'sheets'}, 'err_redundant_parameters'); -- this is a dummy call simply to get the error message and category
local coins_pages;
Page, Pages, At, coins_pages = insource_loc_get (Page, A:ORIGIN('Page'), Pages, A:ORIGIN('Pages'), At);
if utilities.is_set (PublicationPlace) and utilities.is_set (Place) then -- both |publication-place= and |place= (|location=) allowed if different
utilities.add_prop_cat ('location-test'); -- add property cat to evaluate how often PublicationPlace and Place are used together
if PublicationPlace == Place then
Place = ''; -- unset; don't need both if they are the same
end
elseif not utilities.is_set (PublicationPlace) and utilities.is_set (Place) then -- when only |place= (|location=) is set ...
PublicationPlace = Place; -- promote |place= (|location=) to |publication-place
end
if PublicationPlace == Place then Place = ''; end -- don't need both if they are the same
local URL_origin = A:ORIGIN('URL'); -- get name of parameter that holds URL
local ChapterURL_origin = A:ORIGIN('ChapterURL'); -- get name of parameter that holds ChapterURL
local ScriptChapter = A['ScriptChapter'];
local ScriptChapter_origin = A:ORIGIN ('ScriptChapter');
local Format = A['Format'];
local ChapterFormat = A['ChapterFormat'];
local TransChapter = A['TransChapter'];
local TransChapter_origin = A:ORIGIN ('TransChapter');
local TransTitle = A['TransTitle'];
local ScriptTitle = A['ScriptTitle'];
--[[
Parameter remapping for cite encyclopedia:
When the citation has these parameters:
|encyclopedia= and |title= then map |title= to |article= and |encyclopedia= to |title= for rendering
|encyclopedia= and |article= then map |encyclopedia= to |title= for rendering
|trans-title= maps to |trans-chapter= when |title= is re-mapped
|url= maps to |chapter-url= when |title= is remapped
All other combinations of |encyclopedia=, |title=, and |article= are not modified
]]
local Encyclopedia = A['Encyclopedia']; -- used as a flag by this module and by ~/COinS
local ScriptEncyclopedia = A['ScriptEncyclopedia'];
local TransEncyclopedia = A['TransEncyclopedia'];
if utilities.is_set (Encyclopedia) or utilities.is_set (ScriptEncyclopedia) then -- emit error message when Encyclopedia set but template is other than {{cite encyclopedia}} or {{citation}}
if 'encyclopaedia' ~= config.CitationClass and 'citation' ~= config.CitationClass then
if utilities.is_set (Encyclopedia) then
utilities.set_message ('err_parameter_ignored', {A:ORIGIN ('Encyclopedia')});
else
utilities.set_message ('err_parameter_ignored', {A:ORIGIN ('ScriptEncyclopedia')});
end
Encyclopedia = nil; -- unset these because not supported by this template
ScriptEncyclopedia = nil;
TransEncyclopedia = nil;
end
elseif utilities.is_set (TransEncyclopedia) then
utilities.set_message ('err_trans_missing_title', {'encyclopedia'});
end
if ('encyclopaedia' == config.CitationClass) or ('citation' == config.CitationClass and utilities.is_set (Encyclopedia)) then
if utilities.is_set (Periodical) and utilities.is_set (Encyclopedia) then -- when both parameters set emit an error message; {{citation}} only; Periodical not allowed in {{cite encyclopedia}}
utilities.set_message ('err_periodical_ignored', {Periodical_origin});
end
if utilities.is_set (Encyclopedia) or utilities.is_set (ScriptEncyclopedia) then
Periodical = Encyclopedia; -- error or no, set Periodical to Encyclopedia for rendering; {{citation}} could (not legitimately) have both; use Encyclopedia
Periodical_origin = A:ORIGIN ('Encyclopedia');
ScriptPeriodical = ScriptEncyclopedia;
ScriptPeriodical_origin = A:ORIGIN ('ScriptEncyclopedia');
if utilities.is_set (Title) or utilities.is_set (ScriptTitle) then
if not utilities.is_set (Chapter) then
Chapter = Title; -- |encyclopedia= and |title= are set so map |title= params to |article= params for rendering
ScriptChapter = ScriptTitle;
ScriptChapter_origin = A:ORIGIN('ScriptTitle')
TransChapter = TransTitle;
ChapterURL = URL;
ChapterURL_origin = URL_origin;
ChapterUrlAccess = UrlAccess;
ChapterFormat = Format;
if not utilities.is_set (ChapterURL) and utilities.is_set (TitleLink) then
Chapter = utilities.make_wikilink (TitleLink, Chapter);
end
Title = Periodical; -- now map |encyclopedia= params to |title= params for rendering
ScriptTitle = ScriptPeriodical or '';
TransTitle = TransEncyclopedia or '';
Periodical = ''; -- redundant so unset
ScriptPeriodical = '';
URL = '';
Format = '';
TitleLink = '';
end
elseif utilities.is_set (Chapter) or utilities.is_set (ScriptChapter) then -- |title= not set
Title = Periodical; -- |encyclopedia= set and |article= set so map |encyclopedia= to |title= for rendering
ScriptTitle = ScriptPeriodical or '';
TransTitle = TransEncyclopedia or '';
Periodical = ''; -- redundant so unset
ScriptPeriodical = '';
end
end
end
-- special case for cite techreport.
local ID = A['ID'];
if (config.CitationClass == "techreport") then -- special case for cite techreport
if utilities.is_set (A['Number']) then -- cite techreport uses 'number', which other citations alias to 'issue'
if not utilities.is_set (ID) then -- can we use ID for the "number"?
ID = A['Number']; -- yes, use it
else -- ID has a value so emit error message
utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', 'id') .. cfg.presentation['sep_list_pair'] .. utilities.wrap_style ('parameter', 'number')});
end
end
end
-- Account for the oddity that is {{cite conference}}, before generation of COinS data.
local ChapterLink -- = A['ChapterLink']; -- deprecated as a parameter but still used internally by cite episode
local Conference = A['Conference'];
local BookTitle = A['BookTitle'];
local TransTitle_origin = A:ORIGIN ('TransTitle');
if 'conference' == config.CitationClass then
if utilities.is_set (BookTitle) then
Chapter = Title;
Chapter_origin = 'title';
-- ChapterLink = TitleLink; -- |chapter-link= is deprecated
ChapterURL = URL;
ChapterUrlAccess = UrlAccess;
ChapterURL_origin = URL_origin;
URL_origin = '';
ChapterFormat = Format;
TransChapter = TransTitle;
TransChapter_origin = TransTitle_origin;
Title = BookTitle;
Format = '';
-- TitleLink = '';
TransTitle = '';
URL = '';
end
elseif 'speech' ~= config.CitationClass then
Conference = ''; -- not cite conference or cite speech so make sure this is empty string
end
local use_lowercase = ( sepc == ',' ); -- controls capitalization of certain static text
-- cite map oddities
local Cartography = "";
local Scale = "";
local Sheet = A['Sheet'] or '';
local Sheets = A['Sheets'] or '';
if config.CitationClass == "map" then
if utilities.is_set (Chapter) then --TODO: make a function for this and similar?
utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', 'map') .. cfg.presentation['sep_list_pair'] .. utilities.wrap_style ('parameter', Chapter_origin)}); -- add error message
end
Chapter = A['Map'];
Chapter_origin = A:ORIGIN('Map');
ChapterURL = A['MapURL'];
ChapterURL_origin = A:ORIGIN('MapURL');
TransChapter = A['TransMap'];
ScriptChapter = A['ScriptMap']
ScriptChapter_origin = A:ORIGIN('ScriptMap')
ChapterUrlAccess = MapUrlAccess;
ChapterFormat = A['MapFormat'];
Cartography = A['Cartography'];
if utilities.is_set ( Cartography ) then
Cartography = sepc .. " " .. wrap_msg ('cartography', Cartography, use_lowercase);
end
Scale = A['Scale'];
if utilities.is_set ( Scale ) then
Scale = sepc .. " " .. Scale;
end
end
-- Account for the oddities that are {{cite episode}} and {{cite serial}}, before generation of COinS data.
local Series = A['Series'];
if 'episode' == config.CitationClass or 'serial' == config.CitationClass then
local SeriesLink = A['SeriesLink'];
SeriesLink = link_title_ok (SeriesLink, A:ORIGIN ('SeriesLink'), Series, 'series'); -- check for wiki-markup in |series-link= or wiki-markup in |series= when |series-link= is set
local Network = A['Network'];
local Station = A['Station'];
local s, n = {}, {};
-- do common parameters first
if utilities.is_set (Network) then table.insert(n, Network); end
if utilities.is_set (Station) then table.insert(n, Station); end
ID = table.concat(n, sepc .. ' ');
if 'episode' == config.CitationClass then -- handle the oddities that are strictly {{cite episode}}
local Season = A['Season'];
local SeriesNumber = A['SeriesNumber'];
if utilities.is_set (Season) and utilities.is_set (SeriesNumber) then -- these are mutually exclusive so if both are set TODO: make a function for this and similar?
utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', 'season') .. cfg.presentation['sep_list_pair'] .. utilities.wrap_style ('parameter', 'seriesno')}); -- add error message
SeriesNumber = ''; -- unset; prefer |season= over |seriesno=
end
-- assemble a table of parts concatenated later into Series
if utilities.is_set (Season) then table.insert(s, wrap_msg ('season', Season, use_lowercase)); end
if utilities.is_set (SeriesNumber) then table.insert(s, wrap_msg ('seriesnum', SeriesNumber, use_lowercase)); end
if utilities.is_set (Issue) then table.insert(s, wrap_msg ('episode', Issue, use_lowercase)); end
Issue = ''; -- unset because this is not a unique parameter
Chapter = Title; -- promote title parameters to chapter
ScriptChapter = ScriptTitle;
ScriptChapter_origin = A:ORIGIN('ScriptTitle');
ChapterLink = TitleLink; -- alias |episode-link=
TransChapter = TransTitle;
ChapterURL = URL;
ChapterUrlAccess = UrlAccess;
ChapterURL_origin = URL_origin;
ChapterFormat = Format;
Title = Series; -- promote series to title
TitleLink = SeriesLink;
Series = table.concat(s, sepc .. ' '); -- this is concatenation of season, seriesno, episode number
if utilities.is_set (ChapterLink) and not utilities.is_set (ChapterURL) then -- link but not URL
Chapter = utilities.make_wikilink (ChapterLink, Chapter);
elseif utilities.is_set (ChapterLink) and utilities.is_set (ChapterURL) then -- if both are set, URL links episode;
Series = utilities.make_wikilink (ChapterLink, Series);
end
URL = ''; -- unset
TransTitle = '';
ScriptTitle = '';
Format = '';
else -- now oddities that are cite serial
Issue = ''; -- unset because this parameter no longer supported by the citation/core version of cite serial
Chapter = A['Episode']; -- TODO: make |episode= available to cite episode someday?
if utilities.is_set (Series) and utilities.is_set (SeriesLink) then
Series = utilities.make_wikilink (SeriesLink, Series);
end
Series = utilities.wrap_style ('italic-title', Series); -- series is italicized
end
end
-- end of {{cite episode}} stuff
-- handle type parameter for those CS1 citations that have default values
local TitleType = A['TitleType'];
local Degree = A['Degree'];
if utilities.in_array (config.CitationClass, {'AV-media-notes', 'document', 'interview', 'mailinglist', 'map', 'podcast', 'pressrelease', 'report', 'speech', 'techreport', 'thesis'}) then
TitleType = set_titletype (config.CitationClass, TitleType);
if utilities.is_set (Degree) and "Thesis" == TitleType then -- special case for cite thesis
TitleType = Degree .. ' ' .. cfg.title_types ['thesis']:lower();
end
end
if utilities.is_set (TitleType) then -- if type parameter is specified
TitleType = utilities.substitute ( cfg.messages['type'], TitleType); -- display it in parentheses
-- TODO: Hack on TitleType to fix bunched parentheses problem
end
-- legacy: promote PublicationDate to Date if neither Date nor Year are set.
local Date = A['Date'];
local Date_origin; -- to hold the name of parameter promoted to Date; required for date error messaging
local PublicationDate = A['PublicationDate'];
local Year = A['Year'];
if utilities.is_set (Year) then
validation.year_check (Year); -- returns nothing; emits maint message when |year= doesn't hold a 'year' value
end
if not utilities.is_set (Date) then
Date = Year; -- promote Year to Date
Year = nil; -- make nil so Year as empty string isn't used for CITEREF
if not utilities.is_set (Date) and utilities.is_set (PublicationDate) then -- use PublicationDate when |date= and |year= are not set
Date = PublicationDate; -- promote PublicationDate to Date
PublicationDate = ''; -- unset, no longer needed
Date_origin = A:ORIGIN('PublicationDate'); -- save the name of the promoted parameter
else
Date_origin = A:ORIGIN('Year'); -- save the name of the promoted parameter
end
else
Date_origin = A:ORIGIN('Date'); -- not a promotion; name required for error messaging
end
if PublicationDate == Date then PublicationDate = ''; end -- if PublicationDate is same as Date, don't display in rendered citation
--[[
Go test all of the date-holding parameters for valid MOS:DATE format and make sure that dates are real dates. This must be done before we do COinS because here is where
we get the date used in the metadata.
Date validation supporting code is in Module:Citation/CS1/Date_validation
]]
local DF = is_valid_parameter_value (A['DF'], A:ORIGIN('DF'), cfg.keywords_lists['df'], '');
if not utilities.is_set (DF) then
DF = cfg.global_df; -- local |df= if present overrides global df set by {{use xxx date}} template
end
local ArchiveURL;
local ArchiveDate;
local ArchiveFormat = A['ArchiveFormat'];
local archive_url_timestamp; -- timestamp from wayback machine url
ArchiveURL, ArchiveDate, archive_url_timestamp = archive_url_check (A['ArchiveURL'], A['ArchiveDate'])
ArchiveFormat = style_format (ArchiveFormat, ArchiveURL, 'archive-format', 'archive-url');
ArchiveURL, ArchiveDate = is_unique_archive_url (ArchiveURL, URL, ChapterURL, A:ORIGIN('ArchiveURL'), ArchiveDate); -- add error message when URL or ChapterURL == ArchiveURL
local AccessDate = A['AccessDate'];
local COinS_date = {}; -- holds date info extracted from |date= for the COinS metadata by Module:Date verification
local DoiBroken = A['DoiBroken'];
local Embargo = A['Embargo'];
local anchor_year; -- used in the CITEREF identifier
do -- create defined block to contain local variables error_message, date_parameters_list, mismatch
local error_message = '';
-- AirDate has been promoted to Date so not necessary to check it
local date_parameters_list = {
['access-date'] = {val = AccessDate, name = A:ORIGIN ('AccessDate')},
['archive-date'] = {val = ArchiveDate, name = A:ORIGIN ('ArchiveDate')},
['date'] = {val = Date, name = Date_origin},
['doi-broken-date'] = {val = DoiBroken, name = A:ORIGIN ('DoiBroken')},
['pmc-embargo-date'] = {val = Embargo, name = A:ORIGIN ('Embargo')},
['publication-date'] = {val = PublicationDate, name = A:ORIGIN ('PublicationDate')},
['year'] = {val = Year, name = A:ORIGIN ('Year')},
};
local error_list = {};
anchor_year, Embargo = validation.dates(date_parameters_list, COinS_date, error_list);
if utilities.is_set (Year) and utilities.is_set (Date) then -- both |date= and |year= not normally needed;
validation.year_date_check (Year, A:ORIGIN ('Year'), Date, A:ORIGIN ('Date'), error_list);
end
if 0 == #error_list then -- error free dates only; 0 when error_list is empty
local modified = false; -- flag
if utilities.is_set (DF) then -- if we need to reformat dates
modified = validation.reformat_dates (date_parameters_list, DF); -- reformat to DF format, use long month names if appropriate
end
if true == validation.date_hyphen_to_dash (date_parameters_list) then -- convert hyphens to dashes where appropriate
modified = true;
utilities.set_message ('maint_date_format'); -- hyphens were converted so add maint category
end
-- for those wikis that can and want to have English date names translated to the local language; not supported at en.wiki
if cfg.date_name_auto_xlate_enable and validation.date_name_xlate (date_parameters_list, cfg.date_digit_auto_xlate_enable ) then
utilities.set_message ('maint_date_auto_xlated'); -- add maint cat
modified = true;
end
if modified then -- if the date_parameters_list values were modified
AccessDate = date_parameters_list['access-date'].val; -- overwrite date holding parameters with modified values
ArchiveDate = date_parameters_list['archive-date'].val;
Date = date_parameters_list['date'].val;
DoiBroken = date_parameters_list['doi-broken-date'].val;
PublicationDate = date_parameters_list['publication-date'].val;
end
if archive_url_timestamp and utilities.is_set (ArchiveDate) then
validation.archive_date_check (ArchiveDate, archive_url_timestamp, DF); -- does YYYYMMDD in archive_url_timestamp match date in ArchiveDate
end
else
utilities.set_message ('err_bad_date', {utilities.make_sep_list (#error_list, error_list)}); -- add this error message
end
end -- end of do
if utilities.in_array (config.CitationClass, {'book', 'encyclopaedia'}) or -- {{cite book}}, {{cite encyclopedia}}; TODO: {{cite conference}} and others?
('citation' == config.CitationClass and utilities.is_set (Encyclopedia)) or -- {{citation}} as an encylopedia citation
('citation' == config.CitationClass and not utilities.is_set (Periodical)) then -- {{citation}} as a book citation
if utilities.is_set (PublicationPlace) then
if not utilities.is_set (PublisherName) then
local date = COinS_date.rftdate and tonumber (COinS_date.rftdate:match ('%d%d%d%d')); -- get year portion of COinS date (because in Arabic numerals); convert string to number
if date and (1850 <= date) then -- location has no publisher; if date is 1850 or later
utilities.set_message ('maint_location_no_publisher'); -- add maint cat
end
else -- PublisherName has a value
if cfg.keywords_xlate['none'] == PublisherName then -- if that value is 'none' (only for book and encyclopedia citations)
PublisherName = ''; -- unset
end
end
end
end
local ID_list = {}; -- sequence table of rendered identifiers
local ID_list_coins = {}; -- table of identifiers and their values from args; key is same as cfg.id_handlers's key
local Class = A['Class']; -- arxiv class identifier
local ID_support = {
{A['ASINTLD'], 'ASIN', 'err_asintld_missing_asin', A:ORIGIN ('ASINTLD')},
{DoiBroken, 'DOI', 'err_doibroken_missing_doi', A:ORIGIN ('DoiBroken')},
{Embargo, 'PMC', 'err_embargo_missing_pmc', A:ORIGIN ('Embargo')},
}
ID_list, ID_list_coins = identifiers.identifier_lists_get (args, {DoiBroken = DoiBroken, ASINTLD = A['ASINTLD'], Embargo = Embargo, Class = Class, Year=anchor_year}, ID_support);
-- Account for the oddities that are {{cite arxiv}}, {{cite biorxiv}}, {{cite citeseerx}}, {{cite medrxiv}}, {{cite ssrn}}, before generation of COinS data.
if utilities.in_array (config.CitationClass, whitelist.preprint_template_list_t) then -- |arxiv= or |eprint= required for cite arxiv; |biorxiv=, |citeseerx=, |medrxiv=, |ssrn= required for their templates
if not (args[cfg.id_handlers[config.CitationClass:upper()].parameters[1]] or -- can't use ID_list_coins k/v table here because invalid parameters omitted
args[cfg.id_handlers[config.CitationClass:upper()].parameters[2]]) then -- which causes unexpected parameter missing error message
utilities.set_message ('err_' .. config.CitationClass .. '_missing'); -- add error message
end
Periodical = ({['arxiv'] = 'arXiv', ['biorxiv'] = 'bioRxiv', ['citeseerx'] = 'CiteSeerX', ['medrxiv'] = 'medRxiv', ['ssrn'] = 'Social Science Research Network'})[config.CitationClass];
end
-- Link the title of the work if no |url= was provided, but we have a |pmc= or a |doi= with |doi-access=free
if config.CitationClass == "journal" and not utilities.is_set (URL) and not utilities.is_set (TitleLink) and not utilities.in_array (cfg.keywords_xlate[Title], {'off', 'none'}) then -- TODO: remove 'none' once existing citations have been switched to 'off', so 'none' can be used as token for "no title" instead
if 'none' ~= cfg.keywords_xlate[auto_select] then -- if auto-linking not disabled
if identifiers.auto_link_urls[auto_select] then -- manual selection
URL = identifiers.auto_link_urls[auto_select]; -- set URL to be the same as identifier's external link
URL_origin = cfg.id_handlers[auto_select:upper()].parameters[1]; -- set URL_origin to parameter name for use in error message if citation is missing a |title=
elseif identifiers.auto_link_urls['pmc'] then -- auto-select PMC
URL = identifiers.auto_link_urls['pmc']; -- set URL to be the same as the PMC external link if not embargoed
URL_origin = cfg.id_handlers['PMC'].parameters[1]; -- set URL_origin to parameter name for use in error message if citation is missing a |title=
elseif identifiers.auto_link_urls['doi'] then -- auto-select DOI
URL = identifiers.auto_link_urls['doi'];
URL_origin = cfg.id_handlers['DOI'].parameters[1];
end
end
if utilities.is_set (URL) then -- set when using an identifier-created URL
if utilities.is_set (AccessDate) then -- |access-date= requires |url=; identifier-created URL is not |url=
utilities.set_message ('err_accessdate_missing_url'); -- add an error message
AccessDate = ''; -- unset
end
if utilities.is_set (ArchiveURL) then -- |archive-url= requires |url=; identifier-created URL is not |url=
utilities.set_message ('err_archive_missing_url'); -- add an error message
ArchiveURL = ''; -- unset
end
end
end
-- At this point fields may be nil if they weren't specified in the template use. We can use that fact.
-- Test if citation has no title
if not utilities.is_set (Title) and not utilities.is_set (TransTitle) and not utilities.is_set (ScriptTitle) then -- has special case for cite episode
utilities.set_message ('err_citation_missing_title', {'episode' == config.CitationClass and 'series' or 'title'});
end
if utilities.in_array (cfg.keywords_xlate[Title], {'off', 'none'}) and
utilities.in_array (config.CitationClass, {'journal', 'citation'}) and
(utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)) and
('journal' == Periodical_origin or 'script-journal' == ScriptPeriodical_origin) then -- special case for journal cites
Title = ''; -- set title to empty string
utilities.set_message ('maint_untitled'); -- add maint cat
end
-- COinS metadata (see <http://ocoins.info/>) for automated parsing of citation information.
-- handle the oddity that is cite encyclopedia and {{citation |encyclopedia=something}}. Here we presume that
-- when Periodical, Title, and Chapter are all set, then Periodical is the book (encyclopedia) title, Title
-- is the article title, and Chapter is a section within the article. So, we remap
local coins_chapter = Chapter; -- default assuming that remapping not required
local coins_title = Title; -- et tu
if 'encyclopaedia' == config.CitationClass or ('citation' == config.CitationClass and utilities.is_set (Encyclopedia)) then
if utilities.is_set (Chapter) and utilities.is_set (Title) and utilities.is_set (Periodical) then -- if all are used then
coins_chapter = Title; -- remap
coins_title = Periodical;
end
end
local coins_author = a; -- default for coins rft.au
if 0 < #c then -- but if contributor list
coins_author = c; -- use that instead
end
-- this is the function call to COinS()
local OCinSoutput = metadata.COinS({
['Periodical'] = utilities.strip_apostrophe_markup (Periodical), -- no markup in the metadata
['Encyclopedia'] = Encyclopedia, -- just a flag; content ignored by ~/COinS
['Chapter'] = metadata.make_coins_title (coins_chapter, ScriptChapter), -- Chapter and ScriptChapter stripped of bold / italic / accept-as-written markup
['Degree'] = Degree; -- cite thesis only
['Title'] = metadata.make_coins_title (coins_title, ScriptTitle), -- Title and ScriptTitle stripped of bold / italic / accept-as-written markup
['PublicationPlace'] = PublicationPlace,
['Date'] = COinS_date.rftdate, -- COinS_date.* has correctly formatted date values if Date is valid;
['Season'] = COinS_date.rftssn,
['Quarter'] = COinS_date.rftquarter,
['Chron'] = COinS_date.rftchron,
['Series'] = Series,
['Volume'] = Volume,
['Issue'] = Issue,
['ArticleNumber'] = ArticleNumber,
['Pages'] = coins_pages or metadata.get_coins_pages (first_set ({Sheet, Sheets, Page, Pages, At, QuotePage, QuotePages}, 7)), -- pages stripped of external links
['Edition'] = Edition,
['PublisherName'] = PublisherName or Newsgroup, -- any apostrophe markup already removed from PublisherName
['URL'] = first_set ({ChapterURL, URL}, 2),
['Authors'] = coins_author,
['ID_list'] = ID_list_coins,
['RawPage'] = this_page.prefixedText,
}, config.CitationClass);
-- Account for the oddities that are {{cite arxiv}}, {{cite biorxiv}}, {{cite citeseerx}}, {{cite medrxiv}}, and {{cite ssrn}} AFTER generation of COinS data.
if utilities.in_array (config.CitationClass, whitelist.preprint_template_list_t) then -- we have set rft.jtitle in COinS to arXiv, bioRxiv, CiteSeerX, medRxiv, or ssrn now unset so it isn't displayed
Periodical = ''; -- periodical not allowed in these templates; if article has been published, use cite journal
end
-- special case for cite newsgroup. Do this after COinS because we are modifying Publishername to include some static text
if 'newsgroup' == config.CitationClass and utilities.is_set (Newsgroup) then
PublisherName = utilities.substitute (cfg.messages['newsgroup'], external_link( 'news:' .. Newsgroup, Newsgroup, Newsgroup_origin, nil ));
end
local Editors;
local EditorCount; -- used only for choosing {ed.) or (eds.) annotation at end of editor name-list
local Contributors; -- assembled contributors name list
local contributor_etal;
local Translators; -- assembled translators name list
local translator_etal;
local t = {}; -- translators list from |translator-lastn= / translator-firstn= pairs
t = extract_names (args, 'TranslatorList'); -- fetch translator list from |translatorn= / |translator-lastn=, -firstn=, -linkn=, -maskn=
local Interviewers;
local interviewers_list = {};
interviewers_list = extract_names (args, 'InterviewerList'); -- process preferred interviewers parameters
local interviewer_etal;
-- Now perform various field substitutions.
-- We also add leading spaces and surrounding markup and punctuation to the
-- various parts of the citation, but only when they are non-nil.
do
local last_first_list;
local control = {
format = NameListStyle, -- empty string, '&', 'amp', 'and', or 'vanc'
maximum = nil, -- as if display-authors or display-editors not set
mode = Mode
};
do -- do editor name list first because the now unsupported coauthors used to modify control table
local display_names, param = display_names_select (cfg.global_cs1_config_t['DisplayEditors'], A['DisplayEditors'], A:ORIGIN ('DisplayEditors'), #e);
control.maximum, editor_etal = get_display_names (display_names, #e, 'editors', editor_etal, param);
Editors, EditorCount = list_people (control, e, editor_etal);
if 1 == EditorCount and (true == editor_etal or 1 < #e) then -- only one editor displayed but includes etal then
EditorCount = 2; -- spoof to display (eds.) annotation
end
end
do -- now do interviewers
local display_names, param = display_names_select (cfg.global_cs1_config_t['DisplayInterviewers'], A['DisplayInterviewers'], A:ORIGIN ('DisplayInterviewers'), #interviewers_list);
control.maximum, interviewer_etal = get_display_names (display_names, #interviewers_list, 'interviewers', interviewer_etal, param);
Interviewers = list_people (control, interviewers_list, interviewer_etal);
end
do -- now do translators
local display_names, param = display_names_select (cfg.global_cs1_config_t['DisplayTranslators'], A['DisplayTranslators'], A:ORIGIN ('DisplayTranslators'), #t);
control.maximum, translator_etal = get_display_names (display_names, #t, 'translators', translator_etal, param);
Translators = list_people (control, t, translator_etal);
end
do -- now do contributors
local display_names, param = display_names_select (cfg.global_cs1_config_t['DisplayContributors'], A['DisplayContributors'], A:ORIGIN ('DisplayContributors'), #c);
control.maximum, contributor_etal = get_display_names (display_names, #c, 'contributors', contributor_etal, param);
Contributors = list_people (control, c, contributor_etal);
end
do -- now do authors
local display_names, param = display_names_select (cfg.global_cs1_config_t['DisplayAuthors'], A['DisplayAuthors'], A:ORIGIN ('DisplayAuthors'), #a, author_etal);
control.maximum, author_etal = get_display_names (display_names, #a, 'authors', author_etal, param);
last_first_list = list_people (control, a, author_etal);
if utilities.is_set (Authors) then
Authors, author_etal = name_has_etal (Authors, author_etal, false, 'authors'); -- find and remove variations on et al.
if author_etal then
Authors = Authors .. ' ' .. cfg.messages['et al']; -- add et al. to authors parameter
end
else
Authors = last_first_list; -- either an author name list or an empty string
end
end -- end of do
if utilities.is_set (Authors) and utilities.is_set (Collaboration) then
Authors = Authors .. ' (' .. Collaboration .. ')'; -- add collaboration after et al.
end
end
local ConferenceFormat = A['ConferenceFormat'];
local ConferenceURL = A['ConferenceURL'];
ConferenceFormat = style_format (ConferenceFormat, ConferenceURL, 'conference-format', 'conference-url');
Format = style_format (Format, URL, 'format', 'url');
-- special case for chapter format so no error message or cat when chapter not supported
if not (utilities.in_array (config.CitationClass, {'web', 'news', 'journal', 'magazine', 'pressrelease', 'podcast', 'newsgroup', 'arxiv', 'biorxiv', 'citeseerx', 'medrxiv', 'ssrn'}) or
('citation' == config.CitationClass and (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)) and not utilities.is_set (Encyclopedia))) then
ChapterFormat = style_format (ChapterFormat, ChapterURL, 'chapter-format', 'chapter-url');
end
if not utilities.is_set (URL) then
if utilities.in_array (config.CitationClass, {"web", "podcast", "mailinglist"}) or -- |url= required for cite web, cite podcast, and cite mailinglist
('citation' == config.CitationClass and ('website' == Periodical_origin or 'script-website' == ScriptPeriodical_origin)) then -- and required for {{citation}} with |website= or |script-website=
utilities.set_message ('err_cite_web_url');
end
-- do we have |accessdate= without either |url= or |chapter-url=?
if utilities.is_set (AccessDate) and not utilities.is_set (ChapterURL) then -- ChapterURL may be set when URL is not set;
utilities.set_message ('err_accessdate_missing_url');
AccessDate = '';
end
end
local UrlStatus = is_valid_parameter_value (A['UrlStatus'], A:ORIGIN('UrlStatus'), cfg.keywords_lists['url-status'], '');
local OriginalURL
local OriginalURL_origin
local OriginalFormat
local OriginalAccess;
UrlStatus = UrlStatus:lower(); -- used later when assembling archived text
if utilities.is_set ( ArchiveURL ) then
if utilities.is_set (ChapterURL) then -- if chapter-url= is set apply archive url to it
OriginalURL = ChapterURL; -- save copy of source chapter's url for archive text
OriginalURL_origin = ChapterURL_origin; -- name of |chapter-url= parameter for error messages
OriginalFormat = ChapterFormat; -- and original |chapter-format=
if 'live' ~= UrlStatus then
ChapterURL = ArchiveURL -- swap-in the archive's URL
ChapterURL_origin = A:ORIGIN('ArchiveURL') -- name of |archive-url= parameter for error messages
ChapterFormat = ArchiveFormat or ''; -- swap in archive's format
ChapterUrlAccess = nil; -- restricted access levels do not make sense for archived URLs
end
elseif utilities.is_set (URL) then
OriginalURL = URL; -- save copy of original source URL
OriginalURL_origin = URL_origin; -- name of URL parameter for error messages
OriginalFormat = Format; -- and original |format=
OriginalAccess = UrlAccess;
if 'live' ~= UrlStatus then -- if URL set then |archive-url= applies to it
URL = ArchiveURL -- swap-in the archive's URL
URL_origin = A:ORIGIN('ArchiveURL') -- name of archive URL parameter for error messages
Format = ArchiveFormat or ''; -- swap in archive's format
UrlAccess = nil; -- restricted access levels do not make sense for archived URLs
end
end
elseif utilities.is_set (UrlStatus) then -- if |url-status= is set when |archive-url= is not set
utilities.set_message ('maint_url_status'); -- add maint cat
end
if utilities.in_array (config.CitationClass, {'web', 'news', 'journal', 'magazine', 'pressrelease', 'podcast', 'newsgroup', 'arxiv', 'biorxiv', 'citeseerx', 'medrxiv', 'ssrn'}) or -- if any of the 'periodical' cites except encyclopedia
('citation' == config.CitationClass and (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)) and not utilities.is_set (Encyclopedia)) then
local chap_param;
if utilities.is_set (Chapter) then -- get a parameter name from one of these chapter related meta-parameters
chap_param = A:ORIGIN ('Chapter')
elseif utilities.is_set (TransChapter) then
chap_param = A:ORIGIN ('TransChapter')
elseif utilities.is_set (ChapterURL) then
chap_param = A:ORIGIN ('ChapterURL')
elseif utilities.is_set (ScriptChapter) then
chap_param = ScriptChapter_origin;
else utilities.is_set (ChapterFormat)
chap_param = A:ORIGIN ('ChapterFormat')
end
if utilities.is_set (chap_param) then -- if we found one
utilities.set_message ('err_chapter_ignored', {chap_param}); -- add error message
Chapter = ''; -- and set them to empty string to be safe with concatenation
TransChapter = '';
ChapterURL = '';
ScriptChapter = '';
ChapterFormat = '';
end
else -- otherwise, format chapter / article title
local no_quotes = false; -- default assume that we will be quoting the chapter parameter value
if utilities.is_set (Contribution) and 0 < #c then -- if this is a contribution with contributor(s)
if utilities.in_array (Contribution:lower(), cfg.keywords_lists.contribution) then -- and a generic contribution title
no_quotes = true; -- then render it unquoted
end
end
Chapter = format_chapter_title (ScriptChapter, ScriptChapter_origin, Chapter, Chapter_origin, TransChapter, TransChapter_origin, ChapterURL, ChapterURL_origin, no_quotes, ChapterUrlAccess); -- Contribution is also in Chapter
if utilities.is_set (Chapter) then
Chapter = Chapter .. ChapterFormat ;
if 'map' == config.CitationClass and utilities.is_set (TitleType) then
Chapter = Chapter .. ' ' .. TitleType; -- map annotation here; not after title
end
Chapter = Chapter .. sepc .. ' ';
elseif utilities.is_set (ChapterFormat) then -- |chapter= not set but |chapter-format= is so ...
Chapter = ChapterFormat .. sepc .. ' '; -- ... ChapterFormat has error message, we want to see it
end
end
-- Format main title
local plain_title = false;
local accept_title;
Title, accept_title = utilities.has_accept_as_written (Title, true); -- remove accept-this-as-written markup when it wraps all of <Title>
if accept_title and ('' == Title) then -- only support forced empty for now "(())"
Title = cfg.messages['notitle']; -- replace by predefined "No title" message
-- TODO: utilities.set_message ( 'err_redundant_parameters', ...); -- issue proper error message instead of muting
ScriptTitle = ''; -- just mute for now
TransTitle = ''; -- just mute for now
plain_title = true; -- suppress text decoration for descriptive title
utilities.set_message ('maint_untitled'); -- add maint cat
end
if not accept_title then -- <Title> not wrapped in accept-as-written markup
if '...' == Title:sub (-3) then -- if ellipsis is the last three characters of |title=
Title = Title:gsub ('(%.%.%.)%.+$', '%1'); -- limit the number of dots to three
elseif not mw.ustring.find (Title, '%.%s*%a%.$') and -- end of title is not a 'dot-(optional space-)letter-dot' initialism ...
not mw.ustring.find (Title, '%s+%a%.$') then -- ...and not a 'space-letter-dot' initial (''Allium canadense'' L.)
Title = mw.ustring.gsub(Title, '%' .. sepc .. '$', ''); -- remove any trailing separator character; sepc and ms.ustring() here for languages that use multibyte separator characters
end
if utilities.is_set (ArchiveURL) and is_archived_copy (Title) then
utilities.set_message ('maint_archived_copy'); -- add maintenance category before we modify the content of Title
end
if is_generic ('generic_titles', Title) then
utilities.set_message ('err_generic_title'); -- set an error message
end
end
if (not plain_title) and (utilities.in_array (config.CitationClass, {'web', 'news', 'journal', 'magazine', 'document', 'pressrelease', 'podcast', 'newsgroup', 'mailinglist', 'interview', 'arxiv', 'biorxiv', 'citeseerx', 'medrxiv', 'ssrn'}) or
('citation' == config.CitationClass and (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)) and not utilities.is_set (Encyclopedia)) or
('map' == config.CitationClass and (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)))) then -- special case for cite map when the map is in a periodical treat as an article
Title = kern_quotes (Title); -- if necessary, separate title's leading and trailing quote marks from module provided quote marks
Title = utilities.wrap_style ('quoted-title', Title);
Title = script_concatenate (Title, ScriptTitle, 'script-title'); -- <bdi> tags, lang attribute, categorization, etc.; must be done after title is wrapped
TransTitle = utilities.wrap_style ('trans-quoted-title', TransTitle );
elseif plain_title or ('report' == config.CitationClass) then -- no styling for cite report and descriptive titles (otherwise same as above)
Title = script_concatenate (Title, ScriptTitle, 'script-title'); -- <bdi> tags, lang attribute, categorization, etc.; must be done after title is wrapped
TransTitle = utilities.wrap_style ('trans-quoted-title', TransTitle ); -- for cite report, use this form for trans-title
else
Title = utilities.wrap_style ('italic-title', Title);
Title = script_concatenate (Title, ScriptTitle, 'script-title'); -- <bdi> tags, lang attribute, categorization, etc.; must be done after title is wrapped
TransTitle = utilities.wrap_style ('trans-italic-title', TransTitle);
end
if utilities.is_set (TransTitle) then
if utilities.is_set (Title) then
TransTitle = " " .. TransTitle;
else
utilities.set_message ('err_trans_missing_title', {'title'});
end
end
if utilities.is_set (Title) then -- TODO: is this the right place to be making Wikisource URLs?
if utilities.is_set (TitleLink) and utilities.is_set (URL) then
utilities.set_message ('err_wikilink_in_url'); -- set an error message because we can't have both
TitleLink = ''; -- unset
end
if not utilities.is_set (TitleLink) and utilities.is_set (URL) then
Title = external_link (URL, Title, URL_origin, UrlAccess) .. TransTitle .. Format;
URL = ''; -- unset these because no longer needed
Format = "";
elseif utilities.is_set (TitleLink) and not utilities.is_set (URL) then
local ws_url;
ws_url = wikisource_url_make (TitleLink); -- ignore ws_label return; not used here
if ws_url then
Title = external_link (ws_url, Title .. ' ', 'ws link in title-link'); -- space char after Title to move icon away from italic text; TODO: a better way to do this?
Title = utilities.substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], TitleLink, Title});
Title = Title .. TransTitle;
else
Title = utilities.make_wikilink (TitleLink, Title) .. TransTitle;
end
else
local ws_url, ws_label, L; -- Title has italic or quote markup by the time we get here which causes is_wikilink() to return 0 (not a wikilink)
ws_url, ws_label, L = wikisource_url_make (Title:gsub('^[\'"]*(.-)[\'"]*$', '%1')); -- make ws URL from |title= interwiki link (strip italic or quote markup); link portion L becomes tooltip label
if ws_url then
Title = Title:gsub ('%b[]', ws_label); -- replace interwiki link with ws_label to retain markup
Title = external_link (ws_url, Title .. ' ', 'ws link in title'); -- space char after Title to move icon away from italic text; TODO: a better way to do this?
Title = utilities.substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], L, Title});
Title = Title .. TransTitle;
else
Title = Title .. TransTitle;
end
end
else
Title = TransTitle;
end
if utilities.is_set (Place) then
Place = " " .. wrap_msg ('written', Place, use_lowercase) .. sepc .. " ";
end
local ConferenceURL_origin = A:ORIGIN('ConferenceURL'); -- get name of parameter that holds ConferenceURL
if utilities.is_set (Conference) then
if utilities.is_set (ConferenceURL) then
Conference = external_link( ConferenceURL, Conference, ConferenceURL_origin, nil );
end
Conference = sepc .. " " .. Conference .. ConferenceFormat;
elseif utilities.is_set (ConferenceURL) then
Conference = sepc .. " " .. external_link( ConferenceURL, nil, ConferenceURL_origin, nil );
end
local Position = '';
if not utilities.is_set (Position) then
local Minutes = A['Minutes'];
local Time = A['Time'];
if utilities.is_set (Minutes) then
if utilities.is_set (Time) then --TODO: make a function for this and similar?
utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', 'minutes') .. cfg.presentation['sep_list_pair'] .. utilities.wrap_style ('parameter', 'time')});
end
Position = " " .. Minutes .. " " .. cfg.messages['minutes'];
else
if utilities.is_set (Time) then
local TimeCaption = A['TimeCaption']
if not utilities.is_set (TimeCaption) then
TimeCaption = cfg.messages['event'];
if sepc ~= '.' then
TimeCaption = TimeCaption:lower();
end
end
Position = " " .. TimeCaption .. " " .. Time;
end
end
else
Position = " " .. Position;
At = '';
end
Page, Pages, Sheet, Sheets = format_pages_sheets (Page, Pages, Sheet, Sheets, config.CitationClass, Periodical_origin, sepc, NoPP, use_lowercase);
At = utilities.is_set (At) and (sepc .. " " .. At) or "";
Position = utilities.is_set (Position) and (sepc .. " " .. Position) or "";
if config.CitationClass == 'map' then
local Sections = A['Sections']; -- Section (singular) is an alias of Chapter so set earlier
local Inset = A['Inset'];
if utilities.is_set ( Inset ) then
Inset = sepc .. " " .. wrap_msg ('inset', Inset, use_lowercase);
end
if utilities.is_set ( Sections ) then
Section = sepc .. " " .. wrap_msg ('sections', Sections, use_lowercase);
elseif utilities.is_set ( Section ) then
Section = sepc .. " " .. wrap_msg ('section', Section, use_lowercase);
end
At = At .. Inset .. Section;
end
local Others = A['Others'];
if utilities.is_set (Others) and 0 == #a and 0 == #e then -- add maint cat when |others= has value and used without |author=, |editor=
if config.CitationClass == "AV-media-notes"
or config.CitationClass == "audio-visual" then -- special maint for AV/M which has a lot of 'false' positives right now
utilities.set_message ('maint_others_avm')
else
utilities.set_message ('maint_others');
end
end
Others = utilities.is_set (Others) and (sepc .. " " .. Others) or "";
if utilities.is_set (Translators) then
Others = safe_join ({sepc .. ' ', wrap_msg ('translated', Translators, use_lowercase), Others}, sepc);
end
if utilities.is_set (Interviewers) then
Others = safe_join ({sepc .. ' ', wrap_msg ('interview', Interviewers, use_lowercase), Others}, sepc);
end
local TitleNote = A['TitleNote'];
TitleNote = utilities.is_set (TitleNote) and (sepc .. " " .. TitleNote) or "";
if utilities.is_set (Edition) then
if Edition:match ('%f[%a][Ee]d%n?%.?$') or Edition:match ('%f[%a][Ee]dition$') then -- Ed, ed, Ed., ed., Edn, edn, Edn., edn.
utilities.set_message ('err_extra_text_edition'); -- add error message
end
Edition = " " .. wrap_msg ('edition', Edition);
else
Edition = '';
end
Series = utilities.is_set (Series) and wrap_msg ('series', {sepc, Series}) or ""; -- not the same as SeriesNum
local Agency = A['Agency'] or ''; -- |agency= is supported by {{cite magazine}}, {{cite news}}, {{cite press release}}, {{cite web}}, and certain {{citation}} templates
if utilities.is_set (Agency) then -- this testing done here because {{citation}} supports 'news' citations
if utilities.in_array (config.CitationClass, {'magazine', 'news', 'pressrelease', 'web'}) or ('citation' == config.CitationClass and utilities.in_array (Periodical_origin, {"magazine", "newspaper", "work"})) then
Agency = wrap_msg ('agency', {sepc, Agency}); -- format for rendering
else
Agency = ''; -- unset; not supported
utilities.set_message ('err_parameter_ignored', {'agency'}); -- add error message
end
end
Volume = format_volume_issue (Volume, Issue, ArticleNumber, config.CitationClass, Periodical_origin, sepc, use_lowercase);
if utilities.is_set (AccessDate) then
local retrv_text = " " .. cfg.messages['retrieved']
AccessDate = nowrap_date (AccessDate); -- wrap in nowrap span if date in appropriate format
if (sepc ~= ".") then retrv_text = retrv_text:lower() end -- if mode is cs2, lower case
AccessDate = utilities.substitute (retrv_text, AccessDate); -- add retrieved text
AccessDate = utilities.substitute (cfg.presentation['accessdate'], {sepc, AccessDate}); -- allow editors to hide accessdates
end
if utilities.is_set (ID) then ID = sepc .. " " .. ID; end
local Docket = A['Docket'];
if "thesis" == config.CitationClass and utilities.is_set (Docket) then
ID = sepc .. " Docket " .. Docket .. ID;
end
if "report" == config.CitationClass and utilities.is_set (Docket) then -- for cite report when |docket= is set
ID = sepc .. ' ' .. Docket; -- overwrite ID even if |id= is set
end
if utilities.is_set (URL) then
URL = " " .. external_link( URL, nil, URL_origin, UrlAccess );
end
-- We check length of PostScript here because it will have been nuked by
-- the quote parameters. We'd otherwise emit a message even if there wasn't
-- a displayed postscript.
-- TODO: Should the max size (1) be configurable?
-- TODO: Should we check a specific pattern?
if utilities.is_set(PostScript) and mw.ustring.len(PostScript) > 1 then
utilities.set_message ('maint_postscript')
end
local Archived;
if utilities.is_set (ArchiveURL) then
if not utilities.is_set (ArchiveDate) then -- ArchiveURL set but ArchiveDate not set
utilities.set_message ('err_archive_missing_date'); -- emit an error message
ArchiveURL = ''; -- empty string for concatenation
ArchiveDate = ''; -- empty string for concatenation
end
else
if utilities.is_set (ArchiveDate) then -- ArchiveURL not set but ArchiveDate is set
utilities.set_message ('err_archive_date_missing_url'); -- emit an error message
ArchiveURL = ''; -- empty string for concatenation
ArchiveDate = ''; -- empty string for concatenation
end
end
if utilities.is_set (ArchiveURL) then
local arch_text;
if "live" == UrlStatus then
arch_text = cfg.messages['archived'];
if sepc ~= "." then arch_text = arch_text:lower() end
if utilities.is_set (ArchiveDate) then
Archived = sepc .. ' ' .. utilities.substitute ( cfg.messages['archived-live'],
{external_link( ArchiveURL, arch_text, A:ORIGIN('ArchiveURL'), nil) .. ArchiveFormat, ArchiveDate } );
else
Archived = '';
end
if not utilities.is_set (OriginalURL) then
utilities.set_message ('err_archive_missing_url');
Archived = ''; -- empty string for concatenation
end
elseif utilities.is_set (OriginalURL) then -- UrlStatus is empty, 'dead', 'unfit', 'usurped', 'bot: unknown'
if utilities.in_array (UrlStatus, {'unfit', 'usurped', 'bot: unknown'}) then
arch_text = cfg.messages['archived-unfit'];
if sepc ~= "." then arch_text = arch_text:lower() end
Archived = sepc .. ' ' .. arch_text .. ArchiveDate; -- format already styled
if 'bot: unknown' == UrlStatus then
utilities.set_message ('maint_bot_unknown'); -- and add a category if not already added
else
utilities.add_prop_cat ('unfit'); -- and add a category if not already added
end
else -- UrlStatus is empty, 'dead'
arch_text = cfg.messages['archived-dead'];
if sepc ~= "." then arch_text = arch_text:lower() end
if utilities.is_set (ArchiveDate) then
Archived = sepc .. " " .. utilities.substitute ( arch_text,
{ external_link( OriginalURL, cfg.messages['original'], OriginalURL_origin, OriginalAccess ) .. OriginalFormat, ArchiveDate } ); -- format already styled
else
Archived = ''; -- unset for concatenation
end
end
else -- OriginalUrl not set
utilities.set_message ('err_archive_missing_url');
Archived = ''; -- empty string for concatenation
end
elseif utilities.is_set (ArchiveFormat) then
Archived = ArchiveFormat; -- if set and ArchiveURL not set ArchiveFormat has error message
else
Archived = '';
end
local TranscriptURL = A['TranscriptURL']
local TranscriptFormat = A['TranscriptFormat'];
TranscriptFormat = style_format (TranscriptFormat, TranscriptURL, 'transcript-format', 'transcripturl');
local Transcript = A['Transcript'];
local TranscriptURL_origin = A:ORIGIN('TranscriptURL'); -- get name of parameter that holds TranscriptURL
if utilities.is_set (Transcript) then
if utilities.is_set (TranscriptURL) then
Transcript = external_link( TranscriptURL, Transcript, TranscriptURL_origin, nil );
end
Transcript = sepc .. ' ' .. Transcript .. TranscriptFormat;
elseif utilities.is_set (TranscriptURL) then
Transcript = external_link( TranscriptURL, nil, TranscriptURL_origin, nil );
end
local Publisher;
if utilities.is_set (PublicationDate) then
PublicationDate = wrap_msg ('published', PublicationDate);
end
if utilities.is_set (PublisherName) then
if utilities.is_set (PublicationPlace) then
Publisher = sepc .. " " .. PublicationPlace .. ": " .. PublisherName .. PublicationDate;
else
Publisher = sepc .. " " .. PublisherName .. PublicationDate;
end
elseif utilities.is_set (PublicationPlace) then
Publisher= sepc .. " " .. PublicationPlace .. PublicationDate;
else
Publisher = PublicationDate;
end
-- Several of the above rely upon detecting this as nil, so do it last.
if (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical) or utilities.is_set (TransPeriodical)) then
if utilities.is_set (Title) or utilities.is_set (TitleNote) then
Periodical = sepc .. " " .. format_periodical (ScriptPeriodical, ScriptPeriodical_origin, Periodical, TransPeriodical, TransPeriodical_origin);
else
Periodical = format_periodical (ScriptPeriodical, ScriptPeriodical_origin, Periodical, TransPeriodical, TransPeriodical_origin);
end
end
local Language = A['Language'];
if utilities.is_set (Language) then
Language = language_parameter (Language); -- format, categories, name from ISO639-1, etc.
else
Language=''; -- language not specified so make sure this is an empty string;
--[[ TODO: need to extract the wrap_msg from language_parameter
so that we can solve parentheses bunching problem with Format/Language/TitleType
]]
end
--[[
Handle the oddity that is cite speech. This code overrides whatever may be the value assigned to TitleNote (through |department=) and forces it to be " (Speech)" so that
the annotation directly follows the |title= parameter value in the citation rather than the |event= parameter value (if provided).
]]
if "speech" == config.CitationClass then -- cite speech only
TitleNote = TitleType; -- move TitleType to TitleNote so that it renders ahead of |event=
TitleType = ''; -- and unset
if utilities.is_set (Periodical) then -- if Periodical, perhaps because of an included |website= or |journal= parameter
if utilities.is_set (Conference) then -- and if |event= is set
Conference = Conference .. sepc .. " "; -- then add appropriate punctuation to the end of the Conference variable before rendering
end
end
end
-- Piece all bits together at last. Here, all should be non-nil.
-- We build things this way because it is more efficient in LUA
-- not to keep reassigning to the same string variable over and over.
local tcommon;
local tcommon2; -- used for book cite when |contributor= is set
if utilities.in_array (config.CitationClass, {"book", "citation"}) and not utilities.is_set (Periodical) then -- special cases for book cites
if utilities.is_set (Contributors) then -- when we are citing foreword, preface, introduction, etc.
tcommon = safe_join ({Title, TitleNote}, sepc); -- author and other stuff will come after this and before tcommon2
tcommon2 = safe_join ({TitleType, Series, Language, Volume, Others, Edition, Publisher}, sepc);
else
tcommon = safe_join ({Title, TitleNote, TitleType, Series, Language, Volume, Others, Edition, Publisher}, sepc);
end
elseif 'map' == config.CitationClass then -- special cases for cite map
if utilities.is_set (Chapter) then -- map in a book; TitleType is part of Chapter
tcommon = safe_join ({Title, Edition, Scale, Series, Language, Cartography, Others, Publisher, Volume}, sepc);
elseif utilities.is_set (Periodical) then -- map in a periodical
tcommon = safe_join ({Title, TitleType, Periodical, Scale, Series, Language, Cartography, Others, Publisher, Volume}, sepc);
else -- a sheet or stand-alone map
tcommon = safe_join ({Title, TitleType, Edition, Scale, Series, Language, Cartography, Others, Publisher}, sepc);
end
elseif 'episode' == config.CitationClass then -- special case for cite episode
tcommon = safe_join ({Title, TitleNote, TitleType, Series, Language, Edition, Publisher}, sepc);
else -- all other CS1 templates
tcommon = safe_join ({Title, TitleNote, Conference, Periodical, TitleType, Series, Language, Volume, Others, Edition, Publisher, Agency}, sepc);
end
if #ID_list > 0 then
ID_list = safe_join( { sepc .. " ", table.concat( ID_list, sepc .. " " ), ID }, sepc );
else
ID_list = ID;
end
local Via = A['Via'];
Via = utilities.is_set (Via) and wrap_msg ('via', Via) or '';
local idcommon;
if 'audio-visual' == config.CitationClass or 'episode' == config.CitationClass then -- special case for cite AV media & cite episode position transcript
idcommon = safe_join( { ID_list, URL, Archived, Transcript, AccessDate, Via, Quote }, sepc );
else
idcommon = safe_join( { ID_list, URL, Archived, AccessDate, Via, Quote }, sepc );
end
local text;
local pgtext = Position .. Sheet .. Sheets .. Page .. Pages .. At;
local OrigDate = A['OrigDate'];
OrigDate = utilities.is_set (OrigDate) and wrap_msg ('origdate', OrigDate) or '';
if utilities.is_set (Date) then
if utilities.is_set (Authors) or utilities.is_set (Editors) then -- date follows authors or editors when authors not set
Date = " (" .. Date .. ")" .. OrigDate .. sepc .. " "; -- in parentheses
else -- neither of authors and editors set
if (string.sub(tcommon, -1, -1) == sepc) then -- if the last character of tcommon is sepc
Date = " " .. Date .. OrigDate; -- Date does not begin with sepc
else
Date = sepc .. " " .. Date .. OrigDate; -- Date begins with sepc
end
end
end
if utilities.is_set (Authors) then
if (not utilities.is_set (Date)) then -- when date is set it's in parentheses; no Authors termination
Authors = terminate_name_list (Authors, sepc); -- when no date, terminate with 0 or 1 sepc and a space
end
if utilities.is_set (Editors) then
local in_text = '';
local post_text = '';
if utilities.is_set (Chapter) and 0 == #c then
in_text = cfg.messages['in'] .. ' ';
if (sepc ~= '.') then
in_text = in_text:lower(); -- lowercase for cs2
end
end
if EditorCount <= 1 then
post_text = ' (' .. cfg.messages['editor'] .. ')'; -- be consistent with no-author, no-date case
else
post_text = ' (' .. cfg.messages['editors'] .. ')';
end
Editors = terminate_name_list (in_text .. Editors .. post_text, sepc); -- terminate with 0 or 1 sepc and a space
end
if utilities.is_set (Contributors) then -- book cite and we're citing the intro, preface, etc.
local by_text = sepc .. ' ' .. cfg.messages['by'] .. ' ';
if (sepc ~= '.') then by_text = by_text:lower() end -- lowercase for cs2
Authors = by_text .. Authors; -- author follows title so tweak it here
if utilities.is_set (Editors) and utilities.is_set (Date) then -- when Editors make sure that Authors gets terminated
Authors = terminate_name_list (Authors, sepc); -- terminate with 0 or 1 sepc and a space
end
if (not utilities.is_set (Date)) then -- when date is set it's in parentheses; no Contributors termination
Contributors = terminate_name_list (Contributors, sepc); -- terminate with 0 or 1 sepc and a space
end
text = safe_join( {Contributors, Date, Chapter, tcommon, Authors, Place, Editors, tcommon2, pgtext, idcommon }, sepc );
else
text = safe_join( {Authors, Date, Chapter, Place, Editors, tcommon, pgtext, idcommon }, sepc );
end
elseif utilities.is_set (Editors) then
if utilities.is_set (Date) then
if EditorCount <= 1 then
Editors = Editors .. cfg.presentation['sep_name'] .. cfg.messages['editor'];
else
Editors = Editors .. cfg.presentation['sep_name'] .. cfg.messages['editors'];
end
else
if EditorCount <= 1 then
Editors = Editors .. " (" .. cfg.messages['editor'] .. ")" .. sepc .. " "
else
Editors = Editors .. " (" .. cfg.messages['editors'] .. ")" .. sepc .. " "
end
end
text = safe_join( {Editors, Date, Chapter, Place, tcommon, pgtext, idcommon}, sepc );
else
if utilities.in_array (config.CitationClass, {"journal", "citation"}) and utilities.is_set (Periodical) then
text = safe_join( {Chapter, Place, tcommon, pgtext, Date, idcommon}, sepc );
else
text = safe_join( {Chapter, Place, tcommon, Date, pgtext, idcommon}, sepc );
end
end
if utilities.is_set (PostScript) and PostScript ~= sepc then
text = safe_join( {text, sepc}, sepc ); -- Deals with italics, spaces, etc.
if '.' == sepc then -- remove final seperator if present
text = text:gsub ('%' .. sepc .. '$', ''); -- dot must be escaped here
else
text = mw.ustring.gsub (text, sepc .. '$', ''); -- using ustring for non-dot sepc (likely a non-Latin character)
end
end
text = safe_join( {text, PostScript}, sepc );
-- Now enclose the whole thing in a <cite> element
local options_t = {};
options_t.class = cite_class_attribute_make (config.CitationClass, Mode);
local Ref = is_valid_parameter_value (A['Ref'], A:ORIGIN('Ref'), cfg.keywords_lists['ref'], nil, true); -- nil when |ref=harv; A['Ref'] else
if 'none' ~= cfg.keywords_xlate[(Ref and Ref:lower()) or ''] then
local namelist_t = {}; -- holds selected contributor, author, editor name list
local year = first_set ({Year, anchor_year}, 2); -- Year first for legacy citations and for YMD dates that require disambiguation
if #c > 0 then -- if there is a contributor list
namelist_t = c; -- select it
elseif #a > 0 then -- or an author list
namelist_t = a;
elseif #e > 0 then -- or an editor list
namelist_t = e;
end
local citeref_id;
if #namelist_t > 0 then -- if there are names in namelist_t
citeref_id = make_citeref_id (namelist_t, year); -- go make the CITEREF anchor
if mw.uri.anchorEncode (citeref_id) == ((Ref and mw.uri.anchorEncode (Ref)) or '') then -- Ref may already be encoded (by {{sfnref}}) so citeref_id must be encoded before comparison
utilities.set_message ('maint_ref_duplicates_default');
end
else
citeref_id = ''; -- unset
end
options_t.id = Ref or citeref_id;
end
if string.len (text:gsub('%b<>', '')) <= 2 then -- remove html and html-like tags; then get length of what remains;
z.error_cats_t = {}; -- blank the categories list
z.error_msgs_t = {}; -- blank the error messages list
OCinSoutput = nil; -- blank the metadata string
text = ''; -- blank the the citation
utilities.set_message ('err_empty_citation'); -- set empty citation message and category
end
local render_t = {}; -- here we collect the final bits for concatenation into the rendered citation
if utilities.is_set (options_t.id) then -- here we wrap the rendered citation in <cite ...>...</cite> tags
table.insert (render_t, utilities.substitute (cfg.presentation['cite-id'], {mw.uri.anchorEncode(options_t.id), mw.text.nowiki(options_t.class), text})); -- when |ref= is set or when there is a namelist
else
table.insert (render_t, utilities.substitute (cfg.presentation['cite'], {mw.text.nowiki(options_t.class), text})); -- when |ref=none or when namelist_t empty and |ref= is missing or is empty
end
if OCinSoutput then -- blanked when citation is 'empty' so don't bother to add boilerplate metadata span
table.insert (render_t, utilities.substitute (cfg.presentation['ocins'], OCinSoutput)); -- format and append metadata to the citation
end
local template_name = ('citation' == config.CitationClass) and 'citation' or 'cite ' .. (cfg.citation_class_map_t[config.CitationClass] or config.CitationClass);
local template_link = '[[Template:' .. template_name .. '|' .. template_name .. ']]';
local msg_prefix = '<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));
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
}
swdh50lofyqq0wmp3o2c259fg8s2nxi
886343
886342
2025-06-13T17:03:23Z
KartikMistry
10383
[[:en:Module:Citation/CS1]] માંથી આયાત કરેલ ૧ પુનરાવર્તન
886342
Scribunto
text/plain
require ('strict');
--[[--------------------------< F O R W A R D D E C L A R A T I O N S >--------------------------------------
each of these counts against the Lua upvalue limit
]]
local validation; -- functions in Module:Citation/CS1/Date_validation
local utilities; -- functions in Module:Citation/CS1/Utilities
local z = {}; -- table of tables in Module:Citation/CS1/Utilities
local identifiers; -- functions and tables in Module:Citation/CS1/Identifiers
local metadata; -- functions in Module:Citation/CS1/COinS
local cfg = {}; -- table of configuration tables that are defined in Module:Citation/CS1/Configuration
local whitelist = {}; -- table of tables listing valid template parameter names; defined in Module:Citation/CS1/Whitelist
--[[------------------< P A G E S C O P E V A R I A B L E S >---------------
declare variables here that have page-wide scope that are not brought in from
other modules; that are created here and used here
]]
local added_deprecated_cat; -- Boolean flag so that the category is added only once
local added_vanc_errs; -- Boolean flag so we only emit one Vancouver error / category
local added_generic_name_errs; -- Boolean flag so we only emit one generic name error / category and stop testing names once an error is encountered
local added_numeric_name_errs; -- Boolean flag so we only emit one numeric name error / category and stop testing names once an error is encountered
local added_numeric_name_maint; -- Boolean flag so we only emit one numeric name maint category and stop testing names once a category has been emitted
local is_preview_mode; -- true when article is in preview mode; false when using 'Preview page with this template' (previewing the module)
local is_sandbox; -- true when using sandbox modules to render citation
--[[--------------------------< F I R S T _ S E T >------------------------------------------------------------
Locates and returns the first set value in a table of values where the order established in the table,
left-to-right (or top-to-bottom), is the order in which the values are evaluated. Returns nil if none are set.
This version replaces the original 'for _, val in pairs do' and a similar version that used ipairs. With the pairs
version the order of evaluation could not be guaranteed. With the ipairs version, a nil value would terminate
the for-loop before it reached the actual end of the list.
]]
local function first_set (list, count)
local i = 1;
while i <= count do -- loop through all items in list
if utilities.is_set( list[i] ) then
return list[i]; -- return the first set list member
end
i = i + 1; -- point to next
end
end
--[[--------------------------< A D D _ V A N C _ E R R O R >----------------------------------------------------
Adds a single Vancouver system error message to the template's output regardless of how many error actually exist.
To prevent duplication, added_vanc_errs is nil until an error message is emitted.
added_vanc_errs is a Boolean declared in page scope variables above
]]
local function add_vanc_error (source, position)
if added_vanc_errs then return end
added_vanc_errs = true; -- note that we've added this category
utilities.set_message ('err_vancouver', {source, position});
end
--[[--------------------------< I S _ S C H E M E >------------------------------------------------------------
does this thing that purports to be a URI scheme seem to be a valid scheme? The scheme is checked to see if it
is in agreement with http://tools.ietf.org/html/std66#section-3.1 which says:
Scheme names consist of a sequence of characters beginning with a
letter and followed by any combination of letters, digits, plus
("+"), period ("."), or hyphen ("-").
returns true if it does, else false
]]
local function is_scheme (scheme)
return scheme and scheme:match ('^%a[%a%d%+%.%-]*:'); -- true if scheme is set and matches the pattern
end
--[=[-------------------------< I S _ D O M A I N _ N A M E >--------------------------------------------------
Does this thing that purports to be a domain name seem to be a valid domain name?
Syntax defined here: http://tools.ietf.org/html/rfc1034#section-3.5
BNF defined here: https://tools.ietf.org/html/rfc4234
Single character names are generally reserved; see https://tools.ietf.org/html/draft-ietf-dnsind-iana-dns-01#page-15;
see also [[Single-letter second-level domain]]
list of TLDs: https://www.iana.org/domains/root/db
RFC 952 (modified by RFC 1123) requires the first and last character of a hostname to be a letter or a digit. Between
the first and last characters the name may use letters, digits, and the hyphen.
Also allowed are IPv4 addresses. IPv6 not supported
domain is expected to be stripped of any path so that the last character in the last character of the TLD. tld
is two or more alpha characters. Any preceding '//' (from splitting a URL with a scheme) will be stripped
here. Perhaps not necessary but retained in case it is necessary for IPv4 dot decimal.
There are several tests:
the first character of the whole domain name including subdomains must be a letter or a digit
internationalized domain name (ASCII characters with .xn-- ASCII Compatible Encoding (ACE) prefix xn-- in the TLD) see https://tools.ietf.org/html/rfc3490
single-letter/digit second-level domains in the .org, .cash, and .today TLDs
q, x, and z SL domains in the .com TLD
i and q SL domains in the .net TLD
single-letter SL domains in the ccTLDs (where the ccTLD is two letters)
two-character SL domains in gTLDs (where the gTLD is two or more letters)
three-plus-character SL domains in gTLDs (where the gTLD is two or more letters)
IPv4 dot-decimal address format; TLD not allowed
returns true if domain appears to be a proper name and TLD or IPv4 address, else false
]=]
local function is_domain_name (domain)
if not domain then
return false; -- if not set, abandon
end
domain = domain:gsub ('^//', ''); -- strip '//' from domain name if present; done here so we only have to do it once
if not domain:match ('^[%w]') then -- first character must be letter or digit
return false;
end
if domain:match ('^%a+:') then -- hack to detect things that look like s:Page:Title where Page: is namespace at Wikisource
return false;
end
local patterns = { -- patterns that look like URLs
'%f[%w][%w][%w%-]+[%w]%.%a%a+$', -- three or more character hostname.hostname or hostname.tld
'%f[%w][%w][%w%-]+[%w]%.xn%-%-[%w]+$', -- internationalized domain name with ACE prefix
'%f[%a][qxz]%.com$', -- assigned one character .com hostname (x.com times out 2015-12-10)
'%f[%a][iq]%.net$', -- assigned one character .net hostname (q.net registered but not active 2015-12-10)
'%f[%w][%w]%.%a%a$', -- one character hostname and ccTLD (2 chars)
'%f[%w][%w][%w]%.%a%a+$', -- two character hostname and TLD
'^%d%d?%d?%.%d%d?%d?%.%d%d?%d?%.%d%d?%d?', -- IPv4 address
'[%a%d]+%:?' -- IPv6 address
}
for _, pattern in ipairs (patterns) do -- loop through the patterns list
if domain:match (pattern) then
return true; -- if a match then we think that this thing that purports to be a URL is a URL
end
end
for _, d in ipairs (cfg.single_letter_2nd_lvl_domains_t) do -- look for single letter second level domain names for these top level domains
if domain:match ('%f[%w][%w]%.' .. d) then
return true
end
end
return false; -- no matches, we don't know what this thing is
end
--[[--------------------------< I S _ U R L >------------------------------------------------------------------
returns true if the scheme and domain parts of a URL appear to be a valid URL; else false.
This function is the last step in the validation process. This function is separate because there are cases that
are not covered by split_url(), for example is_parameter_ext_wikilink() which is looking for bracketted external
wikilinks.
]]
local function is_url (scheme, domain)
if utilities.is_set (scheme) then -- if scheme is set check it and domain
return is_scheme (scheme) and is_domain_name (domain);
else
return is_domain_name (domain); -- scheme not set when URL is protocol-relative
end
end
--[[--------------------------< S P L I T _ U R L >------------------------------------------------------------
Split a URL into a scheme, authority indicator, and domain.
First remove Fully Qualified Domain Name terminator (a dot following TLD) (if any) and any path(/), query(?) or fragment(#).
If protocol-relative URL, return nil scheme and domain else return nil for both scheme and domain.
When not protocol-relative, get scheme, authority indicator, and domain. If there is an authority indicator (one
or more '/' characters immediately following the scheme's colon), make sure that there are only 2.
Any URL that does not have news: scheme must have authority indicator (//). TODO: are there other common schemes
like news: that don't use authority indicator?
Strip off any port and path;
]]
local function split_url (url_str)
local scheme, authority, domain;
url_str = url_str:gsub ('([%a%d])%.?[/%?#].*$', '%1'); -- strip FQDN terminator and path(/), query(?), fragment (#) (the capture prevents false replacement of '//')
if url_str:match ('^//%S*') then -- if there is what appears to be a protocol-relative URL
domain = url_str:match ('^//(%S*)')
elseif url_str:match ('%S-:/*%S+') then -- if there is what appears to be a scheme, optional authority indicator, and domain name
scheme, authority, domain = url_str:match ('(%S-:)(/*)(%S+)'); -- extract the scheme, authority indicator, and domain portions
if utilities.is_set (authority) then
authority = authority:gsub ('//', '', 1); -- replace place 1 pair of '/' with nothing;
if utilities.is_set(authority) then -- if anything left (1 or 3+ '/' where authority should be) then
return scheme; -- return scheme only making domain nil which will cause an error message
end
else
if not scheme:match ('^news:') then -- except for news:..., MediaWiki won't link URLs that do not have authority indicator; TODO: a better way to do this test?
return scheme; -- return scheme only making domain nil which will cause an error message
end
end
domain = domain:gsub ('(%a):%d+', '%1'); -- strip port number if present
end
return scheme, domain;
end
--[[--------------------------< L I N K _ P A R A M _ O K >---------------------------------------------------
checks the content of |title-link=, |series-link=, |author-link=, etc. for properly formatted content: no wikilinks, no URLs
Link parameters are to hold the title of a Wikipedia article, so none of the WP:TITLESPECIALCHARACTERS are allowed:
# < > [ ] | { } _
except the underscore which is used as a space in wiki URLs and # which is used for section links
returns false when the value contains any of these characters.
When there are no illegal characters, this function returns TRUE if value DOES NOT appear to be a valid URL (the
|<param>-link= parameter is ok); else false when value appears to be a valid URL (the |<param>-link= parameter is NOT ok).
]]
local function link_param_ok (value)
local scheme, domain;
if value:find ('[<>%[%]|{}]') then -- if any prohibited characters
return false;
end
scheme, domain = split_url (value); -- get scheme or nil and domain or nil from URL;
return not is_url (scheme, domain); -- return true if value DOES NOT appear to be a valid URL
end
--[[--------------------------< L I N K _ T I T L E _ O K >---------------------------------------------------
Use link_param_ok() to validate |<param>-link= value and its matching |<title>= value.
|<title>= may be wiki-linked but not when |<param>-link= has a value. This function emits an error message when
that condition exists
check <link> for inter-language interwiki-link prefix. prefix must be a MediaWiki-recognized language
code and must begin with a colon.
]]
local function link_title_ok (link, lorig, title, torig)
local orig;
if utilities.is_set (link) then -- don't bother if <param>-link doesn't have a value
if not link_param_ok (link) then -- check |<param>-link= markup
orig = lorig; -- identify the failing link parameter
elseif title:find ('%[%[') then -- check |title= for wikilink markup
orig = torig; -- identify the failing |title= parameter
elseif link:match ('^%a+:') then -- if the link is what looks like an interwiki
local prefix = link:match ('^(%a+):'):lower(); -- get the interwiki prefix
if cfg.inter_wiki_map[prefix] then -- if prefix is in the map, must have preceding colon
orig = lorig; -- flag as error
end
end
end
if utilities.is_set (orig) then
link = ''; -- unset
utilities.set_message ('err_bad_paramlink', orig); -- URL or wikilink in |title= with |title-link=;
end
return link; -- link if ok, empty string else
end
--[[--------------------------< C H E C K _ U R L >------------------------------------------------------------
Determines whether a URL string appears to be valid.
First we test for space characters. If any are found, return false. Then split the URL into scheme and domain
portions, or for protocol-relative (//example.com) URLs, just the domain. Use is_url() to validate the two
portions of the URL. If both are valid, or for protocol-relative if domain is valid, return true, else false.
Because it is different from a standard URL, and because this module used external_link() to make external links
that work for standard and news: links, we validate newsgroup names here. The specification for a newsgroup name
is at https://tools.ietf.org/html/rfc5536#section-3.1.4
]]
local function check_url( url_str )
if nil == url_str:match ("^%S+$") then -- if there are any spaces in |url=value it can't be a proper URL
return false;
end
local scheme, domain;
scheme, domain = split_url (url_str); -- get scheme or nil and domain or nil from URL;
if 'news:' == scheme then -- special case for newsgroups
return domain:match('^[%a%d%+%-_]+%.[%a%d%+%-_%.]*[%a%d%+%-_]$');
end
return is_url (scheme, domain); -- return true if value appears to be a valid URL
end
--[=[-------------------------< I S _ P A R A M E T E R _ E X T _ W I K I L I N K >----------------------------
Return true if a parameter value has a string that begins and ends with square brackets [ and ] and the first
non-space characters following the opening bracket appear to be a URL. The test will also find external wikilinks
that use protocol-relative URLs. Also finds bare URLs.
The frontier pattern prevents a match on interwiki-links which are similar to scheme:path URLs. The tests that
find bracketed URLs are required because the parameters that call this test (currently |title=, |chapter=, |work=,
and |publisher=) may have wikilinks and there are articles or redirects like '//Hus' so, while uncommon, |title=[[//Hus]]
is possible as might be [[en://Hus]].
]=]
local function is_parameter_ext_wikilink (value)
local scheme, domain;
if value:match ('%f[%[]%[%a%S*:%S+.*%]') then -- if ext. wikilink with scheme and domain: [xxxx://yyyyy.zzz]
scheme, domain = split_url (value:match ('%f[%[]%[(%a%S*:%S+).*%]'));
elseif value:match ('%f[%[]%[//%S+.*%]') then -- if protocol-relative ext. wikilink: [//yyyyy.zzz]
scheme, domain = split_url (value:match ('%f[%[]%[(//%S+).*%]'));
elseif value:match ('%a%S*:%S+') then -- if bare URL with scheme; may have leading or trailing plain text
scheme, domain = split_url (value:match ('(%a%S*:%S+)'));
elseif value:match ('^//%S+') or value:match ('%s//%S+') then -- if protocol-relative bare URL: //yyyyy.zzz; authority indicator (//) must be be preceded nothing or by whitespace
scheme, domain = split_url (value:match ('(//%S+)')); -- what is left should be the domain
else
return false; -- didn't find anything that is obviously a URL
end
return is_url (scheme, domain); -- return true if value appears to be a valid URL
end
--[[-------------------------< C H E C K _ F O R _ U R L >-----------------------------------------------------
loop through a list of parameters and their values. Look at the value and if it has an external link, emit an error message.
]]
local function check_for_url (parameter_list, error_list)
for k, v in pairs (parameter_list) do -- for each parameter in the list
if is_parameter_ext_wikilink (v) then -- look at the value; if there is a URL add an error message
table.insert (error_list, utilities.wrap_style ('parameter', k));
end
end
end
--[[--------------------------< S A F E _ F O R _ U R L >------------------------------------------------------
Escape sequences for content that will be used for URL descriptions
]]
local function safe_for_url( str )
if str:match( "%[%[.-%]%]" ) ~= nil then
utilities.set_message ('err_wikilink_in_url', {});
end
return str:gsub( '[%[%]\n]', {
['['] = '[',
[']'] = ']',
['\n'] = ' ' } );
end
--[[--------------------------< E X T E R N A L _ L I N K >----------------------------------------------------
Format an external link with error checking
]]
local function external_link (URL, label, source, access)
local err_msg = '';
local domain;
local path;
local base_url;
if not utilities.is_set (label) then
label = URL;
if utilities.is_set (source) then
utilities.set_message ('err_bare_url_missing_title', {utilities.wrap_style ('parameter', source)});
else
error (cfg.messages["bare_url_no_origin"]); -- programmer error; valid parameter name does not have matching meta-parameter
end
end
if not check_url (URL) then
utilities.set_message ('err_bad_url', {utilities.wrap_style ('parameter', source)});
end
domain, path = URL:match ('^([/%.%-%+:%a%d]+)([/%?#].*)$'); -- split the URL into scheme plus domain and path
if path then -- if there is a path portion
path = path:gsub ('[%[%]]', {['['] = '%5b', [']'] = '%5d'}); -- replace '[' and ']' with their percent-encoded values
URL = table.concat ({domain, path}); -- and reassemble
end
base_url = table.concat ({ "[", URL, " ", safe_for_url (label), "]" }); -- assemble a wiki-markup URL
if utilities.is_set (access) then -- access level (subscription, registration, limited)
base_url = utilities.substitute (cfg.presentation['ext-link-access-signal'], {cfg.presentation[access].class, cfg.presentation[access].title, base_url}); -- add the appropriate icon
end
return base_url;
end
--[[--------------------------< D E P R E C A T E D _ P A R A M E T E R >--------------------------------------
Categorize and emit an error message when the citation contains one or more deprecated parameters. The function includes the
offending parameter name to the error message. Only one error message is emitted regardless of the number of deprecated
parameters in the citation.
added_deprecated_cat is a Boolean declared in page scope variables above
]]
local function deprecated_parameter(name)
if not added_deprecated_cat then
added_deprecated_cat = true; -- note that we've added this category
utilities.set_message ('err_deprecated_params', {name}); -- add error message
end
end
--[=[-------------------------< K E R N _ Q U O T E S >--------------------------------------------------------
Apply kerning to open the space between the quote mark provided by the module and a leading or trailing quote
mark contained in a |title= or |chapter= parameter's value.
This function will positive kern either single or double quotes:
"'Unkerned title with leading and trailing single quote marks'"
" 'Kerned title with leading and trailing single quote marks' " (in real life the kerning isn't as wide as this example)
Double single quotes (italic or bold wiki-markup) are not kerned.
Replaces Unicode quote marks in plain text or in the label portion of a [[L|D]] style wikilink with typewriter
quote marks regardless of the need for kerning. Unicode quote marks are not replaced in simple [[D]] wikilinks.
Call this function for chapter titles, for website titles, etc.; not for book titles.
]=]
local function kern_quotes (str)
local cap = '';
local wl_type, label, link;
wl_type, label, link = utilities.is_wikilink (str); -- wl_type is: 0, no wl (text in label variable); 1, [[D]]; 2, [[L|D]]
if 1 == wl_type then -- [[D]] simple wikilink with or without quote marks
if mw.ustring.match (str, '%[%[[\"“”\'‘’].+[\"“”\'‘’]%]%]') then -- leading and trailing quote marks
str = utilities.substitute (cfg.presentation['kern-left'], str);
str = utilities.substitute (cfg.presentation['kern-right'], str);
elseif mw.ustring.match (str, '%[%[[\"“”\'‘’].+%]%]') then -- leading quote marks
str = utilities.substitute (cfg.presentation['kern-left'], str);
elseif mw.ustring.match (str, '%[%[.+[\"“”\'‘’]%]%]') then -- trailing quote marks
str = utilities.substitute (cfg.presentation['kern-right'], str);
end
else -- plain text or [[L|D]]; text in label variable
label = mw.ustring.gsub (label, '[“”]', '\"'); -- replace “” (U+201C & U+201D) with " (typewriter double quote mark)
label = mw.ustring.gsub (label, '[‘’]', '\''); -- replace ‘’ (U+2018 & U+2019) with ' (typewriter single quote mark)
cap = mw.ustring.match (label, "^([\"\'][^\'].+)"); -- match leading double or single quote but not doubled single quotes (italic markup)
if utilities.is_set (cap) then
label = utilities.substitute (cfg.presentation['kern-left'], cap);
end
cap = mw.ustring.match (label, "^(.+[^\'][\"\'])$") -- match trailing double or single quote but not doubled single quotes (italic markup)
if utilities.is_set (cap) then
label = utilities.substitute (cfg.presentation['kern-right'], cap);
end
if 2 == wl_type then
str = utilities.make_wikilink (link, label); -- reassemble the wikilink
else
str = label;
end
end
return str;
end
--[[--------------------------< F O R M A T _ S C R I P T _ V A L U E >----------------------------------------
|script-title= holds title parameters that are not written in Latin-based scripts: Chinese, Japanese, Arabic, Hebrew, etc. These scripts should
not be italicized and may be written right-to-left. The value supplied by |script-title= is concatenated onto Title after Title has been wrapped
in italic markup.
Regardless of language, all values provided by |script-title= are wrapped in <bdi>...</bdi> tags to isolate RTL languages from the English left to right.
|script-title= provides a unique feature. The value in |script-title= may be prefixed with a two-character ISO 639-1 language code and a colon:
|script-title=ja:*** *** (where * represents a Japanese character)
Spaces between the two-character code and the colon and the colon and the first script character are allowed:
|script-title=ja : *** ***
|script-title=ja: *** ***
|script-title=ja :*** ***
Spaces preceding the prefix are allowed: |script-title = ja:*** ***
The prefix is checked for validity. If it is a valid ISO 639-1 language code, the lang attribute (lang="ja") is added to the <bdi> tag so that browsers can
know the language the tag contains. This may help the browser render the script more correctly. If the prefix is invalid, the lang attribute
is not added. At this time there is no error message for this condition.
Supports |script-title=, |script-chapter=, |script-<periodical>=
]]
local function format_script_value (script_value, script_param)
local lang=''; -- initialize to empty string
local name;
if script_value:match('^%l%l%l?%s*:') then -- if first 3 or 4 non-space characters are script language prefix
lang = script_value:match('^(%l%l%l?)%s*:%s*%S.*'); -- get the language prefix or nil if there is no script
if not utilities.is_set (lang) then
utilities.set_message ('err_script_parameter', {script_param, cfg.err_msg_supl['missing title part']}); -- prefix without 'title'; add error message
return ''; -- script_value was just the prefix so return empty string
end
-- if we get this far we have prefix and script
name = cfg.lang_tag_remap[lang] or mw.language.fetchLanguageName( lang, cfg.this_wiki_code ); -- get language name so that we can use it to categorize
if utilities.is_set (name) then -- is prefix a proper ISO 639-1 language code?
script_value = script_value:gsub ('^%l+%s*:%s*', ''); -- strip prefix from script
-- is prefix one of these language codes?
if utilities.in_array (lang, cfg.script_lang_codes) then
utilities.add_prop_cat ('script', {name, lang})
else
utilities.set_message ('err_script_parameter', {script_param, cfg.err_msg_supl['unknown language code']}); -- unknown script-language; add error message
end
lang = ' lang="' .. lang .. '" '; -- convert prefix into a lang attribute
else
utilities.set_message ('err_script_parameter', {script_param, cfg.err_msg_supl['invalid language code']}); -- invalid language code; add error message
lang = ''; -- invalid so set lang to empty string
end
else
utilities.set_message ('err_script_parameter', {script_param, cfg.err_msg_supl['missing prefix']}); -- no language code prefix; add error message
end
script_value = utilities.substitute (cfg.presentation['bdi'], {lang, script_value}); -- isolate in case script is RTL
return script_value;
end
--[[--------------------------< S C R I P T _ C O N C A T E N A T E >------------------------------------------
Initially for |title= and |script-title=, this function concatenates those two parameter values after the script
value has been wrapped in <bdi> tags.
]]
local function script_concatenate (title, script, script_param)
if utilities.is_set (script) then
script = format_script_value (script, script_param); -- <bdi> tags, lang attribute, categorization, etc.; returns empty string on error
if utilities.is_set (script) then
title = title .. ' ' .. script; -- concatenate title and script title
end
end
return title;
end
--[[--------------------------< W R A P _ M S G >--------------------------------------------------------------
Applies additional message text to various parameter values. Supplied string is wrapped using a message_list
configuration taking one argument. Supports lower case text for {{citation}} templates. Additional text taken
from citation_config.messages - the reason this function is similar to but separate from wrap_style().
]]
local function wrap_msg (key, str, lower)
if not utilities.is_set ( str ) then
return "";
end
if true == lower then
local msg;
msg = cfg.messages[key]:lower(); -- set the message to lower case before
return utilities.substitute ( msg, str ); -- including template text
else
return utilities.substitute ( cfg.messages[key], str );
end
end
--[[----------------< W I K I S O U R C E _ U R L _ M A K E >-------------------
Makes a Wikisource URL from Wikisource interwiki-link. Returns the URL and appropriate
label; nil else.
str is the value assigned to |chapter= (or aliases) or |title= or |title-link=
]]
local function wikisource_url_make (str)
local wl_type, D, L;
local ws_url, ws_label;
local wikisource_prefix = table.concat ({'https://', cfg.this_wiki_code, '.wikisource.org/wiki/'});
wl_type, D, L = utilities.is_wikilink (str); -- wl_type is 0 (not a wikilink), 1 (simple wikilink), 2 (complex wikilink)
if 0 == wl_type then -- not a wikilink; might be from |title-link=
str = D:match ('^[Ww]ikisource:(.+)') or D:match ('^[Ss]:(.+)'); -- article title from interwiki link with long-form or short-form namespace
if utilities.is_set (str) then
ws_url = table.concat ({ -- build a Wikisource URL
wikisource_prefix, -- prefix
str, -- article title
});
ws_label = str; -- label for the URL
end
elseif 1 == wl_type then -- simple wikilink: [[Wikisource:ws article]]
str = D:match ('^[Ww]ikisource:(.+)') or D:match ('^[Ss]:(.+)'); -- article title from interwiki link with long-form or short-form namespace
if utilities.is_set (str) then
ws_url = table.concat ({ -- build a Wikisource URL
wikisource_prefix, -- prefix
str, -- article title
});
ws_label = str; -- label for the URL
end
elseif 2 == wl_type then -- non-so-simple wikilink: [[Wikisource:ws article|displayed text]] ([[L|D]])
str = L:match ('^[Ww]ikisource:(.+)') or L:match ('^[Ss]:(.+)'); -- article title from interwiki link with long-form or short-form namespace
if utilities.is_set (str) then
ws_label = D; -- get ws article name from display portion of interwiki link
ws_url = table.concat ({ -- build a Wikisource URL
wikisource_prefix, -- prefix
str, -- article title without namespace from link portion of wikilink
});
end
end
if ws_url then
ws_url = mw.uri.encode (ws_url, 'WIKI'); -- make a usable URL
ws_url = ws_url:gsub ('%%23', '#'); -- undo percent-encoding of fragment marker
end
return ws_url, ws_label, L or D; -- return proper URL or nil and a label or nil
end
--[[----------------< F O R M A T _ P E R I O D I C A L >-----------------------
Format the three periodical parameters: |script-<periodical>=, |<periodical>=,
and |trans-<periodical>= into a single Periodical meta-parameter.
]]
local function format_periodical (script_periodical, script_periodical_source, periodical, trans_periodical)
if not utilities.is_set (periodical) then
periodical = ''; -- to be safe for concatenation
else
periodical = utilities.wrap_style ('italic-title', periodical); -- style
end
periodical = script_concatenate (periodical, script_periodical, script_periodical_source); -- <bdi> tags, lang attribute, categorization, etc.; must be done after title is wrapped
if utilities.is_set (trans_periodical) then
trans_periodical = utilities.wrap_style ('trans-italic-title', trans_periodical);
if utilities.is_set (periodical) then
periodical = periodical .. ' ' .. trans_periodical;
else -- here when trans-periodical without periodical or script-periodical
periodical = trans_periodical;
utilities.set_message ('err_trans_missing_title', {'periodical'});
end
end
return periodical;
end
--[[------------------< F O R M A T _ C H A P T E R _ T I T L E >---------------
Format the four chapter parameters: |script-chapter=, |chapter=, |trans-chapter=,
and |chapter-url= into a single chapter meta- parameter (chapter_url_source used
for error messages).
]]
local function format_chapter_title (script_chapter, script_chapter_source, chapter, chapter_source, trans_chapter, trans_chapter_source, chapter_url, chapter_url_source, no_quotes, access)
local ws_url, ws_label, L = wikisource_url_make (chapter); -- make a wikisource URL and label from a wikisource interwiki link
if ws_url then
ws_label = ws_label:gsub ('_', ' '); -- replace underscore separators with space characters
chapter = ws_label;
end
if not utilities.is_set (chapter) then
chapter = ''; -- to be safe for concatenation
else
if false == no_quotes then
chapter = kern_quotes (chapter); -- if necessary, separate chapter title's leading and trailing quote marks from module provided quote marks
chapter = utilities.wrap_style ('quoted-title', chapter);
end
end
chapter = script_concatenate (chapter, script_chapter, script_chapter_source); -- <bdi> tags, lang attribute, categorization, etc.; must be done after title is wrapped
if utilities.is_set (chapter_url) then
chapter = external_link (chapter_url, chapter, chapter_url_source, access); -- adds bare_url_missing_title error if appropriate
elseif ws_url then
chapter = external_link (ws_url, chapter .. ' ', 'ws link in chapter'); -- adds bare_url_missing_title error if appropriate; space char to move icon away from chap text; TODO: better way to do this?
chapter = utilities.substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], L, chapter});
end
if utilities.is_set (trans_chapter) then
trans_chapter = utilities.wrap_style ('trans-quoted-title', trans_chapter);
if utilities.is_set (chapter) then
chapter = chapter .. ' ' .. trans_chapter;
else -- here when trans_chapter without chapter or script-chapter
chapter = trans_chapter;
chapter_source = trans_chapter_source:match ('trans%-?(.+)'); -- when no chapter, get matching name from trans-<param>
utilities.set_message ('err_trans_missing_title', {chapter_source});
end
end
return chapter;
end
--[[----------------< H A S _ I N V I S I B L E _ C H A R S >-------------------
This function searches a parameter's value for non-printable or invisible characters.
The search stops at the first match.
This function will detect the visible replacement character when it is part of the Wikisource.
Detects but ignores nowiki and math stripmarkers. Also detects other named stripmarkers
(gallery, math, pre, ref) and identifies them with a slightly different error message.
See also coins_cleanup().
Output of this function is an error message that identifies the character or the
Unicode group, or the stripmarker that was detected along with its position (or,
for multi-byte characters, the position of its first byte) in the parameter value.
]]
local function has_invisible_chars (param, v)
local position = ''; -- position of invisible char or starting position of stripmarker
local capture; -- used by stripmarker detection to hold name of the stripmarker
local stripmarker; -- boolean set true when a stripmarker is found
capture = string.match (v, '[%w%p ]*'); -- test for values that are simple ASCII text and bypass other tests if true
if capture == v then -- if same there are no Unicode characters
return;
end
for _, invisible_char in ipairs (cfg.invisible_chars) do
local char_name = invisible_char[1]; -- the character or group name
local pattern = invisible_char[2]; -- the pattern used to find it
position, _, capture = mw.ustring.find (v, pattern); -- see if the parameter value contains characters that match the pattern
if position and (cfg.invisible_defs.zwj == capture) then -- if we found a zero-width joiner character
if mw.ustring.find (v, cfg.indic_script) then -- it's ok if one of the Indic scripts
position = nil; -- unset position
elseif cfg.emoji_t[mw.ustring.codepoint (v, position+1)] then -- is zwj followed by a character listed in emoji{}?
position = nil; -- unset position
end
end
if position then
if 'nowiki' == capture or 'math' == capture or -- nowiki and math stripmarkers (not an error condition)
('templatestyles' == capture and utilities.in_array (param, {'id', 'quote'})) then -- templatestyles stripmarker allowed in these parameters
stripmarker = true; -- set a flag
elseif true == stripmarker and cfg.invisible_defs.del == capture then -- because stripmakers begin and end with the delete char, assume that we've found one end of a stripmarker
position = nil; -- unset
else
local err_msg;
if capture and not (cfg.invisible_defs.del == capture or cfg.invisible_defs.zwj == capture) then
err_msg = capture .. ' ' .. char_name;
else
err_msg = char_name .. ' ' .. 'character';
end
utilities.set_message ('err_invisible_char', {err_msg, utilities.wrap_style ('parameter', param), position}); -- add error message
return; -- and done with this parameter
end
end
end
end
--[[-------------------< A R G U M E N T _ W R A P P E R >----------------------
Argument wrapper. This function provides support for argument mapping defined
in the configuration file so that multiple names can be transparently aliased to
single internal variable.
]]
local function argument_wrapper ( args )
local origin = {};
return setmetatable({
ORIGIN = function ( self, k )
local dummy = self[k]; -- force the variable to be loaded.
return origin[k];
end
},
{
__index = function ( tbl, k )
if origin[k] ~= nil then
return nil;
end
local args, list, v = args, cfg.aliases[k];
if type( list ) == 'table' then
v, origin[k] = utilities.select_one ( args, list, 'err_redundant_parameters' );
if origin[k] == nil then
origin[k] = ''; -- Empty string, not nil
end
elseif list ~= nil then
v, origin[k] = args[list], list;
else
-- maybe let through instead of raising an error?
-- v, origin[k] = args[k], k;
error( cfg.messages['unknown_argument_map'] .. ': ' .. k);
end
-- Empty strings, not nil;
if v == nil then
v = '';
origin[k] = '';
end
tbl = rawset( tbl, k, v );
return v;
end,
});
end
--[[--------------------------< N O W R A P _ D A T E >-------------------------
When date is YYYY-MM-DD format wrap in nowrap span: <span ...>YYYY-MM-DD</span>.
When date is DD MMMM YYYY or is MMMM DD, YYYY then wrap in nowrap span:
<span ...>DD MMMM</span> YYYY or <span ...>MMMM DD,</span> YYYY
DOES NOT yet support MMMM YYYY or any of the date ranges.
]]
local function nowrap_date (date)
local cap = '';
local cap2 = '';
if date:match("^%d%d%d%d%-%d%d%-%d%d$") then
date = utilities.substitute (cfg.presentation['nowrap1'], date);
elseif date:match("^%a+%s*%d%d?,%s+%d%d%d%d$") or date:match ("^%d%d?%s*%a+%s+%d%d%d%d$") then
cap, cap2 = string.match (date, "^(.*)%s+(%d%d%d%d)$");
date = utilities.substitute (cfg.presentation['nowrap2'], {cap, cap2});
end
return date;
end
--[[--------------------------< S E T _ T I T L E T Y P E >---------------------
This function sets default title types (equivalent to the citation including
|type=<default value>) for those templates that have defaults. Also handles the
special case where it is desirable to omit the title type from the rendered citation
(|type=none).
]]
local function set_titletype (cite_class, title_type)
if utilities.is_set (title_type) then
if 'none' == cfg.keywords_xlate[title_type] then
title_type = ''; -- if |type=none then type parameter not displayed
end
return title_type; -- if |type= has been set to any other value use that value
end
return cfg.title_types [cite_class] or ''; -- set template's default title type; else empty string for concatenation
end
--[[--------------------------< S A F E _ J O I N >-----------------------------
Joins a sequence of strings together while checking for duplicate separation characters.
]]
local function safe_join( tbl, duplicate_char )
local f = {}; -- create a function table appropriate to type of 'duplicate character'
if 1 == #duplicate_char then -- for single byte ASCII characters use the string library functions
f.gsub = string.gsub
f.match = string.match
f.sub = string.sub
else -- for multi-byte characters use the ustring library functions
f.gsub = mw.ustring.gsub
f.match = mw.ustring.match
f.sub = mw.ustring.sub
end
local str = ''; -- the output string
local comp = ''; -- what does 'comp' mean?
local end_chr = '';
local trim;
for _, value in ipairs( tbl ) do
if value == nil then value = ''; end
if str == '' then -- if output string is empty
str = value; -- assign value to it (first time through the loop)
elseif value ~= '' then
if value:sub(1, 1) == '<' then -- special case of values enclosed in spans and other markup.
comp = value:gsub( "%b<>", "" ); -- remove HTML markup (<span>string</span> -> string)
else
comp = value;
end
-- typically duplicate_char is sepc
if f.sub(comp, 1, 1) == duplicate_char then -- is first character same as duplicate_char? why test first character?
-- Because individual string segments often (always?) begin with terminal punct for the
-- preceding segment: 'First element' .. 'sepc next element' .. etc.?
trim = false;
end_chr = f.sub(str, -1, -1); -- get the last character of the output string
-- str = str .. "<HERE(enchr=" .. end_chr .. ")" -- debug stuff?
if end_chr == duplicate_char then -- if same as separator
str = f.sub(str, 1, -2); -- remove it
elseif end_chr == "'" then -- if it might be wiki-markup
if f.sub(str, -3, -1) == duplicate_char .. "''" then -- if last three chars of str are sepc''
str = f.sub(str, 1, -4) .. "''"; -- remove them and add back ''
elseif f.sub(str, -5, -1) == duplicate_char .. "]]''" then -- if last five chars of str are sepc]]''
trim = true; -- why? why do this and next differently from previous?
elseif f.sub(str, -4, -1) == duplicate_char .. "]''" then -- if last four chars of str are sepc]''
trim = true; -- same question
end
elseif end_chr == "]" then -- if it might be wiki-markup
if f.sub(str, -3, -1) == duplicate_char .. "]]" then -- if last three chars of str are sepc]] wikilink
trim = true;
elseif f.sub(str, -3, -1) == duplicate_char .. '"]' then -- if last three chars of str are sepc"] quoted external link
trim = true;
elseif f.sub(str, -2, -1) == duplicate_char .. "]" then -- if last two chars of str are sepc] external link
trim = true;
elseif f.sub(str, -4, -1) == duplicate_char .. "'']" then -- normal case when |url=something & |title=Title.
trim = true;
end
elseif end_chr == " " then -- if last char of output string is a space
if f.sub(str, -2, -1) == duplicate_char .. " " then -- if last two chars of str are <sepc><space>
str = f.sub(str, 1, -3); -- remove them both
end
end
if trim then
if value ~= comp then -- value does not equal comp when value contains HTML markup
local dup2 = duplicate_char;
if f.match(dup2, "%A" ) then dup2 = "%" .. dup2; end -- if duplicate_char not a letter then escape it
value = f.gsub(value, "(%b<>)" .. dup2, "%1", 1 ) -- remove duplicate_char if it follows HTML markup
else
value = f.sub(value, 2, -1 ); -- remove duplicate_char when it is first character
end
end
end
str = str .. value; -- add it to the output string
end
end
return str;
end
--[[--------------------------< I S _ S U F F I X >-----------------------------
returns true if suffix is properly formed Jr, Sr, or ordinal in the range 1–9.
Puncutation not allowed.
]]
local function is_suffix (suffix)
if utilities.in_array (suffix, {'Jr', 'Sr', 'Jnr', 'Snr', '1st', '2nd', '3rd'}) or suffix:match ('^%dth$') then
return true;
end
return false;
end
--[[--------------------< I S _ G O O D _ V A N C _ N A M E >-------------------
For Vancouver style, author/editor names are supposed to be rendered in Latin
(read ASCII) characters. When a name uses characters that contain diacritical
marks, those characters are to be converted to the corresponding Latin
character. When a name is written using a non-Latin alphabet or logogram, that
name is to be transliterated into Latin characters. The module doesn't do this
so editors may/must.
This test allows |first= and |last= names to contain any of the letters defined
in the four Unicode Latin character sets
[http://www.unicode.org/charts/PDF/U0000.pdf C0 Controls and Basic Latin] 0041–005A, 0061–007A
[http://www.unicode.org/charts/PDF/U0080.pdf C1 Controls and Latin-1 Supplement] 00C0–00D6, 00D8–00F6, 00F8–00FF
[http://www.unicode.org/charts/PDF/U0100.pdf Latin Extended-A] 0100–017F
[http://www.unicode.org/charts/PDF/U0180.pdf Latin Extended-B] 0180–01BF, 01C4–024F
|lastn= also allowed to contain hyphens, spaces, and apostrophes.
(http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35029/)
|firstn= also allowed to contain hyphens, spaces, apostrophes, and periods
This original test:
if nil == mw.ustring.find (last, "^[A-Za-zÀ-ÖØ-öø-ƿDŽ-ɏ%-%s%']*$")
or nil == mw.ustring.find (first, "^[A-Za-zÀ-ÖØ-öø-ƿDŽ-ɏ%-%s%'%.]+[2-6%a]*$") then
was written outside of the code editor and pasted here because the code editor
gets confused between character insertion point and cursor position. The test has
been rewritten to use decimal character escape sequence for the individual bytes
of the Unicode characters so that it is not necessary to use an external editor
to maintain this code.
\195\128-\195\150 – À-Ö (U+00C0–U+00D6 – C0 controls)
\195\152-\195\182 – Ø-ö (U+00D8-U+00F6 – C0 controls)
\195\184-\198\191 – ø-ƿ (U+00F8-U+01BF – C0 controls, Latin extended A & B)
\199\132-\201\143 – DŽ-ɏ (U+01C4-U+024F – Latin extended B)
]]
local function is_good_vanc_name (last, first, suffix, position)
if not suffix then
if first:find ('[,%s]') then -- when there is a space or comma, might be first name/initials + generational suffix
first = first:match ('(.-)[,%s]+'); -- get name/initials
suffix = first:match ('[,%s]+(.+)$'); -- get generational suffix
end
end
if utilities.is_set (suffix) then
if not is_suffix (suffix) then
add_vanc_error (cfg.err_msg_supl.suffix, position);
return false; -- not a name with an appropriate suffix
end
end
if nil == mw.ustring.find (last, "^[A-Za-z\195\128-\195\150\195\152-\195\182\195\184-\198\191\199\132-\201\143\225\184\128-\225\187\191%-%s%']*$") or
nil == mw.ustring.find (first, "^[A-Za-z\195\128-\195\150\195\152-\195\182\195\184-\198\191\199\132-\201\143\225\184\128-\225\187\191%-%s%'%.]*$") then
add_vanc_error (cfg.err_msg_supl['non-Latin char'], position);
return false; -- not a string of Latin characters; Vancouver requires Romanization
end;
return true;
end
--[[--------------------------< R E D U C E _ T O _ I N I T I A L S >------------------------------------------
Attempts to convert names to initials in support of |name-list-style=vanc.
Names in |firstn= may be separated by spaces or hyphens, or for initials, a period.
See http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35062/.
Vancouver style requires family rank designations (Jr, II, III, etc.) to be rendered
as Jr, 2nd, 3rd, etc. See http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35085/.
This code only accepts and understands generational suffix in the Vancouver format
because Roman numerals look like, and can be mistaken for, initials.
This function uses ustring functions because firstname initials may be any of the
Unicode Latin characters accepted by is_good_vanc_name ().
]]
local function reduce_to_initials (first, position)
if first:find (',', 1, true) then
return first; -- commas not allowed; abandon
end
local name, suffix = mw.ustring.match (first, "^(%u+) ([%dJS][%drndth]+)$");
if not name then -- if not initials and a suffix
name = mw.ustring.match (first, "^(%u+)$"); -- is it just initials?
end
if name then -- if first is initials with or without suffix
if 3 > mw.ustring.len (name) then -- if one or two initials
if suffix then -- if there is a suffix
if is_suffix (suffix) then -- is it legitimate?
return first; -- one or two initials and a valid suffix so nothing to do
else
add_vanc_error (cfg.err_msg_supl.suffix, position); -- one or two initials with invalid suffix so error message
return first; -- and return first unmolested
end
else
return first; -- one or two initials without suffix; nothing to do
end
end
end -- if here then name has 3 or more uppercase letters so treat them as a word
local initials_t, names_t = {}, {}; -- tables to hold name parts and initials
local i = 1; -- counter for number of initials
names_t = mw.text.split (first, '[%s%-]+'); -- split into a sequence of names and possible suffix
while names_t[i] do -- loop through the sequence
if 1 < i and names_t[i]:match ('[%dJS][%drndth]+%.?$') then -- if not the first name, and looks like a suffix (may have trailing dot)
names_t[i] = names_t[i]:gsub ('%.', ''); -- remove terminal dot if present
if is_suffix (names_t[i]) then -- if a legitimate suffix
table.insert (initials_t, ' ' .. names_t[i]); -- add a separator space, insert at end of initials sequence
break; -- and done because suffix must fall at the end of a name
end -- no error message if not a suffix; possibly because of Romanization
end
if 3 > i then
table.insert (initials_t, mw.ustring.sub (names_t[i], 1, 1)); -- insert the initial at end of initials sequence
end
i = i + 1; -- bump the counter
end
return table.concat (initials_t); -- Vancouver format does not include spaces.
end
--[[--------------------------< I N T E R W I K I _ P R E F I X E N _ G E T >----------------------------------
extract interwiki prefixen from <value>. Returns two one or two values:
false – no prefixen
nil – prefix exists but not recognized
project prefix, language prefix – when value has either of:
:<project>:<language>:<article>
:<language>:<project>:<article>
project prefix, nil – when <value> has only a known single-letter prefix
nil, language prefix – when <value> has only a known language prefix
accepts single-letter project prefixen: 'd' (wikidata), 's' (wikisource), and 'w' (wikipedia) prefixes; at this
writing, the other single-letter prefixen (b (wikibook), c (commons), m (meta), n (wikinews), q (wikiquote), and
v (wikiversity)) are not supported.
]]
local function interwiki_prefixen_get (value, is_link)
if not value:find (':%l+:') then -- if no prefix
return false; -- abandon; boolean here to distinguish from nil fail returns later
end
local prefix_patterns_linked_t = { -- sequence of valid interwiki and inter project prefixen
'^%[%[:([dsw]):(%l%l+):', -- wikilinked; project and language prefixes
'^%[%[:(%l%l+):([dsw]):', -- wikilinked; language and project prefixes
'^%[%[:([dsw]):', -- wikilinked; project prefix
'^%[%[:(%l%l+):', -- wikilinked; language prefix
}
local prefix_patterns_unlinked_t = { -- sequence of valid interwiki and inter project prefixen
'^:([dsw]):(%l%l+):', -- project and language prefixes
'^:(%l%l+):([dsw]):', -- language and project prefixes
'^:([dsw]):', -- project prefix
'^:(%l%l+):', -- language prefix
}
local cap1, cap2;
for _, pattern in ipairs ((is_link and prefix_patterns_linked_t) or prefix_patterns_unlinked_t) do
cap1, cap2 = value:match (pattern);
if cap1 then
break; -- found a match so stop looking
end
end
if cap1 and cap2 then -- when both then :project:language: or :language:project: (both forms allowed)
if 1 == #cap1 then -- length == 1 then :project:language:
if cfg.inter_wiki_map[cap2] then -- is language prefix in the interwiki map?
return cap1, cap2; -- return interwiki project and interwiki language
end
else -- here when :language:project:
if cfg.inter_wiki_map[cap1] then -- is language prefix in the interwiki map?
return cap2, cap1; -- return interwiki project and interwiki language
end
end
return nil; -- unknown interwiki language
elseif not (cap1 or cap2) then -- both are nil?
return nil; -- we got something that looks like a project prefix but isn't; return fail
elseif 1 == #cap1 then -- here when one capture
return cap1, nil; -- length is 1 so return project, nil language
else -- here when one capture and its length it more than 1
if cfg.inter_wiki_map[cap1] then -- is language prefix in the interwiki map?
return nil, cap1; -- return nil project, language
end
end
end
--[[--------------------------< L I S T _ P E O P L E >--------------------------
Formats a list of people (authors, contributors, editors, interviewers, translators)
names in the list will be linked when
|<name>-link= has a value
|<name>-mask- does NOT have a value; masked names are presumed to have been
rendered previously so should have been linked there
when |<name>-mask=0, the associated name is not rendered
]]
local function list_people (control, people, etal)
local sep;
local namesep;
local format = control.format;
local maximum = control.maximum;
local name_list = {};
if 'vanc' == format then -- Vancouver-like name styling?
sep = cfg.presentation['sep_nl_vanc']; -- name-list separator between names is a comma
namesep = cfg.presentation['sep_name_vanc']; -- last/first separator is a space
else
sep = cfg.presentation['sep_nl']; -- name-list separator between names is a semicolon
namesep = cfg.presentation['sep_name']; -- last/first separator is <comma><space>
end
if sep:sub (-1, -1) ~= " " then sep = sep .. " " end
if utilities.is_set (maximum) and maximum < 1 then return "", 0; end -- returned 0 is for EditorCount; not used for other names
for i, person in ipairs (people) do
if utilities.is_set (person.last) then
local mask = person.mask;
local one;
local sep_one = sep;
if utilities.is_set (maximum) and i > maximum then
etal = true;
break;
end
if mask then
local n = tonumber (mask); -- convert to a number if it can be converted; nil else
if n then
one = 0 ~= n and string.rep("—", n) or nil; -- make a string of (n > 0) mdashes, nil else, to replace name
person.link = nil; -- don't create link to name if name is replaces with mdash string or has been set nil
else
one = mask; -- replace name with mask text (must include name-list separator)
sep_one = " "; -- modify name-list separator
end
else
one = person.last; -- get surname
local first = person.first -- get given name
if utilities.is_set (first) then
if ("vanc" == format) then -- if Vancouver format
one = one:gsub ('%.', ''); -- remove periods from surnames (http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35029/)
if not person.corporate and is_good_vanc_name (one, first, nil, i) then -- and name is all Latin characters; corporate authors not tested
first = reduce_to_initials (first, i); -- attempt to convert first name(s) to initials
end
end
one = one .. namesep .. first;
end
end
if utilities.is_set (person.link) then
one = utilities.make_wikilink (person.link, one); -- link author/editor
end
if one then -- if <one> has a value (name, mdash replacement, or mask text replacement)
local proj, tag = interwiki_prefixen_get (one, true); -- get the interwiki prefixen if present
if 'w' == proj and ('Wikipedia' == mw.site.namespaces.Project['name']) then
proj = nil; -- for stuff like :w:de:<article>, :w is unnecessary TODO: maint cat?
end
if proj then
local proj_name = ({['d'] = 'Wikidata', ['s'] = 'Wikisource', ['w'] = 'Wikipedia'})[proj]; -- :w (wikipedia) for linking from a non-wikipedia project
if proj_name then
one = one .. utilities.wrap_style ('interproj', proj_name); -- add resized leading space, brackets, static text, language name
utilities.add_prop_cat ('interproj-linked-name', proj); -- categorize it; <proj> is sort key
tag = nil; -- unset; don't do both project and language
end
end
if tag == cfg.this_wiki_code then
tag = nil; -- stuff like :en:<article> at en.wiki is pointless TODO: maint cat?
end
if tag then
local lang = cfg.lang_tag_remap[tag] or cfg.mw_languages_by_tag_t[tag];
if lang then -- error messaging done in extract_names() where we know parameter names
one = one .. utilities.wrap_style ('interwiki', lang); -- add resized leading space, brackets, static text, language name
utilities.add_prop_cat ('interwiki-linked-name', tag); -- categorize it; <tag> is sort key
end
end
table.insert (name_list, one); -- add it to the list of names
table.insert (name_list, sep_one); -- add the proper name-list separator
end
end
end
local count = #name_list / 2; -- (number of names + number of separators) divided by 2
if 0 < count then
if 1 < count and not etal then
if 'amp' == format then
name_list[#name_list-2] = " & "; -- replace last separator with ampersand text
elseif 'and' == format then
if 2 == count then
name_list[#name_list-2] = cfg.presentation.sep_nl_and; -- replace last separator with 'and' text
else
name_list[#name_list-2] = cfg.presentation.sep_nl_end; -- replace last separator with '(sep) and' text
end
end
end
name_list[#name_list] = nil; -- erase the last separator
end
local result = table.concat (name_list); -- construct list
if etal and utilities.is_set (result) then -- etal may be set by |display-authors=etal but we might not have a last-first list
result = result .. sep .. cfg.messages['et al']; -- we've got a last-first list and etal so add et al.
end
return result, count; -- return name-list string and count of number of names (count used for editor names only)
end
--[[--------------------< M A K E _ C I T E R E F _ I D >-----------------------
Generates a CITEREF anchor ID if we have at least one name or a date. Otherwise
returns an empty string.
namelist is one of the contributor-, author-, or editor-name lists chosen in that
order. year is Year or anchor_year.
]]
local function make_citeref_id (namelist, year)
local names={}; -- a table for the one to four names and year
for i,v in ipairs (namelist) do -- loop through the list and take up to the first four last names
names[i] = v.last
if i == 4 then break end -- if four then done
end
table.insert (names, year); -- add the year at the end
local id = table.concat(names); -- concatenate names and year for CITEREF id
if utilities.is_set (id) then -- if concatenation is not an empty string
return "CITEREF" .. id; -- add the CITEREF portion
else
return ''; -- return an empty string; no reason to include CITEREF id in this citation
end
end
--[[--------------------------< C I T E _ C L A S S _A T T R I B U T E _M A K E >------------------------------
construct <cite> tag class attribute for this citation.
<cite_class> – config.CitationClass from calling template
<mode> – value from |mode= parameter
]]
local function cite_class_attribute_make (cite_class, mode)
local class_t = {};
table.insert (class_t, 'citation'); -- required for blue highlight
if 'citation' ~= cite_class then
table.insert (class_t, cite_class); -- identify this template for user css
table.insert (class_t, utilities.is_set (mode) and mode or 'cs1'); -- identify the citation style for user css or javascript
else
table.insert (class_t, utilities.is_set (mode) and mode or 'cs2'); -- identify the citation style for user css or javascript
end
for _, prop_key in ipairs (z.prop_keys_t) do
table.insert (class_t, prop_key); -- identify various properties for user css or javascript
end
return table.concat (class_t, ' '); -- make a big string and done
end
--[[---------------------< N A M E _ H A S _ E T A L >--------------------------
Evaluates the content of name parameters (author, editor, etc.) for variations on
the theme of et al. If found, the et al. is removed, a flag is set to true and
the function returns the modified name and the flag.
This function never sets the flag to false but returns its previous state because
it may have been set by previous passes through this function or by the associated
|display-<names>=etal parameter
]]
local function name_has_etal (name, etal, nocat, param)
if utilities.is_set (name) then -- name can be nil in which case just return
local patterns = cfg.et_al_patterns; -- get patterns from configuration
for _, pattern in ipairs (patterns) do -- loop through all of the patterns
if name:match (pattern) then -- if this 'et al' pattern is found in name
name = name:gsub (pattern, ''); -- remove the offending text
etal = true; -- set flag (may have been set previously here or by |display-<names>=etal)
if not nocat then -- no categorization for |vauthors=
utilities.set_message ('err_etal', {param}); -- and set an error if not added
end
end
end
end
return name, etal;
end
--[[---------------------< N A M E _ I S _ N U M E R I C >----------------------
Add an error message and category when <name> parameter value does not contain letters.
Add a maintenance category when <name> parameter value has numeric characters mixed with characters that are
not numeric characters; could be letters and/or punctuation characters.
This function will only emit one error and one maint message for the current template. Does not emit both error
and maint messages/categories for the same parameter value.
returns nothing
]]
local function name_is_numeric (name, name_alias, list_name)
local patterns = {
'^%D+%d', -- <name> must have digits preceded by other characters
'^%D*%d+%D+', -- <name> must have digits followed by other characters
}
if not added_numeric_name_errs and mw.ustring.match (name, '^[%A]+$') then -- if we have not already set an error message and <name> does not have any alpha characters
utilities.set_message ('err_numeric_names', name_alias); -- add an error message
added_numeric_name_errs = true; -- set the flag so we emit only one error message
return; -- when here no point in further testing; abandon
end
if not added_numeric_name_maint then -- if we have already set a maint message
for _, pattern in ipairs (patterns) do -- spin through list of patterns
if mw.ustring.match (name, pattern) then -- digits preceded or followed by anything but digits; %D+ includes punctuation
utilities.set_message ('maint_numeric_names', cfg.special_case_translation [list_name]); -- add a maint cat for this template
added_numeric_name_maint = true; -- set the flag so we emit only one maint message
return; -- when here no point in further testing; abandon
end
end
end
end
--[[-----------------< N A M E _ H A S _ M U L T _ N A M E S >------------------
Evaluates the content of last/surname (authors etc.) parameters for multiple names.
Multiple names are indicated if there is more than one comma or any "unescaped"
semicolons. Escaped semicolons are ones used as part of selected HTML entities.
If the condition is met, the function adds the multiple name maintenance category.
Same test for first except that commas should not appear in given names (MOS:JR says
that the generational suffix does not take a separator character). Titles, degrees,
postnominals, affiliations, all normally comma separated don't belong in a citation.
<name> – name parameter value
<list_name> – AuthorList, EditorList, etc
<limit> – number of allowed commas; 1 (default) for surnames; 0 for given names
returns nothing
]]
local function name_has_mult_names (name, list_name, limit)
local _, commas, semicolons, nbsps;
limit = limit and limit or 1;
if utilities.is_set (name) then
_, commas = name:gsub (',', ''); -- count the number of commas
_, semicolons = name:gsub (';', ''); -- count the number of semicolons
-- nbsps probably should be its own separate count rather than merged in
-- some way with semicolons because Lua patterns do not support the
-- grouping operator that regex does, which means there is no way to add
-- more entities to escape except by adding more counts with the new
-- entities
_, nbsps = name:gsub (' ',''); -- count nbsps
-- There is exactly 1 semicolon per entity, so subtract nbsps
-- from semicolons to 'escape' them. If additional entities are added,
-- they also can be subtracted.
if limit < commas or 0 < (semicolons - nbsps) then
utilities.set_message ('maint_mult_names', cfg.special_case_translation [list_name]); -- add a maint message
end
end
end
--[=[-------------------------< I S _ G E N E R I C >----------------------------------------------------------
Compares values assigned to various parameters according to the string provided as <item> in the function call.
<item> can have on of two values:
'generic_names' – for name-holding parameters: |last=, |first=, |editor-last=, etc
'generic_titles' – for |title=
There are two types of generic tests. The 'accept' tests look for a pattern that should not be rejected by the
'reject' test. For example,
|author=[[John Smith (author)|Smith, John]]
would be rejected by the 'author' reject test. But piped wikilinks with 'author' disambiguation should not be
rejected so the 'accept' test prevents that from happening. Accept tests are always performed before reject
tests.
Each of the 'accept' and 'reject' sequence tables hold tables for en.wiki (['en']) and local.wiki (['local'])
that each can hold a test sequence table The sequence table holds, at index [1], a test pattern, and, at index
[2], a boolean control value. The control value tells string.find() or mw.ustring.find() to do plain-text search (true)
or a pattern search (false). The intent of all this complexity is to make these searches as fast as possible so
that we don't run out of processing time on very large articles.
Returns
true when a reject test finds the pattern or string
false when an accept test finds the pattern or string
nil else
]=]
local function is_generic (item, value, wiki)
local test_val;
local str_lower = { -- use string.lower() for en.wiki (['en']) and use mw.ustring.lower() or local.wiki (['local'])
['en'] = string.lower,
['local'] = mw.ustring.lower,
}
local str_find = { -- use string.find() for en.wiki (['en']) and use mw.ustring.find() or local.wiki (['local'])
['en'] = string.find,
['local'] = mw.ustring.find,
}
local function test (val, test_t, wiki) -- local function to do the testing; <wiki> selects lower() and find() functions
val = test_t[2] and str_lower[wiki](value) or val; -- when <test_t[2]> set to 'true', plaintext search using lowercase value
return str_find[wiki] (val, test_t[1], 1, test_t[2]); -- return nil when not found or matched
end
local test_types_t = {'accept', 'reject'}; -- test accept patterns first, then reject patterns
local wikis_t = {'en', 'local'}; -- do tests for each of these keys; en.wiki first, local.wiki second
for _, test_type in ipairs (test_types_t) do -- for each test type
for _, generic_value in pairs (cfg.special_case_translation[item][test_type]) do -- spin through the list of generic value fragments to accept or reject
for _, wiki in ipairs (wikis_t) do
if generic_value[wiki] then
if test (value, generic_value[wiki], wiki) then -- go do the test
return ('reject' == test_type); -- param value rejected, return true; false else
end
end
end
end
end
end
--[[--------------------------< N A M E _ I S _ G E N E R I C >------------------------------------------------
calls is_generic() to determine if <name> is a 'generic name' listed in cfg.generic_names; <name_alias> is the
parameter name used in error messaging
]]
local function name_is_generic (name, name_alias)
if not added_generic_name_errs and is_generic ('generic_names', name) then
utilities.set_message ('err_generic_name', name_alias); -- set an error message
added_generic_name_errs = true;
end
end
--[[--------------------------< N A M E _ C H E C K S >--------------------------------------------------------
This function calls various name checking functions used to validate the content of the various name-holding parameters.
]]
local function name_checks (last, first, list_name, last_alias, first_alias)
local accept_name;
if utilities.is_set (last) then
last, accept_name = utilities.has_accept_as_written (last); -- remove accept-this-as-written markup when it wraps all of <last>
if not accept_name then -- <last> not wrapped in accept-as-written markup
name_has_mult_names (last, list_name); -- check for multiple names in the parameter
name_is_numeric (last, last_alias, list_name); -- check for names that have no letters or are a mix of digits and other characters
name_is_generic (last, last_alias); -- check for names found in the generic names list
end
end
if utilities.is_set (first) then
first, accept_name = utilities.has_accept_as_written (first); -- remove accept-this-as-written markup when it wraps all of <first>
if not accept_name then -- <first> not wrapped in accept-as-written markup
name_has_mult_names (first, list_name, 0); -- check for multiple names in the parameter; 0 is number of allowed commas in a given name
name_is_numeric (first, first_alias, list_name); -- check for names that have no letters or are a mix of digits and other characters
name_is_generic (first, first_alias); -- check for names found in the generic names list
end
local wl_type, D = utilities.is_wikilink (first);
if 0 ~= wl_type then
first = D;
utilities.set_message ('err_bad_paramlink', first_alias);
end
end
return last, first; -- done
end
--[[----------------------< E X T R A C T _ N A M E S >-------------------------
Gets name list from the input arguments
Searches through args in sequential order to find |lastn= and |firstn= parameters
(or their aliases), and their matching link and mask parameters. Stops searching
when both |lastn= and |firstn= are not found in args after two sequential attempts:
found |last1=, |last2=, and |last3= but doesn't find |last4= and |last5= then the
search is done.
This function emits an error message when there is a |firstn= without a matching
|lastn=. When there are 'holes' in the list of last names, |last1= and |last3=
are present but |last2= is missing, an error message is emitted. |lastn= is not
required to have a matching |firstn=.
When an author or editor parameter contains some form of 'et al.', the 'et al.'
is stripped from the parameter and a flag (etal) returned that will cause list_people()
to add the static 'et al.' text from Module:Citation/CS1/Configuration. This keeps
'et al.' out of the template's metadata. When this occurs, an error is emitted.
]]
local function extract_names(args, list_name)
local names = {}; -- table of names
local last; -- individual name components
local first;
local link;
local mask;
local i = 1; -- loop counter/indexer
local n = 1; -- output table indexer
local count = 0; -- used to count the number of times we haven't found a |last= (or alias for authors, |editor-last or alias for editors)
local etal = false; -- return value set to true when we find some form of et al. in an author parameter
local last_alias, first_alias, link_alias; -- selected parameter aliases used in error messaging
while true do
last, last_alias = utilities.select_one ( args, cfg.aliases[list_name .. '-Last'], 'err_redundant_parameters', i ); -- search through args for name components beginning at 1
first, first_alias = utilities.select_one ( args, cfg.aliases[list_name .. '-First'], 'err_redundant_parameters', i );
link, link_alias = utilities.select_one ( args, cfg.aliases[list_name .. '-Link'], 'err_redundant_parameters', i );
mask = utilities.select_one ( args, cfg.aliases[list_name .. '-Mask'], 'err_redundant_parameters', i );
if last then -- error check |lastn= alias for unknown interwiki link prefix; done here because this is where we have the parameter name
local project, language = interwiki_prefixen_get (last, true); -- true because we expect interwiki links in |lastn= to be wikilinked
if nil == project and nil == language then -- when both are nil
utilities.set_message ('err_bad_paramlink', last_alias); -- not known, emit an error message -- TODO: err_bad_interwiki?
last = utilities.remove_wiki_link (last); -- remove wikilink markup; show display value only
end
end
if link then -- error check |linkn= alias for unknown interwiki link prefix
local project, language = interwiki_prefixen_get (link, false); -- false because wiki links in |author-linkn= is an error
if nil == project and nil == language then -- when both are nil
utilities.set_message ('err_bad_paramlink', link_alias); -- not known, emit an error message -- TODO: err_bad_interwiki?
link = nil; -- unset so we don't link
link_alias = nil;
end
end
last, etal = name_has_etal (last, etal, false, last_alias); -- find and remove variations on et al.
first, etal = name_has_etal (first, etal, false, first_alias); -- find and remove variations on et al.
last, first = name_checks (last, first, list_name, last_alias, first_alias); -- multiple names, extraneous annotation, etc. checks
if first and not last then -- if there is a firstn without a matching lastn
local alias = first_alias:find ('given', 1, true) and 'given' or 'first'; -- get first or given form of the alias
utilities.set_message ('err_first_missing_last', {
first_alias, -- param name of alias missing its mate
first_alias:gsub (alias, {['first'] = 'last', ['given'] = 'surname'}), -- make param name appropriate to the alias form
}); -- add this error message
elseif not first and not last then -- if both firstn and lastn aren't found, are we done?
count = count + 1; -- number of times we haven't found last and first
if 2 <= count then -- two missing names and we give up
break; -- normal exit or there is a two-name hole in the list; can't tell which
end
else -- we have last with or without a first
local result;
link = link_title_ok (link, link_alias, last, last_alias); -- check for improper wiki-markup
if first then
link = link_title_ok (link, link_alias, first, first_alias); -- check for improper wiki-markup
end
names[n] = {last = last, first = first, link = link, mask = mask, corporate = false}; -- add this name to our names list (corporate for |vauthors= only)
n = n + 1; -- point to next location in the names table
if 1 == count then -- if the previous name was missing
utilities.set_message ('err_missing_name', {list_name:match ("(%w+)List"):lower(), i - 1}); -- add this error message
end
count = 0; -- reset the counter, we're looking for two consecutive missing names
end
i = i + 1; -- point to next args location
end
return names, etal; -- all done, return our list of names and the etal flag
end
--[[--------------------------< N A M E _ T A G _ G E T >------------------------------------------------------
attempt to decode |language=<lang_param> and return language name and matching tag; nil else.
This function looks for:
<lang_param> as a tag in cfg.lang_tag_remap{}
<lang_param> as a name in cfg.lang_name_remap{}
<lang_param> as a name in cfg.mw_languages_by_name_t
<lang_param> as a tag in cfg.mw_languages_by_tag_t
when those fail, presume that <lang_param> is an IETF-like tag that MediaWiki does not recognize. Strip all
script, region, variant, whatever subtags from <lang_param> to leave just a two or three character language tag
and look for the new <lang_param> in cfg.mw_languages_by_tag_t{}
on success, returns name (in properly capitalized form) and matching tag (in lowercase); on failure returns nil
]]
local function name_tag_get (lang_param)
local lang_param_lc = mw.ustring.lower (lang_param); -- use lowercase as an index into the various tables
local name;
local tag;
name = cfg.lang_tag_remap[lang_param_lc]; -- assume <lang_param_lc> is a tag; attempt to get remapped language name
if name then -- when <name>, <lang_param> is a tag for a remapped language name
if cfg.lang_name_remap[name:lower()][2] ~= lang_param_lc then
utilities.set_message ('maint_unknown_lang'); -- add maint category if not already added
return name, cfg.lang_name_remap[name:lower()][2]; -- so return name and tag from lang_name_remap[name]; special case to xlate sr-ec and sr-el to sr-cyrl and sr-latn
end
return name, lang_param_lc; -- so return <name> from remap and <lang_param_lc>
end
tag = lang_param_lc:match ('^(%a%a%a?)%-.*'); -- still assuming that <lang_param_lc> is a tag; strip script, region, variant subtags
name = cfg.lang_tag_remap[tag]; -- attempt to get remapped language name with language subtag only
if name then -- when <name>, <tag> is a tag for a remapped language name
return name, tag; -- so return <name> from remap and <tag>
end
if cfg.lang_name_remap[lang_param_lc] then -- not a remapped tag, assume <lang_param_lc> is a name; attempt to get remapped language tag
return cfg.lang_name_remap[lang_param_lc][1], cfg.lang_name_remap[lang_param_lc][2]; -- for this <lang_param_lc>, return a (possibly) new name and appropriate tag
end
name = cfg.mw_languages_by_tag_t[lang_param_lc]; -- assume that <lang_param_lc> is a tag; attempt to get its matching language name
if name then
return name, lang_param_lc; -- <lang_param_lc> is a tag so return it and <name>
end
tag = cfg.mw_languages_by_name_t[lang_param_lc]; -- assume that <lang_param_lc> is a language name; attempt to get its matching tag
if tag then
return cfg.mw_languages_by_tag_t[tag], tag; -- <lang_param_lc> is a name so return the name from the table and <tag>
end
tag = lang_param_lc:match ('^(%a%a%a?)%-.*'); -- is <lang_param_lc> an IETF-like tag that MediaWiki doesn't recognize? <tag> gets the language subtag; nil else
if tag then
name = cfg.mw_languages_by_tag_t[tag]; -- attempt to get a language name using the shortened <tag>
if name then
return name, tag; -- <lang_param_lc> is an unrecognized IETF-like tag so return <name> and language subtag
end
end
end
--[[-------------------< L A N G U A G E _ P A R A M E T E R >------------------
Gets language name from a provided two- or three-character ISO 639 code. If a code
is recognized by MediaWiki, use the returned name; if not, then use the value that
was provided with the language parameter.
When |language= contains a recognized language (either code or name), the page is
assigned to the category for that code: Category:Norwegian-language sources (no).
For valid three-character code languages, the page is assigned to the single category
for '639-2' codes: Category:CS1 ISO 639-2 language sources.
Languages that are the same as the local wiki are not categorized. MediaWiki does
not recognize three-character equivalents of two-character codes: code 'ar' is
recognized but code 'ara' is not.
This function supports multiple languages in the form |language=nb, French, th
where the language names or codes are separated from each other by commas with
optional space characters.
]]
local function language_parameter (lang)
local tag; -- some form of IETF-like language tag; language subtag with optional region, sript, vatiant, etc subtags
local lang_subtag; -- ve populates |language= with mostly unecessary region subtags the MediaWiki does not recognize; this is the base language subtag
local name; -- the language name
local language_list = {}; -- table of language names to be rendered
local names_t = {}; -- table made from the value assigned to |language=
local this_wiki_name = mw.language.fetchLanguageName (cfg.this_wiki_code, cfg.this_wiki_code); -- get this wiki's language name
names_t = mw.text.split (lang, '%s*,%s*'); -- names should be a comma separated list
for _, lang in ipairs (names_t) do -- reuse lang here because we don't yet know if lang is a language name or a language tag
name, tag = name_tag_get (lang); -- attempt to get name/tag pair for <lang>; <name> has proper capitalization; <tag> is lowercase
if utilities.is_set (tag) then
lang_subtag = tag:gsub ('^(%a%a%a?)%-.*', '%1'); -- for categorization, strip any IETF-like tags from language tag
if cfg.this_wiki_code ~= lang_subtag then -- when the language is not the same as this wiki's language
if 2 == lang_subtag:len() then -- and is a two-character tag
utilities.add_prop_cat ('foreign-lang-source', {name, tag}, lang_subtag); -- categorize it; tag appended to allow for multiple language categorization
else -- or is a recognized language (but has a three-character tag)
utilities.add_prop_cat ('foreign-lang-source-2', {lang_subtag}, lang_subtag); -- categorize it differently TODO: support multiple three-character tag categories per cs1|2 template?
end
elseif cfg.local_lang_cat_enable then -- when the language and this wiki's language are the same and categorization is enabled
utilities.add_prop_cat ('local-lang-source', {name, lang_subtag}); -- categorize it
end
else
name = lang; -- return whatever <lang> has so that we show something
utilities.set_message ('maint_unknown_lang'); -- add maint category if not already added
end
table.insert (language_list, name);
name = ''; -- so we can reuse it
end
name = utilities.make_sep_list (#language_list, language_list);
if (1 == #language_list) and (lang_subtag == cfg.this_wiki_code) then -- when only one language, find lang name in this wiki lang name; for |language=en-us, 'English' in 'American English'
return ''; -- if one language and that language is this wiki's return an empty string (no annotation)
end
return (" " .. wrap_msg ('language', name)); -- otherwise wrap with '(in ...)'
--[[ TODO: should only return blank or name rather than full list
so we can clean up the bunched parenthetical elements Language, Type, Format
]]
end
--[[-----------------------< S E T _ C S _ S T Y L E >--------------------------
Gets the default CS style configuration for the given mode.
Returns default separator and either postscript as passed in or the default.
In CS1, the default postscript and separator are '.'.
In CS2, the default postscript is the empty string and the default separator is ','.
]]
local function set_cs_style (postscript, mode)
if utilities.is_set(postscript) then
-- emit a maintenance message if user postscript is the default cs1 postscript
-- we catch the opposite case for cs2 in set_style
if mode == 'cs1' and postscript == cfg.presentation['ps_' .. mode] then
utilities.set_message ('maint_postscript');
end
else
postscript = cfg.presentation['ps_' .. mode];
end
return cfg.presentation['sep_' .. mode], postscript;
end
--[[--------------------------< S E T _ S T Y L E >-----------------------------
Sets the separator and postscript styles. Checks the |mode= first and the
#invoke CitationClass second. Removes the postscript if postscript == none.
]]
local function set_style (mode, postscript, cite_class)
local sep;
if 'cs2' == mode then
sep, postscript = set_cs_style (postscript, 'cs2');
elseif 'cs1' == mode then
sep, postscript = set_cs_style (postscript, 'cs1');
elseif 'citation' == cite_class then
sep, postscript = set_cs_style (postscript, 'cs2');
else
sep, postscript = set_cs_style (postscript, 'cs1');
end
if cfg.keywords_xlate[postscript:lower()] == 'none' then
-- emit a maintenance message if user postscript is the default cs2 postscript
-- we catch the opposite case for cs1 in set_cs_style
if 'cs2' == mode or ('cs1' ~= mode and 'citation' == cite_class) then -- {{citation |title=Title |mode=cs1 |postscript=none}} should not emit maint message
utilities.set_message ('maint_postscript');
end
postscript = '';
end
return sep, postscript
end
--[=[-------------------------< I S _ P D F >-----------------------------------
Determines if a URL has the file extension that is one of the PDF file extensions
used by [[MediaWiki:Common.css]] when applying the PDF icon to external links.
returns true if file extension is one of the recognized extensions, else false
]=]
local function is_pdf (url)
return url:match ('%.pdf$') or url:match ('%.PDF$') or
url:match ('%.pdf[%?#]') or url:match ('%.PDF[%?#]') or
url:match ('%.PDF#') or url:match ('%.pdf#');
end
--[[--------------------------< S T Y L E _ F O R M A T >-----------------------
Applies CSS style to |format=, |chapter-format=, etc. Also emits an error message
if the format parameter does not have a matching URL parameter. If the format parameter
is not set and the URL contains a file extension that is recognized as a PDF document
by MediaWiki's commons.css, this code will set the format parameter to (PDF) with
the appropriate styling.
]]
local function style_format (format, url, fmt_param, url_param)
if utilities.is_set (format) then
format = utilities.wrap_style ('format', format); -- add leading space, parentheses, resize
if not utilities.is_set (url) then
utilities.set_message ('err_format_missing_url', {fmt_param, url_param}); -- add an error message
end
elseif is_pdf (url) then -- format is not set so if URL is a PDF file then
format = utilities.wrap_style ('format', 'PDF'); -- set format to PDF
else
format = ''; -- empty string for concatenation
end
return format;
end
--[[---------------------< G E T _ D I S P L A Y _ N A M E S >------------------
Returns a number that defines the number of names displayed for author and editor
name lists and a Boolean flag to indicate when et al. should be appended to the name list.
When the value assigned to |display-xxxxors= is a number greater than or equal to zero,
return the number and the previous state of the 'etal' flag (false by default
but may have been set to true if the name list contains some variant of the text 'et al.').
When the value assigned to |display-xxxxors= is the keyword 'etal', return a number
that is one greater than the number of authors in the list and set the 'etal' flag true.
This will cause the list_people() to display all of the names in the name list followed by 'et al.'
In all other cases, returns nil and the previous state of the 'etal' flag.
inputs:
max: A['DisplayAuthors'] or A['DisplayEditors'], etc; a number or some flavor of etal
count: #a or #e
list_name: 'authors' or 'editors'
etal: author_etal or editor_etal
This function sets an error message when |display-xxxxors= value greater than or equal to number of names but
not when <max> comes from {{cs1 config}} global settings. When using global settings, <param> is set to the
keyword 'cs1 config' which is used to supress the normal error. Error is suppressed because it is to be expected
that some citations in an article will have the same or fewer names that the limit specified in {{cs1 config}}.
]]
local function get_display_names (max, count, list_name, etal, param)
if utilities.is_set (max) then
if 'etal' == max:lower():gsub("[ '%.]", '') then -- the :gsub() portion makes 'etal' from a variety of 'et al.' spellings and stylings
max = count + 1; -- number of authors + 1 so display all author name plus et al.
etal = true; -- overrides value set by extract_names()
elseif max:match ('^%d+$') then -- if is a string of numbers
max = tonumber (max); -- make it a number
if (max >= count) and ('cs1 config' ~= param) then -- error when local |display-xxxxors= value greater than or equal to number of names; not an error when using global setting
utilities.set_message ('err_disp_name', {param, max}); -- add error message
max = nil;
end
else -- not a valid keyword or number
utilities.set_message ('err_disp_name', {param, max}); -- add error message
max = nil; -- unset; as if |display-xxxxors= had not been set
end
end
return max, etal;
end
--[[----------< E X T R A _ T E X T _ I N _ P A G E _ C H E C K >---------------
Adds error if |page=, |pages=, |quote-page=, |quote-pages= has what appears to be
some form of p. or pp. abbreviation in the first characters of the parameter content.
check page for extraneous p, p., pp, pp., pg, pg. at start of parameter value:
good pattern: '^P[^%.P%l]' matches when page begins PX or P# but not Px
where x and X are letters and # is a digit
bad pattern: '^[Pp][PpGg]' matches when page begins pp, pP, Pp, PP, pg, pG, Pg, PG
]]
local function extra_text_in_page_check (val, name)
if not val:match (cfg.vol_iss_pg_patterns.good_ppattern) then
for _, pattern in ipairs (cfg.vol_iss_pg_patterns.bad_ppatterns) do -- spin through the selected sequence table of patterns
if val:match (pattern) then -- when a match, error so
utilities.set_message ('err_extra_text_pages', name); -- add error message
return; -- and done
end
end
end
end
--[[--------------------------< E X T R A _ T E X T _ I N _ V O L _ I S S _ C H E C K >------------------------
Adds error if |volume= or |issue= has what appears to be some form of redundant 'type' indicator. Applies to
both; this function looks for issue text in both |issue= and |volume= and looks for volume-like text in |voluem=
and |issue=.
For |volume=:
'V.', or 'Vol.' (with or without the dot) abbreviations or 'Volume' in the first characters of the parameter
content (all case insensitive). 'V' and 'v' (without the dot) are presumed to be roman numerals so
are allowed.
For |issue=:
'No.', 'I.', 'Iss.' (with or without the dot) abbreviations, or 'Issue' in the first characters of the
parameter content (all case insensitive); numero styling: 'n°' with degree sign U+00B0, and № precomposed
numero sign U+2116.
Single character values ('v', 'i', 'n') allowed when not followed by separator character ('.', ':', '=', or
whitespace character) – param values are trimmed of whitespace by MediaWiki before delivered to the module.
<val> is |volume= or |issue= parameter value
<name> is |volume= or |issue= parameter name for error message
<selector> is 'v' for |volume=, 'i' for |issue=
sets error message on failure; returns nothing
]]
local function extra_text_in_vol_iss_check (val, name, selector)
if not utilities.is_set (val) then
return;
end
local handler = 'v' == selector and 'err_extra_text_volume' or 'err_extra_text_issue';
val = val:lower(); -- force parameter value to lower case
for _, pattern in ipairs (cfg.vol_iss_pg_patterns.vi_patterns_t) do -- spin through the sequence table of patterns
if val:match (pattern) then -- when a match, error so
utilities.set_message (handler, name); -- add error message
return; -- and done
end
end
end
--[=[-------------------------< G E T _ V _ N A M E _ T A B L E >----------------------------------------------
split apart a |vauthors= or |veditors= parameter. This function allows for corporate names, wrapped in doubled
parentheses to also have commas; in the old version of the code, the doubled parentheses were included in the
rendered citation and in the metadata. Individual author names may be wikilinked
|vauthors=Jones AB, [[E. B. White|White EB]], ((Black, Brown, and Co.))
]=]
local function get_v_name_table (vparam, output_table, output_link_table)
local _, accept = utilities.has_accept_as_written (vparam);
if accept then
utilities.add_prop_cat ('vanc-accept'); -- add properties category
end
local name_table = mw.text.split(vparam, "%s*,%s*"); -- names are separated by commas
local wl_type, label, link; -- wl_type not used here; just a placeholder
local i = 1;
while name_table[i] do
if name_table[i]:match ('^%(%(.*[^%)][^%)]$') then -- first segment of corporate with one or more commas; this segment has the opening doubled parentheses
local name = name_table[i];
i = i + 1; -- bump indexer to next segment
while name_table[i] do
name = name .. ', ' .. name_table[i]; -- concatenate with previous segments
if name_table[i]:match ('^.*%)%)$') then -- if this table member has the closing doubled parentheses
break; -- and done reassembling so
end
i = i + 1; -- bump indexer
end
table.insert (output_table, name); -- and add corporate name to the output table
table.insert (output_link_table, ''); -- no wikilink
else
wl_type, label, link = utilities.is_wikilink (name_table[i]); -- wl_type is: 0, no wl (text in label variable); 1, [[D]]; 2, [[L|D]]
table.insert (output_table, label); -- add this name
if 1 == wl_type then
table.insert (output_link_table, label); -- simple wikilink [[D]]
else
table.insert (output_link_table, link); -- no wikilink or [[L|D]]; add this link if there is one, else empty string
end
end
i = i + 1;
end
return output_table;
end
--[[--------------------------< P A R S E _ V A U T H O R S _ V E D I T O R S >--------------------------------
This function extracts author / editor names from |vauthors= or |veditors= and finds matching |xxxxor-maskn= and
|xxxxor-linkn= in args. It then returns a table of assembled names just as extract_names() does.
Author / editor names in |vauthors= or |veditors= must be in Vancouver system style. Corporate or institutional names
may sometimes be required and because such names will often fail the is_good_vanc_name() and other format compliance
tests, are wrapped in doubled parentheses ((corporate name)) to suppress the format tests.
Supports generational suffixes Jr, 2nd, 3rd, 4th–6th.
This function sets the Vancouver error when a required comma is missing and when there is a space between an author's initials.
]]
local function parse_vauthors_veditors (args, vparam, list_name)
local names = {}; -- table of names assembled from |vauthors=, |author-maskn=, |author-linkn=
local v_name_table = {};
local v_link_table = {}; -- when name is wikilinked, targets go in this table
local etal = false; -- return value set to true when we find some form of et al. vauthors parameter
local last, first, link, mask, suffix;
local corporate = false;
vparam, etal = name_has_etal (vparam, etal, true); -- find and remove variations on et al. do not categorize (do it here because et al. might have a period)
v_name_table = get_v_name_table (vparam, v_name_table, v_link_table); -- names are separated by commas
for i, v_name in ipairs(v_name_table) do
first = ''; -- set to empty string for concatenation and because it may have been set for previous author/editor
local accept_name;
v_name, accept_name = utilities.has_accept_as_written (v_name); -- remove accept-this-as-written markup when it wraps all of <v_name>
if accept_name then
last = v_name;
corporate = true; -- flag used in list_people()
elseif string.find(v_name, "%s") then
if v_name:find('[;%.]') then -- look for commonly occurring punctuation characters;
add_vanc_error (cfg.err_msg_supl.punctuation, i);
end
local lastfirstTable = {}
lastfirstTable = mw.text.split(v_name, "%s+")
first = table.remove(lastfirstTable); -- removes and returns value of last element in table which should be initials or generational suffix
if not mw.ustring.match (first, '^%u+$') then -- mw.ustring here so that later we will catch non-Latin characters
suffix = first; -- not initials so assume that whatever we got is a generational suffix
first = table.remove(lastfirstTable); -- get what should be the initials from the table
end
last = table.concat(lastfirstTable, ' ') -- returns a string that is the concatenation of all other names that are not initials and generational suffix
if not utilities.is_set (last) then
first = ''; -- unset
last = v_name; -- last empty because something wrong with first
add_vanc_error (cfg.err_msg_supl.name, i);
end
if mw.ustring.match (last, '%a+%s+%u+%s+%a+') then
add_vanc_error (cfg.err_msg_supl['missing comma'], i); -- matches last II last; the case when a comma is missing
end
if mw.ustring.match (v_name, ' %u %u$') then -- this test is in the wrong place TODO: move or replace with a more appropriate test
add_vanc_error (cfg.err_msg_supl.initials, i); -- matches a space between two initials
end
else
last = v_name; -- last name or single corporate name? Doesn't support multiword corporate names? do we need this?
end
if utilities.is_set (first) then
if not mw.ustring.match (first, "^%u?%u$") then -- first shall contain one or two upper-case letters, nothing else
add_vanc_error (cfg.err_msg_supl.initials, i); -- too many initials; mixed case initials (which may be ok Romanization); hyphenated initials
end
is_good_vanc_name (last, first, suffix, i); -- check first and last before restoring the suffix which may have a non-Latin digit
if utilities.is_set (suffix) then
first = first .. ' ' .. suffix; -- if there was a suffix concatenate with the initials
suffix = ''; -- unset so we don't add this suffix to all subsequent names
end
else
if not corporate then
is_good_vanc_name (last, '', nil, i);
end
end
link = utilities.select_one ( args, cfg.aliases[list_name .. '-Link'], 'err_redundant_parameters', i ) or v_link_table[i];
mask = utilities.select_one ( args, cfg.aliases[list_name .. '-Mask'], 'err_redundant_parameters', i );
names[i] = {last = last, first = first, link = link, mask = mask, corporate = corporate}; -- add this assembled name to our names list
end
return names, etal; -- all done, return our list of names
end
--[[--------------------------< S E L E C T _ A U T H O R _ E D I T O R _ S O U R C E >------------------------
Select one of |authors=, |authorn= / |lastn / firstn=, or |vauthors= as the source of the author name list or
select one of |editorn= / editor-lastn= / |editor-firstn= or |veditors= as the source of the editor name list.
Only one of these appropriate three will be used. The hierarchy is: |authorn= (and aliases) highest and |authors= lowest;
|editorn= (and aliases) highest and |veditors= lowest (support for |editors= withdrawn)
When looking for |authorn= / |editorn= parameters, test |xxxxor1= and |xxxxor2= (and all of their aliases); stops after the second
test which mimicks the test used in extract_names() when looking for a hole in the author name list. There may be a better
way to do this, I just haven't discovered what that way is.
Emits an error message when more than one xxxxor name source is provided.
In this function, vxxxxors = vauthors or veditors; xxxxors = authors as appropriate.
]]
local function select_author_editor_source (vxxxxors, xxxxors, args, list_name)
local lastfirst = false;
if utilities.select_one ( args, cfg.aliases[list_name .. '-Last'], 'none', 1 ) or -- do this twice in case we have a |first1= without a |last1=; this ...
utilities.select_one ( args, cfg.aliases[list_name .. '-First'], 'none', 1 ) or -- ... also catches the case where |first= is used with |vauthors=
utilities.select_one ( args, cfg.aliases[list_name .. '-Last'], 'none', 2 ) or
utilities.select_one ( args, cfg.aliases[list_name .. '-First'], 'none', 2 ) then
lastfirst = true;
end
if (utilities.is_set (vxxxxors) and true == lastfirst) or -- these are the three error conditions
(utilities.is_set (vxxxxors) and utilities.is_set (xxxxors)) or
(true == lastfirst and utilities.is_set (xxxxors)) then
local err_name;
if 'AuthorList' == list_name then -- figure out which name should be used in error message
err_name = 'author';
else
err_name = 'editor';
end
utilities.set_message ('err_redundant_parameters', err_name .. '-name-list parameters'); -- add error message
end
if true == lastfirst then return 1 end; -- return a number indicating which author name source to use
if utilities.is_set (vxxxxors) then return 2 end;
if utilities.is_set (xxxxors) then return 3 end;
return 1; -- no authors so return 1; this allows missing author name test to run in case there is a first without last
end
--[[--------------------------< I S _ V A L I D _ P A R A M E T E R _ V A L U E >------------------------------
This function is used to validate a parameter's assigned value for those parameters that have only a limited number
of allowable values (yes, y, true, live, dead, etc.). When the parameter value has not been assigned a value (missing
or empty in the source template) the function returns the value specified by ret_val. If the parameter value is one
of the list of allowed values returns the translated value; else, emits an error message and returns the value
specified by ret_val.
TODO: explain <invert>
]]
local function is_valid_parameter_value (value, name, possible, ret_val, invert)
if not utilities.is_set (value) then
return ret_val; -- an empty parameter is ok
end
if (not invert and utilities.in_array (value, possible)) then -- normal; <value> is in <possible> table
return cfg.keywords_xlate[value]; -- return translation of parameter keyword
elseif invert and not utilities.in_array (value, possible) then -- invert; <value> is not in <possible> table
return value; -- return <value> as it is
else
utilities.set_message ('err_invalid_param_val', {name, value}); -- not an allowed value so add error message
return ret_val;
end
end
--[[--------------------------< T E R M I N A T E _ N A M E _ L I S T >----------------------------------------
This function terminates a name list (author, contributor, editor) with a separator character (sepc) and a space
when the last character is not a sepc character or when the last three characters are not sepc followed by two
closing square brackets (close of a wikilink). When either of these is true, the name_list is terminated with a
single space character.
]]
local function terminate_name_list (name_list, sepc)
if (string.sub (name_list, -3, -1) == sepc .. '. ') then -- if already properly terminated
return name_list; -- just return the name list
elseif (string.sub (name_list, -1, -1) == sepc) or (string.sub (name_list, -3, -1) == sepc .. ']]') then -- if last name in list ends with sepc char
return name_list .. " "; -- don't add another
else
return name_list .. sepc .. ' '; -- otherwise terminate the name list
end
end
--[[-------------------------< F O R M A T _ V O L U M E _ I S S U E >-----------------------------------------
returns the concatenation of the formatted volume and issue (or journal article number) parameters as a single
string; or formatted volume or formatted issue, or an empty string if neither are set.
]]
local function format_volume_issue (volume, issue, article, cite_class, origin, sepc, lower)
if not utilities.is_set (volume) and not utilities.is_set (issue) and not utilities.is_set (article) then
return '';
end
-- same condition as in format_pages_sheets()
local is_journal = 'journal' == cite_class or (utilities.in_array (cite_class, {'citation', 'map', 'interview'}) and 'journal' == origin);
local is_numeric_vol = volume and (volume:match ('^[MDCLXVI]+$') or volume:match ('^%d+$')); -- is only uppercase roman numerals or only digits?
local is_long_vol = volume and (4 < mw.ustring.len(volume)); -- is |volume= value longer than 4 characters?
if volume and (not is_numeric_vol and is_long_vol) then -- when not all digits or Roman numerals, is |volume= longer than 4 characters?
utilities.add_prop_cat ('long-vol'); -- yes, add properties cat
end
if is_journal then -- journal-style formatting
local vol = '';
if utilities.is_set (volume) then
if is_numeric_vol then -- |volume= value all digits or all uppercase Roman numerals?
vol = utilities.substitute (cfg.presentation['vol-bold'], {sepc, volume}); -- render in bold face
elseif is_long_vol then -- not all digits or Roman numerals; longer than 4 characters?
vol = utilities.substitute (cfg.messages['j-vol'], {sepc, utilities.hyphen_to_dash (volume)}); -- not bold
else -- four or fewer characters
vol = utilities.substitute (cfg.presentation['vol-bold'], {sepc, utilities.hyphen_to_dash (volume)}); -- bold
end
end
vol = vol .. (utilities.is_set (issue) and utilities.substitute (cfg.messages['j-issue'], issue) or '')
vol = vol .. (utilities.is_set (article) and utilities.substitute (cfg.messages['j-article-num'], article) or '')
return vol;
end
if 'podcast' == cite_class and utilities.is_set (issue) then
return wrap_msg ('issue', {sepc, issue}, lower);
end
if 'conference' == cite_class and utilities.is_set (article) then -- |article-number= supported only in journal and conference cites
if utilities.is_set (volume) and utilities.is_set (article) then -- both volume and article number
return wrap_msg ('vol-art', {sepc, utilities.hyphen_to_dash (volume), article}, lower);
elseif utilities.is_set (article) then -- article number alone; when volume alone, handled below
return wrap_msg ('art', {sepc, article}, lower);
end
end
-- all other types of citation
if utilities.is_set (volume) and utilities.is_set (issue) then
return wrap_msg ('vol-no', {sepc, utilities.hyphen_to_dash (volume), issue}, lower);
elseif utilities.is_set (volume) then
return wrap_msg ('vol', {sepc, utilities.hyphen_to_dash (volume)}, lower);
else
return wrap_msg ('issue', {sepc, issue}, lower);
end
end
--[[-------------------------< F O R M A T _ P A G E S _ S H E E T S >-----------------------------------------
adds static text to one of |page(s)= or |sheet(s)= values and returns it with all of the others set to empty strings.
The return order is:
page, pages, sheet, sheets
Singular has priority over plural when both are provided.
]]
local function format_pages_sheets (page, pages, sheet, sheets, cite_class, origin, sepc, nopp, lower)
if 'map' == cite_class then -- only cite map supports sheet(s) as in-source locators
if utilities.is_set (sheet) then
if 'journal' == origin then
return '', '', wrap_msg ('j-sheet', sheet, lower), '';
else
return '', '', wrap_msg ('sheet', {sepc, sheet}, lower), '';
end
elseif utilities.is_set (sheets) then
if 'journal' == origin then
return '', '', '', wrap_msg ('j-sheets', sheets, lower);
else
return '', '', '', wrap_msg ('sheets', {sepc, sheets}, lower);
end
end
end
local is_journal = 'journal' == cite_class or (utilities.in_array (cite_class, {'citation', 'map', 'interview'}) and 'journal' == origin);
if utilities.is_set (page) then
if is_journal then
return utilities.substitute (cfg.messages['j-page(s)'], page), '', '', '';
elseif not nopp then
return utilities.substitute (cfg.messages['p-prefix'], {sepc, page}), '', '', '';
else
return utilities.substitute (cfg.messages['nopp'], {sepc, page}), '', '', '';
end
elseif utilities.is_set (pages) then
if is_journal then
return utilities.substitute (cfg.messages['j-page(s)'], pages), '', '', '';
elseif tonumber(pages) ~= nil and not nopp then -- if pages is only digits, assume a single page number
return '', utilities.substitute (cfg.messages['p-prefix'], {sepc, pages}), '', '';
elseif not nopp then
return '', utilities.substitute (cfg.messages['pp-prefix'], {sepc, pages}), '', '';
else
return '', utilities.substitute (cfg.messages['nopp'], {sepc, pages}), '', '';
end
end
return '', '', '', ''; -- return empty strings
end
--[[--------------------------< I N S O U R C E _ L O C _ G E T >----------------------------------------------
returns one of the in-source locators: page, pages, or at.
If any of these are interwiki links to Wikisource, returns the label portion of the interwiki-link as plain text
for use in COinS. This COinS thing is done because here we convert an interwiki-link to an external link and
add an icon span around that; get_coins_pages() doesn't know about the span. TODO: should it?
TODO: add support for sheet and sheets?; streamline;
TODO: make it so that this function returns only one of the three as the single in-source (the return value assigned
to a new name)?
]]
local function insource_loc_get (page, page_orig, pages, pages_orig, at)
local ws_url, ws_label, coins_pages, L; -- for Wikisource interwiki-links; TODO: this corrupts page metadata (span remains in place after cleanup; fix there?)
if utilities.is_set (page) then
if utilities.is_set (pages) or utilities.is_set (at) then
pages = ''; -- unset the others
at = '';
end
extra_text_in_page_check (page, page_orig); -- emit error message when |page= value begins with what looks like p., pp., etc.
ws_url, ws_label, L = wikisource_url_make (page); -- make ws URL from |page= interwiki link; link portion L becomes tooltip label
if ws_url then
page = external_link (ws_url, ws_label .. ' ', 'ws link in page'); -- space char after label to move icon away from in-source text; TODO: a better way to do this?
page = utilities.substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], L, page});
coins_pages = ws_label;
end
elseif utilities.is_set (pages) then
if utilities.is_set (at) then
at = ''; -- unset
end
extra_text_in_page_check (pages, pages_orig); -- emit error message when |page= value begins with what looks like p., pp., etc.
ws_url, ws_label, L = wikisource_url_make (pages); -- make ws URL from |pages= interwiki link; link portion L becomes tooltip label
if ws_url then
pages = external_link (ws_url, ws_label .. ' ', 'ws link in pages'); -- space char after label to move icon away from in-source text; TODO: a better way to do this?
pages = utilities.substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], L, pages});
coins_pages = ws_label;
end
elseif utilities.is_set (at) then
ws_url, ws_label, L = wikisource_url_make (at); -- make ws URL from |at= interwiki link; link portion L becomes tooltip label
if ws_url then
at = external_link (ws_url, ws_label .. ' ', 'ws link in at'); -- space char after label to move icon away from in-source text; TODO: a better way to do this?
at = utilities.substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], L, at});
coins_pages = ws_label;
end
end
return page, pages, at, coins_pages;
end
--[[--------------------------< I S _ U N I Q U E _ A R C H I V E _ U R L >------------------------------------
add error message when |archive-url= value is same as |url= or chapter-url= (or alias...) value
]]
local function is_unique_archive_url (archive, url, c_url, source, date)
if utilities.is_set (archive) then
if archive == url or archive == c_url then
utilities.set_message ('err_bad_url', {utilities.wrap_style ('parameter', source)}); -- add error message
return '', ''; -- unset |archive-url= and |archive-date= because same as |url= or |chapter-url=
end
end
return archive, date;
end
--[=[-------------------------< A R C H I V E _ U R L _ C H E C K >--------------------------------------------
Check archive.org URLs to make sure they at least look like they are pointing at valid archives and not to the
save snapshot URL or to calendar pages. When the archive URL is 'https://web.archive.org/save/' (or http://...)
archive.org saves a snapshot of the target page in the URL. That is something that Wikipedia should not allow
unwitting readers to do.
When the archive.org URL does not have a complete timestamp, archive.org chooses a snapshot according to its own
algorithm or provides a calendar 'search' result. [[WP:ELNO]] discourages links to search results.
This function looks at the value assigned to |archive-url= and returns empty strings for |archive-url= and
|archive-date= and an error message when:
|archive-url= holds an archive.org save command URL
|archive-url= is an archive.org URL that does not have a complete timestamp (YYYYMMDDhhmmss 14 digits) in the
correct place
otherwise returns |archive-url= and |archive-date=
There are two mostly compatible archive.org URLs:
//web.archive.org/<timestamp>... -- the old form
//web.archive.org/web/<timestamp>... -- the new form
The old form does not support or map to the new form when it contains a display flag. There are four identified flags
('id_', 'js_', 'cs_', 'im_') but since archive.org ignores others following the same form (two letters and an underscore)
we don't check for these specific flags but we do check the form.
This function supports a preview mode. When the article is rendered in preview mode, this function may return a modified
archive URL:
for save command errors, return undated wildcard (/*/)
for timestamp errors when the timestamp has a wildcard, return the URL unmodified
for timestamp errors when the timestamp does not have a wildcard, return with timestamp limited to six digits plus wildcard (/yyyymm*/)
A secondary function is to return an archive-url timestamp from those urls that have them (archive.org and
archive.today). The timestamp is used by validation.archive_date_check() to see if the value in |archive-date=
matches the timestamp in the archive url.
]=]
local function archive_url_check (url, date)
local err_msg = ''; -- start with the error message empty
local path, timestamp, flag; -- portions of the archive.org URL
timestamp = url:match ('//archive.today/(%d%d%d%d%d%d%d%d%d%d%d%d%d%d)/') or -- get timestamp from archive.today urls
url:match ('//archive.today/(%d%d%d%d%.%d%d%.%d%d%-%d%d%d%d%d%d)/'); -- this timestamp needs cleanup
if timestamp then -- if this was an archive.today url ...
return url, date, timestamp:gsub ('[%.%-]', ''); -- return ArchiveURL, ArchiveDate, and timestamp (dots and dashes removed) from |archive-url=, and done
end
-- here for archive.org urls
if (not url:match('//web%.archive%.org/')) and (not url:match('//liveweb%.archive%.org/')) then -- also deprecated liveweb Wayback machine URL
return url, date; -- not an archive.org archive, return ArchiveURL and ArchiveDate
end
if url:match('//web%.archive%.org/save/') then -- if a save command URL, we don't want to allow saving of the target page
err_msg = cfg.err_msg_supl.save;
url = url:gsub ('(//web%.archive%.org)/save/', '%1/*/', 1); -- for preview mode: modify ArchiveURL
elseif url:match('//liveweb%.archive%.org/') then
err_msg = cfg.err_msg_supl.liveweb;
else
path, timestamp, flag = url:match('//web%.archive%.org/([^%d]*)(%d+)([^/]*)/'); -- split out some of the URL parts for evaluation
if not path then -- malformed in some way; pattern did not match
err_msg = cfg.err_msg_supl.timestamp;
elseif 14 ~= timestamp:len() then -- path and flag optional, must have 14-digit timestamp here
err_msg = cfg.err_msg_supl.timestamp;
if '*' ~= flag then
local replacement = timestamp:match ('^%d%d%d%d%d%d') or timestamp:match ('^%d%d%d%d'); -- get the first 6 (YYYYMM) or first 4 digits (YYYY)
if replacement then -- nil if there aren't at least 4 digits (year)
replacement = replacement .. string.rep ('0', 14 - replacement:len()); -- year or yearmo (4 or 6 digits) zero-fill to make 14-digit timestamp
url=url:gsub ('(//web%.archive%.org/[^%d]*)%d[^/]*', '%1' .. replacement .. '*', 1) -- for preview, modify ts to 14 digits plus splat for calendar display
end
end
elseif utilities.is_set (path) and 'web/' ~= path then -- older archive URLs do not have the extra 'web/' path element
err_msg = cfg.err_msg_supl.path;
elseif utilities.is_set (flag) and not utilities.is_set (path) then -- flag not allowed with the old form URL (without the 'web/' path element)
err_msg = cfg.err_msg_supl.flag;
elseif utilities.is_set (flag) and not flag:match ('%a%a_') then -- flag if present must be two alpha characters and underscore (requires 'web/' path element)
err_msg = cfg.err_msg_supl.flag;
else
return url, date, timestamp; -- return ArchiveURL, ArchiveDate, and timestamp from |archive-url=
end
end
-- if here, something not right so
utilities.set_message ('err_archive_url', {err_msg}); -- add error message and
if is_preview_mode then
return url, date, timestamp; -- preview mode so return ArchiveURL, ArchiveDate, and timestamp from |archive-url=
else
return '', ''; -- return empty strings for ArchiveURL and ArchiveDate
end
end
--[[--------------------------< P L A C E _ C H E C K >--------------------------------------------------------
check |place=, |publication-place=, |location= to see if these params include digits. This function added because
many editors misuse location to specify the in-source location (|page(s)= and |at= are supposed to do that)
returns the original parameter value without modification; added maint cat when parameter value contains digits
]]
local function place_check (param_val)
if not utilities.is_set (param_val) then -- parameter empty or omitted
return param_val; -- return that empty state
end
if mw.ustring.find (param_val, '%d') then -- not empty, are there digits in the parameter value
utilities.set_message ('maint_location'); -- yep, add maint cat
end
return param_val; -- and done
end
--[[--------------------------< I S _ A R C H I V E D _ C O P Y >----------------------------------------------
compares |title= to 'Archived copy' (placeholder added by bots that can't find proper title); if matches, return true; nil else
]]
local function is_archived_copy (title)
title = mw.ustring.lower(title); -- switch title to lower case
if title:find (cfg.special_case_translation.archived_copy.en) then -- if title is 'Archived copy'
return true;
elseif cfg.special_case_translation.archived_copy['local'] then
if mw.ustring.find (title, cfg.special_case_translation.archived_copy['local']) then -- mw.ustring() because might not be Latin script
return true;
end
end
end
--[[--------------------------< D I S P L A Y _ N A M E S _ S E L E C T >--------------------------------------
for any of the |display-authors=, |display-editors=, etc parameters, select either the local or global setting.
When both are present, look at <local_display_names> value. When the value is some sort of 'et al.'string,
special handling is required.
When {{cs1 config}} has |display-<namelist>= AND this template has |display-<namelist>=etal AND:
the number of names specified by <number_of_names> is:
greater than the number specified in the global |display-<namelist>= parameter (<global_display_names>)
use global |display-<namelist>= parameter value
set overridden maint category
less than or equal to the number specified in the global |display-<namelist>= parameter
use local |display-<namelist>= parameter value
The purpose of this function is to prevent categorizing a template that has fewer names than the global setting
to keep the etal annotation specified by <local_display_names>.
]]
local function display_names_select (global_display_names, local_display_names, param_name, number_of_names, test)
if global_display_names and utilities.is_set (local_display_names) then -- when both
if 'etal' == local_display_names:lower():gsub("[ '%.]", '') then -- the :gsub() portion makes 'etal' from a variety of 'et al.' spellings and stylings
number_of_names = tonumber (number_of_names); -- convert these to numbers for comparison
local global_display_names_num = tonumber (global_display_names); -- <global_display_names> not set when parameter value is not digits
if number_of_names > global_display_names_num then -- template has more names than global config allows to be displayed?
utilities.set_message ('maint_overridden_setting'); -- set a maint message because global is overriding local |display-<namelist>=etal
return global_display_names, 'cs1 config'; -- return global with spoof parameter name (for get_display_names())
else
return local_display_names, param_name; -- return local because fewer names so let <local_display_names> control
end
end
-- here when <global_display_names> and <local_display_names> both numbers; <global_display_names> controls
utilities.set_message ('maint_overridden_setting'); -- set a maint message
return global_display_names, 'cs1 config'; -- return global with spoof parameter name (for get_display_names())
end
-- here when only one of <global_display_names> or <local_display_names> set
if global_display_names then
return global_display_names, 'cs1 config'; -- return global with spoof parameter name (for get_display_names())
else
return local_display_names, param_name; -- return local
end
end
--[[--------------------------< M O D E _ S E T >--------------------------------------------------------------
fetch global mode setting from {{cs1 config}} (if present) or from |mode= (if present); global setting overrides
local |mode= parameter value. When both are present, emit maintenance message
]]
local function mode_set (Mode, Mode_origin)
local mode;
if cfg.global_cs1_config_t['Mode'] then -- global setting in {{cs1 config}}; nil when empty or assigned value invalid
mode = is_valid_parameter_value (cfg.global_cs1_config_t['Mode'], 'cs1 config: mode', cfg.keywords_lists['mode'], ''); -- error messaging 'param' here is a hoax
else
mode = is_valid_parameter_value (Mode, Mode_origin, cfg.keywords_lists['mode'], '');
end
if cfg.global_cs1_config_t['Mode'] and utilities.is_set (Mode) then -- when template has |mode=<something> which global setting has overridden
utilities.set_message ('maint_overridden_setting'); -- set a maint message
end
return mode;
end
--[[--------------------------< Q U O T E _ M A K E >----------------------------------------------------------
create quotation from |quote=, |trans-quote=, and/or script-quote= with or without |quote-page= or |quote-pages=
when any of those three quote parameters are set, this function unsets <PostScript>. When none of those parameters
are set, |quote-page= and |quote-pages= are unset to nil so that they are not included in the template's metadata
]]
local function quote_make (quote, trans_quote, script_quote, quote_page, quote_pages, nopp, sepc, postscript)
if utilities.is_set (quote) or utilities.is_set (trans_quote) or utilities.is_set (script_quote) then
if utilities.is_set (quote) then
if quote:sub(1, 1) == '"' and quote:sub(-1, -1) == '"' then -- if first and last characters of quote are quote marks
quote = quote:sub(2, -2); -- strip them off
end
end
quote = kern_quotes (quote); -- kern if needed
quote = utilities.wrap_style ('quoted-text', quote ); -- wrap in <q>...</q> tags
if utilities.is_set (script_quote) then
quote = script_concatenate (quote, script_quote, 'script-quote'); -- <bdi> tags, lang attribute, categorization, etc.; must be done after quote is wrapped
end
if utilities.is_set (trans_quote) then
if trans_quote:sub(1, 1) == '"' and trans_quote:sub(-1, -1) == '"' then -- if first and last characters of |trans-quote are quote marks
trans_quote = trans_quote:sub(2, -2); -- strip them off
end
quote = quote .. " " .. utilities.wrap_style ('trans-quoted-title', trans_quote );
end
if utilities.is_set (quote_page) or utilities.is_set (quote_pages) then -- add page prefix
local quote_prefix = '';
if utilities.is_set (quote_page) then
extra_text_in_page_check (quote_page, 'quote-page'); -- add to maint cat if |quote-page= value begins with what looks like p., pp., etc.
if not nopp then
quote_prefix = utilities.substitute (cfg.messages['p-prefix'], {sepc, quote_page}), '', '', '';
else
quote_prefix = utilities.substitute (cfg.messages['nopp'], {sepc, quote_page}), '', '', '';
end
elseif utilities.is_set (quote_pages) then
extra_text_in_page_check (quote_pages, 'quote-pages'); -- add to maint cat if |quote-pages= value begins with what looks like p., pp., etc.
if tonumber(quote_pages) ~= nil and not nopp then -- if only digits, assume single page
quote_prefix = utilities.substitute (cfg.messages['p-prefix'], {sepc, quote_pages}), '', '';
elseif not nopp then
quote_prefix = utilities.substitute (cfg.messages['pp-prefix'], {sepc, quote_pages}), '', '';
else
quote_prefix = utilities.substitute (cfg.messages['nopp'], {sepc, quote_pages}), '', '';
end
end
quote = quote_prefix .. ": " .. quote;
else
quote = sepc .. " " .. quote;
end
postscript = ""; -- cs1|2 does not supply terminal punctuation when |quote= is set
elseif utilities.is_set (quote_page) or utilities.is_set (quote_pages) then
quote_page = nil; -- unset; these require |quote=; TODO: error message?
quote_pages = nil;
end
return quote, quote_page, quote_pages, postscript;
end
--[[--------------------------< C H E C K _ P U B L I S H E R _ N A M E >--------------------------------------
look for variations of '<text>: <text>' that might be '<location>: <publisher>' in |publisher= parameter value.
when found, emit a maintenance message; return nil else
<publisher> is the value assigned to |publisher= or |institution=
]]
local function check_publisher_name (publisher)
local patterns_t = {
'^[%w%s]+%s*:%s*[%w%s]+$', -- plain text <location>: <publisher>
'^%[+[%w%s:|]+%]+%s*:%s*[%w%s]+$', -- partially wikilinked [[<location>]]: <publisher>
'^[%w%s]+%s*:%s*%[+[%w%s:|]+%]+$', -- partially wikilinked <location>: [[<publisher>]]
'^%[+[%w%s:|]+%]+%s*:%s*%[+[%w%s:|]+%]+$', -- wikilinked [[<location>]]: [[<publisher>]]
}
for _, pattern in ipairs (patterns_t) do -- spin through the patterns_t sequence
if mw.ustring.match (publisher, pattern) then -- does this pattern match?
utilities.set_message ('maint_publisher_location'); -- set a maint message
return; -- and done
end
end
end
--[[--------------------------< C I T A T I O N 0 >------------------------------------------------------------
This is the main function doing the majority of the citation formatting.
]]
local function citation0( config, args )
--[[
Load Input Parameters
The argument_wrapper facilitates the mapping of multiple aliases to single internal variable.
]]
local A = argument_wrapper ( args );
local i
-- Pick out the relevant fields from the arguments. Different citation templates
-- define different field names for the same underlying things.
local author_etal;
local a = {}; -- authors list from |lastn= / |firstn= pairs or |vauthors=
local Authors;
local NameListStyle;
if cfg.global_cs1_config_t['NameListStyle'] then -- global setting in {{cs1 config}} overrides local |name-list-style= parameter value; nil when empty or assigned value invalid
NameListStyle = is_valid_parameter_value (cfg.global_cs1_config_t['NameListStyle'], 'cs1 config: name-list-style', cfg.keywords_lists['name-list-style'], ''); -- error messaging 'param' here is a hoax
else
NameListStyle = is_valid_parameter_value (A['NameListStyle'], A:ORIGIN('NameListStyle'), cfg.keywords_lists['name-list-style'], '');
end
if cfg.global_cs1_config_t['NameListStyle'] and utilities.is_set (A['NameListStyle']) then -- when template has |name-list-style=<something> which global setting has overridden
utilities.set_message ('maint_overridden_setting'); -- set a maint message
end
local Collaboration = A['Collaboration'];
do -- to limit scope of selected
local selected = select_author_editor_source (A['Vauthors'], A['Authors'], args, 'AuthorList');
if 1 == selected then
a, author_etal = extract_names (args, 'AuthorList'); -- fetch author list from |authorn= / |lastn= / |firstn=, |author-linkn=, and |author-maskn=
elseif 2 == selected then
NameListStyle = 'vanc'; -- override whatever |name-list-style= might be
a, author_etal = parse_vauthors_veditors (args, A['Vauthors'], 'AuthorList'); -- fetch author list from |vauthors=, |author-linkn=, and |author-maskn=
elseif 3 == selected then
Authors = A['Authors']; -- use content of |people= or |credits=; |authors= is deprecated; TODO: constrain |people= and |credits= to cite av media, episode, serial?
end
if utilities.is_set (Collaboration) then
author_etal = true; -- so that |display-authors=etal not required
end
end
local editor_etal;
local e = {}; -- editors list from |editor-lastn= / |editor-firstn= pairs or |veditors=
do -- to limit scope of selected
local selected = select_author_editor_source (A['Veditors'], nil, args, 'EditorList'); -- support for |editors= withdrawn
if 1 == selected then
e, editor_etal = extract_names (args, 'EditorList'); -- fetch editor list from |editorn= / |editor-lastn= / |editor-firstn=, |editor-linkn=, and |editor-maskn=
elseif 2 == selected then
NameListStyle = 'vanc'; -- override whatever |name-list-style= might be
e, editor_etal = parse_vauthors_veditors (args, args.veditors, 'EditorList'); -- fetch editor list from |veditors=, |editor-linkn=, and |editor-maskn=
end
end
local Chapter = A['Chapter']; -- done here so that we have access to |contribution= from |chapter= aliases
local Chapter_origin = A:ORIGIN ('Chapter');
local Contribution; -- because contribution is required for contributor(s)
if 'contribution' == Chapter_origin then
Contribution = Chapter; -- get the name of the contribution
end
local c = {}; -- contributors list from |contributor-lastn= / contributor-firstn= pairs
if utilities.in_array (config.CitationClass, {"book", "citation"}) and not utilities.is_set (A['Periodical']) then -- |contributor= and |contribution= only supported in book cites
c = extract_names (args, 'ContributorList'); -- fetch contributor list from |contributorn= / |contributor-lastn=, -firstn=, -linkn=, -maskn=
if 0 < #c then
if not utilities.is_set (Contribution) then -- |contributor= requires |contribution=
utilities.set_message ('err_contributor_missing_required_param', 'contribution'); -- add missing contribution error message
c = {}; -- blank the contributors' table; it is used as a flag later
end
if 0 == #a then -- |contributor= requires |author=
utilities.set_message ('err_contributor_missing_required_param', 'author'); -- add missing author error message
c = {}; -- blank the contributors' table; it is used as a flag later
end
end
else -- if not a book cite
if utilities.select_one (args, cfg.aliases['ContributorList-Last'], 'err_redundant_parameters', 1 ) then -- are there contributor name list parameters?
utilities.set_message ('err_contributor_ignored'); -- add contributor ignored error message
end
Contribution = nil; -- unset
end
local Title = A['Title'];
local TitleLink = A['TitleLink'];
local auto_select = ''; -- default is auto
local accept_link;
TitleLink, accept_link = utilities.has_accept_as_written (TitleLink, true); -- test for accept-this-as-written markup
if (not accept_link) and utilities.in_array (TitleLink, {'none', 'pmc', 'doi'}) then -- check for special keywords
auto_select = TitleLink; -- remember selection for later
TitleLink = ''; -- treat as if |title-link= would have been empty
end
TitleLink = link_title_ok (TitleLink, A:ORIGIN ('TitleLink'), Title, 'title'); -- check for wiki-markup in |title-link= or wiki-markup in |title= when |title-link= is set
local Section = ''; -- {{cite map}} only; preset to empty string for concatenation if not used
if 'map' == config.CitationClass and 'section' == Chapter_origin then
Section = A['Chapter']; -- get |section= from |chapter= alias list; |chapter= and the other aliases not supported in {{cite map}}
Chapter = ''; -- unset for now; will be reset later from |map= if present
end
local Periodical = A['Periodical'];
local Periodical_origin = A:ORIGIN('Periodical');
local ScriptPeriodical = A['ScriptPeriodical'];
local ScriptPeriodical_origin = A:ORIGIN('ScriptPeriodical');
local TransPeriodical = A['TransPeriodical'];
local TransPeriodical_origin = A:ORIGIN ('TransPeriodical');
if (utilities.in_array (config.CitationClass, {'book', 'encyclopaedia'}) and (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical) or utilities.is_set (TransPeriodical))) then
local param;
if utilities.is_set (Periodical) then -- get a parameter name from one of these periodical related meta-parameters
Periodical = ''; -- unset because not valid {{cite book}} or {{cite encyclopedia}} parameters
param = Periodical_origin -- get parameter name for error messaging
elseif utilities.is_set (TransPeriodical) then
TransPeriodical = ''; -- unset because not valid {{cite book}} or {{cite encyclopedia}} parameters
param = TransPeriodical_origin; -- get parameter name for error messaging
elseif utilities.is_set (ScriptPeriodical) then
ScriptPeriodical = ''; -- unset because not valid {{cite book}} or {{cite encyclopedia}} parameters
param = ScriptPeriodical_origin; -- get parameter name for error messaging
end
if utilities.is_set (param) then -- if we found one
utilities.set_message ('err_periodical_ignored', {param}); -- emit an error message
end
end
if utilities.is_set (Periodical) then
local i;
Periodical, i = utilities.strip_apostrophe_markup (Periodical); -- strip apostrophe markup so that metadata isn't contaminated
if i then -- non-zero when markup was stripped so emit an error message
utilities.set_message ('err_apostrophe_markup', {Periodical_origin});
end
end
if 'mailinglist' == config.CitationClass then -- special case for {{cite mailing list}}
if utilities.is_set (Periodical) and utilities.is_set (A ['MailingList']) then -- both set emit an error TODO: make a function for this and similar?
utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', Periodical_origin) .. cfg.presentation['sep_list_pair'] .. utilities.wrap_style ('parameter', 'mailinglist')});
end
Periodical = A ['MailingList']; -- error or no, set Periodical to |mailinglist= value because this template is {{cite mailing list}}
Periodical_origin = A:ORIGIN('MailingList');
end
-- web and news not tested for now because of
-- Wikipedia:Administrators%27_noticeboard#Is_there_a_semi-automated_tool_that_could_fix_these_annoying_"Cite_Web"_errors?
if not (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)) then -- 'periodical' templates require periodical parameter
-- local p = {['journal'] = 'journal', ['magazine'] = 'magazine', ['news'] = 'newspaper', ['web'] = 'website'}; -- for error message
local p = {['journal'] = 'journal', ['magazine'] = 'magazine'}; -- for error message
if p[config.CitationClass] then
utilities.set_message ('err_missing_periodical', {config.CitationClass, p[config.CitationClass]});
end
end
local Volume;
if 'citation' == config.CitationClass then
if utilities.is_set (Periodical) then
if not utilities.in_array (Periodical_origin, cfg.citation_no_volume_t) then -- {{citation}} does not render |volume= when these parameters are used
Volume = A['Volume']; -- but does for all other 'periodicals'
end
elseif utilities.is_set (ScriptPeriodical) then
if 'script-website' ~= ScriptPeriodical_origin then -- {{citation}} does not render volume for |script-website=
Volume = A['Volume']; -- but does for all other 'periodicals'
end
else
Volume = A['Volume']; -- and does for non-'periodical' cites
end
elseif utilities.in_array (config.CitationClass, cfg.templates_using_volume) then -- render |volume= for cs1 according to the configuration settings
Volume = A['Volume'];
end
extra_text_in_vol_iss_check (Volume, A:ORIGIN ('Volume'), 'v');
local Issue;
if 'citation' == config.CitationClass then
if utilities.is_set (Periodical) and utilities.in_array (Periodical_origin, cfg.citation_issue_t) then -- {{citation}} may render |issue= when these parameters are used
Issue = utilities.hyphen_to_dash (A['Issue']);
end
elseif utilities.in_array (config.CitationClass, cfg.templates_using_issue) then -- conference & map books do not support issue; {{citation}} listed here because included in settings table
if not (utilities.in_array (config.CitationClass, {'conference', 'map', 'citation'}) and not (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical))) then
Issue = utilities.hyphen_to_dash (A['Issue']);
end
end
local ArticleNumber;
if utilities.in_array (config.CitationClass, {'journal', 'conference'}) or ('citation' == config.CitationClass and utilities.is_set (Periodical) and 'journal' == Periodical_origin) then
ArticleNumber = A['ArticleNumber'];
end
extra_text_in_vol_iss_check (Issue, A:ORIGIN ('Issue'), 'i');
local Page;
local Pages;
local At;
local QuotePage;
local QuotePages;
if not utilities.in_array (config.CitationClass, cfg.templates_not_using_page) then -- TODO: rewrite to emit ignored parameter error message?
Page = A['Page'];
Pages = utilities.hyphen_to_dash (A['Pages']);
At = A['At'];
QuotePage = A['QuotePage'];
QuotePages = utilities.hyphen_to_dash (A['QuotePages']);
end
local NoPP = is_valid_parameter_value (A['NoPP'], A:ORIGIN('NoPP'), cfg.keywords_lists['yes_true_y'], nil);
local Mode = mode_set (A['Mode'], A:ORIGIN('Mode'));
-- separator character and postscript
local sepc, PostScript = set_style (Mode:lower(), A['PostScript'], config.CitationClass);
local Quote;
Quote, QuotePage, QuotePages, PostScript = quote_make (A['Quote'], A['TransQuote'], A['ScriptQuote'], QuotePage, QuotePages, NoPP, sepc, PostScript);
local Edition = A['Edition'];
local PublicationPlace = place_check (A['PublicationPlace'], A:ORIGIN('PublicationPlace'));
local Place = place_check (A['Place'], A:ORIGIN('Place'));
local PublisherName = A['PublisherName'];
local PublisherName_origin = A:ORIGIN('PublisherName');
if utilities.is_set (PublisherName) and (cfg.keywords_xlate['none'] ~= PublisherName) then
local i = 0;
PublisherName, i = utilities.strip_apostrophe_markup (PublisherName); -- strip apostrophe markup so that metadata isn't contaminated; publisher is never italicized
if i and (0 < i) then -- non-zero when markup was stripped so emit an error message
utilities.set_message ('err_apostrophe_markup', {PublisherName_origin});
end
end
if ('document' == config.CitationClass) and not utilities.is_set (PublisherName) then
utilities.set_message ('err_missing_publisher', {config.CitationClass, 'publisher'});
end
local Newsgroup = A['Newsgroup']; -- TODO: strip apostrophe markup?
local Newsgroup_origin = A:ORIGIN('Newsgroup');
if 'newsgroup' == config.CitationClass then
if utilities.is_set (PublisherName) and (cfg.keywords_xlate['none'] ~= PublisherName) then -- general use parameter |publisher= not allowed in cite newsgroup
utilities.set_message ('err_parameter_ignored', {PublisherName_origin});
end
PublisherName = nil; -- ensure that this parameter is unset for the time being; will be used again after COinS
end
if 'book' == config.CitationClass or 'encyclopaedia' == config.CitationClass or ('citation' == config.CitationClass and not utilities.is_set (Periodical)) then
local accept;
PublisherName, accept = utilities.has_accept_as_written (PublisherName); -- check for and remove accept-as-written markup from |publisher= wrapped
if not accept then -- when no accept-as-written markup
check_publisher_name (PublisherName); -- emit maint message when |publisher= might be prefixed with publisher's location
end
end
local URL = A['URL']; -- TODO: better way to do this for URL, ChapterURL, and MapURL?
local UrlAccess = is_valid_parameter_value (A['UrlAccess'], A:ORIGIN('UrlAccess'), cfg.keywords_lists['url-access'], nil);
if not utilities.is_set (URL) and utilities.is_set (UrlAccess) then
UrlAccess = nil;
utilities.set_message ('err_param_access_requires_param', 'url');
end
local ChapterURL = A['ChapterURL'];
local ChapterUrlAccess = is_valid_parameter_value (A['ChapterUrlAccess'], A:ORIGIN('ChapterUrlAccess'), cfg.keywords_lists['url-access'], nil);
if not utilities.is_set (ChapterURL) and utilities.is_set (ChapterUrlAccess) then
ChapterUrlAccess = nil;
utilities.set_message ('err_param_access_requires_param', {A:ORIGIN('ChapterUrlAccess'):gsub ('%-access', '')});
end
local MapUrlAccess = is_valid_parameter_value (A['MapUrlAccess'], A:ORIGIN('MapUrlAccess'), cfg.keywords_lists['url-access'], nil);
if not utilities.is_set (A['MapURL']) and utilities.is_set (MapUrlAccess) then
MapUrlAccess = nil;
utilities.set_message ('err_param_access_requires_param', {'map-url'});
end
local this_page = mw.title.getCurrentTitle(); -- also used for COinS and for language
local no_tracking_cats = is_valid_parameter_value (A['NoTracking'], A:ORIGIN('NoTracking'), cfg.keywords_lists['yes_true_y'], nil);
-- check this page to see if it is in one of the namespaces that cs1 is not supposed to add to the error categories
if not utilities.is_set (no_tracking_cats) then -- ignore if we are already not going to categorize this page
if cfg.uncategorized_namespaces[this_page.namespace] then -- is this page's namespace id one of the uncategorized namespace ids?
no_tracking_cats = "true"; -- set no_tracking_cats
end
for _, v in ipairs (cfg.uncategorized_subpages) do -- cycle through page name patterns
if this_page.text:match (v) then -- test page name against each pattern
no_tracking_cats = "true"; -- set no_tracking_cats
break; -- bail out if one is found
end
end
end
-- check for extra |page=, |pages= or |at= parameters. (also sheet and sheets while we're at it)
utilities.select_one (args, {'page', 'p', 'pp', 'pages', 'at', 'sheet', 'sheets'}, 'err_redundant_parameters'); -- this is a dummy call simply to get the error message and category
local coins_pages;
Page, Pages, At, coins_pages = insource_loc_get (Page, A:ORIGIN('Page'), Pages, A:ORIGIN('Pages'), At);
if utilities.is_set (PublicationPlace) and utilities.is_set (Place) then -- both |publication-place= and |place= (|location=) allowed if different
utilities.add_prop_cat ('location-test'); -- add property cat to evaluate how often PublicationPlace and Place are used together
if PublicationPlace == Place then
Place = ''; -- unset; don't need both if they are the same
end
elseif not utilities.is_set (PublicationPlace) and utilities.is_set (Place) then -- when only |place= (|location=) is set ...
PublicationPlace = Place; -- promote |place= (|location=) to |publication-place
end
if PublicationPlace == Place then Place = ''; end -- don't need both if they are the same
local URL_origin = A:ORIGIN('URL'); -- get name of parameter that holds URL
local ChapterURL_origin = A:ORIGIN('ChapterURL'); -- get name of parameter that holds ChapterURL
local ScriptChapter = A['ScriptChapter'];
local ScriptChapter_origin = A:ORIGIN ('ScriptChapter');
local Format = A['Format'];
local ChapterFormat = A['ChapterFormat'];
local TransChapter = A['TransChapter'];
local TransChapter_origin = A:ORIGIN ('TransChapter');
local TransTitle = A['TransTitle'];
local ScriptTitle = A['ScriptTitle'];
--[[
Parameter remapping for cite encyclopedia:
When the citation has these parameters:
|encyclopedia= and |title= then map |title= to |article= and |encyclopedia= to |title= for rendering
|encyclopedia= and |article= then map |encyclopedia= to |title= for rendering
|trans-title= maps to |trans-chapter= when |title= is re-mapped
|url= maps to |chapter-url= when |title= is remapped
All other combinations of |encyclopedia=, |title=, and |article= are not modified
]]
local Encyclopedia = A['Encyclopedia']; -- used as a flag by this module and by ~/COinS
local ScriptEncyclopedia = A['ScriptEncyclopedia'];
local TransEncyclopedia = A['TransEncyclopedia'];
if utilities.is_set (Encyclopedia) or utilities.is_set (ScriptEncyclopedia) then -- emit error message when Encyclopedia set but template is other than {{cite encyclopedia}} or {{citation}}
if 'encyclopaedia' ~= config.CitationClass and 'citation' ~= config.CitationClass then
if utilities.is_set (Encyclopedia) then
utilities.set_message ('err_parameter_ignored', {A:ORIGIN ('Encyclopedia')});
else
utilities.set_message ('err_parameter_ignored', {A:ORIGIN ('ScriptEncyclopedia')});
end
Encyclopedia = nil; -- unset these because not supported by this template
ScriptEncyclopedia = nil;
TransEncyclopedia = nil;
end
elseif utilities.is_set (TransEncyclopedia) then
utilities.set_message ('err_trans_missing_title', {'encyclopedia'});
end
if ('encyclopaedia' == config.CitationClass) or ('citation' == config.CitationClass and utilities.is_set (Encyclopedia)) then
if utilities.is_set (Periodical) and utilities.is_set (Encyclopedia) then -- when both parameters set emit an error message; {{citation}} only; Periodical not allowed in {{cite encyclopedia}}
utilities.set_message ('err_periodical_ignored', {Periodical_origin});
end
if utilities.is_set (Encyclopedia) or utilities.is_set (ScriptEncyclopedia) then
Periodical = Encyclopedia; -- error or no, set Periodical to Encyclopedia for rendering; {{citation}} could (not legitimately) have both; use Encyclopedia
Periodical_origin = A:ORIGIN ('Encyclopedia');
ScriptPeriodical = ScriptEncyclopedia;
ScriptPeriodical_origin = A:ORIGIN ('ScriptEncyclopedia');
if utilities.is_set (Title) or utilities.is_set (ScriptTitle) then
if not utilities.is_set (Chapter) then
Chapter = Title; -- |encyclopedia= and |title= are set so map |title= params to |article= params for rendering
ScriptChapter = ScriptTitle;
ScriptChapter_origin = A:ORIGIN('ScriptTitle')
TransChapter = TransTitle;
ChapterURL = URL;
ChapterURL_origin = URL_origin;
ChapterUrlAccess = UrlAccess;
ChapterFormat = Format;
if not utilities.is_set (ChapterURL) and utilities.is_set (TitleLink) then
Chapter = utilities.make_wikilink (TitleLink, Chapter);
end
Title = Periodical; -- now map |encyclopedia= params to |title= params for rendering
ScriptTitle = ScriptPeriodical or '';
TransTitle = TransEncyclopedia or '';
Periodical = ''; -- redundant so unset
ScriptPeriodical = '';
URL = '';
Format = '';
TitleLink = '';
end
elseif utilities.is_set (Chapter) or utilities.is_set (ScriptChapter) then -- |title= not set
Title = Periodical; -- |encyclopedia= set and |article= set so map |encyclopedia= to |title= for rendering
ScriptTitle = ScriptPeriodical or '';
TransTitle = TransEncyclopedia or '';
Periodical = ''; -- redundant so unset
ScriptPeriodical = '';
end
end
end
-- special case for cite techreport.
local ID = A['ID'];
if (config.CitationClass == "techreport") then -- special case for cite techreport
if utilities.is_set (A['Number']) then -- cite techreport uses 'number', which other citations alias to 'issue'
if not utilities.is_set (ID) then -- can we use ID for the "number"?
ID = A['Number']; -- yes, use it
else -- ID has a value so emit error message
utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', 'id') .. cfg.presentation['sep_list_pair'] .. utilities.wrap_style ('parameter', 'number')});
end
end
end
-- Account for the oddity that is {{cite conference}}, before generation of COinS data.
local ChapterLink -- = A['ChapterLink']; -- deprecated as a parameter but still used internally by cite episode
local Conference = A['Conference'];
local BookTitle = A['BookTitle'];
local TransTitle_origin = A:ORIGIN ('TransTitle');
if 'conference' == config.CitationClass then
if utilities.is_set (BookTitle) then
Chapter = Title;
Chapter_origin = 'title';
-- ChapterLink = TitleLink; -- |chapter-link= is deprecated
ChapterURL = URL;
ChapterUrlAccess = UrlAccess;
ChapterURL_origin = URL_origin;
URL_origin = '';
ChapterFormat = Format;
TransChapter = TransTitle;
TransChapter_origin = TransTitle_origin;
Title = BookTitle;
Format = '';
-- TitleLink = '';
TransTitle = '';
URL = '';
end
elseif 'speech' ~= config.CitationClass then
Conference = ''; -- not cite conference or cite speech so make sure this is empty string
end
local use_lowercase = ( sepc == ',' ); -- controls capitalization of certain static text
-- cite map oddities
local Cartography = "";
local Scale = "";
local Sheet = A['Sheet'] or '';
local Sheets = A['Sheets'] or '';
if config.CitationClass == "map" then
if utilities.is_set (Chapter) then --TODO: make a function for this and similar?
utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', 'map') .. cfg.presentation['sep_list_pair'] .. utilities.wrap_style ('parameter', Chapter_origin)}); -- add error message
end
Chapter = A['Map'];
Chapter_origin = A:ORIGIN('Map');
ChapterURL = A['MapURL'];
ChapterURL_origin = A:ORIGIN('MapURL');
TransChapter = A['TransMap'];
ScriptChapter = A['ScriptMap']
ScriptChapter_origin = A:ORIGIN('ScriptMap')
ChapterUrlAccess = MapUrlAccess;
ChapterFormat = A['MapFormat'];
Cartography = A['Cartography'];
if utilities.is_set ( Cartography ) then
Cartography = sepc .. " " .. wrap_msg ('cartography', Cartography, use_lowercase);
end
Scale = A['Scale'];
if utilities.is_set ( Scale ) then
Scale = sepc .. " " .. Scale;
end
end
-- Account for the oddities that are {{cite episode}} and {{cite serial}}, before generation of COinS data.
local Series = A['Series'];
if 'episode' == config.CitationClass or 'serial' == config.CitationClass then
local SeriesLink = A['SeriesLink'];
SeriesLink = link_title_ok (SeriesLink, A:ORIGIN ('SeriesLink'), Series, 'series'); -- check for wiki-markup in |series-link= or wiki-markup in |series= when |series-link= is set
local Network = A['Network'];
local Station = A['Station'];
local s, n = {}, {};
-- do common parameters first
if utilities.is_set (Network) then table.insert(n, Network); end
if utilities.is_set (Station) then table.insert(n, Station); end
ID = table.concat(n, sepc .. ' ');
if 'episode' == config.CitationClass then -- handle the oddities that are strictly {{cite episode}}
local Season = A['Season'];
local SeriesNumber = A['SeriesNumber'];
if utilities.is_set (Season) and utilities.is_set (SeriesNumber) then -- these are mutually exclusive so if both are set TODO: make a function for this and similar?
utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', 'season') .. cfg.presentation['sep_list_pair'] .. utilities.wrap_style ('parameter', 'seriesno')}); -- add error message
SeriesNumber = ''; -- unset; prefer |season= over |seriesno=
end
-- assemble a table of parts concatenated later into Series
if utilities.is_set (Season) then table.insert(s, wrap_msg ('season', Season, use_lowercase)); end
if utilities.is_set (SeriesNumber) then table.insert(s, wrap_msg ('seriesnum', SeriesNumber, use_lowercase)); end
if utilities.is_set (Issue) then table.insert(s, wrap_msg ('episode', Issue, use_lowercase)); end
Issue = ''; -- unset because this is not a unique parameter
Chapter = Title; -- promote title parameters to chapter
ScriptChapter = ScriptTitle;
ScriptChapter_origin = A:ORIGIN('ScriptTitle');
ChapterLink = TitleLink; -- alias |episode-link=
TransChapter = TransTitle;
ChapterURL = URL;
ChapterUrlAccess = UrlAccess;
ChapterURL_origin = URL_origin;
ChapterFormat = Format;
Title = Series; -- promote series to title
TitleLink = SeriesLink;
Series = table.concat(s, sepc .. ' '); -- this is concatenation of season, seriesno, episode number
if utilities.is_set (ChapterLink) and not utilities.is_set (ChapterURL) then -- link but not URL
Chapter = utilities.make_wikilink (ChapterLink, Chapter);
elseif utilities.is_set (ChapterLink) and utilities.is_set (ChapterURL) then -- if both are set, URL links episode;
Series = utilities.make_wikilink (ChapterLink, Series);
end
URL = ''; -- unset
TransTitle = '';
ScriptTitle = '';
Format = '';
else -- now oddities that are cite serial
Issue = ''; -- unset because this parameter no longer supported by the citation/core version of cite serial
Chapter = A['Episode']; -- TODO: make |episode= available to cite episode someday?
if utilities.is_set (Series) and utilities.is_set (SeriesLink) then
Series = utilities.make_wikilink (SeriesLink, Series);
end
Series = utilities.wrap_style ('italic-title', Series); -- series is italicized
end
end
-- end of {{cite episode}} stuff
-- handle type parameter for those CS1 citations that have default values
local TitleType = A['TitleType'];
local Degree = A['Degree'];
if utilities.in_array (config.CitationClass, {'AV-media-notes', 'document', 'interview', 'mailinglist', 'map', 'podcast', 'pressrelease', 'report', 'speech', 'techreport', 'thesis'}) then
TitleType = set_titletype (config.CitationClass, TitleType);
if utilities.is_set (Degree) and "Thesis" == TitleType then -- special case for cite thesis
TitleType = Degree .. ' ' .. cfg.title_types ['thesis']:lower();
end
end
if utilities.is_set (TitleType) then -- if type parameter is specified
TitleType = utilities.substitute ( cfg.messages['type'], TitleType); -- display it in parentheses
-- TODO: Hack on TitleType to fix bunched parentheses problem
end
-- legacy: promote PublicationDate to Date if neither Date nor Year are set.
local Date = A['Date'];
local Date_origin; -- to hold the name of parameter promoted to Date; required for date error messaging
local PublicationDate = A['PublicationDate'];
local Year = A['Year'];
if utilities.is_set (Year) then
validation.year_check (Year); -- returns nothing; emits maint message when |year= doesn't hold a 'year' value
end
if not utilities.is_set (Date) then
Date = Year; -- promote Year to Date
Year = nil; -- make nil so Year as empty string isn't used for CITEREF
if not utilities.is_set (Date) and utilities.is_set (PublicationDate) then -- use PublicationDate when |date= and |year= are not set
Date = PublicationDate; -- promote PublicationDate to Date
PublicationDate = ''; -- unset, no longer needed
Date_origin = A:ORIGIN('PublicationDate'); -- save the name of the promoted parameter
else
Date_origin = A:ORIGIN('Year'); -- save the name of the promoted parameter
end
else
Date_origin = A:ORIGIN('Date'); -- not a promotion; name required for error messaging
end
if PublicationDate == Date then PublicationDate = ''; end -- if PublicationDate is same as Date, don't display in rendered citation
--[[
Go test all of the date-holding parameters for valid MOS:DATE format and make sure that dates are real dates. This must be done before we do COinS because here is where
we get the date used in the metadata.
Date validation supporting code is in Module:Citation/CS1/Date_validation
]]
local DF = is_valid_parameter_value (A['DF'], A:ORIGIN('DF'), cfg.keywords_lists['df'], '');
if not utilities.is_set (DF) then
DF = cfg.global_df; -- local |df= if present overrides global df set by {{use xxx date}} template
end
local ArchiveURL;
local ArchiveDate;
local ArchiveFormat = A['ArchiveFormat'];
local archive_url_timestamp; -- timestamp from wayback machine url
ArchiveURL, ArchiveDate, archive_url_timestamp = archive_url_check (A['ArchiveURL'], A['ArchiveDate'])
ArchiveFormat = style_format (ArchiveFormat, ArchiveURL, 'archive-format', 'archive-url');
ArchiveURL, ArchiveDate = is_unique_archive_url (ArchiveURL, URL, ChapterURL, A:ORIGIN('ArchiveURL'), ArchiveDate); -- add error message when URL or ChapterURL == ArchiveURL
local AccessDate = A['AccessDate'];
local COinS_date = {}; -- holds date info extracted from |date= for the COinS metadata by Module:Date verification
local DoiBroken = A['DoiBroken'];
local Embargo = A['Embargo'];
local anchor_year; -- used in the CITEREF identifier
do -- create defined block to contain local variables error_message, date_parameters_list, mismatch
local error_message = '';
-- AirDate has been promoted to Date so not necessary to check it
local date_parameters_list = {
['access-date'] = {val = AccessDate, name = A:ORIGIN ('AccessDate')},
['archive-date'] = {val = ArchiveDate, name = A:ORIGIN ('ArchiveDate')},
['date'] = {val = Date, name = Date_origin},
['doi-broken-date'] = {val = DoiBroken, name = A:ORIGIN ('DoiBroken')},
['pmc-embargo-date'] = {val = Embargo, name = A:ORIGIN ('Embargo')},
['publication-date'] = {val = PublicationDate, name = A:ORIGIN ('PublicationDate')},
['year'] = {val = Year, name = A:ORIGIN ('Year')},
};
local error_list = {};
anchor_year, Embargo = validation.dates(date_parameters_list, COinS_date, error_list);
if utilities.is_set (Year) and utilities.is_set (Date) then -- both |date= and |year= not normally needed;
validation.year_date_check (Year, A:ORIGIN ('Year'), Date, A:ORIGIN ('Date'), error_list);
end
if 0 == #error_list then -- error free dates only; 0 when error_list is empty
local modified = false; -- flag
if utilities.is_set (DF) then -- if we need to reformat dates
modified = validation.reformat_dates (date_parameters_list, DF); -- reformat to DF format, use long month names if appropriate
end
if true == validation.date_hyphen_to_dash (date_parameters_list) then -- convert hyphens to dashes where appropriate
modified = true;
utilities.set_message ('maint_date_format'); -- hyphens were converted so add maint category
end
-- for those wikis that can and want to have English date names translated to the local language; not supported at en.wiki
if cfg.date_name_auto_xlate_enable and validation.date_name_xlate (date_parameters_list, cfg.date_digit_auto_xlate_enable ) then
utilities.set_message ('maint_date_auto_xlated'); -- add maint cat
modified = true;
end
if modified then -- if the date_parameters_list values were modified
AccessDate = date_parameters_list['access-date'].val; -- overwrite date holding parameters with modified values
ArchiveDate = date_parameters_list['archive-date'].val;
Date = date_parameters_list['date'].val;
DoiBroken = date_parameters_list['doi-broken-date'].val;
PublicationDate = date_parameters_list['publication-date'].val;
end
if archive_url_timestamp and utilities.is_set (ArchiveDate) then
validation.archive_date_check (ArchiveDate, archive_url_timestamp, DF); -- does YYYYMMDD in archive_url_timestamp match date in ArchiveDate
end
else
utilities.set_message ('err_bad_date', {utilities.make_sep_list (#error_list, error_list)}); -- add this error message
end
end -- end of do
if utilities.in_array (config.CitationClass, {'book', 'encyclopaedia'}) or -- {{cite book}}, {{cite encyclopedia}}; TODO: {{cite conference}} and others?
('citation' == config.CitationClass and utilities.is_set (Encyclopedia)) or -- {{citation}} as an encylopedia citation
('citation' == config.CitationClass and not utilities.is_set (Periodical)) then -- {{citation}} as a book citation
if utilities.is_set (PublicationPlace) then
if not utilities.is_set (PublisherName) then
local date = COinS_date.rftdate and tonumber (COinS_date.rftdate:match ('%d%d%d%d')); -- get year portion of COinS date (because in Arabic numerals); convert string to number
if date and (1850 <= date) then -- location has no publisher; if date is 1850 or later
utilities.set_message ('maint_location_no_publisher'); -- add maint cat
end
else -- PublisherName has a value
if cfg.keywords_xlate['none'] == PublisherName then -- if that value is 'none' (only for book and encyclopedia citations)
PublisherName = ''; -- unset
end
end
end
end
local ID_list = {}; -- sequence table of rendered identifiers
local ID_list_coins = {}; -- table of identifiers and their values from args; key is same as cfg.id_handlers's key
local Class = A['Class']; -- arxiv class identifier
local ID_support = {
{A['ASINTLD'], 'ASIN', 'err_asintld_missing_asin', A:ORIGIN ('ASINTLD')},
{DoiBroken, 'DOI', 'err_doibroken_missing_doi', A:ORIGIN ('DoiBroken')},
{Embargo, 'PMC', 'err_embargo_missing_pmc', A:ORIGIN ('Embargo')},
}
ID_list, ID_list_coins = identifiers.identifier_lists_get (args, {DoiBroken = DoiBroken, ASINTLD = A['ASINTLD'], Embargo = Embargo, Class = Class, Year=anchor_year}, ID_support);
-- Account for the oddities that are {{cite arxiv}}, {{cite biorxiv}}, {{cite citeseerx}}, {{cite medrxiv}}, {{cite ssrn}}, before generation of COinS data.
if utilities.in_array (config.CitationClass, whitelist.preprint_template_list_t) then -- |arxiv= or |eprint= required for cite arxiv; |biorxiv=, |citeseerx=, |medrxiv=, |ssrn= required for their templates
if not (args[cfg.id_handlers[config.CitationClass:upper()].parameters[1]] or -- can't use ID_list_coins k/v table here because invalid parameters omitted
args[cfg.id_handlers[config.CitationClass:upper()].parameters[2]]) then -- which causes unexpected parameter missing error message
utilities.set_message ('err_' .. config.CitationClass .. '_missing'); -- add error message
end
Periodical = ({['arxiv'] = 'arXiv', ['biorxiv'] = 'bioRxiv', ['citeseerx'] = 'CiteSeerX', ['medrxiv'] = 'medRxiv', ['ssrn'] = 'Social Science Research Network'})[config.CitationClass];
end
-- Link the title of the work if no |url= was provided, but we have a |pmc= or a |doi= with |doi-access=free
if config.CitationClass == "journal" and not utilities.is_set (URL) and not utilities.is_set (TitleLink) and not utilities.in_array (cfg.keywords_xlate[Title], {'off', 'none'}) then -- TODO: remove 'none' once existing citations have been switched to 'off', so 'none' can be used as token for "no title" instead
if 'none' ~= cfg.keywords_xlate[auto_select] then -- if auto-linking not disabled
if identifiers.auto_link_urls[auto_select] then -- manual selection
URL = identifiers.auto_link_urls[auto_select]; -- set URL to be the same as identifier's external link
URL_origin = cfg.id_handlers[auto_select:upper()].parameters[1]; -- set URL_origin to parameter name for use in error message if citation is missing a |title=
elseif identifiers.auto_link_urls['pmc'] then -- auto-select PMC
URL = identifiers.auto_link_urls['pmc']; -- set URL to be the same as the PMC external link if not embargoed
URL_origin = cfg.id_handlers['PMC'].parameters[1]; -- set URL_origin to parameter name for use in error message if citation is missing a |title=
elseif identifiers.auto_link_urls['doi'] then -- auto-select DOI
URL = identifiers.auto_link_urls['doi'];
URL_origin = cfg.id_handlers['DOI'].parameters[1];
end
end
if utilities.is_set (URL) then -- set when using an identifier-created URL
if utilities.is_set (AccessDate) then -- |access-date= requires |url=; identifier-created URL is not |url=
utilities.set_message ('err_accessdate_missing_url'); -- add an error message
AccessDate = ''; -- unset
end
if utilities.is_set (ArchiveURL) then -- |archive-url= requires |url=; identifier-created URL is not |url=
utilities.set_message ('err_archive_missing_url'); -- add an error message
ArchiveURL = ''; -- unset
end
end
end
-- At this point fields may be nil if they weren't specified in the template use. We can use that fact.
-- Test if citation has no title
if not utilities.is_set (Title) and not utilities.is_set (TransTitle) and not utilities.is_set (ScriptTitle) then -- has special case for cite episode
utilities.set_message ('err_citation_missing_title', {'episode' == config.CitationClass and 'series' or 'title'});
end
if utilities.in_array (cfg.keywords_xlate[Title], {'off', 'none'}) and
utilities.in_array (config.CitationClass, {'journal', 'citation'}) and
(utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)) and
('journal' == Periodical_origin or 'script-journal' == ScriptPeriodical_origin) then -- special case for journal cites
Title = ''; -- set title to empty string
utilities.set_message ('maint_untitled'); -- add maint cat
end
-- COinS metadata (see <http://ocoins.info/>) for automated parsing of citation information.
-- handle the oddity that is cite encyclopedia and {{citation |encyclopedia=something}}. Here we presume that
-- when Periodical, Title, and Chapter are all set, then Periodical is the book (encyclopedia) title, Title
-- is the article title, and Chapter is a section within the article. So, we remap
local coins_chapter = Chapter; -- default assuming that remapping not required
local coins_title = Title; -- et tu
if 'encyclopaedia' == config.CitationClass or ('citation' == config.CitationClass and utilities.is_set (Encyclopedia)) then
if utilities.is_set (Chapter) and utilities.is_set (Title) and utilities.is_set (Periodical) then -- if all are used then
coins_chapter = Title; -- remap
coins_title = Periodical;
end
end
local coins_author = a; -- default for coins rft.au
if 0 < #c then -- but if contributor list
coins_author = c; -- use that instead
end
-- this is the function call to COinS()
local OCinSoutput = metadata.COinS({
['Periodical'] = utilities.strip_apostrophe_markup (Periodical), -- no markup in the metadata
['Encyclopedia'] = Encyclopedia, -- just a flag; content ignored by ~/COinS
['Chapter'] = metadata.make_coins_title (coins_chapter, ScriptChapter), -- Chapter and ScriptChapter stripped of bold / italic / accept-as-written markup
['Degree'] = Degree; -- cite thesis only
['Title'] = metadata.make_coins_title (coins_title, ScriptTitle), -- Title and ScriptTitle stripped of bold / italic / accept-as-written markup
['PublicationPlace'] = PublicationPlace,
['Date'] = COinS_date.rftdate, -- COinS_date.* has correctly formatted date values if Date is valid;
['Season'] = COinS_date.rftssn,
['Quarter'] = COinS_date.rftquarter,
['Chron'] = COinS_date.rftchron,
['Series'] = Series,
['Volume'] = Volume,
['Issue'] = Issue,
['ArticleNumber'] = ArticleNumber,
['Pages'] = coins_pages or metadata.get_coins_pages (first_set ({Sheet, Sheets, Page, Pages, At, QuotePage, QuotePages}, 7)), -- pages stripped of external links
['Edition'] = Edition,
['PublisherName'] = PublisherName or Newsgroup, -- any apostrophe markup already removed from PublisherName
['URL'] = first_set ({ChapterURL, URL}, 2),
['Authors'] = coins_author,
['ID_list'] = ID_list_coins,
['RawPage'] = this_page.prefixedText,
}, config.CitationClass);
-- Account for the oddities that are {{cite arxiv}}, {{cite biorxiv}}, {{cite citeseerx}}, {{cite medrxiv}}, and {{cite ssrn}} AFTER generation of COinS data.
if utilities.in_array (config.CitationClass, whitelist.preprint_template_list_t) then -- we have set rft.jtitle in COinS to arXiv, bioRxiv, CiteSeerX, medRxiv, or ssrn now unset so it isn't displayed
Periodical = ''; -- periodical not allowed in these templates; if article has been published, use cite journal
end
-- special case for cite newsgroup. Do this after COinS because we are modifying Publishername to include some static text
if 'newsgroup' == config.CitationClass and utilities.is_set (Newsgroup) then
PublisherName = utilities.substitute (cfg.messages['newsgroup'], external_link( 'news:' .. Newsgroup, Newsgroup, Newsgroup_origin, nil ));
end
local Editors;
local EditorCount; -- used only for choosing {ed.) or (eds.) annotation at end of editor name-list
local Contributors; -- assembled contributors name list
local contributor_etal;
local Translators; -- assembled translators name list
local translator_etal;
local t = {}; -- translators list from |translator-lastn= / translator-firstn= pairs
t = extract_names (args, 'TranslatorList'); -- fetch translator list from |translatorn= / |translator-lastn=, -firstn=, -linkn=, -maskn=
local Interviewers;
local interviewers_list = {};
interviewers_list = extract_names (args, 'InterviewerList'); -- process preferred interviewers parameters
local interviewer_etal;
-- Now perform various field substitutions.
-- We also add leading spaces and surrounding markup and punctuation to the
-- various parts of the citation, but only when they are non-nil.
do
local last_first_list;
local control = {
format = NameListStyle, -- empty string, '&', 'amp', 'and', or 'vanc'
maximum = nil, -- as if display-authors or display-editors not set
mode = Mode
};
do -- do editor name list first because the now unsupported coauthors used to modify control table
local display_names, param = display_names_select (cfg.global_cs1_config_t['DisplayEditors'], A['DisplayEditors'], A:ORIGIN ('DisplayEditors'), #e);
control.maximum, editor_etal = get_display_names (display_names, #e, 'editors', editor_etal, param);
Editors, EditorCount = list_people (control, e, editor_etal);
if 1 == EditorCount and (true == editor_etal or 1 < #e) then -- only one editor displayed but includes etal then
EditorCount = 2; -- spoof to display (eds.) annotation
end
end
do -- now do interviewers
local display_names, param = display_names_select (cfg.global_cs1_config_t['DisplayInterviewers'], A['DisplayInterviewers'], A:ORIGIN ('DisplayInterviewers'), #interviewers_list);
control.maximum, interviewer_etal = get_display_names (display_names, #interviewers_list, 'interviewers', interviewer_etal, param);
Interviewers = list_people (control, interviewers_list, interviewer_etal);
end
do -- now do translators
local display_names, param = display_names_select (cfg.global_cs1_config_t['DisplayTranslators'], A['DisplayTranslators'], A:ORIGIN ('DisplayTranslators'), #t);
control.maximum, translator_etal = get_display_names (display_names, #t, 'translators', translator_etal, param);
Translators = list_people (control, t, translator_etal);
end
do -- now do contributors
local display_names, param = display_names_select (cfg.global_cs1_config_t['DisplayContributors'], A['DisplayContributors'], A:ORIGIN ('DisplayContributors'), #c);
control.maximum, contributor_etal = get_display_names (display_names, #c, 'contributors', contributor_etal, param);
Contributors = list_people (control, c, contributor_etal);
end
do -- now do authors
local display_names, param = display_names_select (cfg.global_cs1_config_t['DisplayAuthors'], A['DisplayAuthors'], A:ORIGIN ('DisplayAuthors'), #a, author_etal);
control.maximum, author_etal = get_display_names (display_names, #a, 'authors', author_etal, param);
last_first_list = list_people (control, a, author_etal);
if utilities.is_set (Authors) then
Authors, author_etal = name_has_etal (Authors, author_etal, false, 'authors'); -- find and remove variations on et al.
if author_etal then
Authors = Authors .. ' ' .. cfg.messages['et al']; -- add et al. to authors parameter
end
else
Authors = last_first_list; -- either an author name list or an empty string
end
end -- end of do
if utilities.is_set (Authors) and utilities.is_set (Collaboration) then
Authors = Authors .. ' (' .. Collaboration .. ')'; -- add collaboration after et al.
end
end
local ConferenceFormat = A['ConferenceFormat'];
local ConferenceURL = A['ConferenceURL'];
ConferenceFormat = style_format (ConferenceFormat, ConferenceURL, 'conference-format', 'conference-url');
Format = style_format (Format, URL, 'format', 'url');
-- special case for chapter format so no error message or cat when chapter not supported
if not (utilities.in_array (config.CitationClass, {'web', 'news', 'journal', 'magazine', 'pressrelease', 'podcast', 'newsgroup', 'arxiv', 'biorxiv', 'citeseerx', 'medrxiv', 'ssrn'}) or
('citation' == config.CitationClass and (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)) and not utilities.is_set (Encyclopedia))) then
ChapterFormat = style_format (ChapterFormat, ChapterURL, 'chapter-format', 'chapter-url');
end
if not utilities.is_set (URL) then
if utilities.in_array (config.CitationClass, {"web", "podcast", "mailinglist"}) or -- |url= required for cite web, cite podcast, and cite mailinglist
('citation' == config.CitationClass and ('website' == Periodical_origin or 'script-website' == ScriptPeriodical_origin)) then -- and required for {{citation}} with |website= or |script-website=
utilities.set_message ('err_cite_web_url');
end
-- do we have |accessdate= without either |url= or |chapter-url=?
if utilities.is_set (AccessDate) and not utilities.is_set (ChapterURL) then -- ChapterURL may be set when URL is not set;
utilities.set_message ('err_accessdate_missing_url');
AccessDate = '';
end
end
local UrlStatus = is_valid_parameter_value (A['UrlStatus'], A:ORIGIN('UrlStatus'), cfg.keywords_lists['url-status'], '');
local OriginalURL
local OriginalURL_origin
local OriginalFormat
local OriginalAccess;
UrlStatus = UrlStatus:lower(); -- used later when assembling archived text
if utilities.is_set ( ArchiveURL ) then
if utilities.is_set (ChapterURL) then -- if chapter-url= is set apply archive url to it
OriginalURL = ChapterURL; -- save copy of source chapter's url for archive text
OriginalURL_origin = ChapterURL_origin; -- name of |chapter-url= parameter for error messages
OriginalFormat = ChapterFormat; -- and original |chapter-format=
if 'live' ~= UrlStatus then
ChapterURL = ArchiveURL -- swap-in the archive's URL
ChapterURL_origin = A:ORIGIN('ArchiveURL') -- name of |archive-url= parameter for error messages
ChapterFormat = ArchiveFormat or ''; -- swap in archive's format
ChapterUrlAccess = nil; -- restricted access levels do not make sense for archived URLs
end
elseif utilities.is_set (URL) then
OriginalURL = URL; -- save copy of original source URL
OriginalURL_origin = URL_origin; -- name of URL parameter for error messages
OriginalFormat = Format; -- and original |format=
OriginalAccess = UrlAccess;
if 'live' ~= UrlStatus then -- if URL set then |archive-url= applies to it
URL = ArchiveURL -- swap-in the archive's URL
URL_origin = A:ORIGIN('ArchiveURL') -- name of archive URL parameter for error messages
Format = ArchiveFormat or ''; -- swap in archive's format
UrlAccess = nil; -- restricted access levels do not make sense for archived URLs
end
end
elseif utilities.is_set (UrlStatus) then -- if |url-status= is set when |archive-url= is not set
utilities.set_message ('maint_url_status'); -- add maint cat
end
if utilities.in_array (config.CitationClass, {'web', 'news', 'journal', 'magazine', 'pressrelease', 'podcast', 'newsgroup', 'arxiv', 'biorxiv', 'citeseerx', 'medrxiv', 'ssrn'}) or -- if any of the 'periodical' cites except encyclopedia
('citation' == config.CitationClass and (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)) and not utilities.is_set (Encyclopedia)) then
local chap_param;
if utilities.is_set (Chapter) then -- get a parameter name from one of these chapter related meta-parameters
chap_param = A:ORIGIN ('Chapter')
elseif utilities.is_set (TransChapter) then
chap_param = A:ORIGIN ('TransChapter')
elseif utilities.is_set (ChapterURL) then
chap_param = A:ORIGIN ('ChapterURL')
elseif utilities.is_set (ScriptChapter) then
chap_param = ScriptChapter_origin;
else utilities.is_set (ChapterFormat)
chap_param = A:ORIGIN ('ChapterFormat')
end
if utilities.is_set (chap_param) then -- if we found one
utilities.set_message ('err_chapter_ignored', {chap_param}); -- add error message
Chapter = ''; -- and set them to empty string to be safe with concatenation
TransChapter = '';
ChapterURL = '';
ScriptChapter = '';
ChapterFormat = '';
end
else -- otherwise, format chapter / article title
local no_quotes = false; -- default assume that we will be quoting the chapter parameter value
if utilities.is_set (Contribution) and 0 < #c then -- if this is a contribution with contributor(s)
if utilities.in_array (Contribution:lower(), cfg.keywords_lists.contribution) then -- and a generic contribution title
no_quotes = true; -- then render it unquoted
end
end
Chapter = format_chapter_title (ScriptChapter, ScriptChapter_origin, Chapter, Chapter_origin, TransChapter, TransChapter_origin, ChapterURL, ChapterURL_origin, no_quotes, ChapterUrlAccess); -- Contribution is also in Chapter
if utilities.is_set (Chapter) then
Chapter = Chapter .. ChapterFormat ;
if 'map' == config.CitationClass and utilities.is_set (TitleType) then
Chapter = Chapter .. ' ' .. TitleType; -- map annotation here; not after title
end
Chapter = Chapter .. sepc .. ' ';
elseif utilities.is_set (ChapterFormat) then -- |chapter= not set but |chapter-format= is so ...
Chapter = ChapterFormat .. sepc .. ' '; -- ... ChapterFormat has error message, we want to see it
end
end
-- Format main title
local plain_title = false;
local accept_title;
Title, accept_title = utilities.has_accept_as_written (Title, true); -- remove accept-this-as-written markup when it wraps all of <Title>
if accept_title and ('' == Title) then -- only support forced empty for now "(())"
Title = cfg.messages['notitle']; -- replace by predefined "No title" message
-- TODO: utilities.set_message ( 'err_redundant_parameters', ...); -- issue proper error message instead of muting
ScriptTitle = ''; -- just mute for now
TransTitle = ''; -- just mute for now
plain_title = true; -- suppress text decoration for descriptive title
utilities.set_message ('maint_untitled'); -- add maint cat
end
if not accept_title then -- <Title> not wrapped in accept-as-written markup
if '...' == Title:sub (-3) then -- if ellipsis is the last three characters of |title=
Title = Title:gsub ('(%.%.%.)%.+$', '%1'); -- limit the number of dots to three
elseif not mw.ustring.find (Title, '%.%s*%a%.$') and -- end of title is not a 'dot-(optional space-)letter-dot' initialism ...
not mw.ustring.find (Title, '%s+%a%.$') then -- ...and not a 'space-letter-dot' initial (''Allium canadense'' L.)
Title = mw.ustring.gsub(Title, '%' .. sepc .. '$', ''); -- remove any trailing separator character; sepc and ms.ustring() here for languages that use multibyte separator characters
end
if utilities.is_set (ArchiveURL) and is_archived_copy (Title) then
utilities.set_message ('maint_archived_copy'); -- add maintenance category before we modify the content of Title
end
if is_generic ('generic_titles', Title) then
utilities.set_message ('err_generic_title'); -- set an error message
end
end
if (not plain_title) and (utilities.in_array (config.CitationClass, {'web', 'news', 'journal', 'magazine', 'document', 'pressrelease', 'podcast', 'newsgroup', 'mailinglist', 'interview', 'arxiv', 'biorxiv', 'citeseerx', 'medrxiv', 'ssrn'}) or
('citation' == config.CitationClass and (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)) and not utilities.is_set (Encyclopedia)) or
('map' == config.CitationClass and (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)))) then -- special case for cite map when the map is in a periodical treat as an article
Title = kern_quotes (Title); -- if necessary, separate title's leading and trailing quote marks from module provided quote marks
Title = utilities.wrap_style ('quoted-title', Title);
Title = script_concatenate (Title, ScriptTitle, 'script-title'); -- <bdi> tags, lang attribute, categorization, etc.; must be done after title is wrapped
TransTitle = utilities.wrap_style ('trans-quoted-title', TransTitle );
elseif plain_title or ('report' == config.CitationClass) then -- no styling for cite report and descriptive titles (otherwise same as above)
Title = script_concatenate (Title, ScriptTitle, 'script-title'); -- <bdi> tags, lang attribute, categorization, etc.; must be done after title is wrapped
TransTitle = utilities.wrap_style ('trans-quoted-title', TransTitle ); -- for cite report, use this form for trans-title
else
Title = utilities.wrap_style ('italic-title', Title);
Title = script_concatenate (Title, ScriptTitle, 'script-title'); -- <bdi> tags, lang attribute, categorization, etc.; must be done after title is wrapped
TransTitle = utilities.wrap_style ('trans-italic-title', TransTitle);
end
if utilities.is_set (TransTitle) then
if utilities.is_set (Title) then
TransTitle = " " .. TransTitle;
else
utilities.set_message ('err_trans_missing_title', {'title'});
end
end
if utilities.is_set (Title) then -- TODO: is this the right place to be making Wikisource URLs?
if utilities.is_set (TitleLink) and utilities.is_set (URL) then
utilities.set_message ('err_wikilink_in_url'); -- set an error message because we can't have both
TitleLink = ''; -- unset
end
if not utilities.is_set (TitleLink) and utilities.is_set (URL) then
Title = external_link (URL, Title, URL_origin, UrlAccess) .. TransTitle .. Format;
URL = ''; -- unset these because no longer needed
Format = "";
elseif utilities.is_set (TitleLink) and not utilities.is_set (URL) then
local ws_url;
ws_url = wikisource_url_make (TitleLink); -- ignore ws_label return; not used here
if ws_url then
Title = external_link (ws_url, Title .. ' ', 'ws link in title-link'); -- space char after Title to move icon away from italic text; TODO: a better way to do this?
Title = utilities.substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], TitleLink, Title});
Title = Title .. TransTitle;
else
Title = utilities.make_wikilink (TitleLink, Title) .. TransTitle;
end
else
local ws_url, ws_label, L; -- Title has italic or quote markup by the time we get here which causes is_wikilink() to return 0 (not a wikilink)
ws_url, ws_label, L = wikisource_url_make (Title:gsub('^[\'"]*(.-)[\'"]*$', '%1')); -- make ws URL from |title= interwiki link (strip italic or quote markup); link portion L becomes tooltip label
if ws_url then
Title = Title:gsub ('%b[]', ws_label); -- replace interwiki link with ws_label to retain markup
Title = external_link (ws_url, Title .. ' ', 'ws link in title'); -- space char after Title to move icon away from italic text; TODO: a better way to do this?
Title = utilities.substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], L, Title});
Title = Title .. TransTitle;
else
Title = Title .. TransTitle;
end
end
else
Title = TransTitle;
end
if utilities.is_set (Place) then
Place = " " .. wrap_msg ('written', Place, use_lowercase) .. sepc .. " ";
end
local ConferenceURL_origin = A:ORIGIN('ConferenceURL'); -- get name of parameter that holds ConferenceURL
if utilities.is_set (Conference) then
if utilities.is_set (ConferenceURL) then
Conference = external_link( ConferenceURL, Conference, ConferenceURL_origin, nil );
end
Conference = sepc .. " " .. Conference .. ConferenceFormat;
elseif utilities.is_set (ConferenceURL) then
Conference = sepc .. " " .. external_link( ConferenceURL, nil, ConferenceURL_origin, nil );
end
local Position = '';
if not utilities.is_set (Position) then
local Minutes = A['Minutes'];
local Time = A['Time'];
if utilities.is_set (Minutes) then
if utilities.is_set (Time) then --TODO: make a function for this and similar?
utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', 'minutes') .. cfg.presentation['sep_list_pair'] .. utilities.wrap_style ('parameter', 'time')});
end
Position = " " .. Minutes .. " " .. cfg.messages['minutes'];
else
if utilities.is_set (Time) then
local TimeCaption = A['TimeCaption']
if not utilities.is_set (TimeCaption) then
TimeCaption = cfg.messages['event'];
if sepc ~= '.' then
TimeCaption = TimeCaption:lower();
end
end
Position = " " .. TimeCaption .. " " .. Time;
end
end
else
Position = " " .. Position;
At = '';
end
Page, Pages, Sheet, Sheets = format_pages_sheets (Page, Pages, Sheet, Sheets, config.CitationClass, Periodical_origin, sepc, NoPP, use_lowercase);
At = utilities.is_set (At) and (sepc .. " " .. At) or "";
Position = utilities.is_set (Position) and (sepc .. " " .. Position) or "";
if config.CitationClass == 'map' then
local Sections = A['Sections']; -- Section (singular) is an alias of Chapter so set earlier
local Inset = A['Inset'];
if utilities.is_set ( Inset ) then
Inset = sepc .. " " .. wrap_msg ('inset', Inset, use_lowercase);
end
if utilities.is_set ( Sections ) then
Section = sepc .. " " .. wrap_msg ('sections', Sections, use_lowercase);
elseif utilities.is_set ( Section ) then
Section = sepc .. " " .. wrap_msg ('section', Section, use_lowercase);
end
At = At .. Inset .. Section;
end
local Others = A['Others'];
if utilities.is_set (Others) and 0 == #a and 0 == #e then -- add maint cat when |others= has value and used without |author=, |editor=
if config.CitationClass == "AV-media-notes"
or config.CitationClass == "audio-visual" then -- special maint for AV/M which has a lot of 'false' positives right now
utilities.set_message ('maint_others_avm')
else
utilities.set_message ('maint_others');
end
end
Others = utilities.is_set (Others) and (sepc .. " " .. Others) or "";
if utilities.is_set (Translators) then
Others = safe_join ({sepc .. ' ', wrap_msg ('translated', Translators, use_lowercase), Others}, sepc);
end
if utilities.is_set (Interviewers) then
Others = safe_join ({sepc .. ' ', wrap_msg ('interview', Interviewers, use_lowercase), Others}, sepc);
end
local TitleNote = A['TitleNote'];
TitleNote = utilities.is_set (TitleNote) and (sepc .. " " .. TitleNote) or "";
if utilities.is_set (Edition) then
if Edition:match ('%f[%a][Ee]d%n?%.?$') or Edition:match ('%f[%a][Ee]dition$') then -- Ed, ed, Ed., ed., Edn, edn, Edn., edn.
utilities.set_message ('err_extra_text_edition'); -- add error message
end
Edition = " " .. wrap_msg ('edition', Edition);
else
Edition = '';
end
Series = utilities.is_set (Series) and wrap_msg ('series', {sepc, Series}) or ""; -- not the same as SeriesNum
local Agency = A['Agency'] or ''; -- |agency= is supported by {{cite magazine}}, {{cite news}}, {{cite press release}}, {{cite web}}, and certain {{citation}} templates
if utilities.is_set (Agency) then -- this testing done here because {{citation}} supports 'news' citations
if utilities.in_array (config.CitationClass, {'magazine', 'news', 'pressrelease', 'web'}) or ('citation' == config.CitationClass and utilities.in_array (Periodical_origin, {"magazine", "newspaper", "work"})) then
Agency = wrap_msg ('agency', {sepc, Agency}); -- format for rendering
else
Agency = ''; -- unset; not supported
utilities.set_message ('err_parameter_ignored', {'agency'}); -- add error message
end
end
Volume = format_volume_issue (Volume, Issue, ArticleNumber, config.CitationClass, Periodical_origin, sepc, use_lowercase);
if utilities.is_set (AccessDate) then
local retrv_text = " " .. cfg.messages['retrieved']
AccessDate = nowrap_date (AccessDate); -- wrap in nowrap span if date in appropriate format
if (sepc ~= ".") then retrv_text = retrv_text:lower() end -- if mode is cs2, lower case
AccessDate = utilities.substitute (retrv_text, AccessDate); -- add retrieved text
AccessDate = utilities.substitute (cfg.presentation['accessdate'], {sepc, AccessDate}); -- allow editors to hide accessdates
end
if utilities.is_set (ID) then ID = sepc .. " " .. ID; end
local Docket = A['Docket'];
if "thesis" == config.CitationClass and utilities.is_set (Docket) then
ID = sepc .. " Docket " .. Docket .. ID;
end
if "report" == config.CitationClass and utilities.is_set (Docket) then -- for cite report when |docket= is set
ID = sepc .. ' ' .. Docket; -- overwrite ID even if |id= is set
end
if utilities.is_set (URL) then
URL = " " .. external_link( URL, nil, URL_origin, UrlAccess );
end
-- We check length of PostScript here because it will have been nuked by
-- the quote parameters. We'd otherwise emit a message even if there wasn't
-- a displayed postscript.
-- TODO: Should the max size (1) be configurable?
-- TODO: Should we check a specific pattern?
if utilities.is_set(PostScript) and mw.ustring.len(PostScript) > 1 then
utilities.set_message ('maint_postscript')
end
local Archived;
if utilities.is_set (ArchiveURL) then
if not utilities.is_set (ArchiveDate) then -- ArchiveURL set but ArchiveDate not set
utilities.set_message ('err_archive_missing_date'); -- emit an error message
ArchiveURL = ''; -- empty string for concatenation
ArchiveDate = ''; -- empty string for concatenation
end
else
if utilities.is_set (ArchiveDate) then -- ArchiveURL not set but ArchiveDate is set
utilities.set_message ('err_archive_date_missing_url'); -- emit an error message
ArchiveURL = ''; -- empty string for concatenation
ArchiveDate = ''; -- empty string for concatenation
end
end
if utilities.is_set (ArchiveURL) then
local arch_text;
if "live" == UrlStatus then
arch_text = cfg.messages['archived'];
if sepc ~= "." then arch_text = arch_text:lower() end
if utilities.is_set (ArchiveDate) then
Archived = sepc .. ' ' .. utilities.substitute ( cfg.messages['archived-live'],
{external_link( ArchiveURL, arch_text, A:ORIGIN('ArchiveURL'), nil) .. ArchiveFormat, ArchiveDate } );
else
Archived = '';
end
if not utilities.is_set (OriginalURL) then
utilities.set_message ('err_archive_missing_url');
Archived = ''; -- empty string for concatenation
end
elseif utilities.is_set (OriginalURL) then -- UrlStatus is empty, 'dead', 'unfit', 'usurped', 'bot: unknown'
if utilities.in_array (UrlStatus, {'unfit', 'usurped', 'bot: unknown'}) then
arch_text = cfg.messages['archived-unfit'];
if sepc ~= "." then arch_text = arch_text:lower() end
Archived = sepc .. ' ' .. arch_text .. ArchiveDate; -- format already styled
if 'bot: unknown' == UrlStatus then
utilities.set_message ('maint_bot_unknown'); -- and add a category if not already added
else
utilities.add_prop_cat ('unfit'); -- and add a category if not already added
end
else -- UrlStatus is empty, 'dead'
arch_text = cfg.messages['archived-dead'];
if sepc ~= "." then arch_text = arch_text:lower() end
if utilities.is_set (ArchiveDate) then
Archived = sepc .. " " .. utilities.substitute ( arch_text,
{ external_link( OriginalURL, cfg.messages['original'], OriginalURL_origin, OriginalAccess ) .. OriginalFormat, ArchiveDate } ); -- format already styled
else
Archived = ''; -- unset for concatenation
end
end
else -- OriginalUrl not set
utilities.set_message ('err_archive_missing_url');
Archived = ''; -- empty string for concatenation
end
elseif utilities.is_set (ArchiveFormat) then
Archived = ArchiveFormat; -- if set and ArchiveURL not set ArchiveFormat has error message
else
Archived = '';
end
local TranscriptURL = A['TranscriptURL']
local TranscriptFormat = A['TranscriptFormat'];
TranscriptFormat = style_format (TranscriptFormat, TranscriptURL, 'transcript-format', 'transcripturl');
local Transcript = A['Transcript'];
local TranscriptURL_origin = A:ORIGIN('TranscriptURL'); -- get name of parameter that holds TranscriptURL
if utilities.is_set (Transcript) then
if utilities.is_set (TranscriptURL) then
Transcript = external_link( TranscriptURL, Transcript, TranscriptURL_origin, nil );
end
Transcript = sepc .. ' ' .. Transcript .. TranscriptFormat;
elseif utilities.is_set (TranscriptURL) then
Transcript = external_link( TranscriptURL, nil, TranscriptURL_origin, nil );
end
local Publisher;
if utilities.is_set (PublicationDate) then
PublicationDate = wrap_msg ('published', PublicationDate);
end
if utilities.is_set (PublisherName) then
if utilities.is_set (PublicationPlace) then
Publisher = sepc .. " " .. PublicationPlace .. ": " .. PublisherName .. PublicationDate;
else
Publisher = sepc .. " " .. PublisherName .. PublicationDate;
end
elseif utilities.is_set (PublicationPlace) then
Publisher= sepc .. " " .. PublicationPlace .. PublicationDate;
else
Publisher = PublicationDate;
end
-- Several of the above rely upon detecting this as nil, so do it last.
if (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical) or utilities.is_set (TransPeriodical)) then
if utilities.is_set (Title) or utilities.is_set (TitleNote) then
Periodical = sepc .. " " .. format_periodical (ScriptPeriodical, ScriptPeriodical_origin, Periodical, TransPeriodical, TransPeriodical_origin);
else
Periodical = format_periodical (ScriptPeriodical, ScriptPeriodical_origin, Periodical, TransPeriodical, TransPeriodical_origin);
end
end
local Language = A['Language'];
if utilities.is_set (Language) then
Language = language_parameter (Language); -- format, categories, name from ISO639-1, etc.
else
Language=''; -- language not specified so make sure this is an empty string;
--[[ TODO: need to extract the wrap_msg from language_parameter
so that we can solve parentheses bunching problem with Format/Language/TitleType
]]
end
--[[
Handle the oddity that is cite speech. This code overrides whatever may be the value assigned to TitleNote (through |department=) and forces it to be " (Speech)" so that
the annotation directly follows the |title= parameter value in the citation rather than the |event= parameter value (if provided).
]]
if "speech" == config.CitationClass then -- cite speech only
TitleNote = TitleType; -- move TitleType to TitleNote so that it renders ahead of |event=
TitleType = ''; -- and unset
if utilities.is_set (Periodical) then -- if Periodical, perhaps because of an included |website= or |journal= parameter
if utilities.is_set (Conference) then -- and if |event= is set
Conference = Conference .. sepc .. " "; -- then add appropriate punctuation to the end of the Conference variable before rendering
end
end
end
-- Piece all bits together at last. Here, all should be non-nil.
-- We build things this way because it is more efficient in LUA
-- not to keep reassigning to the same string variable over and over.
local tcommon;
local tcommon2; -- used for book cite when |contributor= is set
if utilities.in_array (config.CitationClass, {"book", "citation"}) and not utilities.is_set (Periodical) then -- special cases for book cites
if utilities.is_set (Contributors) then -- when we are citing foreword, preface, introduction, etc.
tcommon = safe_join ({Title, TitleNote}, sepc); -- author and other stuff will come after this and before tcommon2
tcommon2 = safe_join ({TitleType, Series, Language, Volume, Others, Edition, Publisher}, sepc);
else
tcommon = safe_join ({Title, TitleNote, TitleType, Series, Language, Volume, Others, Edition, Publisher}, sepc);
end
elseif 'map' == config.CitationClass then -- special cases for cite map
if utilities.is_set (Chapter) then -- map in a book; TitleType is part of Chapter
tcommon = safe_join ({Title, Edition, Scale, Series, Language, Cartography, Others, Publisher, Volume}, sepc);
elseif utilities.is_set (Periodical) then -- map in a periodical
tcommon = safe_join ({Title, TitleType, Periodical, Scale, Series, Language, Cartography, Others, Publisher, Volume}, sepc);
else -- a sheet or stand-alone map
tcommon = safe_join ({Title, TitleType, Edition, Scale, Series, Language, Cartography, Others, Publisher}, sepc);
end
elseif 'episode' == config.CitationClass then -- special case for cite episode
tcommon = safe_join ({Title, TitleNote, TitleType, Series, Language, Edition, Publisher}, sepc);
else -- all other CS1 templates
tcommon = safe_join ({Title, TitleNote, Conference, Periodical, TitleType, Series, Language, Volume, Others, Edition, Publisher, Agency}, sepc);
end
if #ID_list > 0 then
ID_list = safe_join( { sepc .. " ", table.concat( ID_list, sepc .. " " ), ID }, sepc );
else
ID_list = ID;
end
local Via = A['Via'];
Via = utilities.is_set (Via) and wrap_msg ('via', Via) or '';
local idcommon;
if 'audio-visual' == config.CitationClass or 'episode' == config.CitationClass then -- special case for cite AV media & cite episode position transcript
idcommon = safe_join( { ID_list, URL, Archived, Transcript, AccessDate, Via, Quote }, sepc );
else
idcommon = safe_join( { ID_list, URL, Archived, AccessDate, Via, Quote }, sepc );
end
local text;
local pgtext = Position .. Sheet .. Sheets .. Page .. Pages .. At;
local OrigDate = A['OrigDate'];
OrigDate = utilities.is_set (OrigDate) and wrap_msg ('origdate', OrigDate) or '';
if utilities.is_set (Date) then
if utilities.is_set (Authors) or utilities.is_set (Editors) then -- date follows authors or editors when authors not set
Date = " (" .. Date .. ")" .. OrigDate .. sepc .. " "; -- in parentheses
else -- neither of authors and editors set
if (string.sub(tcommon, -1, -1) == sepc) then -- if the last character of tcommon is sepc
Date = " " .. Date .. OrigDate; -- Date does not begin with sepc
else
Date = sepc .. " " .. Date .. OrigDate; -- Date begins with sepc
end
end
end
if utilities.is_set (Authors) then
if (not utilities.is_set (Date)) then -- when date is set it's in parentheses; no Authors termination
Authors = terminate_name_list (Authors, sepc); -- when no date, terminate with 0 or 1 sepc and a space
end
if utilities.is_set (Editors) then
local in_text = '';
local post_text = '';
if utilities.is_set (Chapter) and 0 == #c then
in_text = cfg.messages['in'] .. ' ';
if (sepc ~= '.') then
in_text = in_text:lower(); -- lowercase for cs2
end
end
if EditorCount <= 1 then
post_text = ' (' .. cfg.messages['editor'] .. ')'; -- be consistent with no-author, no-date case
else
post_text = ' (' .. cfg.messages['editors'] .. ')';
end
Editors = terminate_name_list (in_text .. Editors .. post_text, sepc); -- terminate with 0 or 1 sepc and a space
end
if utilities.is_set (Contributors) then -- book cite and we're citing the intro, preface, etc.
local by_text = sepc .. ' ' .. cfg.messages['by'] .. ' ';
if (sepc ~= '.') then by_text = by_text:lower() end -- lowercase for cs2
Authors = by_text .. Authors; -- author follows title so tweak it here
if utilities.is_set (Editors) and utilities.is_set (Date) then -- when Editors make sure that Authors gets terminated
Authors = terminate_name_list (Authors, sepc); -- terminate with 0 or 1 sepc and a space
end
if (not utilities.is_set (Date)) then -- when date is set it's in parentheses; no Contributors termination
Contributors = terminate_name_list (Contributors, sepc); -- terminate with 0 or 1 sepc and a space
end
text = safe_join( {Contributors, Date, Chapter, tcommon, Authors, Place, Editors, tcommon2, pgtext, idcommon }, sepc );
else
text = safe_join( {Authors, Date, Chapter, Place, Editors, tcommon, pgtext, idcommon }, sepc );
end
elseif utilities.is_set (Editors) then
if utilities.is_set (Date) then
if EditorCount <= 1 then
Editors = Editors .. cfg.presentation['sep_name'] .. cfg.messages['editor'];
else
Editors = Editors .. cfg.presentation['sep_name'] .. cfg.messages['editors'];
end
else
if EditorCount <= 1 then
Editors = Editors .. " (" .. cfg.messages['editor'] .. ")" .. sepc .. " "
else
Editors = Editors .. " (" .. cfg.messages['editors'] .. ")" .. sepc .. " "
end
end
text = safe_join( {Editors, Date, Chapter, Place, tcommon, pgtext, idcommon}, sepc );
else
if utilities.in_array (config.CitationClass, {"journal", "citation"}) and utilities.is_set (Periodical) then
text = safe_join( {Chapter, Place, tcommon, pgtext, Date, idcommon}, sepc );
else
text = safe_join( {Chapter, Place, tcommon, Date, pgtext, idcommon}, sepc );
end
end
if utilities.is_set (PostScript) and PostScript ~= sepc then
text = safe_join( {text, sepc}, sepc ); -- Deals with italics, spaces, etc.
if '.' == sepc then -- remove final seperator if present
text = text:gsub ('%' .. sepc .. '$', ''); -- dot must be escaped here
else
text = mw.ustring.gsub (text, sepc .. '$', ''); -- using ustring for non-dot sepc (likely a non-Latin character)
end
end
text = safe_join( {text, PostScript}, sepc );
-- Now enclose the whole thing in a <cite> element
local options_t = {};
options_t.class = cite_class_attribute_make (config.CitationClass, Mode);
local Ref = is_valid_parameter_value (A['Ref'], A:ORIGIN('Ref'), cfg.keywords_lists['ref'], nil, true); -- nil when |ref=harv; A['Ref'] else
if 'none' ~= cfg.keywords_xlate[(Ref and Ref:lower()) or ''] then
local namelist_t = {}; -- holds selected contributor, author, editor name list
local year = first_set ({Year, anchor_year}, 2); -- Year first for legacy citations and for YMD dates that require disambiguation
if #c > 0 then -- if there is a contributor list
namelist_t = c; -- select it
elseif #a > 0 then -- or an author list
namelist_t = a;
elseif #e > 0 then -- or an editor list
namelist_t = e;
end
local citeref_id;
if #namelist_t > 0 then -- if there are names in namelist_t
citeref_id = make_citeref_id (namelist_t, year); -- go make the CITEREF anchor
if mw.uri.anchorEncode (citeref_id) == ((Ref and mw.uri.anchorEncode (Ref)) or '') then -- Ref may already be encoded (by {{sfnref}}) so citeref_id must be encoded before comparison
utilities.set_message ('maint_ref_duplicates_default');
end
else
citeref_id = ''; -- unset
end
options_t.id = Ref or citeref_id;
end
if string.len (text:gsub('%b<>', '')) <= 2 then -- remove html and html-like tags; then get length of what remains;
z.error_cats_t = {}; -- blank the categories list
z.error_msgs_t = {}; -- blank the error messages list
OCinSoutput = nil; -- blank the metadata string
text = ''; -- blank the the citation
utilities.set_message ('err_empty_citation'); -- set empty citation message and category
end
local render_t = {}; -- here we collect the final bits for concatenation into the rendered citation
if utilities.is_set (options_t.id) then -- here we wrap the rendered citation in <cite ...>...</cite> tags
table.insert (render_t, utilities.substitute (cfg.presentation['cite-id'], {mw.uri.anchorEncode(options_t.id), mw.text.nowiki(options_t.class), text})); -- when |ref= is set or when there is a namelist
else
table.insert (render_t, utilities.substitute (cfg.presentation['cite'], {mw.text.nowiki(options_t.class), text})); -- when |ref=none or when namelist_t empty and |ref= is missing or is empty
end
if OCinSoutput then -- blanked when citation is 'empty' so don't bother to add boilerplate metadata span
table.insert (render_t, utilities.substitute (cfg.presentation['ocins'], OCinSoutput)); -- format and append metadata to the citation
end
local template_name = ('citation' == config.CitationClass) and 'citation' or 'cite ' .. (cfg.citation_class_map_t[config.CitationClass] or config.CitationClass);
local template_link = '[[Template:' .. template_name .. '|' .. template_name .. ']]';
local msg_prefix = '<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));
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
}
swdh50lofyqq0wmp3o2c259fg8s2nxi
વિભાગ:Citation/CS1/Configuration
828
50995
886344
825434
2025-04-19T11:34:27Z
en>Nthep
0
change line 1754 parameter to true to hide ISBN/ date compatibility issues. See [[Help talk:Citation Style 1#ISBN / Date incompatibility]] and [[Wikipedia:Administrators' noticeboard/Incidents#Template edit incorrectly creating error cat with 25000+ entries needs reverting]]
886344
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 = {'/[Ss]andbox', '/[Tt]estcases', '/[^/]*[Ll]og', '/[Aa]rchive'}; -- 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'] = 'Archived from $1 on $2',
['archived-live'] = '$1 from the original on $2',
['archived-unfit'] = 'Archived from the original on ',
['archived'] = 'Archived',
['by'] = 'By', -- contributions to authored works: introduction, foreword, afterword
['cartography'] = 'Cartography by $1',
['editor'] = 'ed.',
['editors'] = 'eds.',
['edition'] = '($1 ed.)',
['episode'] = 'Episode $1',
['et al'] = 'et al.',
['in'] = 'In', -- edited works
['inactive'] = 'inactive',
['inset'] = '$1 inset',
['interview'] = 'Interviewed by $1',
['mismatch'] = '<code class="cs1-code">|$1=</code> / <code class="cs1-code">|$2=</code> mismatch', -- $1 is year param name; $2 is date param name
['newsgroup'] = '[[Usenet newsgroup|Newsgroup]]: $1',
['notitle'] = 'No title', -- for |title=(()) and (in the future) |title=none
['original'] = 'the original',
['origdate'] = ' [$1]',
['published'] = ' (published $1)',
['retrieved'] = 'Retrieved $1',
['season'] = 'Season $1',
['section'] = '§ $1',
['sections'] = '§§ $1',
['series'] = '$1 $2', -- $1 is sepc, $2 is series
['seriesnum'] = 'Series $1',
['translated'] = 'Translated by $1',
['type'] = ' ($1)', -- for titletype
['written'] = 'Written at $1',
['vol'] = '$1 Vol. $2', -- $1 is sepc; bold journal style volume is in presentation{}
['vol-no'] = '$1 Vol. $2, no. $3', -- sepc, volume, issue (alternatively insert $1 after $2, but then we'd also have to change capitalization)
['issue'] = '$1 No. $2', -- $1 is sepc
['art'] = '$1 Art. $2', -- $1 is sepc; for {{cite conference}} only
['vol-art'] = '$1 Vol. $2, art. $3', -- sepc, volume, article-number; for {{cite conference}} only
['j-vol'] = '$1 $2', -- sepc, volume; bold journal volume is in presentation{}
['j-issue'] = ' ($1)',
['j-article-num'] = ' $1', -- TODO: any punctuation here? static text?
['nopp'] = '$1 $2'; -- page(s) without prefix; $1 is sepc
['p-prefix'] = "$1 p. $2", -- $1 is sepc
['pp-prefix'] = "$1 pp. $2", -- $1 is sepc
['j-page(s)'] = ': $1', -- same for page and pages
['sheet'] = '$1 Sheet $2', -- $1 is sepc
['sheets'] = '$1 Sheets $2', -- $1 is sepc
['j-sheet'] = ': Sheet $1',
['j-sheets'] = ': Sheets $1',
['language'] = '(in $1)',
['via'] = " – via $1",
['event'] = 'Event occurs at',
['minutes'] = 'minutes in',
-- Determines the location of the help page
['help page link'] = 'Help:CS1 errors',
['help page label'] = 'help',
-- categories
['cat wikilink'] = '[[Category:$1]]', -- $1 is the category name
['cat wikilink sk'] = '[[Category:$1|$2]]', -- $1 is the category name; $2 is namespace sort key
[':cat wikilink'] = '[[:Category:$1|link]]', -- category name as maintenance message wikilink; $1 is the category name
-- Internal errors (should only occur if configuration is bad)
['undefined_error'] = 'Called with an undefined error condition',
['unknown_ID_key'] = 'Unrecognized ID key: ', -- an ID key in id_handlers not found in ~/Identifiers func_map{}
['unknown_ID_access'] = 'Unrecognized ID access keyword: ', -- an ID access keyword in id_handlers not found in keywords_lists['id-access']{}
['unknown_argument_map'] = 'Argument map not defined for this variable',
['bare_url_no_origin'] = 'Bare URL found but origin indicator is nil or empty',
['warning_msg_e'] = '<span style="color:#d33">One or more <code style="color: inherit; background: inherit; border: none; padding: inherit;">{{$1}}</code> templates have errors</span>; messages may be hidden ([[Help:CS1_errors#Controlling_error_message_display|help]]).'; -- $1 is template link
['warning_msg_m'] = '<span style="color:#3a3">One or more <code style="color: inherit; background: inherit; border: none; padding: inherit;">{{$1}}</code> templates have maintenance messages</span>; messages may be hidden ([[Help:CS1_errors#Controlling_error_message_display|help]]).'; -- $1 is template link
}
--[[--------------------------< C I T A T I O N _ C L A S S _ M A P >------------------------------------------
this table maps the value assigned to |CitationClass= in the cs1|2 templates to the canonical template name when
the value assigned to |CitationClass= is different from the canonical template name. |CitationClass= values are
used as class attributes in the <cite> tag that encloses the citation so these names may not contain spaces while
the canonical template name may. These names are used in warning_msg_e and warning_msg_m to create links to the
template's documentation when an article is displayed in preview mode.
Most cs1|2 template |CitationClass= values at en.wiki match their canonical template names so are not listed here.
]]
local citation_class_map_t = { -- TODO: if kept, these and all other config.CitationClass 'names' require some sort of i18n
['arxiv'] = 'arXiv',
['audio-visual'] = 'AV media',
['AV-media-notes'] = 'AV media notes',
['biorxiv'] = 'bioRxiv',
['citeseerx'] = 'CiteSeerX',
['encyclopaedia'] = 'encyclopedia',
['mailinglist'] = 'mailing list',
['medrxiv'] = 'medRxiv',
['pressrelease'] = 'press release',
['ssrn'] = 'SSRN',
['techreport'] = 'tech report',
}
--[=[-------------------------< E T _ A L _ P A T T E R N S >--------------------------------------------------
This table provides Lua patterns for the phrase "et al" and variants in name text
(author, editor, etc.). The main module uses these to identify and emit the 'etal' message.
]=]
local et_al_patterns = {
"[;,]? *[\"']*%f[%a][Ee][Tt]%.? *[Aa][Ll][%.;,\"']*$", -- variations on the 'et al' theme
"[;,]? *[\"']*%f[%a][Ee][Tt]%.? *[Aa][Ll][Ii][AaIi][Ee]?[%.;,\"']*$", -- variations on the 'et alia', 'et alii' and 'et aliae' themes (false positive 'et aliie' unlikely to match)
"[;,]? *%f[%a]and [Oo]thers", -- an alternative to et al.
"%[%[ *[Ee][Tt]%.? *[Aa][Ll]%.? *%]%]", -- a wikilinked form
"%(%( *[Ee][Tt]%.? *[Aa][Ll]%.? *%)%)", -- a double-bracketed form (to counter partial removal of ((...)) syntax)
"[%(%[] *[Ee][Tt]%.? *[Aa][Ll]%.? *[%)%]]", -- a bracketed form
}
--[[--------------------------< P R E S E N T A T I O N >------------------------
Fixed presentation markup. Originally part of citation_config.messages it has
been moved into its own, more semantically correct place.
]]
local presentation =
{
-- .citation-comment class is specified at Help:CS1_errors#Controlling_error_message_display
['hidden-error'] = '<span class="cs1-hidden-error citation-comment">$1</span>',
['visible-error'] = '<span class="cs1-visible-error citation-comment">$1</span>',
['hidden-maint'] = '<span class="cs1-maint citation-comment">$1</span>',
['accessdate'] = '<span class="reference-accessdate">$1$2</span>', -- to allow editors to hide accessdate using personal CSS
['bdi'] = '<bdi$1>$2</bdi>', -- bidirectional isolation used with |script-title= and the like
['cite'] = '<cite class="$1">$2</cite>'; -- for use when citation does not have a namelist and |ref= not set so no id="..." attribute
['cite-id'] = '<cite id="$1" class="$2">$3</cite>'; -- for use when when |ref= is set or when citation has a namelist
['format'] = ' <span class="cs1-format">($1)</span>', -- for |format=, |chapter-format=, etc.
['interwiki'] = ' <span class="cs1-format">[in $1]</span>', -- for interwiki-language-linked author, editor, etc
['interproj'] = ' <span class="cs1-format">[at $1]</span>', -- for interwiki-project-linked author, editor, etc (:d: and :s: supported; :w: ignored)
-- various access levels, for |access=, |doi-access=, |arxiv=, ...
-- narrow no-break space   may work better than nowrap CSS. Or not? Browser support?
['ext-link-access-signal'] = '<span class="$1" title="$2">$3</span>', -- external link with appropriate lock icon
['free'] = {class='id-lock-free', title='Freely accessible'}, -- classes defined in Module:Citation/CS1/styles.css
['registration'] = {class='id-lock-registration', title='Free registration required'},
['limited'] = {class='id-lock-limited', title='Free access subject to limited trial, subscription normally required'},
['subscription'] = {class='id-lock-subscription', title='Paid subscription required'},
['interwiki-icon'] = '<span class="$1" title="$2">$3</span>',
['class-wikisource'] = 'cs1-ws-icon',
['italic-title'] = "''$1''",
['kern-left'] = '<span class="cs1-kern-left"></span>$1', -- spacing to use when title contains leading single or double quote mark
['kern-right'] = '$1<span class="cs1-kern-right"></span>', -- spacing to use when title contains trailing single or double quote mark
['nowrap1'] = '<span class="nowrap">$1</span>', -- for nowrapping an item: <span ...>yyyy-mm-dd</span>
['nowrap2'] = '<span class="nowrap">$1</span> $2', -- for nowrapping portions of an item: <span ...>dd mmmm</span> yyyy (note white space)
['ocins'] = '<span title="$1" class="Z3988"></span>',
['parameter'] = '<code class="cs1-code">|$1=</code>',
['ps_cs1'] = '.'; -- CS1 style postscript (terminal) character
['ps_cs2'] = ''; -- CS2 style postscript (terminal) character (empty string)
['quoted-text'] = '<q>$1</q>', -- for wrapping |quote= content
['quoted-title'] = '"$1"',
['sep_cs1'] = '.', -- CS1 element separator
['sep_cs2'] = ',', -- CS2 separator
['sep_nl'] = ';', -- CS1|2 style name-list separator between names is a semicolon
['sep_nl_and'] = ' and ', -- used as last nl sep when |name-list-style=and and list has 2 items
['sep_nl_end'] = '; and ', -- used as last nl sep when |name-list-style=and and list has 3+ names
['sep_name'] = ', ', -- CS1|2 style last/first separator is <comma><space>
['sep_nl_vanc'] = ',', -- Vancouver style name-list separator between authors is a comma
['sep_name_vanc'] = ' ', -- Vancouver style last/first separator is a space
['sep_list'] = ', ', -- used for |language= when list has 3+ items except for last sep which uses sep_list_end
['sep_list_pair'] = ' and ', -- used for |language= when list has 2 items
['sep_list_end'] = ', and ', -- used as last list sep for |language= when list has 3+ items
['trans-italic-title'] = "[''$1'']",
['trans-quoted-title'] = "[$1]", -- for |trans-title= and |trans-quote=
['vol-bold'] = '$1 <b>$2</b>', -- sepc, volume; for bold journal cites; for other cites ['vol'] in messages{}
}
--[[--------------------------< A L I A S E S >---------------------------------
Aliases table for commonly passed parameters.
Parameter names on the right side in the assignments in this table must have been
defined in the Whitelist before they will be recognized as valid parameter names
]]
local aliases = {
['AccessDate'] = {'access-date', 'accessdate'}, -- Used by InternetArchiveBot
['Agency'] = 'agency',
['ArchiveDate'] = {'archive-date', 'archivedate'}, -- Used by InternetArchiveBot
['ArchiveFormat'] = 'archive-format',
['ArchiveURL'] = {'archive-url', 'archiveurl'}, -- Used by InternetArchiveBot
['ArticleNumber'] = 'article-number',
['ASINTLD'] = 'asin-tld',
['At'] = 'at', -- Used by InternetArchiveBot
['Authors'] = {'people', 'credits'},
['BookTitle'] = {'book-title', 'booktitle'},
['Cartography'] = 'cartography',
['Chapter'] = {'chapter', 'contribution', 'entry', 'article', 'section'},
['ChapterFormat'] = {'chapter-format', 'contribution-format', 'entry-format',
'article-format', 'section-format'};
['ChapterURL'] = {'chapter-url', 'contribution-url', 'entry-url', 'article-url', 'section-url'}, -- Used by InternetArchiveBot
['ChapterUrlAccess'] = {'chapter-url-access', 'contribution-url-access',
'entry-url-access', 'article-url-access', 'section-url-access'}, -- Used by InternetArchiveBot
['Class'] = 'class', -- cite arxiv and arxiv identifier
['Collaboration'] = 'collaboration',
['Conference'] = {'conference', 'event'},
['ConferenceFormat'] = 'conference-format',
['ConferenceURL'] = 'conference-url', -- Used by InternetArchiveBot
['Date'] = {'date', 'air-date', 'airdate'}, -- air-date and airdate for cite episode and cite serial only
['Degree'] = 'degree',
['DF'] = 'df',
['DisplayAuthors'] = {'display-authors', 'display-subjects'},
['DisplayContributors'] = 'display-contributors',
['DisplayEditors'] = 'display-editors',
['DisplayInterviewers'] = 'display-interviewers',
['DisplayTranslators'] = 'display-translators',
['Docket'] = 'docket',
['DoiBroken'] = 'doi-broken-date',
['Edition'] = 'edition',
['Embargo'] = 'pmc-embargo-date',
['Encyclopedia'] = {'encyclopedia', 'encyclopaedia', 'dictionary'}, -- cite encyclopedia only
['Episode'] = 'episode', -- cite serial only TODO: make available to cite episode?
['Format'] = 'format',
['ID'] = {'id', 'ID'},
['Inset'] = 'inset',
['Issue'] = {'issue', 'number'},
['Language'] = {'language', 'lang'},
['MailingList'] = {'mailing-list', 'mailinglist'}, -- cite mailing list only
['Map'] = 'map', -- cite map only
['MapFormat'] = 'map-format', -- cite map only
['MapURL'] = {'map-url', 'mapurl'}, -- cite map only -- Used by InternetArchiveBot
['MapUrlAccess'] = 'map-url-access', -- cite map only -- Used by InternetArchiveBot
['Minutes'] = 'minutes',
['Mode'] = 'mode',
['NameListStyle'] = 'name-list-style',
['Network'] = 'network',
['Newsgroup'] = 'newsgroup', -- cite newsgroup only
['NoPP'] = {'no-pp', 'nopp'},
['NoTracking'] = {'no-tracking', 'template-doc-demo'},
['Number'] = 'number', -- this case only for cite techreport
['OrigDate'] = {'orig-date', 'orig-year', 'origyear'},
['Others'] = 'others',
['Page'] = {'page', 'p'}, -- Used by InternetArchiveBot
['Pages'] = {'pages', 'pp'}, -- Used by InternetArchiveBot
['Periodical'] = {'journal', 'magazine', 'newspaper', 'periodical', 'website', 'work'},
['Place'] = {'place', 'location'},
['PostScript'] = 'postscript',
['PublicationDate'] = {'publication-date', 'publicationdate'},
['PublicationPlace'] = {'publication-place', 'publicationplace'},
['PublisherName'] = {'publisher', 'institution'},
['Quote'] = {'quote', 'quotation'},
['QuotePage'] = 'quote-page',
['QuotePages'] = 'quote-pages',
['Ref'] = 'ref',
['Scale'] = 'scale',
['ScriptChapter'] = {'script-chapter', 'script-contribution', 'script-entry',
'script-article', 'script-section'},
['ScriptEncyclopedia'] = {'script-encyclopedia', 'script-encyclopaedia'}, -- cite encyclopedia only
['ScriptMap'] = 'script-map',
['ScriptPeriodical'] = {'script-journal', 'script-magazine', 'script-newspaper',
'script-periodical', 'script-website', 'script-work'},
['ScriptQuote'] = 'script-quote',
['ScriptTitle'] = 'script-title', -- Used by InternetArchiveBot
['Season'] = 'season',
['Sections'] = 'sections', -- cite map only
['Series'] = {'series', 'version'},
['SeriesLink'] = {'series-link', 'serieslink'},
['SeriesNumber'] = {'series-number', 'series-no'},
['Sheet'] = 'sheet', -- cite map only
['Sheets'] = 'sheets', -- cite map only
['Station'] = 'station',
['Time'] = 'time',
['TimeCaption'] = 'time-caption',
['Title'] = 'title', -- Used by InternetArchiveBot
['TitleLink'] = {'title-link', 'episode-link', 'episodelink'}, -- Used by InternetArchiveBot
['TitleNote'] = {'title-note', 'department'},
['TitleType'] = {'type', 'medium'},
['TransChapter'] = {'trans-article', 'trans-chapter', 'trans-contribution',
'trans-entry', 'trans-section'},
['Transcript'] = 'transcript',
['TranscriptFormat'] = 'transcript-format',
['TranscriptURL'] = 'transcript-url', -- Used by InternetArchiveBot
['TransEncyclopedia'] = {'trans-encyclopedia', 'trans-encyclopaedia'}, -- cite encyclopedia only
['TransMap'] = 'trans-map', -- cite map only
['TransPeriodical'] = {'trans-journal', 'trans-magazine', 'trans-newspaper',
'trans-periodical', 'trans-website', 'trans-work'},
['TransQuote'] = 'trans-quote',
['TransTitle'] = 'trans-title', -- Used by InternetArchiveBot
['URL'] = {'url', 'URL'}, -- Used by InternetArchiveBot
['UrlAccess'] = 'url-access', -- Used by InternetArchiveBot
['UrlStatus'] = 'url-status', -- Used by InternetArchiveBot
['Vauthors'] = 'vauthors',
['Veditors'] = 'veditors',
['Via'] = 'via',
['Volume'] = 'volume',
['Year'] = 'year',
['AuthorList-First'] = {"first#", "author-first#", "author#-first", "author-given#", "author#-given",
"subject-first#", "subject#-first", "subject-given#", "subject#-given",
"given#"},
['AuthorList-Last'] = {"last#", "author-last#", "author#-last", "author-surname#", "author#-surname",
"subject-last#", "subject#-last", "subject-surname#", "subject#-surname",
"author#", 'host#', "subject#", "surname#"},
['AuthorList-Link'] = {"author-link#", "author#-link", "subject-link#",
"subject#-link", "authorlink#", "author#link"},
['AuthorList-Mask'] = {"author-mask#", "author#-mask", "subject-mask#", "subject#-mask"},
['ContributorList-First'] = {'contributor-first#', 'contributor#-first',
'contributor-given#', 'contributor#-given'},
['ContributorList-Last'] = {'contributor-last#', 'contributor#-last',
'contributor-surname#', 'contributor#-surname', 'contributor#'},
['ContributorList-Link'] = {'contributor-link#', 'contributor#-link'},
['ContributorList-Mask'] = {'contributor-mask#', 'contributor#-mask'},
['EditorList-First'] = {"editor-first#", "editor#-first", "editor-given#", "editor#-given"},
['EditorList-Last'] = {"editor-last#", "editor#-last", "editor-surname#",
"editor#-surname", "editor#"},
['EditorList-Link'] = {"editor-link#", "editor#-link"},
['EditorList-Mask'] = {"editor-mask#", "editor#-mask"},
['InterviewerList-First'] = {'interviewer-first#', 'interviewer#-first',
'interviewer-given#', 'interviewer#-given'},
['InterviewerList-Last'] = {'interviewer-last#', 'interviewer#-last',
'interviewer-surname#', 'interviewer#-surname', 'interviewer#'},
['InterviewerList-Link'] = {'interviewer-link#', 'interviewer#-link'},
['InterviewerList-Mask'] = {'interviewer-mask#', 'interviewer#-mask'},
['TranslatorList-First'] = {'translator-first#', 'translator#-first',
'translator-given#', 'translator#-given'},
['TranslatorList-Last'] = {'translator-last#', 'translator#-last',
'translator-surname#', 'translator#-surname', 'translator#'},
['TranslatorList-Link'] = {'translator-link#', 'translator#-link'},
['TranslatorList-Mask'] = {'translator-mask#', 'translator#-mask'},
}
--[[--------------------------< P U N C T _ S K I P >---------------------------
builds a table of parameter names that the extraneous terminal punctuation check should not check.
]]
local punct_meta_params = { -- table of aliases[] keys (meta parameters); each key has a table of parameter names for a value
'BookTitle', 'Chapter', 'ScriptChapter', 'ScriptTitle', 'Title', 'TransChapter', 'Transcript', 'TransMap', 'TransTitle', -- title-holding parameters
'AuthorList-Mask', 'ContributorList-Mask', 'EditorList-Mask', 'InterviewerList-Mask', 'TranslatorList-Mask', -- name-list mask may have name separators
'PostScript', 'Quote', 'ScriptQuote', 'TransQuote', 'Ref', -- miscellaneous
'ArchiveURL', 'ChapterURL', 'ConferenceURL', 'MapURL', 'TranscriptURL', 'URL', -- URL-holding parameters
}
local url_meta_params = { -- table of aliases[] keys (meta parameters); each key has a table of parameter names for a value
'ArchiveURL', 'ChapterURL', 'ConferenceURL', 'ID', 'MapURL', 'TranscriptURL', 'URL', -- parameters allowed to hold urls
'Page', 'Pages', 'At', 'QuotePage', 'QuotePages', -- insource locators allowed to hold urls
}
local function build_skip_table (skip_t, meta_params)
for _, meta_param in ipairs (meta_params) do -- for each meta parameter key
local params = aliases[meta_param]; -- get the parameter or the table of parameters associated with the meta parameter name
if 'string' == type (params) then
skip_t[params] = 1; -- just a single parameter
else
for _, param in ipairs (params) do -- get the parameter name
skip_t[param] = 1; -- add the parameter name to the skip table
local count;
param, count = param:gsub ('#', ''); -- remove enumerator marker from enumerated parameters
if 0 ~= count then -- if removed
skip_t[param] = 1; -- add param name without enumerator marker
end
end
end
end
return skip_t;
end
local punct_skip = {};
local url_skip = {};
--[[--------------------------< S I N G L E - L E T T E R S E C O N D - L E V E L D O M A I N S >----------
this is a list of tlds that are known to have single-letter second-level domain names. This list does not include
ccTLDs which are accepted in is_domain_name().
]]
local single_letter_2nd_lvl_domains_t = {'cash', 'company', 'foundation', 'media', 'org', 'today'};
--[[-----------< S P E C I A L C A S E T R A N S L A T I O N S >------------
This table is primarily here to support internationalization. Translations in
this table are used, for example, when an error message, category name, etc.,
is extracted from the English alias key. There may be other cases where
this translation table may be useful.
]]
local is_Latn = 'A-Za-z\195\128-\195\150\195\152-\195\182\195\184-\198\191\199\132-\201\143\225\184\128-\225\187\191';
local special_case_translation = {
['AuthorList'] = 'authors list', -- used to assemble maintenance category names
['ContributorList'] = 'contributors list', -- translation of these names plus translation of the base maintenance category names in maint_cats{} table below
['EditorList'] = 'editors list', -- must match the names of the actual categories
['InterviewerList'] = 'interviewers list', -- this group or translations used by name_has_ed_markup() and name_has_mult_names()
['TranslatorList'] = 'translators list',
-- 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'] = nil, -- 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'] = nil},
{['en'] = {'page not found', true}, ['local'] = nil},
{['en'] = {'subscribe to read', true}, ['local'] = nil},
{['en'] = {'^[%(%[{<]?unknown[>}%]%)]?$', false}, ['local'] = nil},
{['en'] = {'website is for sale', true}, ['local'] = nil},
{['en'] = {'^404', false}, ['local'] = nil},
{['en'] = {'error[ %-]404', false}, ['local'] = nil},
{['en'] = {'internet archive wayback machine', true}, ['local'] = nil},
{['en'] = {'log into facebook', true}, ['local'] = nil},
{['en'] = {'login • instagram', true}, ['local'] = nil},
{['en'] = {'redirecting...', true}, ['local'] = nil},
{['en'] = {'usurped title', true}, ['local'] = nil}, -- 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'] = nil},
},
['reject'] = {
{['en'] = {'about us', true}, ['local'] = nil},
{['en'] = {'%f[%a][Aa]dvisor%f[%A]', false}, ['local'] = nil},
{['en'] = {'allmusic', true}, ['local'] = nil},
{['en'] = {'%f[%a][Aa]uthor%f[%A]', false}, ['local'] = nil},
{['en'] = {'^[Bb]ureau$', false}, ['local'] = nil},
{['en'] = {'business', true}, ['local'] = nil},
{['en'] = {'cnn', true}, ['local'] = nil},
{['en'] = {'collaborator', true}, ['local'] = nil},
{['en'] = {'^[Cc]ompany$', false}, ['local'] = nil},
{['en'] = {'contributor', true}, ['local'] = nil},
{['en'] = {'contact us', true}, ['local'] = nil},
{['en'] = {'correspondent', true}, ['local'] = nil},
{['en'] = {'^[Dd]esk$', false}, ['local'] = nil},
{['en'] = {'directory', true}, ['local'] = nil},
{['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'] = nil},
{['en'] = {'%f[%a][Ee]ditors?%f[%A]', false}, ['local'] = nil},
{['en'] = {'%f[%a][Ee]mail%f[%A]', false}, ['local'] = nil},
{['en'] = {'facebook', true}, ['local'] = nil},
{['en'] = {'google', true}, ['local'] = nil},
{['en'] = {'^[Gg]roup$', false}, ['local'] = nil},
{['en'] = {'home page', true}, ['local'] = nil},
{['en'] = {'^[Ii]nc%.?$', false}, ['local'] = nil},
{['en'] = {'instagram', true}, ['local'] = nil},
{['en'] = {'interviewer', true}, ['local'] = nil},
{['en'] = {'^[Ll]imited$', false}, ['local'] = nil},
{['en'] = {'linkedIn', true}, ['local'] = nil},
{['en'] = {'^[Nn]ews$', false}, ['local'] = nil},
{['en'] = {'[Nn]ews[ %-]?[Rr]oom', false}, ['local'] = nil},
{['en'] = {'pinterest', true}, ['local'] = nil},
{['en'] = {'policy', true}, ['local'] = nil},
{['en'] = {'privacy', true}, ['local'] = nil},
{['en'] = {'reuters', true}, ['local'] = nil},
{['en'] = {'translator', true}, ['local'] = nil},
{['en'] = {'tumblr', true}, ['local'] = nil},
{['en'] = {'twitter', true}, ['local'] = nil},
{['en'] = {'site name', true}, ['local'] = nil},
{['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'] = nil},
{['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'] = {['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},
},
['inv_local_long'] = {}, -- used in date reformatting & translation; copy of date_names['local'].long where k/v are inverted: [1]='<local name>' etc.
['inv_local_short'] = {}, -- used in date reformatting & translation; copy of date_names['local'].short where k/v are inverted: [1]='<local name>' etc.
['inv_local_quarter'] = {}, -- used in date translation; copy of date_names['local'].quarter where k/v are inverted: [1]='<local name>' etc.
['inv_local_season'] = {}, -- used in date translation; copy of date_names['local'].season where k/v are inverted: [1]='<local name>' etc.
['inv_local_named'] = {}, -- used in date translation; copy of date_names['local'].named where k/v are inverted: [1]='<local name>' etc.
['local_digits'] = {['0'] = '0', ['1'] = '1', ['2'] = '2', ['3'] = '3', ['4'] = '4', ['5'] = '5', ['6'] = '6', ['7'] = '7', ['8'] = '8', ['9'] = '9'}, -- used to convert local language digits to Western 0-9
['xlate_digits'] = {},
}
if local_date_names_from_mediawiki then -- if fetching local month names from MediaWiki is enabled
local long_t = {};
local short_t = {};
for i=1, 12 do -- loop 12x and
local name = lang_obj:formatDate('F', '2022-' .. i .. '-1'); -- get long month name for each i
long_t[name] = i; -- save it
name = lang_obj:formatDate('M', '2022-' .. i .. '-1'); -- get short month name for each i
short_t[name] = i; -- save it
end
date_names['local']['long'] = long_t; -- write the long table – overwrites manual translation
date_names['local']['short'] = short_t; -- write the short table – overwrites manual translation
end
-- create inverted date-name tables for reformatting and/or translation
for _, invert_t in pairs {{'long', 'inv_local_long'}, {'short', 'inv_local_short'}, {'quarter', 'inv_local_quarter'}, {'season', 'inv_local_season'}, {'named', 'inv_local_named'}} do
for name, i in pairs (date_names['local'][invert_t[1]]) do -- this table is ['name'] = i
date_names[invert_t[2]][i] = name; -- invert to get [i] = 'name' for conversions from ymd
end
end
if local_digits_from_mediawiki then -- if fetching local digits from MediaWiki is enabled
local digits_t = {};
for i=0, 9 do -- loop 10x and
digits_t [lang_obj:formatNum (i)] = tostring (i); -- format the loop indexer as local lang table index and assign loop indexer (a string) as the value
end
date_names['local_digits'] = digits_t;
end
for ld, ed in pairs (date_names.local_digits) do -- make a digit translation table for simple date translation from en to local language using local_digits table
date_names.xlate_digits [ed] = ld; -- en digit becomes index with local digit as the value
end
local df_template_patterns = { -- table of redirects to {{Use dmy dates}} and {{Use mdy dates}}
'{{ *[Uu]se +(dmy) +dates *[|}]', -- 1159k -- sorted by approximate transclusion count
'{{ *[Uu]se +(mdy) +dates *[|}]', -- 212k
'{{ *[Uu]se +(MDY) +dates *[|}]', -- 788
'{{ *[Uu]se +(DMY) +dates *[|}]', -- 343
'{{ *([Mm]dy) *[|}]', -- 176
'{{ *[Uu]se *(dmy) *[|}]', -- 156 + 18
'{{ *[Uu]se *(mdy) *[|}]', -- 149 + 11
'{{ *([Dd]my) *[|}]', -- 56
'{{ *[Uu]se +(MDY) *[|}]', -- 5
'{{ *([Dd]MY) *[|}]', -- 3
'{{ *[Uu]se(mdy)dates *[|}]', -- 1
'{{ *[Uu]se +(DMY) *[|}]', -- 0
'{{ *([Mm]DY) *[|}]', -- 0
}
local title_object = mw.title.getCurrentTitle();
local content; -- done this way so that unused templates appear in unused-template-reports; self-transcluded makes them look like they are used
if 10 ~= title_object.namespace then -- all namespaces except Template
content = title_object:getContent() or ''; -- get the content of the article or ''; new pages edited w/ve do not have 'content' until saved; ve does not preview; phab:T221625
end
local function get_date_format ()
if not content then -- nil content when we're in template
return nil; -- auto-formatting does not work in Template space so don't set global_df
end
for _, pattern in ipairs (df_template_patterns) do -- loop through the patterns looking for {{Use dmy dates}} or {{Use mdy dates}} or any of their redirects
local start, _, match = content:find(pattern); -- match is the three letters indicating desired date format
if match then
local use_dates_template = content:match ('%b{}', start); -- get the whole template
if use_dates_template:match ('| *cs1%-dates *= *[lsy][sy]?') then -- look for |cs1-dates=publication date length access-/archive-date length
return match:lower() .. '-' .. use_dates_template:match ('| *cs1%-dates *= *([lsy][sy]?)');
else
return match:lower() .. '-all'; -- no |cs1-dates= k/v pair; return value appropriate for use in |df=
end
end
end
end
local global_df; -- TODO: add this to <global_cs1_config_t>?
--[[-----------------< V O L U M E , I S S U E , P A G E S >------------------
These tables hold cite class values (from the template invocation) and identify those templates that support
|volume=, |issue=, and |page(s)= parameters. Cite conference and cite map require further qualification which
is handled in the main module.
]]
local templates_using_volume = {'citation', 'audio-visual', 'book', 'conference', 'encyclopaedia', 'interview', 'journal', 'magazine', 'map', 'news', 'report', 'techreport', 'thesis'}
local templates_using_issue = {'citation', 'conference', 'episode', 'interview', 'journal', 'magazine', 'map', 'news', 'podcast'}
local templates_not_using_page = {'audio-visual', 'episode', 'mailinglist', 'newsgroup', 'podcast', 'serial', 'sign', 'speech'}
--[[
These tables control when it is appropriate for {{citation}} to render |volume= and/or |issue=. The parameter
names in the tables constrain {{citation}} so that its renderings match the renderings of the equivalent cs1
templates. For example, {{cite web}} does not support |volume= so the equivalent {{citation |website=...}} must
not support |volume=.
]]
local citation_no_volume_t = { -- {{citation}} does not render |volume= when these parameters are used
'website', 'mailinglist', 'script-website',
}
local citation_issue_t = { -- {{citation}} may render |issue= when these parameters are used
'journal', 'magazine', 'newspaper', 'periodical', 'work',
'script-journal', 'script-magazine', 'script-newspaper', 'script-periodical', 'script-work',
}
--[[
Patterns for finding extra text in |volume=, |issue=, |page=, |pages=
]]
local vol_iss_pg_patterns = {
good_ppattern = '^P[^%.PpGg]', -- OK to begin with uppercase P: P7 (page 7 of section P), but not p123 (page 123); TODO: this allows 'Pages' which it should not
bad_ppatterns = { -- patterns for |page= and |pages=
'^[Pp][PpGg]?%.?[ %d]',
'^[Pp][Pp]?%. ', -- from {{p.}} and {{pp.}} templates
'^[Pp]ages?',
'^[Pp]gs.?',
},
vi_patterns_t = { -- combined to catch volume-like text in |issue= and issue-like text in |volume=
'^volumes?', -- volume-like text
'^vols?[%.:=]?',
'^issues?', --issue-like text
'^iss[%.:=]?',
'^numbers?',
'^nos?%A', -- don't match 'november' or 'nostradamus'
'^nr[%.:=]?',
'^n[%.:= ]', -- might be a valid issue without separator (space char is sep char here)
'^n°', -- 'n' with degree sign (U+00B0)
'^№', -- precomposed unicode numero character (U+2116)
},
}
--[[--------------------------< K E Y W O R D S >-------------------------------
These tables hold keywords for those parameters that have defined sets of acceptable keywords.
]]
--[[-------------------< K E Y W O R D S T A B L E >--------------------------
this is a list of keywords; each key in the list is associated with a table of
synonymous keywords possibly from different languages.
for I18N: add local-language keywords to value table; do not change the key.
For example, adding the German keyword 'ja':
['affirmative'] = {'yes', 'true', 'y', 'ja'},
Because CS1|2 templates from en.wiki articles are often copied to other local wikis,
it is recommended that the English keywords remain in these tables.
]]
local keywords = {
['amp'] = {'&', 'amp', 'ampersand'}, -- |name-list-style=
['and'] = {'and', 'serial'}, -- |name-list-style=
['affirmative'] = {'yes', 'true', 'y'}, -- |no-tracking=, |no-pp= -- Used by InternetArchiveBot
['afterword'] = {'afterword'}, -- |contribution=
['bot: unknown'] = {'bot: unknown'}, -- |url-status= -- Used by InternetArchiveBot
['cs1'] = {'cs1'}, -- |mode=
['cs2'] = {'cs2'}, -- |mode=
['dead'] = {'dead', 'deviated'}, -- |url-status= -- Used by InternetArchiveBot
['dmy'] = {'dmy'}, -- |df=
['dmy-all'] = {'dmy-all'}, -- |df=
['foreword'] = {'foreword'}, -- |contribution=
['free'] = {'free'}, -- |<id>-access= -- Used by InternetArchiveBot
['harv'] = {'harv'}, -- |ref=; this no longer supported; is_valid_parameter_value() called with <invert> = true
['introduction'] = {'introduction'}, -- |contribution=
['limited'] = {'limited'}, -- |url-access= -- Used by InternetArchiveBot
['live'] = {'live'}, -- |url-status= -- Used by InternetArchiveBot
['mdy'] = {'mdy'}, -- |df=
['mdy-all'] = {'mdy-all'}, -- |df=
['none'] = {'none'}, -- |postscript=, |ref=, |title=, |type= -- Used by InternetArchiveBot
['off'] = {'off'}, -- |title= (potentially also: |title-link=, |postscript=, |ref=, |type=)
['preface'] = {'preface'}, -- |contribution=
['registration'] = {'registration'}, -- |url-access= -- Used by InternetArchiveBot
['subscription'] = {'subscription'}, -- |url-access= -- Used by InternetArchiveBot
['unfit'] = {'unfit'}, -- |url-status= -- Used by InternetArchiveBot
['usurped'] = {'usurped'}, -- |url-status= -- Used by InternetArchiveBot
['vanc'] = {'vanc'}, -- |name-list-style=
['ymd'] = {'ymd'}, -- |df=
['ymd-all'] = {'ymd-all'}, -- |df=
-- ['yMd'] = {'yMd'}, -- |df=; not supported at en.wiki
-- ['yMd-all'] = {'yMd-all'}, -- |df=; not supported at en.wiki
}
--[[------------------------< X L A T E _ K E Y W O R D S >---------------------
this function builds a list, keywords_xlate{}, of the keywords found in keywords{} where the values from keywords{}
become the keys in keywords_xlate{} and the keys from keywords{} become the values in keywords_xlate{}:
['affirmative'] = {'yes', 'true', 'y'}, -- in keywords{}
becomes
['yes'] = 'affirmative', -- in keywords_xlate{}
['true'] = 'affirmative',
['y'] = 'affirmative',
the purpose of this function is to act as a translator between a non-English keyword and its English equivalent
that may be used in other modules of this suite
]]
local function xlate_keywords ()
local out_table = {}; -- output goes here
for k, keywords_t in pairs (keywords) do -- spin through the keywords table
for _, keyword in ipairs (keywords_t) do -- for each keyword
out_table[keyword] = k; -- create an entry in the output table where keyword is the key
end
end
return out_table;
end
local keywords_xlate = xlate_keywords (); -- the list of translated keywords
--[[----------------< M A K E _ K E Y W O R D S _ L I S T >---------------------
this function assembles, for parameter-value validation, the list of keywords appropriate to that parameter.
keywords_lists{}, is a table of tables from keywords{}
]]
local function make_keywords_list (keywords_lists)
local out_table = {}; -- output goes here
for _, keyword_list in ipairs (keywords_lists) do -- spin through keywords_lists{} and get a table of keywords
for _, keyword in ipairs (keyword_list) do -- spin through keyword_list{} and add each keyword, ...
table.insert (out_table, keyword); -- ... as plain text, to the output list
end
end
return out_table;
end
--[[----------------< K E Y W O R D S _ L I S T S >-----------------------------
this is a list of lists of valid keywords for the various parameters in [key].
Generally the keys in this table are the canonical en.wiki parameter names though
some are contrived because of use in multiple differently named parameters:
['yes_true_y'], ['id-access'].
The function make_keywords_list() extracts the individual keywords from the
appropriate list in keywords{}.
The lists in this table are used to validate the keyword assignment for the
parameters named in this table's keys.
]]
local keywords_lists = {
['yes_true_y'] = make_keywords_list ({keywords.affirmative}),
['contribution'] = make_keywords_list ({keywords.afterword, keywords.foreword, keywords.introduction, keywords.preface}),
['df'] = make_keywords_list ({keywords.dmy, keywords['dmy-all'], keywords.mdy, keywords['mdy-all'], keywords.ymd, keywords['ymd-all']}),
-- ['df'] = make_keywords_list ({keywords.dmy, keywords['dmy-all'], keywords.mdy, keywords['mdy-all'], keywords.ymd, keywords['ymd-all'], keywords.yMd, keywords['yMd-all']}), -- not supported at en.wiki
['mode'] = make_keywords_list ({keywords.cs1, keywords.cs2}),
['name-list-style'] = make_keywords_list ({keywords.amp, keywords['and'], keywords.vanc}),
['ref'] = make_keywords_list ({keywords.harv}), -- inverted check; |ref=harv no longer supported
['url-access'] = make_keywords_list ({keywords.subscription, keywords.limited, keywords.registration}),
['url-status'] = make_keywords_list ({keywords.dead, keywords.live, keywords.unfit, keywords.usurped, keywords['bot: unknown']}),
['id-access'] = make_keywords_list ({keywords.free}),
}
--[[--------------------------< C S 1 _ C O N F I G _ G E T >--------------------------------------------------
fetch and validate values from {{cs1 config}} template to fill <global_cs1_config_t>
no error messages; when errors are detected, the parameter value from {{cs1 config}} is blanked.
Supports all parameters and aliases associated with the metaparameters: DisplayAuthors, DisplayContributors,
DisplayEditors, DisplayInterviewers, DisplayTranslators, NameListStyle, and Mode. The DisplayWhatever metaparameters
accept numeric values only (|display-authors=etal and the like is not supported).
]]
local global_cs1_config_t = {}; -- TODO: add value returned from get_date_format() to this table?
local function get_cs1_config ()
if not content then -- nil content when we're in template
return nil; -- auto-formatting does not work in Template space so don't set global_df
end
local start = content:find('{{ *[Cc][Ss]1 config *[|}]'); -- <start> is offset into <content> when {{cs1 config}} found; nil else
if start then
local cs1_config_template = content:match ('%b{}', start); -- get the whole template
if not cs1_config_template then
return nil;
end
local params_t = mw.text.split (cs1_config_template:gsub ('^{{%s*', ''):gsub ('%s*}}$', ''), '%s*|%s*'); -- remove '{{' and '}}'; make a sequence of parameter/value pairs (split on the pipe)
table.remove (params_t, 1); -- remove the template name because it isn't a parameter/value pair
local config_meta_params_t = {'DisplayAuthors', 'DisplayContributors', 'DisplayEditors', 'DisplayInterviewers', 'DisplayTranslators', 'NameListStyle', 'Mode'};
local meta_param_map_t = {}; -- list of accepted parameter names usable in {{cs1 config}} goes here
for _, meta_param in ipairs (config_meta_params_t) do -- for i18n using <config_meta_params_t>, map template parameter names to their metaparameter equivalents
if 'table' == type (aliases[meta_param]) then -- if <meta_param> is a sequence,
for _, param in ipairs (aliases[meta_param]) do -- extract its contents
meta_param_map_t[param] = meta_param; -- and add to <meta_param_map_t>
end
else
meta_param_map_t[aliases[meta_param]] = meta_param; -- not a sequence so just add the parameter to <meta_param_map_t>
end
end
local keywords_t = {}; -- map valid keywords to their associate metaparameter; reverse form of <keyword_lists[key] for these metaparameters
for _, metaparam_t in ipairs ({{'NameListStyle', 'name-list-style'}, {'Mode', 'mode'}}) do -- only these metaparameter / keywords_lists key pairs
for _, keyword in ipairs (keywords_lists[metaparam_t[2]]) do -- spin through the list of keywords
keywords_t[keyword] = metaparam_t[1]; -- add [keyword] = metaparameter to the map
end
end
for _, param in ipairs (params_t) do -- spin through the {{cs1 config}} parameters and fill <global_cs1_config_t>
local k, v = param:match ('([^=]-)%s*=%s*(.+)'); -- <k> is the parameter name; <v> is parameter's assigned value
if k then
if k:find ('^display') then -- if <k> is one of the |display-<namelist>= parameters
if v:match ('%d+') then -- the assigned value must be digits; doesn't accept 'etal'
global_cs1_config_t[meta_param_map_t[k]]=v; -- add the display param and its value to globals table
end
else
if keywords_t[v] == meta_param_map_t[k] then -- keywords_t[v] returns nil or the metaparam name; these must be the same
global_cs1_config_t[meta_param_map_t[k]]=v; -- add the parameter and its value to globals table
end
end
end
end
end
end
get_cs1_config (); -- fill <global_cs1_config_t>
--[[---------------------< S T R I P M A R K E R S >----------------------------
Common pattern definition location for stripmarkers so that we don't have to go
hunting for them if (when) MediaWiki changes their form.
]]
local stripmarkers = {
['any'] = '\127[^\127]*UNIQ%-%-(%a+)%-[%a%d]+%-QINU[^\127]*\127', -- capture returns name of stripmarker
['math'] = '\127[^\127]*UNIQ%-%-math%-[%a%d]+%-QINU[^\127]*\127' -- math stripmarkers used in coins_cleanup() and coins_replace_math_stripmarker()
}
--[[------------< I N V I S I B L E _ C H A R A C T E R S >---------------------
This table holds non-printing or invisible characters indexed either by name or
by Unicode group. Values are decimal representations of UTF-8 codes. The table
is organized as a table of tables because the Lua pairs keyword returns table
data in an arbitrary order. Here, we want to process the table from top to bottom
because the entries at the top of the table are also found in the ranges specified
by the entries at the bottom of the table.
Also here is a pattern that recognizes stripmarkers that begin and end with the
delete characters. The nowiki stripmarker is not an error but some others are
because the parameter values that include them become part of the template's
metadata before stripmarker replacement.
]]
local invisible_defs = {
del = '\127', -- used to distinguish between stripmarker and del char
zwj = '\226\128\141', -- used with capture because zwj may be allowed
}
local invisible_chars = {
{'replacement', '\239\191\189'}, -- U+FFFD, EF BF BD
{'zero width joiner', '('.. invisible_defs.zwj .. ')'}, -- U+200D, E2 80 8D; capture because zwj may be allowed
{'zero width space', '\226\128\139'}, -- U+200B, E2 80 8B
{'hair space', '\226\128\138'}, -- U+200A, E2 80 8A
{'soft hyphen', '\194\173'}, -- U+00AD, C2 AD
{'horizontal tab', '\009'}, -- U+0009 (HT), 09
{'line feed', '\010'}, -- U+000A (LF), 0A
{'no-break space', '\194\160'}, -- U+00A0 (NBSP), C2 A0
{'carriage return', '\013'}, -- U+000D (CR), 0D
{'stripmarker', stripmarkers.any}, -- stripmarker; may or may not be an error; capture returns the stripmaker type
{'delete', '('.. invisible_defs.del .. ')'}, -- U+007F (DEL), 7F; must be done after stripmarker test; capture to distinguish isolated del chars not part of stripmarker
{'C0 control', '[\000-\008\011\012\014-\031]'}, -- U+0000–U+001F (NULL–US), 00–1F (except HT, LF, CR (09, 0A, 0D))
{'C1 control', '[\194\128-\194\159]'}, -- U+0080–U+009F (XXX–APC), C2 80 – C2 9F
-- {'Specials', '[\239\191\185-\239\191\191]'}, -- U+FFF9-U+FFFF, EF BF B9 – EF BF BF
-- {'Private use area', '[\238\128\128-\239\163\191]'}, -- U+E000–U+F8FF, EE 80 80 – EF A3 BF
-- {'Supplementary Private Use Area-A', '[\243\176\128\128-\243\191\191\189]'}, -- U+F0000–U+FFFFD, F3 B0 80 80 – F3 BF BF BD
-- {'Supplementary Private Use Area-B', '[\244\128\128\128-\244\143\191\189]'}, -- U+100000–U+10FFFD, F4 80 80 80 – F4 8F BF BD
}
--[[
Indic script makes use of zero width joiner as a character modifier so zwj
characters must be left in. This pattern covers all of the unicode characters
for these languages:
Devanagari 0900–097F – https://unicode.org/charts/PDF/U0900.pdf
Devanagari extended A8E0–A8FF – https://unicode.org/charts/PDF/UA8E0.pdf
Bengali 0980–09FF – https://unicode.org/charts/PDF/U0980.pdf
Gurmukhi 0A00–0A7F – https://unicode.org/charts/PDF/U0A00.pdf
Gujarati 0A80–0AFF – https://unicode.org/charts/PDF/U0A80.pdf
Oriya 0B00–0B7F – https://unicode.org/charts/PDF/U0B00.pdf
Tamil 0B80–0BFF – https://unicode.org/charts/PDF/U0B80.pdf
Telugu 0C00–0C7F – https://unicode.org/charts/PDF/U0C00.pdf
Kannada 0C80–0CFF – https://unicode.org/charts/PDF/U0C80.pdf
Malayalam 0D00–0D7F – https://unicode.org/charts/PDF/U0D00.pdf
plus the not-necessarily Indic scripts for Sinhala and Burmese:
Sinhala 0D80-0DFF - https://unicode.org/charts/PDF/U0D80.pdf
Myanmar 1000-109F - https://unicode.org/charts/PDF/U1000.pdf
Myanmar extended A AA60-AA7F - https://unicode.org/charts/PDF/UAA60.pdf
Myanmar extended B A9E0-A9FF - https://unicode.org/charts/PDF/UA9E0.pdf
the pattern is used by has_invisible_chars() and coins_cleanup()
]]
local indic_script = '[\224\164\128-\224\181\191\224\163\160-\224\183\191\225\128\128-\225\130\159\234\167\160-\234\167\191\234\169\160-\234\169\191]';
-- list of emoji that use a zwj character (U+200D) to combine with another emoji
-- from: https://unicode.org/Public/emoji/16.0/emoji-zwj-sequences.txt; version: 16.0; 2024-08-14
-- table created by: [[:en:Module:Make emoji zwj table]]
local emoji_t = { -- indexes are decimal forms of the hex values in U+xxxx
[8596] = true, -- U+2194 ↔ left right arrow
[8597] = true, -- U+2195 ↕ up down arrow
[9760] = true, -- U+2620 ☠ skull and crossbones
[9792] = true, -- U+2640 ♀ female sign
[9794] = true, -- U+2642 ♂ male sign
[9877] = true, -- U+2695 ⚕ staff of aesculapius
[9878] = true, -- U+2696 ⚖ scales
[9895] = true, -- U+26A7 ⚧ male with stroke and male and female sign
[9992] = true, -- U+2708 ✈ airplane
[10052] = true, -- U+2744 ❄ snowflake
[10084] = true, -- U+2764 ❤ heavy black heart
[10145] = true, -- U+27A1 ➡ black rightwards arrow
[11035] = true, -- U+2B1B ⬛ black large square
[127752] = true, -- U+1F308 🌈 rainbow
[127787] = true, -- U+1F32B 🌫 fog
[127806] = true, -- U+1F33E 🌾 ear of rice
[127859] = true, -- U+1F373 🍳 cooking
[127868] = true, -- U+1F37C 🍼 baby bottle
[127876] = true, -- U+1F384 🎄 christmas tree
[127891] = true, -- U+1F393 🎓 graduation cap
[127908] = true, -- U+1F3A4 🎤 microphone
[127912] = true, -- U+1F3A8 🎨 artist palette
[127979] = true, -- U+1F3EB 🏫 school
[127981] = true, -- U+1F3ED 🏭 factory
[128102] = true, -- U+1F466 👦 boy
[128103] = true, -- U+1F467 👧 girl
[128104] = true, -- U+1F468 👨 man
[128105] = true, -- U+1F469 👩 woman
[128139] = true, -- U+1F48B 💋 kiss mark
[128165] = true, -- U+1F4A5 💥 collision symbol
[128168] = true, -- U+1F4A8 💨 dash symbol
[128171] = true, -- U+1F4AB 💫 dizzy symbol
[128187] = true, -- U+1F4BB 💻 personal computer
[128188] = true, -- U+1F4BC 💼 brief case
[128293] = true, -- U+1F525 🔥 fire
[128295] = true, -- U+1F527 🔧 wrench
[128300] = true, -- U+1F52C 🔬 microscope
[128488] = true, -- U+1F5E8 🗨 left speech bubble
[128640] = true, -- U+1F680 🚀 rocket
[128658] = true, -- U+1F692 🚒 fire engine
[129001] = true, -- U+1F7E9 🟩 large green square
[129003] = true, -- U+1F7EB 🟫 large brown square
[129309] = true, -- U+1F91D 🤝 handshake
[129455] = true, -- U+1F9AF 🦯 probing cane
[129456] = true, -- U+1F9B0 🦰 emoji component red hair
[129457] = true, -- U+1F9B1 🦱 emoji component curly hair
[129458] = true, -- U+1F9B2 🦲 emoji component bald
[129459] = true, -- U+1F9B3 🦳 emoji component white hair
[129466] = true, -- U+1F9BA 🦺 safety vest
[129468] = true, -- U+1F9BC 🦼 motorized wheelchair
[129469] = true, -- U+1F9BD 🦽 manual wheelchair
[129489] = true, -- U+1F9D1 🧑 adult
[129490] = true, -- U+1F9D2 🧒 child
[129657] = true, -- U+1FA79 🩹 adhesive bandage
[129778] = true, -- U+1FAF2 🫲 leftwards hand
}
--[[----------------------< L A N G U A G E S U P P O R T >-------------------
These tables and constants support various language-specific functionality.
]]
--local this_wiki_code = mw.getContentLanguage():getCode(); -- get this wiki's language code
local this_wiki_code = lang_obj:getCode(); -- get this wiki's language code
if string.match (mw.site.server, 'wikidata') then
this_wiki_code = mw.getCurrentFrame():callParserFunction('int', {'lang'}); -- on Wikidata so use interface language setting instead
end
local mw_languages_by_tag_t = mw.language.fetchLanguageNames (this_wiki_code, 'all'); -- get a table of language tag/name pairs known to Wikimedia; used for interwiki tests
local mw_languages_by_name_t = {};
for k, v in pairs (mw_languages_by_tag_t) do -- build a 'reversed' table name/tag language pairs know to MediaWiki; used for |language=
v = mw.ustring.lower (v); -- lowercase for tag fetch; get name's proper case from mw_languages_by_tag_t[<tag>]
if mw_languages_by_name_t[v] then -- when name already in the table
if 2 == #k or 3 == #k then -- if tag does not have subtags
mw_languages_by_name_t[v] = k; -- prefer the shortest tag for this name
end
else -- here when name not in the table
mw_languages_by_name_t[v] = k; -- so add name and matching tag
end
end
local inter_wiki_map = {}; -- map of interwiki prefixes that are language-code prefixes
for k, v in pairs (mw.site.interwikiMap ('local')) do -- spin through the base interwiki map (limited to local)
if mw_languages_by_tag_t[v["prefix"]] then -- if the prefix matches a known language tag
inter_wiki_map[v["prefix"]] = true; -- add it to our local map
end
end
--[[--------------------< S C R I P T _ L A N G _ C O D E S >-------------------
This table is used to hold ISO 639-1 two-character and ISO 639-3 three-character
language codes that apply only to |script-title= and |script-chapter=
]]
local script_lang_codes = {
'ab', 'am', 'ar', 'az', 'be', 'bg', 'bn', 'bo', 'bs', 'ce', 'chr', 'dv', 'dz',
'el', 'fa', 'grc', 'gu', 'he', 'hi', 'hy', 'ja', 'ka', 'kk', 'km', 'kn', 'ko',
'ku', 'ky', 'lo', 'mk', 'ml', 'mn', 'mni', 'mr', 'my', 'ne', 'or', 'ota',
'pa', 'ps', 'ru', 'sd', 'si', 'sr', 'syc', 'ta', 'te', 'tg', 'th', 'ti', 'tt',
'ug', 'uk', 'ur', 'uz', 'yi', 'yue', 'zh', 'zgh'
};
--[[---------------< L A N G U A G E R E M A P P I N G >----------------------
These tables hold language information that is different (correct) from MediaWiki's definitions
For each ['<tag>'] = 'language name' in lang_code_remap{} there must be a matching ['language name'] = {'language name', '<tag>'} in lang_name_remap{}
lang_tag_remap{}:
key is always lowercase ISO 639-1, -2, -3 language tag or a valid lowercase IETF language tag
value is properly spelled and capitalized language name associated with <tag>
only one language name per <tag>;
key/value pair must have matching entry in lang_name_remap{}
lang_name_remap{}:
key is always lowercase language name
value is a table the holds correctly spelled and capitalized language name [1] and associated tag [2] (tag must match a tag key in lang_tag_remap{})
may have multiple keys referring to a common preferred name and tag; For example:
['kolsch'] and ['kölsch'] both refer to 'Kölsch' and 'ksh'
]]
local lang_tag_remap = { -- used for |language= and |script-title= / |script-chapter=
['als'] = 'Tosk Albanian', -- MediaWiki returns Alemannisch
['bh'] = 'Bihari', -- MediaWiki uses 'bh' as a subdomain name for Bhojpuri Wikipedia: bh.wikipedia.org
['bla'] = 'Blackfoot', -- MediaWiki/IANA/ISO 639: Siksika; use en.wiki preferred name
['bn'] = 'Bengali', -- MediaWiki returns Bangla
['ca-valencia'] = 'Valencian', -- IETF variant of Catalan
['fkv'] = 'Kven', -- MediaWiki returns Kvensk
['gsw'] = 'Swiss German',
['ilo'] = 'Ilocano', -- MediaWiki/IANA/ISO 639: Iloko; use en.wiki preferred name
['ksh'] = 'Kölsch', -- MediaWiki: Colognian; use IANA/ISO 639 preferred name
['ksh-x-colog'] = 'Colognian', -- override MediaWiki ksh; no IANA/ISO 639 code for Colognian; IETF private code created at Module:Lang/data
['mis-x-ripuar'] = 'Ripuarian', -- override MediaWiki ksh; no IANA/ISO 639 code for Ripuarian; IETF private code created at Module:Lang/data
['nan-tw'] = 'Taiwanese Hokkien', -- make room for MediaWiki/IANA/ISO 639 nan: Min Nan Chinese and support en.wiki preferred name
['sr-ec'] = 'Serbian (Cyrillic script)', -- MediaWiki returns српски (ћирилица)
['sr-el'] = 'Serbian (Latin script)', -- MediaWiki returns srpski (latinica)
}
local lang_name_remap = { -- used for |language=; names require proper capitalization; tags must be lowercase
['alemannic'] = {'Swiss German', 'gsw'}, -- ISO 639-2, -3 alternate for Swiss German; MediaWiki mediawiki returns Alemannic for gsw; en.wiki preferred name
['alemannisch'] = {'Swiss German', 'gsw'}, -- not an ISO or IANA language name; MediaWiki uses 'als' as a subdomain name for Alemannic Wikipedia: als.wikipedia.org
['bangla'] = {'Bengali', 'bn'}, -- MediaWiki returns Bangla (the endonym) but we want Bengali (the exonym); here we remap
['bengali'] = {'Bengali', 'bn'}, -- MediaWiki doesn't use exonym so here we provide correct language name and 639-1 code
['bhojpuri'] = {'Bhojpuri', 'bho'}, -- MediaWiki uses 'bh' as a subdomain name for Bhojpuri Wikipedia: bh.wikipedia.org
['bihari'] = {'Bihari', 'bh'}, -- MediaWiki replaces 'Bihari' with 'Bhojpuri' so 'Bihari' cannot be found
['blackfoot'] = {'Blackfoot', 'bla'}, -- MediaWiki/IANA/ISO 639: Siksika; use en.wiki preferred name
['colognian'] = {'Colognian', 'ksh-x-colog'}, -- MediaWiki preferred name for ksh
['ilocano'] = {'Ilocano', 'ilo'}, -- MediaWiki/IANA/ISO 639: Iloko; use en.wiki preferred name
['kolsch'] = {'Kölsch', 'ksh'}, -- use IANA/ISO 639 preferred name (use non-diacritical o instead of umlaut ö)
['kölsch'] = {'Kölsch', 'ksh'}, -- use IANA/ISO 639 preferred name
['kven'] = {'Kven', 'fkv'}, -- Unicode CLDR have decided not to support English language name for these two...
['kvensk'] = {'Kven', 'fkv'}, -- ...they say to refer to IANA registry for English names
['ripuarian'] = {'Ripuarian', 'mis-x-ripuar'}, -- group of dialects; no code in MediaWiki or in IANA/ISO 639
['serbian (cyrillic script)'] = {'Serbian (Cyrillic script)', 'sr-cyrl'}, -- special case to get correct tag when |language=sr-ec
['serbian (latin script)'] = {'Serbian (Latin script)', 'sr-latn'}, -- special case to get correct tag when |language=sr-el
['swiss german'] = {'Swiss German', 'gsw'},
['taiwanese hokkien'] = {'Taiwanese Hokkien', 'nan-tw'}, -- make room for MediaWiki/IANA/ISO 639 nan: Min Nan Chinese
['tosk albanian'] = {'Tosk Albanian', 'als'}, -- MediaWiki replaces 'Tosk Albanian' with 'Alemannisch' so 'Tosk Albanian' cannot be found
['valencian'] = {'Valencian', 'ca-valencia'}, -- variant of Catalan; categorizes as Valencian
}
--[[---------------< P R O P E R T I E S _ C A T E G O R I E S >----------------
Properties categories. These are used for investigating qualities of citations.
]]
local prop_cats = {
['foreign-lang-source'] = 'CS1 $1-language sources ($2)', -- |language= categories; $1 is foreign-language name, $2 is ISO639-1 code
['foreign-lang-source-2'] = 'CS1 foreign language sources (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 interproject-linked names|$1', -- any author, editor, etc that has an interproject link; $1 is interproject tag used as a sort key
['interwiki-linked-name'] = 'CS1 interwiki-linked names|$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 $1-language sources ($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 location test',
['long-vol'] = 'CS1: long volume value', -- probably temporary cat to identify scope of |volume= values longer than 4 characters
['script'] = 'CS1 uses $1-language script ($2)', -- |script-title=xx: has matching category; $1 is language name, $2 is language tag
['tracked-param'] = 'CS1 tracked parameter: $1', -- $1 is base (enumerators removed) parameter name
['unfit'] = 'CS1: unfit URL', -- |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: abbreviated year range', -- 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 = '<code class="cs1-code">|access-date=</code> requires <code class="cs1-code">|url=</code>',
anchor = 'accessdate_missing_url',
category = 'CS1 errors: access-date without URL',
hidden = false
},
err_apostrophe_markup = {
message = 'Italic or bold markup not allowed in: <code class="cs1-code">|$1=</code>', -- $1 is parameter name
anchor = 'apostrophe_markup',
category = 'CS1 errors: markup',
hidden = false
},
err_archive_date_missing_url = {
message = '<code class="cs1-code">|archive-date=</code> requires <code class="cs1-code">|archive-url=</code>',
anchor = 'archive_date_missing_url',
category = 'CS1 errors: archive-url',
hidden = false
},
err_archive_date_url_ts_mismatch = {
message = '<code class="cs1-code">|archive-date=</code> / <code class="cs1-code">|archive-url=</code> timestamp mismatch; $1 suggested',
anchor = 'archive_date_url_ts_mismatch',
category = 'CS1 errors: archive-url',
hidden = false
},
err_archive_missing_date = {
message = '<code class="cs1-code">|archive-url=</code> requires <code class="cs1-code">|archive-date=</code>',
anchor = 'archive_missing_date',
category = 'CS1 errors: archive-url',
hidden = false
},
err_archive_missing_url = {
message = '<code class="cs1-code">|archive-url=</code> requires <code class="cs1-code">|url=</code>',
anchor = 'archive_missing_url',
category = 'CS1 errors: archive-url',
hidden = false
},
err_archive_url = {
message = '<code class="cs1-code">|archive-url=</code> is malformed: $1', -- $1 is error message detail
anchor = 'archive_url',
category = 'CS1 errors: archive-url',
hidden = false
},
err_arxiv_missing = {
message = '<code class="cs1-code">|arxiv=</code> required',
anchor = 'arxiv_missing',
category = 'CS1 errors: arXiv', -- same as bad arxiv
hidden = false
},
err_asintld_missing_asin = {
message = '<code class="cs1-code">|$1=</code> requires <code class="cs1-code">|asin=</code>', -- $1 is parameter name
anchor = 'asintld_missing_asin',
category = 'CS1 errors: ASIN TLD',
hidden = false
},
err_bad_arxiv = {
message = 'Check <code class="cs1-code">|arxiv=</code> value',
anchor = 'bad_arxiv',
category = 'CS1 errors: arXiv',
hidden = false
},
err_bad_asin = {
message = 'Check <code class="cs1-code">|asin=</code> value',
anchor = 'bad_asin',
category ='CS1 errors: ASIN',
hidden = false
},
err_bad_asin_tld = {
message = 'Check <code class="cs1-code">|asin-tld=</code> value',
anchor = 'bad_asin_tld',
category ='CS1 errors: ASIN TLD',
hidden = false
},
err_bad_bibcode = {
message = 'Check <code class="cs1-code">|bibcode=</code> $1', -- $1 is error message detail
anchor = 'bad_bibcode',
category = 'CS1 errors: bibcode',
hidden = false
},
err_bad_biorxiv = {
message = 'Check <code class="cs1-code">|biorxiv=</code> value',
anchor = 'bad_biorxiv',
category = 'CS1 errors: bioRxiv',
hidden = false
},
err_bad_citeseerx = {
message = 'Check <code class="cs1-code">|citeseerx=</code> value',
anchor = 'bad_citeseerx',
category = 'CS1 errors: citeseerx',
hidden = false
},
err_bad_date = {
message = 'Check date values in: $1', -- $1 is a parameter name list
anchor = 'bad_date',
category = 'CS1 errors: dates',
hidden = false
},
err_bad_doi = {
message = 'Check <code class="cs1-code">|doi=</code> value',
anchor = 'bad_doi',
category = 'CS1 errors: DOI',
hidden = false
},
err_bad_hdl = {
message = 'Check <code class="cs1-code">|hdl=</code> value',
anchor = 'bad_hdl',
category = 'CS1 errors: HDL',
hidden = false
},
err_bad_isbn = {
message = 'Check <code class="cs1-code">|isbn=</code> value: $1', -- $1 is error message detail
anchor = 'bad_isbn',
category = 'CS1 errors: ISBN',
hidden = false
},
err_bad_ismn = {
message = 'Check <code class="cs1-code">|ismn=</code> value',
anchor = 'bad_ismn',
category = 'CS1 errors: ISMN',
hidden = false
},
err_bad_issn = {
message = 'Check <code class="cs1-code">|$1issn=</code> value', -- $1 is 'e' or '' for eissn or issn
anchor = 'bad_issn',
category = 'CS1 errors: ISSN',
hidden = false
},
err_bad_jfm = {
message = 'Check <code class="cs1-code">|jfm=</code> value',
anchor = 'bad_jfm',
category = 'CS1 errors: JFM',
hidden = false
},
err_bad_jstor = {
message = 'Check <code class="cs1-code">|jstor=</code> value',
anchor = 'bad_jstor',
category = 'CS1 errors: JSTOR',
hidden = false
},
err_bad_lccn = {
message = 'Check <code class="cs1-code">|lccn=</code> value',
anchor = 'bad_lccn',
category = 'CS1 errors: LCCN',
hidden = false
},
err_bad_medrxiv = {
message = 'Check <code class="cs1-code">|medrxiv=</code> value',
anchor = 'bad_medrxiv',
category = 'CS1 errors: medRxiv',
hidden = false
},
err_bad_mr = {
message = 'Check <code class="cs1-code">|mr=</code> value',
anchor = 'bad_mr',
category = 'CS1 errors: MR',
hidden = false
},
err_bad_oclc = {
message = 'Check <code class="cs1-code">|oclc=</code> value',
anchor = 'bad_oclc',
category = 'CS1 errors: OCLC',
hidden = false
},
err_bad_ol = {
message = 'Check <code class="cs1-code">|ol=</code> value',
anchor = 'bad_ol',
category = 'CS1 errors: OL',
hidden = false
},
err_bad_osti = {
message = 'Check <code class="cs1-code">|osti=</code> value',
anchor = 'bad_osti',
category = 'CS1 errors: OSTI',
hidden = false
},
err_bad_paramlink = { -- for |title-link=, |author/editor/translator-link=, |series-link=, |episode-link=
message = 'Check <code class="cs1-code">|$1=</code> value', -- $1 is parameter name
anchor = 'bad_paramlink',
category = 'CS1 errors: parameter link',
hidden = false
},
err_bad_pmc = {
message = 'Check <code class="cs1-code">|pmc=</code> value',
anchor = 'bad_pmc',
category = 'CS1 errors: PMC',
hidden = false
},
err_bad_pmid = {
message = 'Check <code class="cs1-code">|pmid=</code> value',
anchor = 'bad_pmid',
category = 'CS1 errors: PMID',
hidden = false
},
err_bad_rfc = {
message = 'Check <code class="cs1-code">|rfc=</code> value',
anchor = 'bad_rfc',
category = 'CS1 errors: RFC',
hidden = false
},
err_bad_s2cid = {
message = 'Check <code class="cs1-code">|s2cid=</code> value',
anchor = 'bad_s2cid',
category = 'CS1 errors: S2CID',
hidden = false
},
err_bad_sbn = {
message = 'Check <code class="cs1-code">|sbn=</code> value: $1', -- $1 is error message detail
anchor = 'bad_sbn',
category = 'CS1 errors: SBN',
hidden = false
},
err_bad_ssrn = {
message = 'Check <code class="cs1-code">|ssrn=</code> value',
anchor = 'bad_ssrn',
category = 'CS1 errors: SSRN',
hidden = false
},
err_bad_url = {
message = 'Check $1 value', -- $1 is parameter name
anchor = 'bad_url',
category = 'CS1 errors: URL',
hidden = false
},
err_bad_usenet_id = {
message = 'Check <code class="cs1-code">|message-id=</code> value',
anchor = 'bad_message_id',
category = 'CS1 errors: message-id',
hidden = false
},
err_bad_zbl = {
message = 'Check <code class="cs1-code">|zbl=</code> value',
anchor = 'bad_zbl',
category = 'CS1 errors: Zbl',
hidden = false
},
err_bare_url_missing_title = {
message = '$1 missing title', -- $1 is parameter name
anchor = 'bare_url_missing_title',
category = 'CS1 errors: bare URL',
hidden = false
},
err_biorxiv_missing = {
message = '<code class="cs1-code">|biorxiv=</code> required',
anchor = 'biorxiv_missing',
category = 'CS1 errors: bioRxiv', -- same as bad bioRxiv
hidden = false
},
err_chapter_ignored = {
message = '<code class="cs1-code">|$1=</code> ignored', -- $1 is parameter name
anchor = 'chapter_ignored',
category = 'CS1 errors: chapter ignored',
hidden = false
},
err_citation_missing_title = {
message = 'Missing or empty <code class="cs1-code">|$1=</code>', -- $1 is parameter name
anchor = 'citation_missing_title',
category = 'CS1 errors: missing title',
hidden = false
},
err_citeseerx_missing = {
message = '<code class="cs1-code">|citeseerx=</code> required',
anchor = 'citeseerx_missing',
category = 'CS1 errors: citeseerx', -- same as bad citeseerx
hidden = false
},
err_cite_web_url = { -- this error applies to cite web and to cite podcast
message = 'Missing or empty <code class="cs1-code">|url=</code>',
anchor = 'cite_web_url',
category = 'CS1 errors: requires URL',
hidden = false
},
err_class_ignored = {
message = '<code class="cs1-code">|class=</code> ignored',
anchor = 'class_ignored',
category = 'CS1 errors: class',
hidden = false
},
err_contributor_ignored = {
message = '<code class="cs1-code">|contributor=</code> ignored',
anchor = 'contributor_ignored',
category = 'CS1 errors: contributor',
hidden = false
},
err_contributor_missing_required_param = {
message = '<code class="cs1-code">|contributor=</code> requires <code class="cs1-code">|$1=</code>', -- $1 is parameter name
anchor = 'contributor_missing_required_param',
category = 'CS1 errors: contributor',
hidden = false
},
err_deprecated_params = {
message = 'Cite uses deprecated parameter <code class="cs1-code">|$1=</code>', -- $1 is parameter name
anchor = 'deprecated_params',
category = 'CS1 errors: deprecated parameters',
hidden = false
},
err_disp_name = {
message = 'Invalid <code class="cs1-code">|$1=$2</code>', -- $1 is parameter name; $2 is the assigned value
anchor = 'disp_name',
category = 'CS1 errors: display-names',
hidden = false,
},
err_doibroken_missing_doi = {
message = '<code class="cs1-code">|$1=</code> requires <code class="cs1-code">|doi=</code>', -- $1 is parameter name
anchor = 'doibroken_missing_doi',
category = 'CS1 errors: DOI',
hidden = false
},
err_embargo_missing_pmc = {
message = '<code class="cs1-code">|$1=</code> requires <code class="cs1-code">|pmc=</code>', -- $1 is parameter name
anchor = 'embargo_missing_pmc',
category = 'CS1 errors: PMC embargo',
hidden = false
},
err_empty_citation = {
message = 'Empty citation',
anchor = 'empty_citation',
category = 'CS1 errors: empty citation',
hidden = false
},
err_etal = {
message = 'Explicit use of et al. in: <code class="cs1-code">|$1=</code>', -- $1 is parameter name
anchor = 'explicit_et_al',
category = 'CS1 errors: explicit use of et al.',
hidden = false
},
err_extra_text_edition = {
message = '<code class="cs1-code">|edition=</code> has extra text',
anchor = 'extra_text_edition',
category = 'CS1 errors: extra text: edition',
hidden = false,
},
err_extra_text_issue = {
message = '<code class="cs1-code">|$1=</code> has extra text', -- $1 is parameter name
anchor = 'extra_text_issue',
category = 'CS1 errors: extra text: issue',
hidden = false,
},
err_extra_text_pages = {
message = '<code class="cs1-code">|$1=</code> has extra text', -- $1 is parameter name
anchor = 'extra_text_pages',
category = 'CS1 errors: extra text: pages',
hidden = false,
},
err_extra_text_volume = {
message = '<code class="cs1-code">|$1=</code> has extra text', -- $1 is parameter name
anchor = 'extra_text_volume',
category = 'CS1 errors: extra text: volume',
hidden = false,
},
err_first_missing_last = {
message = '<code class="cs1-code">|$1=</code> missing <code class="cs1-code">|$2=</code>', -- $1 is first alias, $2 is matching last alias
anchor = 'first_missing_last',
category = 'CS1 errors: missing name', -- author, contributor, editor, interviewer, translator
hidden = false
},
err_format_missing_url = {
message = '<code class="cs1-code">|$1=</code> requires <code class="cs1-code">|$2=</code>', -- $1 is format parameter $2 is url parameter
anchor = 'format_missing_url',
category = 'CS1 errors: format without URL',
hidden = false
},
err_generic_name = {
message = '<code class="cs1-code">|$1=</code> has generic name', -- $1 is parameter name
anchor = 'generic_name',
category = 'CS1 errors: generic name',
hidden = false,
},
err_generic_title = {
message = 'Cite uses generic title',
anchor = 'generic_title',
category = 'CS1 errors: generic title',
hidden = false,
},
err_invalid_isbn_date = {
message = 'ISBN / Date incompatibility',
anchor = 'invalid_isbn_date',
category = 'CS1 errors: ISBN date',
hidden = true
},
err_invalid_param_val = {
message = 'Invalid <code class="cs1-code">|$1=$2</code>', -- $1 is parameter name $2 is parameter value
anchor = 'invalid_param_val',
category = 'CS1 errors: invalid parameter value',
hidden = false
},
err_invisible_char = {
message = '$1 in $2 at position $3', -- $1 is invisible char $2 is parameter name $3 is position number
anchor = 'invisible_char',
category = 'CS1 errors: invisible characters',
hidden = false
},
err_medrxiv_missing = {
message = '<code class="cs1-code">|medrxiv=</code> required',
anchor = 'medrxiv_missing',
category = 'CS1 errors: medRxiv', -- same as bad medRxiv
hidden = false
},
err_missing_name = {
message = 'Missing <code class="cs1-code">|$1$2=</code>', -- $1 is modified NameList; $2 is enumerator
anchor = 'missing_name',
category = 'CS1 errors: missing name', -- author, contributor, editor, interviewer, translator
hidden = false
},
err_missing_periodical = {
message = 'Cite $1 requires <code class="cs1-code">|$2=</code>', -- $1 is cs1 template name; $2 is canonical periodical parameter name for cite $1
anchor = 'missing_periodical',
category = 'CS1 errors: missing periodical',
hidden = false
},
err_missing_pipe = {
message = 'Missing pipe in: <code class="cs1-code">|$1=</code>', -- $1 is parameter name
anchor = 'missing_pipe',
category = 'CS1 errors: missing pipe',
hidden = false
},
err_missing_publisher = {
message = 'Cite $1 requires <code class="cs1-code">|$2=</code>', -- $1 is cs1 template name; $2 is canonical publisher parameter name for cite $1
anchor = 'missing_publisher',
category = 'CS1 errors: missing publisher',
hidden = false
},
err_numeric_names = {
message = '<code class="cs1-code">|$1=</code> has numeric name', -- $1 is parameter name',
anchor = 'numeric_names',
category = 'CS1 errors: numeric name',
hidden = false,
},
err_param_access_requires_param = {
message = '<code class="cs1-code">|$1-access=</code> requires <code class="cs1-code">|$1=</code>', -- $1 is parameter name
anchor = 'param_access_requires_param',
category = 'CS1 errors: param-access',
hidden = false
},
err_param_has_ext_link = {
message = 'External link in <code class="cs1-code">$1</code>', -- $1 is parameter name
anchor = 'param_has_ext_link',
category = 'CS1 errors: external links',
hidden = false
},
err_param_has_twl_url = {
message = 'Wikipedia Library link in <code class="cs1-code">$1</code>', -- $1 is parameter name
anchor = 'param_has_twl_url',
category = 'CS1 errors: URL',
hidden = false
},
err_parameter_ignored = {
message = 'Unknown parameter <code class="cs1-code">|$1=</code> ignored', -- $1 is parameter name
anchor = 'parameter_ignored',
category = 'CS1 errors: unsupported parameter',
hidden = false
},
err_parameter_ignored_suggest = {
message = 'Unknown parameter <code class="cs1-code">|$1=</code> ignored (<code class="cs1-code">|$2=</code> suggested)', -- $1 is unknown parameter $2 is suggested parameter name
anchor = 'parameter_ignored_suggest',
category = 'CS1 errors: unsupported parameter',
hidden = false
},
err_periodical_ignored = {
message = '<code class="cs1-code">|$1=</code> ignored', -- $1 is parameter name
anchor = 'periodical_ignored',
category = 'CS1 errors: periodical ignored',
hidden = false
},
err_redundant_parameters = {
message = 'More than one of $1 specified', -- $1 is error message detail
anchor = 'redundant_parameters',
category = 'CS1 errors: redundant parameter',
hidden = false
},
err_script_parameter = {
message = 'Invalid <code class="cs1-code">|$1=</code>: $2', -- $1 is parameter name $2 is script language code or error detail
anchor = 'script_parameter',
category = 'CS1 errors: script parameters',
hidden = false
},
err_ssrn_missing = {
message = '<code class="cs1-code">|ssrn=</code> required',
anchor = 'ssrn_missing',
category = 'CS1 errors: SSRN',
hidden = false
},
err_text_ignored = {
message = 'Text "$1" ignored', -- $1 is ignored text
anchor = 'text_ignored',
category = 'CS1 errors: unrecognized parameter',
hidden = false
},
err_trans_missing_title = {
message = '<code class="cs1-code">|trans-$1=</code> requires <code class="cs1-code">|$1=</code> or <code class="cs1-code">|script-$1=</code>', -- $1 is base parameter name
anchor = 'trans_missing_title',
category = 'CS1 errors: translated title',
hidden = false
},
err_param_unknown_empty = {
message = 'Cite has empty unknown parameter$1: $2', -- $1 is 's' or empty space; $2 is emty unknown param list
anchor = 'param_unknown_empty',
category = 'CS1 errors: empty unknown parameters',
hidden = false
},
err_vancouver = {
message = 'Vancouver style error: $1 in name $2', -- $1 is error detail, $2 is the nth name
anchor = 'vancouver',
category = 'CS1 errors: Vancouver style',
hidden = false
},
err_wikilink_in_url = {
message = 'URL–wikilink conflict', -- uses ndash
anchor = 'wikilink_in_url',
category = 'CS1 errors: URL–wikilink conflict', -- 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 = 'CS1 maint: archived copy as title',
hidden = true,
},
maint_bibcode = {
message = nil,
anchor = 'bibcode',
category = 'CS1 maint: 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 = 'CS1 maint: location missing publisher',
hidden = true,
},
maint_bot_unknown = {
message = nil,
anchor = 'bot:_unknown',
category = 'CS1 maint: bot: original URL status unknown',
hidden = true,
},
maint_date_auto_xlated = { -- date auto-translation not supported by en.wiki
message = nil,
anchor = 'date_auto_xlated',
category = 'CS1 maint: date auto-translated',
hidden = true,
},
maint_date_format = {
message = nil,
anchor = 'date_format',
category = 'CS1 maint: date format',
hidden = true,
},
maint_date_year = {
message = nil,
anchor = 'date_year',
category = 'CS1 maint: date and year',
hidden = true,
},
maint_doi_ignore = {
message = nil,
anchor = 'doi_ignore',
category = 'CS1 maint: ignored DOI errors',
hidden = true,
},
maint_doi_inactive = {
message = nil,
anchor = 'doi_inactive',
category = 'CS1 maint: DOI inactive',
hidden = true,
},
maint_doi_inactive_dated = {
message = nil,
anchor = 'doi_inactive_dated',
category = 'CS1 maint: DOI inactive as of $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 = 'CS1 maint: unflagged free DOI',
hidden = true,
},
maint_extra_punct = {
message = nil,
anchor = 'extra_punct',
category = 'CS1 maint: extra punctuation',
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 = 'CS1 maint: ID limit load fail',
hidden = true,
},
maint_isbn_ignore = {
message = nil,
anchor = 'ignore_isbn_err',
category = 'CS1 maint: ignored ISBN errors',
hidden = true,
},
maint_issn_ignore = {
message = nil,
anchor = 'ignore_issn',
category = 'CS1 maint: ignored ISSN errors',
hidden = true,
},
maint_jfm_format = {
message = nil,
anchor = 'jfm_format',
category = 'CS1 maint: JFM format',
hidden = true,
},
maint_location = {
message = nil,
anchor = 'location',
category = 'CS1 maint: location',
hidden = true,
},
maint_mr_format = {
message = nil,
anchor = 'mr_format',
category = 'CS1 maint: MR format',
hidden = true,
},
maint_mult_names = {
message = nil,
anchor = 'mult_names',
category = 'CS1 maint: multiple names: $1', -- $1 is '<name>s list'; gets value from special_case_translation table
hidden = true,
},
maint_numeric_names = {
message = nil,
anchor = 'numeric_names',
category = 'CS1 maint: numeric names: $1', -- $1 is '<name>s list'; gets value from special_case_translation table
hidden = true,
},
maint_others = {
message = nil,
anchor = 'others',
category = 'CS1 maint: others',
hidden = true,
},
maint_others_avm = {
message = nil,
anchor = 'others_avm',
category = 'CS1 maint: others in cite AV media (notes)',
hidden = true,
},
maint_overridden_setting = {
message = nil,
anchor = 'overridden',
category = 'CS1 maint: overridden setting',
hidden = true,
},
maint_pmc_embargo = {
message = nil,
anchor = 'embargo',
category = 'CS1 maint: PMC embargo expired',
hidden = true,
},
maint_pmc_format = {
message = nil,
anchor = 'pmc_format',
category = 'CS1 maint: PMC format',
hidden = true,
},
maint_postscript = {
message = nil,
anchor = 'postscript',
category = 'CS1 maint: postscript',
hidden = true,
},
maint_publisher_location = {
message = nil,
anchor = 'publisher_location',
category = 'CS1 maint: publisher location',
hidden = true,
},
maint_ref_duplicates_default = {
message = nil,
anchor = 'ref_default',
category = 'CS1 maint: ref duplicates default',
hidden = true,
},
maint_unknown_lang = {
message = nil,
anchor = 'unknown_lang',
category = 'CS1 maint: unrecognized language',
hidden = true,
},
maint_untitled = {
message = nil,
anchor = 'untitled',
category = 'CS1 maint: untitled periodical',
hidden = true,
},
maint_url_status = {
message = nil,
anchor = 'url_status',
category = 'CS1 maint: url-status',
hidden = true,
},
maint_year= {
message = nil,
anchor = 'year',
category = 'CS1 maint: year',
hidden = true,
},
maint_zbl = {
message = nil,
anchor = 'zbl',
category = 'CS1 maint: 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 (identifier)',
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 = 'Amazon Standard Identification Number',
redirect = 'ASIN (identifier)',
q = 'Q1753278',
label = 'ASIN',
prefix = 'https://www.amazon.',
COinS = 'url',
separator = ' ',
encode = false;
},
['BIBCODE'] = {
parameters = {'bibcode'},
link = 'Bibcode',
redirect = 'Bibcode (identifier)',
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 (identifier)',
q = 'Q19835482',
label = 'bioRxiv',
prefix = 'https://doi.org/',
COinS = 'pre', -- use prefix value
access = 'free', -- free to read
encode = true,
separator = ' ',
},
['CITESEERX'] = {
parameters = {'citeseerx'},
link = 'CiteSeerX',
redirect = 'CiteSeerX (identifier)',
q = 'Q2715061',
label = 'CiteSeerX',
prefix = 'https://citeseerx.ist.psu.edu/viewdoc/summary?doi=',
COinS = 'pre', -- use prefix value
access = 'free', -- free to read
encode = true,
separator = ' ',
},
['DOI'] = { -- Used by InternetArchiveBot
parameters = { 'doi', 'DOI'},
link = 'Digital object identifier',
redirect = 'doi (identifier)',
q = 'Q25670',
label = 'doi',
prefix = 'https://doi.org/',
COinS = 'info:doi',
separator = ':',
encode = true,
custom_access = 'doi-access',
},
['EISSN'] = {
parameters = {'eissn', 'EISSN'},
link = 'International Standard Serial Number#Electronic ISSN',
redirect = 'eISSN (identifier)',
q = 'Q46339674',
label = 'eISSN',
prefix = 'https://search.worldcat.org/issn/',
COinS = 'rft.eissn',
encode = false,
separator = ' ',
},
['HDL'] = {
parameters = { 'hdl', 'HDL' },
link = 'Handle System',
redirect = 'hdl (identifier)',
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 = 'International Standard Book Number',
redirect = 'ISBN (identifier)',
q = 'Q33057',
label = 'ISBN',
prefix = 'Special:BookSources/',
COinS = 'rft.isbn',
separator = ' ',
},
['ISMN'] = {
parameters = {'ismn', 'ISMN'},
link = 'International Standard Music Number',
redirect = 'ISMN (identifier)',
q = 'Q1666938',
label = 'ISMN',
prefix = '', -- not currently used;
COinS = nil, -- nil because we can't use pre or rft or info:
separator = ' ',
},
['ISSN'] = {
parameters = {'issn', 'ISSN'},
link = 'International Standard Serial Number',
redirect = 'ISSN (identifier)',
q = 'Q131276',
label = 'ISSN',
prefix = 'https://search.worldcat.org/issn/',
COinS = 'rft.issn',
encode = false,
separator = ' ',
},
['JFM'] = {
parameters = {'jfm', 'JFM'},
link = 'Jahrbuch über die Fortschritte der Mathematik',
redirect = 'JFM (identifier)',
q = '',
label = 'JFM',
prefix = 'https://zbmath.org/?format=complete&q=an:',
COinS = 'pre', -- use prefix value
encode = true,
separator = ' ',
},
['JSTOR'] = {
parameters = {'jstor', 'JSTOR'},
link = 'JSTOR',
redirect = 'JSTOR (identifier)',
q = 'Q1420342',
label = 'JSTOR',
prefix = 'https://www.jstor.org/stable/',
COinS = 'pre', -- use prefix value
encode = false,
separator = ' ',
custom_access = 'jstor-access',
},
['LCCN'] = {
parameters = {'lccn', 'LCCN'},
link = 'Library of Congress Control Number',
redirect = 'LCCN (identifier)',
q = 'Q620946',
label = 'LCCN',
prefix = 'https://lccn.loc.gov/',
COinS = 'info:lccn',
encode = false,
separator = ' ',
},
['MEDRXIV'] = {
parameters = {'medrxiv'},
link = 'medRxiv',
redirect = 'medRxiv (identifier)',
q = 'Q58465838',
label = 'medRxiv',
prefix = 'https://www.medrxiv.org/content/',
COinS = 'pre', -- use prefix value
access = 'free', -- free to read
encode = false,
separator = ' ',
},
['MR'] = {
parameters = {'mr', 'MR'},
link = 'Mathematical Reviews',
redirect = 'MR (identifier)',
q = 'Q211172',
label = 'MR',
prefix = 'https://mathscinet.ams.org/mathscinet-getitem?mr=',
COinS = 'pre', -- use prefix value
encode = true,
separator = ' ',
},
['OCLC'] = {
parameters = {'oclc', 'OCLC'},
link = 'OCLC',
redirect = 'OCLC (identifier)',
q = 'Q190593',
label = 'OCLC',
prefix = 'https://search.worldcat.org/oclc/',
COinS = 'info:oclcnum',
encode = true,
separator = ' ',
id_limit = id_limits_data_t.OCLC or 0,
},
['OL'] = {
parameters = { 'ol', 'OL' },
link = 'Open Library',
redirect = 'OL (identifier)',
q = 'Q1201876',
label = 'OL',
prefix = 'https://openlibrary.org/',
COinS = 'url',
separator = ' ',
encode = true,
custom_access = 'ol-access',
},
['OSTI'] = {
parameters = {'osti', 'OSTI'},
link = 'Office of Scientific and Technical Information',
redirect = 'OSTI (identifier)',
q = 'Q2015776',
label = 'OSTI',
prefix = 'https://www.osti.gov/biblio/',
COinS = 'pre', -- use prefix value
encode = true,
separator = ' ',
id_limit = id_limits_data_t.OSTI or 0,
custom_access = 'osti-access',
},
['PMC'] = {
parameters = {'pmc', 'PMC'},
link = 'PubMed Central',
redirect = 'PMC (identifier)',
q = 'Q229883',
label = 'PMC',
prefix = 'https://www.ncbi.nlm.nih.gov/pmc/articles/PMC',
suffix = '',
COinS = 'pre', -- use prefix value
encode = true,
separator = ' ',
id_limit = id_limits_data_t.PMC or 0,
access = 'free', -- free to read
},
['PMID'] = {
parameters = {'pmid', 'PMID'},
link = 'PubMed Identifier',
redirect = 'PMID (identifier)',
q = 'Q2082879',
label = 'PMID',
prefix = 'https://pubmed.ncbi.nlm.nih.gov/',
COinS = 'info:pmid',
encode = false,
separator = ' ',
id_limit = id_limits_data_t.PMID or 0,
},
['RFC'] = {
parameters = {'rfc', 'RFC'},
link = 'Request for Comments',
redirect = 'RFC (identifier)',
q = 'Q212971',
label = 'RFC',
prefix = 'https://tools.ietf.org/html/rfc',
COinS = 'pre', -- use prefix value
encode = false,
separator = ' ',
id_limit = id_limits_data_t.RFC or 0,
access = 'free', -- free to read
},
['SBN'] = {
parameters = {'sbn', 'SBN'},
link = 'Standard Book Number', -- redirect to International_Standard_Book_Number#History
redirect = 'SBN (identifier)',
label = 'SBN',
prefix = 'Special:BookSources/0-', -- prefix has leading zero necessary to make 9-digit sbn a 10-digit isbn
COinS = nil, -- nil because we can't use pre or rft or info:
separator = ' ',
},
['SSRN'] = {
parameters = {'ssrn', 'SSRN'},
link = 'Social Science Research Network',
redirect = 'SSRN (identifier)',
q = 'Q7550801',
label = 'SSRN',
prefix = 'https://papers.ssrn.com/sol3/papers.cfm?abstract_id=',
COinS = 'pre', -- use prefix value
encode = true,
separator = ' ',
id_limit = id_limits_data_t.SSRN or 0,
custom_access = 'ssrn-access',
},
['S2CID'] = {
parameters = {'s2cid', 'S2CID'},
link = 'Semantic Scholar',
redirect = 'S2CID (identifier)',
q = 'Q22908627',
label = 'S2CID',
prefix = 'https://api.semanticscholar.org/CorpusID:',
COinS = 'pre', -- use prefix value
encode = false,
separator = ' ',
id_limit = id_limits_data_t.S2CID or 0,
custom_access = 's2cid-access',
},
['USENETID'] = {
parameters = {'message-id'},
link = 'Usenet',
redirect = 'Usenet (identifier)',
q = 'Q193162',
label = 'Usenet:',
prefix = 'news:',
encode = false,
COinS = 'pre', -- use prefix value
separator = ' ',
},
['ZBL'] = {
parameters = {'zbl', 'ZBL' },
link = 'Zentralblatt MATH',
redirect = 'Zbl (identifier)',
q = 'Q190269',
label = 'Zbl',
prefix = 'https://zbmath.org/?format=complete&q=an:',
COinS = 'pre', -- use prefix value
encode = true,
separator = ' ',
},
}
--[[--------------------------< E X P O R T S >---------------------------------
]]
return {
use_identifier_redirects = use_identifier_redirects, -- booleans defined in the settings at the top of this module
local_lang_cat_enable = local_lang_cat_enable,
date_name_auto_xlate_enable = date_name_auto_xlate_enable,
date_digit_auto_xlate_enable = date_digit_auto_xlate_enable,
enable_sort_keys = enable_sort_keys,
-- tables and variables created when this module is loaded
global_df = get_date_format (), -- this line can be replaced with "global_df = 'dmy-all'," to have all dates auto translated to dmy format.
global_cs1_config_t = global_cs1_config_t, -- global settings from {{cs1 config}}
punct_skip = build_skip_table (punct_skip, punct_meta_params),
url_skip = build_skip_table (url_skip, url_meta_params),
known_free_doi_registrants_t = build_free_doi_registrants_table(),
id_limits_data_load_fail = id_limits_data_load_fail, -- true when commons tabular identifier-limit data fails to load
name_space_sort_keys = name_space_sort_keys,
aliases = aliases,
special_case_translation = special_case_translation,
date_names = date_names,
err_msg_supl = err_msg_supl,
error_conditions = error_conditions,
editor_markup_patterns = editor_markup_patterns,
et_al_patterns = et_al_patterns,
extended_registrants_t = extended_registrants_t,
id_handlers = id_handlers,
keywords_lists = keywords_lists,
keywords_xlate = keywords_xlate,
stripmarkers = stripmarkers,
invisible_chars = invisible_chars,
invisible_defs = invisible_defs,
indic_script = indic_script,
emoji_t = emoji_t,
maint_cats = maint_cats,
messages = messages,
presentation = presentation,
prop_cats = prop_cats,
script_lang_codes = script_lang_codes,
lang_tag_remap = lang_tag_remap,
lang_name_remap = lang_name_remap,
this_wiki_code = this_wiki_code,
title_types = title_types,
uncategorized_namespaces = uncategorized_namespaces_t,
uncategorized_subpages = uncategorized_subpages,
templates_using_volume = templates_using_volume,
templates_using_issue = templates_using_issue,
templates_not_using_page = templates_not_using_page,
vol_iss_pg_patterns = vol_iss_pg_patterns,
single_letter_2nd_lvl_domains_t = single_letter_2nd_lvl_domains_t,
inter_wiki_map = inter_wiki_map,
mw_languages_by_tag_t = mw_languages_by_tag_t,
mw_languages_by_name_t = mw_languages_by_name_t,
citation_class_map_t = citation_class_map_t,
citation_issue_t = citation_issue_t,
citation_no_volume_t = citation_no_volume_t,
}
1qtnsk18jlmk0vxedax9rhtzltma8m9
886345
886344
2025-06-13T17:03:23Z
KartikMistry
10383
[[:en:Module:Citation/CS1/Configuration]] માંથી આયાત કરેલ ૧ પુનરાવર્તન
886344
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 = {'/[Ss]andbox', '/[Tt]estcases', '/[^/]*[Ll]og', '/[Aa]rchive'}; -- 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'] = 'Archived from $1 on $2',
['archived-live'] = '$1 from the original on $2',
['archived-unfit'] = 'Archived from the original on ',
['archived'] = 'Archived',
['by'] = 'By', -- contributions to authored works: introduction, foreword, afterword
['cartography'] = 'Cartography by $1',
['editor'] = 'ed.',
['editors'] = 'eds.',
['edition'] = '($1 ed.)',
['episode'] = 'Episode $1',
['et al'] = 'et al.',
['in'] = 'In', -- edited works
['inactive'] = 'inactive',
['inset'] = '$1 inset',
['interview'] = 'Interviewed by $1',
['mismatch'] = '<code class="cs1-code">|$1=</code> / <code class="cs1-code">|$2=</code> mismatch', -- $1 is year param name; $2 is date param name
['newsgroup'] = '[[Usenet newsgroup|Newsgroup]]: $1',
['notitle'] = 'No title', -- for |title=(()) and (in the future) |title=none
['original'] = 'the original',
['origdate'] = ' [$1]',
['published'] = ' (published $1)',
['retrieved'] = 'Retrieved $1',
['season'] = 'Season $1',
['section'] = '§ $1',
['sections'] = '§§ $1',
['series'] = '$1 $2', -- $1 is sepc, $2 is series
['seriesnum'] = 'Series $1',
['translated'] = 'Translated by $1',
['type'] = ' ($1)', -- for titletype
['written'] = 'Written at $1',
['vol'] = '$1 Vol. $2', -- $1 is sepc; bold journal style volume is in presentation{}
['vol-no'] = '$1 Vol. $2, no. $3', -- sepc, volume, issue (alternatively insert $1 after $2, but then we'd also have to change capitalization)
['issue'] = '$1 No. $2', -- $1 is sepc
['art'] = '$1 Art. $2', -- $1 is sepc; for {{cite conference}} only
['vol-art'] = '$1 Vol. $2, art. $3', -- sepc, volume, article-number; for {{cite conference}} only
['j-vol'] = '$1 $2', -- sepc, volume; bold journal volume is in presentation{}
['j-issue'] = ' ($1)',
['j-article-num'] = ' $1', -- TODO: any punctuation here? static text?
['nopp'] = '$1 $2'; -- page(s) without prefix; $1 is sepc
['p-prefix'] = "$1 p. $2", -- $1 is sepc
['pp-prefix'] = "$1 pp. $2", -- $1 is sepc
['j-page(s)'] = ': $1', -- same for page and pages
['sheet'] = '$1 Sheet $2', -- $1 is sepc
['sheets'] = '$1 Sheets $2', -- $1 is sepc
['j-sheet'] = ': Sheet $1',
['j-sheets'] = ': Sheets $1',
['language'] = '(in $1)',
['via'] = " – via $1",
['event'] = 'Event occurs at',
['minutes'] = 'minutes in',
-- Determines the location of the help page
['help page link'] = 'Help:CS1 errors',
['help page label'] = 'help',
-- categories
['cat wikilink'] = '[[Category:$1]]', -- $1 is the category name
['cat wikilink sk'] = '[[Category:$1|$2]]', -- $1 is the category name; $2 is namespace sort key
[':cat wikilink'] = '[[:Category:$1|link]]', -- category name as maintenance message wikilink; $1 is the category name
-- Internal errors (should only occur if configuration is bad)
['undefined_error'] = 'Called with an undefined error condition',
['unknown_ID_key'] = 'Unrecognized ID key: ', -- an ID key in id_handlers not found in ~/Identifiers func_map{}
['unknown_ID_access'] = 'Unrecognized ID access keyword: ', -- an ID access keyword in id_handlers not found in keywords_lists['id-access']{}
['unknown_argument_map'] = 'Argument map not defined for this variable',
['bare_url_no_origin'] = 'Bare URL found but origin indicator is nil or empty',
['warning_msg_e'] = '<span style="color:#d33">One or more <code style="color: inherit; background: inherit; border: none; padding: inherit;">{{$1}}</code> templates have errors</span>; messages may be hidden ([[Help:CS1_errors#Controlling_error_message_display|help]]).'; -- $1 is template link
['warning_msg_m'] = '<span style="color:#3a3">One or more <code style="color: inherit; background: inherit; border: none; padding: inherit;">{{$1}}</code> templates have maintenance messages</span>; messages may be hidden ([[Help:CS1_errors#Controlling_error_message_display|help]]).'; -- $1 is template link
}
--[[--------------------------< C I T A T I O N _ C L A S S _ M A P >------------------------------------------
this table maps the value assigned to |CitationClass= in the cs1|2 templates to the canonical template name when
the value assigned to |CitationClass= is different from the canonical template name. |CitationClass= values are
used as class attributes in the <cite> tag that encloses the citation so these names may not contain spaces while
the canonical template name may. These names are used in warning_msg_e and warning_msg_m to create links to the
template's documentation when an article is displayed in preview mode.
Most cs1|2 template |CitationClass= values at en.wiki match their canonical template names so are not listed here.
]]
local citation_class_map_t = { -- TODO: if kept, these and all other config.CitationClass 'names' require some sort of i18n
['arxiv'] = 'arXiv',
['audio-visual'] = 'AV media',
['AV-media-notes'] = 'AV media notes',
['biorxiv'] = 'bioRxiv',
['citeseerx'] = 'CiteSeerX',
['encyclopaedia'] = 'encyclopedia',
['mailinglist'] = 'mailing list',
['medrxiv'] = 'medRxiv',
['pressrelease'] = 'press release',
['ssrn'] = 'SSRN',
['techreport'] = 'tech report',
}
--[=[-------------------------< E T _ A L _ P A T T E R N S >--------------------------------------------------
This table provides Lua patterns for the phrase "et al" and variants in name text
(author, editor, etc.). The main module uses these to identify and emit the 'etal' message.
]=]
local et_al_patterns = {
"[;,]? *[\"']*%f[%a][Ee][Tt]%.? *[Aa][Ll][%.;,\"']*$", -- variations on the 'et al' theme
"[;,]? *[\"']*%f[%a][Ee][Tt]%.? *[Aa][Ll][Ii][AaIi][Ee]?[%.;,\"']*$", -- variations on the 'et alia', 'et alii' and 'et aliae' themes (false positive 'et aliie' unlikely to match)
"[;,]? *%f[%a]and [Oo]thers", -- an alternative to et al.
"%[%[ *[Ee][Tt]%.? *[Aa][Ll]%.? *%]%]", -- a wikilinked form
"%(%( *[Ee][Tt]%.? *[Aa][Ll]%.? *%)%)", -- a double-bracketed form (to counter partial removal of ((...)) syntax)
"[%(%[] *[Ee][Tt]%.? *[Aa][Ll]%.? *[%)%]]", -- a bracketed form
}
--[[--------------------------< P R E S E N T A T I O N >------------------------
Fixed presentation markup. Originally part of citation_config.messages it has
been moved into its own, more semantically correct place.
]]
local presentation =
{
-- .citation-comment class is specified at Help:CS1_errors#Controlling_error_message_display
['hidden-error'] = '<span class="cs1-hidden-error citation-comment">$1</span>',
['visible-error'] = '<span class="cs1-visible-error citation-comment">$1</span>',
['hidden-maint'] = '<span class="cs1-maint citation-comment">$1</span>',
['accessdate'] = '<span class="reference-accessdate">$1$2</span>', -- to allow editors to hide accessdate using personal CSS
['bdi'] = '<bdi$1>$2</bdi>', -- bidirectional isolation used with |script-title= and the like
['cite'] = '<cite class="$1">$2</cite>'; -- for use when citation does not have a namelist and |ref= not set so no id="..." attribute
['cite-id'] = '<cite id="$1" class="$2">$3</cite>'; -- for use when when |ref= is set or when citation has a namelist
['format'] = ' <span class="cs1-format">($1)</span>', -- for |format=, |chapter-format=, etc.
['interwiki'] = ' <span class="cs1-format">[in $1]</span>', -- for interwiki-language-linked author, editor, etc
['interproj'] = ' <span class="cs1-format">[at $1]</span>', -- for interwiki-project-linked author, editor, etc (:d: and :s: supported; :w: ignored)
-- various access levels, for |access=, |doi-access=, |arxiv=, ...
-- narrow no-break space   may work better than nowrap CSS. Or not? Browser support?
['ext-link-access-signal'] = '<span class="$1" title="$2">$3</span>', -- external link with appropriate lock icon
['free'] = {class='id-lock-free', title='Freely accessible'}, -- classes defined in Module:Citation/CS1/styles.css
['registration'] = {class='id-lock-registration', title='Free registration required'},
['limited'] = {class='id-lock-limited', title='Free access subject to limited trial, subscription normally required'},
['subscription'] = {class='id-lock-subscription', title='Paid subscription required'},
['interwiki-icon'] = '<span class="$1" title="$2">$3</span>',
['class-wikisource'] = 'cs1-ws-icon',
['italic-title'] = "''$1''",
['kern-left'] = '<span class="cs1-kern-left"></span>$1', -- spacing to use when title contains leading single or double quote mark
['kern-right'] = '$1<span class="cs1-kern-right"></span>', -- spacing to use when title contains trailing single or double quote mark
['nowrap1'] = '<span class="nowrap">$1</span>', -- for nowrapping an item: <span ...>yyyy-mm-dd</span>
['nowrap2'] = '<span class="nowrap">$1</span> $2', -- for nowrapping portions of an item: <span ...>dd mmmm</span> yyyy (note white space)
['ocins'] = '<span title="$1" class="Z3988"></span>',
['parameter'] = '<code class="cs1-code">|$1=</code>',
['ps_cs1'] = '.'; -- CS1 style postscript (terminal) character
['ps_cs2'] = ''; -- CS2 style postscript (terminal) character (empty string)
['quoted-text'] = '<q>$1</q>', -- for wrapping |quote= content
['quoted-title'] = '"$1"',
['sep_cs1'] = '.', -- CS1 element separator
['sep_cs2'] = ',', -- CS2 separator
['sep_nl'] = ';', -- CS1|2 style name-list separator between names is a semicolon
['sep_nl_and'] = ' and ', -- used as last nl sep when |name-list-style=and and list has 2 items
['sep_nl_end'] = '; and ', -- used as last nl sep when |name-list-style=and and list has 3+ names
['sep_name'] = ', ', -- CS1|2 style last/first separator is <comma><space>
['sep_nl_vanc'] = ',', -- Vancouver style name-list separator between authors is a comma
['sep_name_vanc'] = ' ', -- Vancouver style last/first separator is a space
['sep_list'] = ', ', -- used for |language= when list has 3+ items except for last sep which uses sep_list_end
['sep_list_pair'] = ' and ', -- used for |language= when list has 2 items
['sep_list_end'] = ', and ', -- used as last list sep for |language= when list has 3+ items
['trans-italic-title'] = "[''$1'']",
['trans-quoted-title'] = "[$1]", -- for |trans-title= and |trans-quote=
['vol-bold'] = '$1 <b>$2</b>', -- sepc, volume; for bold journal cites; for other cites ['vol'] in messages{}
}
--[[--------------------------< A L I A S E S >---------------------------------
Aliases table for commonly passed parameters.
Parameter names on the right side in the assignments in this table must have been
defined in the Whitelist before they will be recognized as valid parameter names
]]
local aliases = {
['AccessDate'] = {'access-date', 'accessdate'}, -- Used by InternetArchiveBot
['Agency'] = 'agency',
['ArchiveDate'] = {'archive-date', 'archivedate'}, -- Used by InternetArchiveBot
['ArchiveFormat'] = 'archive-format',
['ArchiveURL'] = {'archive-url', 'archiveurl'}, -- Used by InternetArchiveBot
['ArticleNumber'] = 'article-number',
['ASINTLD'] = 'asin-tld',
['At'] = 'at', -- Used by InternetArchiveBot
['Authors'] = {'people', 'credits'},
['BookTitle'] = {'book-title', 'booktitle'},
['Cartography'] = 'cartography',
['Chapter'] = {'chapter', 'contribution', 'entry', 'article', 'section'},
['ChapterFormat'] = {'chapter-format', 'contribution-format', 'entry-format',
'article-format', 'section-format'};
['ChapterURL'] = {'chapter-url', 'contribution-url', 'entry-url', 'article-url', 'section-url'}, -- Used by InternetArchiveBot
['ChapterUrlAccess'] = {'chapter-url-access', 'contribution-url-access',
'entry-url-access', 'article-url-access', 'section-url-access'}, -- Used by InternetArchiveBot
['Class'] = 'class', -- cite arxiv and arxiv identifier
['Collaboration'] = 'collaboration',
['Conference'] = {'conference', 'event'},
['ConferenceFormat'] = 'conference-format',
['ConferenceURL'] = 'conference-url', -- Used by InternetArchiveBot
['Date'] = {'date', 'air-date', 'airdate'}, -- air-date and airdate for cite episode and cite serial only
['Degree'] = 'degree',
['DF'] = 'df',
['DisplayAuthors'] = {'display-authors', 'display-subjects'},
['DisplayContributors'] = 'display-contributors',
['DisplayEditors'] = 'display-editors',
['DisplayInterviewers'] = 'display-interviewers',
['DisplayTranslators'] = 'display-translators',
['Docket'] = 'docket',
['DoiBroken'] = 'doi-broken-date',
['Edition'] = 'edition',
['Embargo'] = 'pmc-embargo-date',
['Encyclopedia'] = {'encyclopedia', 'encyclopaedia', 'dictionary'}, -- cite encyclopedia only
['Episode'] = 'episode', -- cite serial only TODO: make available to cite episode?
['Format'] = 'format',
['ID'] = {'id', 'ID'},
['Inset'] = 'inset',
['Issue'] = {'issue', 'number'},
['Language'] = {'language', 'lang'},
['MailingList'] = {'mailing-list', 'mailinglist'}, -- cite mailing list only
['Map'] = 'map', -- cite map only
['MapFormat'] = 'map-format', -- cite map only
['MapURL'] = {'map-url', 'mapurl'}, -- cite map only -- Used by InternetArchiveBot
['MapUrlAccess'] = 'map-url-access', -- cite map only -- Used by InternetArchiveBot
['Minutes'] = 'minutes',
['Mode'] = 'mode',
['NameListStyle'] = 'name-list-style',
['Network'] = 'network',
['Newsgroup'] = 'newsgroup', -- cite newsgroup only
['NoPP'] = {'no-pp', 'nopp'},
['NoTracking'] = {'no-tracking', 'template-doc-demo'},
['Number'] = 'number', -- this case only for cite techreport
['OrigDate'] = {'orig-date', 'orig-year', 'origyear'},
['Others'] = 'others',
['Page'] = {'page', 'p'}, -- Used by InternetArchiveBot
['Pages'] = {'pages', 'pp'}, -- Used by InternetArchiveBot
['Periodical'] = {'journal', 'magazine', 'newspaper', 'periodical', 'website', 'work'},
['Place'] = {'place', 'location'},
['PostScript'] = 'postscript',
['PublicationDate'] = {'publication-date', 'publicationdate'},
['PublicationPlace'] = {'publication-place', 'publicationplace'},
['PublisherName'] = {'publisher', 'institution'},
['Quote'] = {'quote', 'quotation'},
['QuotePage'] = 'quote-page',
['QuotePages'] = 'quote-pages',
['Ref'] = 'ref',
['Scale'] = 'scale',
['ScriptChapter'] = {'script-chapter', 'script-contribution', 'script-entry',
'script-article', 'script-section'},
['ScriptEncyclopedia'] = {'script-encyclopedia', 'script-encyclopaedia'}, -- cite encyclopedia only
['ScriptMap'] = 'script-map',
['ScriptPeriodical'] = {'script-journal', 'script-magazine', 'script-newspaper',
'script-periodical', 'script-website', 'script-work'},
['ScriptQuote'] = 'script-quote',
['ScriptTitle'] = 'script-title', -- Used by InternetArchiveBot
['Season'] = 'season',
['Sections'] = 'sections', -- cite map only
['Series'] = {'series', 'version'},
['SeriesLink'] = {'series-link', 'serieslink'},
['SeriesNumber'] = {'series-number', 'series-no'},
['Sheet'] = 'sheet', -- cite map only
['Sheets'] = 'sheets', -- cite map only
['Station'] = 'station',
['Time'] = 'time',
['TimeCaption'] = 'time-caption',
['Title'] = 'title', -- Used by InternetArchiveBot
['TitleLink'] = {'title-link', 'episode-link', 'episodelink'}, -- Used by InternetArchiveBot
['TitleNote'] = {'title-note', 'department'},
['TitleType'] = {'type', 'medium'},
['TransChapter'] = {'trans-article', 'trans-chapter', 'trans-contribution',
'trans-entry', 'trans-section'},
['Transcript'] = 'transcript',
['TranscriptFormat'] = 'transcript-format',
['TranscriptURL'] = 'transcript-url', -- Used by InternetArchiveBot
['TransEncyclopedia'] = {'trans-encyclopedia', 'trans-encyclopaedia'}, -- cite encyclopedia only
['TransMap'] = 'trans-map', -- cite map only
['TransPeriodical'] = {'trans-journal', 'trans-magazine', 'trans-newspaper',
'trans-periodical', 'trans-website', 'trans-work'},
['TransQuote'] = 'trans-quote',
['TransTitle'] = 'trans-title', -- Used by InternetArchiveBot
['URL'] = {'url', 'URL'}, -- Used by InternetArchiveBot
['UrlAccess'] = 'url-access', -- Used by InternetArchiveBot
['UrlStatus'] = 'url-status', -- Used by InternetArchiveBot
['Vauthors'] = 'vauthors',
['Veditors'] = 'veditors',
['Via'] = 'via',
['Volume'] = 'volume',
['Year'] = 'year',
['AuthorList-First'] = {"first#", "author-first#", "author#-first", "author-given#", "author#-given",
"subject-first#", "subject#-first", "subject-given#", "subject#-given",
"given#"},
['AuthorList-Last'] = {"last#", "author-last#", "author#-last", "author-surname#", "author#-surname",
"subject-last#", "subject#-last", "subject-surname#", "subject#-surname",
"author#", 'host#', "subject#", "surname#"},
['AuthorList-Link'] = {"author-link#", "author#-link", "subject-link#",
"subject#-link", "authorlink#", "author#link"},
['AuthorList-Mask'] = {"author-mask#", "author#-mask", "subject-mask#", "subject#-mask"},
['ContributorList-First'] = {'contributor-first#', 'contributor#-first',
'contributor-given#', 'contributor#-given'},
['ContributorList-Last'] = {'contributor-last#', 'contributor#-last',
'contributor-surname#', 'contributor#-surname', 'contributor#'},
['ContributorList-Link'] = {'contributor-link#', 'contributor#-link'},
['ContributorList-Mask'] = {'contributor-mask#', 'contributor#-mask'},
['EditorList-First'] = {"editor-first#", "editor#-first", "editor-given#", "editor#-given"},
['EditorList-Last'] = {"editor-last#", "editor#-last", "editor-surname#",
"editor#-surname", "editor#"},
['EditorList-Link'] = {"editor-link#", "editor#-link"},
['EditorList-Mask'] = {"editor-mask#", "editor#-mask"},
['InterviewerList-First'] = {'interviewer-first#', 'interviewer#-first',
'interviewer-given#', 'interviewer#-given'},
['InterviewerList-Last'] = {'interviewer-last#', 'interviewer#-last',
'interviewer-surname#', 'interviewer#-surname', 'interviewer#'},
['InterviewerList-Link'] = {'interviewer-link#', 'interviewer#-link'},
['InterviewerList-Mask'] = {'interviewer-mask#', 'interviewer#-mask'},
['TranslatorList-First'] = {'translator-first#', 'translator#-first',
'translator-given#', 'translator#-given'},
['TranslatorList-Last'] = {'translator-last#', 'translator#-last',
'translator-surname#', 'translator#-surname', 'translator#'},
['TranslatorList-Link'] = {'translator-link#', 'translator#-link'},
['TranslatorList-Mask'] = {'translator-mask#', 'translator#-mask'},
}
--[[--------------------------< P U N C T _ S K I P >---------------------------
builds a table of parameter names that the extraneous terminal punctuation check should not check.
]]
local punct_meta_params = { -- table of aliases[] keys (meta parameters); each key has a table of parameter names for a value
'BookTitle', 'Chapter', 'ScriptChapter', 'ScriptTitle', 'Title', 'TransChapter', 'Transcript', 'TransMap', 'TransTitle', -- title-holding parameters
'AuthorList-Mask', 'ContributorList-Mask', 'EditorList-Mask', 'InterviewerList-Mask', 'TranslatorList-Mask', -- name-list mask may have name separators
'PostScript', 'Quote', 'ScriptQuote', 'TransQuote', 'Ref', -- miscellaneous
'ArchiveURL', 'ChapterURL', 'ConferenceURL', 'MapURL', 'TranscriptURL', 'URL', -- URL-holding parameters
}
local url_meta_params = { -- table of aliases[] keys (meta parameters); each key has a table of parameter names for a value
'ArchiveURL', 'ChapterURL', 'ConferenceURL', 'ID', 'MapURL', 'TranscriptURL', 'URL', -- parameters allowed to hold urls
'Page', 'Pages', 'At', 'QuotePage', 'QuotePages', -- insource locators allowed to hold urls
}
local function build_skip_table (skip_t, meta_params)
for _, meta_param in ipairs (meta_params) do -- for each meta parameter key
local params = aliases[meta_param]; -- get the parameter or the table of parameters associated with the meta parameter name
if 'string' == type (params) then
skip_t[params] = 1; -- just a single parameter
else
for _, param in ipairs (params) do -- get the parameter name
skip_t[param] = 1; -- add the parameter name to the skip table
local count;
param, count = param:gsub ('#', ''); -- remove enumerator marker from enumerated parameters
if 0 ~= count then -- if removed
skip_t[param] = 1; -- add param name without enumerator marker
end
end
end
end
return skip_t;
end
local punct_skip = {};
local url_skip = {};
--[[--------------------------< S I N G L E - L E T T E R S E C O N D - L E V E L D O M A I N S >----------
this is a list of tlds that are known to have single-letter second-level domain names. This list does not include
ccTLDs which are accepted in is_domain_name().
]]
local single_letter_2nd_lvl_domains_t = {'cash', 'company', 'foundation', 'media', 'org', 'today'};
--[[-----------< S P E C I A L C A S E T R A N S L A T I O N S >------------
This table is primarily here to support internationalization. Translations in
this table are used, for example, when an error message, category name, etc.,
is extracted from the English alias key. There may be other cases where
this translation table may be useful.
]]
local is_Latn = 'A-Za-z\195\128-\195\150\195\152-\195\182\195\184-\198\191\199\132-\201\143\225\184\128-\225\187\191';
local special_case_translation = {
['AuthorList'] = 'authors list', -- used to assemble maintenance category names
['ContributorList'] = 'contributors list', -- translation of these names plus translation of the base maintenance category names in maint_cats{} table below
['EditorList'] = 'editors list', -- must match the names of the actual categories
['InterviewerList'] = 'interviewers list', -- this group or translations used by name_has_ed_markup() and name_has_mult_names()
['TranslatorList'] = 'translators list',
-- 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'] = nil, -- 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'] = nil},
{['en'] = {'page not found', true}, ['local'] = nil},
{['en'] = {'subscribe to read', true}, ['local'] = nil},
{['en'] = {'^[%(%[{<]?unknown[>}%]%)]?$', false}, ['local'] = nil},
{['en'] = {'website is for sale', true}, ['local'] = nil},
{['en'] = {'^404', false}, ['local'] = nil},
{['en'] = {'error[ %-]404', false}, ['local'] = nil},
{['en'] = {'internet archive wayback machine', true}, ['local'] = nil},
{['en'] = {'log into facebook', true}, ['local'] = nil},
{['en'] = {'login • instagram', true}, ['local'] = nil},
{['en'] = {'redirecting...', true}, ['local'] = nil},
{['en'] = {'usurped title', true}, ['local'] = nil}, -- 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'] = nil},
},
['reject'] = {
{['en'] = {'about us', true}, ['local'] = nil},
{['en'] = {'%f[%a][Aa]dvisor%f[%A]', false}, ['local'] = nil},
{['en'] = {'allmusic', true}, ['local'] = nil},
{['en'] = {'%f[%a][Aa]uthor%f[%A]', false}, ['local'] = nil},
{['en'] = {'^[Bb]ureau$', false}, ['local'] = nil},
{['en'] = {'business', true}, ['local'] = nil},
{['en'] = {'cnn', true}, ['local'] = nil},
{['en'] = {'collaborator', true}, ['local'] = nil},
{['en'] = {'^[Cc]ompany$', false}, ['local'] = nil},
{['en'] = {'contributor', true}, ['local'] = nil},
{['en'] = {'contact us', true}, ['local'] = nil},
{['en'] = {'correspondent', true}, ['local'] = nil},
{['en'] = {'^[Dd]esk$', false}, ['local'] = nil},
{['en'] = {'directory', true}, ['local'] = nil},
{['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'] = nil},
{['en'] = {'%f[%a][Ee]ditors?%f[%A]', false}, ['local'] = nil},
{['en'] = {'%f[%a][Ee]mail%f[%A]', false}, ['local'] = nil},
{['en'] = {'facebook', true}, ['local'] = nil},
{['en'] = {'google', true}, ['local'] = nil},
{['en'] = {'^[Gg]roup$', false}, ['local'] = nil},
{['en'] = {'home page', true}, ['local'] = nil},
{['en'] = {'^[Ii]nc%.?$', false}, ['local'] = nil},
{['en'] = {'instagram', true}, ['local'] = nil},
{['en'] = {'interviewer', true}, ['local'] = nil},
{['en'] = {'^[Ll]imited$', false}, ['local'] = nil},
{['en'] = {'linkedIn', true}, ['local'] = nil},
{['en'] = {'^[Nn]ews$', false}, ['local'] = nil},
{['en'] = {'[Nn]ews[ %-]?[Rr]oom', false}, ['local'] = nil},
{['en'] = {'pinterest', true}, ['local'] = nil},
{['en'] = {'policy', true}, ['local'] = nil},
{['en'] = {'privacy', true}, ['local'] = nil},
{['en'] = {'reuters', true}, ['local'] = nil},
{['en'] = {'translator', true}, ['local'] = nil},
{['en'] = {'tumblr', true}, ['local'] = nil},
{['en'] = {'twitter', true}, ['local'] = nil},
{['en'] = {'site name', true}, ['local'] = nil},
{['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'] = nil},
{['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'] = {['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},
},
['inv_local_long'] = {}, -- used in date reformatting & translation; copy of date_names['local'].long where k/v are inverted: [1]='<local name>' etc.
['inv_local_short'] = {}, -- used in date reformatting & translation; copy of date_names['local'].short where k/v are inverted: [1]='<local name>' etc.
['inv_local_quarter'] = {}, -- used in date translation; copy of date_names['local'].quarter where k/v are inverted: [1]='<local name>' etc.
['inv_local_season'] = {}, -- used in date translation; copy of date_names['local'].season where k/v are inverted: [1]='<local name>' etc.
['inv_local_named'] = {}, -- used in date translation; copy of date_names['local'].named where k/v are inverted: [1]='<local name>' etc.
['local_digits'] = {['0'] = '0', ['1'] = '1', ['2'] = '2', ['3'] = '3', ['4'] = '4', ['5'] = '5', ['6'] = '6', ['7'] = '7', ['8'] = '8', ['9'] = '9'}, -- used to convert local language digits to Western 0-9
['xlate_digits'] = {},
}
if local_date_names_from_mediawiki then -- if fetching local month names from MediaWiki is enabled
local long_t = {};
local short_t = {};
for i=1, 12 do -- loop 12x and
local name = lang_obj:formatDate('F', '2022-' .. i .. '-1'); -- get long month name for each i
long_t[name] = i; -- save it
name = lang_obj:formatDate('M', '2022-' .. i .. '-1'); -- get short month name for each i
short_t[name] = i; -- save it
end
date_names['local']['long'] = long_t; -- write the long table – overwrites manual translation
date_names['local']['short'] = short_t; -- write the short table – overwrites manual translation
end
-- create inverted date-name tables for reformatting and/or translation
for _, invert_t in pairs {{'long', 'inv_local_long'}, {'short', 'inv_local_short'}, {'quarter', 'inv_local_quarter'}, {'season', 'inv_local_season'}, {'named', 'inv_local_named'}} do
for name, i in pairs (date_names['local'][invert_t[1]]) do -- this table is ['name'] = i
date_names[invert_t[2]][i] = name; -- invert to get [i] = 'name' for conversions from ymd
end
end
if local_digits_from_mediawiki then -- if fetching local digits from MediaWiki is enabled
local digits_t = {};
for i=0, 9 do -- loop 10x and
digits_t [lang_obj:formatNum (i)] = tostring (i); -- format the loop indexer as local lang table index and assign loop indexer (a string) as the value
end
date_names['local_digits'] = digits_t;
end
for ld, ed in pairs (date_names.local_digits) do -- make a digit translation table for simple date translation from en to local language using local_digits table
date_names.xlate_digits [ed] = ld; -- en digit becomes index with local digit as the value
end
local df_template_patterns = { -- table of redirects to {{Use dmy dates}} and {{Use mdy dates}}
'{{ *[Uu]se +(dmy) +dates *[|}]', -- 1159k -- sorted by approximate transclusion count
'{{ *[Uu]se +(mdy) +dates *[|}]', -- 212k
'{{ *[Uu]se +(MDY) +dates *[|}]', -- 788
'{{ *[Uu]se +(DMY) +dates *[|}]', -- 343
'{{ *([Mm]dy) *[|}]', -- 176
'{{ *[Uu]se *(dmy) *[|}]', -- 156 + 18
'{{ *[Uu]se *(mdy) *[|}]', -- 149 + 11
'{{ *([Dd]my) *[|}]', -- 56
'{{ *[Uu]se +(MDY) *[|}]', -- 5
'{{ *([Dd]MY) *[|}]', -- 3
'{{ *[Uu]se(mdy)dates *[|}]', -- 1
'{{ *[Uu]se +(DMY) *[|}]', -- 0
'{{ *([Mm]DY) *[|}]', -- 0
}
local title_object = mw.title.getCurrentTitle();
local content; -- done this way so that unused templates appear in unused-template-reports; self-transcluded makes them look like they are used
if 10 ~= title_object.namespace then -- all namespaces except Template
content = title_object:getContent() or ''; -- get the content of the article or ''; new pages edited w/ve do not have 'content' until saved; ve does not preview; phab:T221625
end
local function get_date_format ()
if not content then -- nil content when we're in template
return nil; -- auto-formatting does not work in Template space so don't set global_df
end
for _, pattern in ipairs (df_template_patterns) do -- loop through the patterns looking for {{Use dmy dates}} or {{Use mdy dates}} or any of their redirects
local start, _, match = content:find(pattern); -- match is the three letters indicating desired date format
if match then
local use_dates_template = content:match ('%b{}', start); -- get the whole template
if use_dates_template:match ('| *cs1%-dates *= *[lsy][sy]?') then -- look for |cs1-dates=publication date length access-/archive-date length
return match:lower() .. '-' .. use_dates_template:match ('| *cs1%-dates *= *([lsy][sy]?)');
else
return match:lower() .. '-all'; -- no |cs1-dates= k/v pair; return value appropriate for use in |df=
end
end
end
end
local global_df; -- TODO: add this to <global_cs1_config_t>?
--[[-----------------< V O L U M E , I S S U E , P A G E S >------------------
These tables hold cite class values (from the template invocation) and identify those templates that support
|volume=, |issue=, and |page(s)= parameters. Cite conference and cite map require further qualification which
is handled in the main module.
]]
local templates_using_volume = {'citation', 'audio-visual', 'book', 'conference', 'encyclopaedia', 'interview', 'journal', 'magazine', 'map', 'news', 'report', 'techreport', 'thesis'}
local templates_using_issue = {'citation', 'conference', 'episode', 'interview', 'journal', 'magazine', 'map', 'news', 'podcast'}
local templates_not_using_page = {'audio-visual', 'episode', 'mailinglist', 'newsgroup', 'podcast', 'serial', 'sign', 'speech'}
--[[
These tables control when it is appropriate for {{citation}} to render |volume= and/or |issue=. The parameter
names in the tables constrain {{citation}} so that its renderings match the renderings of the equivalent cs1
templates. For example, {{cite web}} does not support |volume= so the equivalent {{citation |website=...}} must
not support |volume=.
]]
local citation_no_volume_t = { -- {{citation}} does not render |volume= when these parameters are used
'website', 'mailinglist', 'script-website',
}
local citation_issue_t = { -- {{citation}} may render |issue= when these parameters are used
'journal', 'magazine', 'newspaper', 'periodical', 'work',
'script-journal', 'script-magazine', 'script-newspaper', 'script-periodical', 'script-work',
}
--[[
Patterns for finding extra text in |volume=, |issue=, |page=, |pages=
]]
local vol_iss_pg_patterns = {
good_ppattern = '^P[^%.PpGg]', -- OK to begin with uppercase P: P7 (page 7 of section P), but not p123 (page 123); TODO: this allows 'Pages' which it should not
bad_ppatterns = { -- patterns for |page= and |pages=
'^[Pp][PpGg]?%.?[ %d]',
'^[Pp][Pp]?%. ', -- from {{p.}} and {{pp.}} templates
'^[Pp]ages?',
'^[Pp]gs.?',
},
vi_patterns_t = { -- combined to catch volume-like text in |issue= and issue-like text in |volume=
'^volumes?', -- volume-like text
'^vols?[%.:=]?',
'^issues?', --issue-like text
'^iss[%.:=]?',
'^numbers?',
'^nos?%A', -- don't match 'november' or 'nostradamus'
'^nr[%.:=]?',
'^n[%.:= ]', -- might be a valid issue without separator (space char is sep char here)
'^n°', -- 'n' with degree sign (U+00B0)
'^№', -- precomposed unicode numero character (U+2116)
},
}
--[[--------------------------< K E Y W O R D S >-------------------------------
These tables hold keywords for those parameters that have defined sets of acceptable keywords.
]]
--[[-------------------< K E Y W O R D S T A B L E >--------------------------
this is a list of keywords; each key in the list is associated with a table of
synonymous keywords possibly from different languages.
for I18N: add local-language keywords to value table; do not change the key.
For example, adding the German keyword 'ja':
['affirmative'] = {'yes', 'true', 'y', 'ja'},
Because CS1|2 templates from en.wiki articles are often copied to other local wikis,
it is recommended that the English keywords remain in these tables.
]]
local keywords = {
['amp'] = {'&', 'amp', 'ampersand'}, -- |name-list-style=
['and'] = {'and', 'serial'}, -- |name-list-style=
['affirmative'] = {'yes', 'true', 'y'}, -- |no-tracking=, |no-pp= -- Used by InternetArchiveBot
['afterword'] = {'afterword'}, -- |contribution=
['bot: unknown'] = {'bot: unknown'}, -- |url-status= -- Used by InternetArchiveBot
['cs1'] = {'cs1'}, -- |mode=
['cs2'] = {'cs2'}, -- |mode=
['dead'] = {'dead', 'deviated'}, -- |url-status= -- Used by InternetArchiveBot
['dmy'] = {'dmy'}, -- |df=
['dmy-all'] = {'dmy-all'}, -- |df=
['foreword'] = {'foreword'}, -- |contribution=
['free'] = {'free'}, -- |<id>-access= -- Used by InternetArchiveBot
['harv'] = {'harv'}, -- |ref=; this no longer supported; is_valid_parameter_value() called with <invert> = true
['introduction'] = {'introduction'}, -- |contribution=
['limited'] = {'limited'}, -- |url-access= -- Used by InternetArchiveBot
['live'] = {'live'}, -- |url-status= -- Used by InternetArchiveBot
['mdy'] = {'mdy'}, -- |df=
['mdy-all'] = {'mdy-all'}, -- |df=
['none'] = {'none'}, -- |postscript=, |ref=, |title=, |type= -- Used by InternetArchiveBot
['off'] = {'off'}, -- |title= (potentially also: |title-link=, |postscript=, |ref=, |type=)
['preface'] = {'preface'}, -- |contribution=
['registration'] = {'registration'}, -- |url-access= -- Used by InternetArchiveBot
['subscription'] = {'subscription'}, -- |url-access= -- Used by InternetArchiveBot
['unfit'] = {'unfit'}, -- |url-status= -- Used by InternetArchiveBot
['usurped'] = {'usurped'}, -- |url-status= -- Used by InternetArchiveBot
['vanc'] = {'vanc'}, -- |name-list-style=
['ymd'] = {'ymd'}, -- |df=
['ymd-all'] = {'ymd-all'}, -- |df=
-- ['yMd'] = {'yMd'}, -- |df=; not supported at en.wiki
-- ['yMd-all'] = {'yMd-all'}, -- |df=; not supported at en.wiki
}
--[[------------------------< X L A T E _ K E Y W O R D S >---------------------
this function builds a list, keywords_xlate{}, of the keywords found in keywords{} where the values from keywords{}
become the keys in keywords_xlate{} and the keys from keywords{} become the values in keywords_xlate{}:
['affirmative'] = {'yes', 'true', 'y'}, -- in keywords{}
becomes
['yes'] = 'affirmative', -- in keywords_xlate{}
['true'] = 'affirmative',
['y'] = 'affirmative',
the purpose of this function is to act as a translator between a non-English keyword and its English equivalent
that may be used in other modules of this suite
]]
local function xlate_keywords ()
local out_table = {}; -- output goes here
for k, keywords_t in pairs (keywords) do -- spin through the keywords table
for _, keyword in ipairs (keywords_t) do -- for each keyword
out_table[keyword] = k; -- create an entry in the output table where keyword is the key
end
end
return out_table;
end
local keywords_xlate = xlate_keywords (); -- the list of translated keywords
--[[----------------< M A K E _ K E Y W O R D S _ L I S T >---------------------
this function assembles, for parameter-value validation, the list of keywords appropriate to that parameter.
keywords_lists{}, is a table of tables from keywords{}
]]
local function make_keywords_list (keywords_lists)
local out_table = {}; -- output goes here
for _, keyword_list in ipairs (keywords_lists) do -- spin through keywords_lists{} and get a table of keywords
for _, keyword in ipairs (keyword_list) do -- spin through keyword_list{} and add each keyword, ...
table.insert (out_table, keyword); -- ... as plain text, to the output list
end
end
return out_table;
end
--[[----------------< K E Y W O R D S _ L I S T S >-----------------------------
this is a list of lists of valid keywords for the various parameters in [key].
Generally the keys in this table are the canonical en.wiki parameter names though
some are contrived because of use in multiple differently named parameters:
['yes_true_y'], ['id-access'].
The function make_keywords_list() extracts the individual keywords from the
appropriate list in keywords{}.
The lists in this table are used to validate the keyword assignment for the
parameters named in this table's keys.
]]
local keywords_lists = {
['yes_true_y'] = make_keywords_list ({keywords.affirmative}),
['contribution'] = make_keywords_list ({keywords.afterword, keywords.foreword, keywords.introduction, keywords.preface}),
['df'] = make_keywords_list ({keywords.dmy, keywords['dmy-all'], keywords.mdy, keywords['mdy-all'], keywords.ymd, keywords['ymd-all']}),
-- ['df'] = make_keywords_list ({keywords.dmy, keywords['dmy-all'], keywords.mdy, keywords['mdy-all'], keywords.ymd, keywords['ymd-all'], keywords.yMd, keywords['yMd-all']}), -- not supported at en.wiki
['mode'] = make_keywords_list ({keywords.cs1, keywords.cs2}),
['name-list-style'] = make_keywords_list ({keywords.amp, keywords['and'], keywords.vanc}),
['ref'] = make_keywords_list ({keywords.harv}), -- inverted check; |ref=harv no longer supported
['url-access'] = make_keywords_list ({keywords.subscription, keywords.limited, keywords.registration}),
['url-status'] = make_keywords_list ({keywords.dead, keywords.live, keywords.unfit, keywords.usurped, keywords['bot: unknown']}),
['id-access'] = make_keywords_list ({keywords.free}),
}
--[[--------------------------< C S 1 _ C O N F I G _ G E T >--------------------------------------------------
fetch and validate values from {{cs1 config}} template to fill <global_cs1_config_t>
no error messages; when errors are detected, the parameter value from {{cs1 config}} is blanked.
Supports all parameters and aliases associated with the metaparameters: DisplayAuthors, DisplayContributors,
DisplayEditors, DisplayInterviewers, DisplayTranslators, NameListStyle, and Mode. The DisplayWhatever metaparameters
accept numeric values only (|display-authors=etal and the like is not supported).
]]
local global_cs1_config_t = {}; -- TODO: add value returned from get_date_format() to this table?
local function get_cs1_config ()
if not content then -- nil content when we're in template
return nil; -- auto-formatting does not work in Template space so don't set global_df
end
local start = content:find('{{ *[Cc][Ss]1 config *[|}]'); -- <start> is offset into <content> when {{cs1 config}} found; nil else
if start then
local cs1_config_template = content:match ('%b{}', start); -- get the whole template
if not cs1_config_template then
return nil;
end
local params_t = mw.text.split (cs1_config_template:gsub ('^{{%s*', ''):gsub ('%s*}}$', ''), '%s*|%s*'); -- remove '{{' and '}}'; make a sequence of parameter/value pairs (split on the pipe)
table.remove (params_t, 1); -- remove the template name because it isn't a parameter/value pair
local config_meta_params_t = {'DisplayAuthors', 'DisplayContributors', 'DisplayEditors', 'DisplayInterviewers', 'DisplayTranslators', 'NameListStyle', 'Mode'};
local meta_param_map_t = {}; -- list of accepted parameter names usable in {{cs1 config}} goes here
for _, meta_param in ipairs (config_meta_params_t) do -- for i18n using <config_meta_params_t>, map template parameter names to their metaparameter equivalents
if 'table' == type (aliases[meta_param]) then -- if <meta_param> is a sequence,
for _, param in ipairs (aliases[meta_param]) do -- extract its contents
meta_param_map_t[param] = meta_param; -- and add to <meta_param_map_t>
end
else
meta_param_map_t[aliases[meta_param]] = meta_param; -- not a sequence so just add the parameter to <meta_param_map_t>
end
end
local keywords_t = {}; -- map valid keywords to their associate metaparameter; reverse form of <keyword_lists[key] for these metaparameters
for _, metaparam_t in ipairs ({{'NameListStyle', 'name-list-style'}, {'Mode', 'mode'}}) do -- only these metaparameter / keywords_lists key pairs
for _, keyword in ipairs (keywords_lists[metaparam_t[2]]) do -- spin through the list of keywords
keywords_t[keyword] = metaparam_t[1]; -- add [keyword] = metaparameter to the map
end
end
for _, param in ipairs (params_t) do -- spin through the {{cs1 config}} parameters and fill <global_cs1_config_t>
local k, v = param:match ('([^=]-)%s*=%s*(.+)'); -- <k> is the parameter name; <v> is parameter's assigned value
if k then
if k:find ('^display') then -- if <k> is one of the |display-<namelist>= parameters
if v:match ('%d+') then -- the assigned value must be digits; doesn't accept 'etal'
global_cs1_config_t[meta_param_map_t[k]]=v; -- add the display param and its value to globals table
end
else
if keywords_t[v] == meta_param_map_t[k] then -- keywords_t[v] returns nil or the metaparam name; these must be the same
global_cs1_config_t[meta_param_map_t[k]]=v; -- add the parameter and its value to globals table
end
end
end
end
end
end
get_cs1_config (); -- fill <global_cs1_config_t>
--[[---------------------< S T R I P M A R K E R S >----------------------------
Common pattern definition location for stripmarkers so that we don't have to go
hunting for them if (when) MediaWiki changes their form.
]]
local stripmarkers = {
['any'] = '\127[^\127]*UNIQ%-%-(%a+)%-[%a%d]+%-QINU[^\127]*\127', -- capture returns name of stripmarker
['math'] = '\127[^\127]*UNIQ%-%-math%-[%a%d]+%-QINU[^\127]*\127' -- math stripmarkers used in coins_cleanup() and coins_replace_math_stripmarker()
}
--[[------------< I N V I S I B L E _ C H A R A C T E R S >---------------------
This table holds non-printing or invisible characters indexed either by name or
by Unicode group. Values are decimal representations of UTF-8 codes. The table
is organized as a table of tables because the Lua pairs keyword returns table
data in an arbitrary order. Here, we want to process the table from top to bottom
because the entries at the top of the table are also found in the ranges specified
by the entries at the bottom of the table.
Also here is a pattern that recognizes stripmarkers that begin and end with the
delete characters. The nowiki stripmarker is not an error but some others are
because the parameter values that include them become part of the template's
metadata before stripmarker replacement.
]]
local invisible_defs = {
del = '\127', -- used to distinguish between stripmarker and del char
zwj = '\226\128\141', -- used with capture because zwj may be allowed
}
local invisible_chars = {
{'replacement', '\239\191\189'}, -- U+FFFD, EF BF BD
{'zero width joiner', '('.. invisible_defs.zwj .. ')'}, -- U+200D, E2 80 8D; capture because zwj may be allowed
{'zero width space', '\226\128\139'}, -- U+200B, E2 80 8B
{'hair space', '\226\128\138'}, -- U+200A, E2 80 8A
{'soft hyphen', '\194\173'}, -- U+00AD, C2 AD
{'horizontal tab', '\009'}, -- U+0009 (HT), 09
{'line feed', '\010'}, -- U+000A (LF), 0A
{'no-break space', '\194\160'}, -- U+00A0 (NBSP), C2 A0
{'carriage return', '\013'}, -- U+000D (CR), 0D
{'stripmarker', stripmarkers.any}, -- stripmarker; may or may not be an error; capture returns the stripmaker type
{'delete', '('.. invisible_defs.del .. ')'}, -- U+007F (DEL), 7F; must be done after stripmarker test; capture to distinguish isolated del chars not part of stripmarker
{'C0 control', '[\000-\008\011\012\014-\031]'}, -- U+0000–U+001F (NULL–US), 00–1F (except HT, LF, CR (09, 0A, 0D))
{'C1 control', '[\194\128-\194\159]'}, -- U+0080–U+009F (XXX–APC), C2 80 – C2 9F
-- {'Specials', '[\239\191\185-\239\191\191]'}, -- U+FFF9-U+FFFF, EF BF B9 – EF BF BF
-- {'Private use area', '[\238\128\128-\239\163\191]'}, -- U+E000–U+F8FF, EE 80 80 – EF A3 BF
-- {'Supplementary Private Use Area-A', '[\243\176\128\128-\243\191\191\189]'}, -- U+F0000–U+FFFFD, F3 B0 80 80 – F3 BF BF BD
-- {'Supplementary Private Use Area-B', '[\244\128\128\128-\244\143\191\189]'}, -- U+100000–U+10FFFD, F4 80 80 80 – F4 8F BF BD
}
--[[
Indic script makes use of zero width joiner as a character modifier so zwj
characters must be left in. This pattern covers all of the unicode characters
for these languages:
Devanagari 0900–097F – https://unicode.org/charts/PDF/U0900.pdf
Devanagari extended A8E0–A8FF – https://unicode.org/charts/PDF/UA8E0.pdf
Bengali 0980–09FF – https://unicode.org/charts/PDF/U0980.pdf
Gurmukhi 0A00–0A7F – https://unicode.org/charts/PDF/U0A00.pdf
Gujarati 0A80–0AFF – https://unicode.org/charts/PDF/U0A80.pdf
Oriya 0B00–0B7F – https://unicode.org/charts/PDF/U0B00.pdf
Tamil 0B80–0BFF – https://unicode.org/charts/PDF/U0B80.pdf
Telugu 0C00–0C7F – https://unicode.org/charts/PDF/U0C00.pdf
Kannada 0C80–0CFF – https://unicode.org/charts/PDF/U0C80.pdf
Malayalam 0D00–0D7F – https://unicode.org/charts/PDF/U0D00.pdf
plus the not-necessarily Indic scripts for Sinhala and Burmese:
Sinhala 0D80-0DFF - https://unicode.org/charts/PDF/U0D80.pdf
Myanmar 1000-109F - https://unicode.org/charts/PDF/U1000.pdf
Myanmar extended A AA60-AA7F - https://unicode.org/charts/PDF/UAA60.pdf
Myanmar extended B A9E0-A9FF - https://unicode.org/charts/PDF/UA9E0.pdf
the pattern is used by has_invisible_chars() and coins_cleanup()
]]
local indic_script = '[\224\164\128-\224\181\191\224\163\160-\224\183\191\225\128\128-\225\130\159\234\167\160-\234\167\191\234\169\160-\234\169\191]';
-- list of emoji that use a zwj character (U+200D) to combine with another emoji
-- from: https://unicode.org/Public/emoji/16.0/emoji-zwj-sequences.txt; version: 16.0; 2024-08-14
-- table created by: [[:en:Module:Make emoji zwj table]]
local emoji_t = { -- indexes are decimal forms of the hex values in U+xxxx
[8596] = true, -- U+2194 ↔ left right arrow
[8597] = true, -- U+2195 ↕ up down arrow
[9760] = true, -- U+2620 ☠ skull and crossbones
[9792] = true, -- U+2640 ♀ female sign
[9794] = true, -- U+2642 ♂ male sign
[9877] = true, -- U+2695 ⚕ staff of aesculapius
[9878] = true, -- U+2696 ⚖ scales
[9895] = true, -- U+26A7 ⚧ male with stroke and male and female sign
[9992] = true, -- U+2708 ✈ airplane
[10052] = true, -- U+2744 ❄ snowflake
[10084] = true, -- U+2764 ❤ heavy black heart
[10145] = true, -- U+27A1 ➡ black rightwards arrow
[11035] = true, -- U+2B1B ⬛ black large square
[127752] = true, -- U+1F308 🌈 rainbow
[127787] = true, -- U+1F32B 🌫 fog
[127806] = true, -- U+1F33E 🌾 ear of rice
[127859] = true, -- U+1F373 🍳 cooking
[127868] = true, -- U+1F37C 🍼 baby bottle
[127876] = true, -- U+1F384 🎄 christmas tree
[127891] = true, -- U+1F393 🎓 graduation cap
[127908] = true, -- U+1F3A4 🎤 microphone
[127912] = true, -- U+1F3A8 🎨 artist palette
[127979] = true, -- U+1F3EB 🏫 school
[127981] = true, -- U+1F3ED 🏭 factory
[128102] = true, -- U+1F466 👦 boy
[128103] = true, -- U+1F467 👧 girl
[128104] = true, -- U+1F468 👨 man
[128105] = true, -- U+1F469 👩 woman
[128139] = true, -- U+1F48B 💋 kiss mark
[128165] = true, -- U+1F4A5 💥 collision symbol
[128168] = true, -- U+1F4A8 💨 dash symbol
[128171] = true, -- U+1F4AB 💫 dizzy symbol
[128187] = true, -- U+1F4BB 💻 personal computer
[128188] = true, -- U+1F4BC 💼 brief case
[128293] = true, -- U+1F525 🔥 fire
[128295] = true, -- U+1F527 🔧 wrench
[128300] = true, -- U+1F52C 🔬 microscope
[128488] = true, -- U+1F5E8 🗨 left speech bubble
[128640] = true, -- U+1F680 🚀 rocket
[128658] = true, -- U+1F692 🚒 fire engine
[129001] = true, -- U+1F7E9 🟩 large green square
[129003] = true, -- U+1F7EB 🟫 large brown square
[129309] = true, -- U+1F91D 🤝 handshake
[129455] = true, -- U+1F9AF 🦯 probing cane
[129456] = true, -- U+1F9B0 🦰 emoji component red hair
[129457] = true, -- U+1F9B1 🦱 emoji component curly hair
[129458] = true, -- U+1F9B2 🦲 emoji component bald
[129459] = true, -- U+1F9B3 🦳 emoji component white hair
[129466] = true, -- U+1F9BA 🦺 safety vest
[129468] = true, -- U+1F9BC 🦼 motorized wheelchair
[129469] = true, -- U+1F9BD 🦽 manual wheelchair
[129489] = true, -- U+1F9D1 🧑 adult
[129490] = true, -- U+1F9D2 🧒 child
[129657] = true, -- U+1FA79 🩹 adhesive bandage
[129778] = true, -- U+1FAF2 🫲 leftwards hand
}
--[[----------------------< L A N G U A G E S U P P O R T >-------------------
These tables and constants support various language-specific functionality.
]]
--local this_wiki_code = mw.getContentLanguage():getCode(); -- get this wiki's language code
local this_wiki_code = lang_obj:getCode(); -- get this wiki's language code
if string.match (mw.site.server, 'wikidata') then
this_wiki_code = mw.getCurrentFrame():callParserFunction('int', {'lang'}); -- on Wikidata so use interface language setting instead
end
local mw_languages_by_tag_t = mw.language.fetchLanguageNames (this_wiki_code, 'all'); -- get a table of language tag/name pairs known to Wikimedia; used for interwiki tests
local mw_languages_by_name_t = {};
for k, v in pairs (mw_languages_by_tag_t) do -- build a 'reversed' table name/tag language pairs know to MediaWiki; used for |language=
v = mw.ustring.lower (v); -- lowercase for tag fetch; get name's proper case from mw_languages_by_tag_t[<tag>]
if mw_languages_by_name_t[v] then -- when name already in the table
if 2 == #k or 3 == #k then -- if tag does not have subtags
mw_languages_by_name_t[v] = k; -- prefer the shortest tag for this name
end
else -- here when name not in the table
mw_languages_by_name_t[v] = k; -- so add name and matching tag
end
end
local inter_wiki_map = {}; -- map of interwiki prefixes that are language-code prefixes
for k, v in pairs (mw.site.interwikiMap ('local')) do -- spin through the base interwiki map (limited to local)
if mw_languages_by_tag_t[v["prefix"]] then -- if the prefix matches a known language tag
inter_wiki_map[v["prefix"]] = true; -- add it to our local map
end
end
--[[--------------------< S C R I P T _ L A N G _ C O D E S >-------------------
This table is used to hold ISO 639-1 two-character and ISO 639-3 three-character
language codes that apply only to |script-title= and |script-chapter=
]]
local script_lang_codes = {
'ab', 'am', 'ar', 'az', 'be', 'bg', 'bn', 'bo', 'bs', 'ce', 'chr', 'dv', 'dz',
'el', 'fa', 'grc', 'gu', 'he', 'hi', 'hy', 'ja', 'ka', 'kk', 'km', 'kn', 'ko',
'ku', 'ky', 'lo', 'mk', 'ml', 'mn', 'mni', 'mr', 'my', 'ne', 'or', 'ota',
'pa', 'ps', 'ru', 'sd', 'si', 'sr', 'syc', 'ta', 'te', 'tg', 'th', 'ti', 'tt',
'ug', 'uk', 'ur', 'uz', 'yi', 'yue', 'zh', 'zgh'
};
--[[---------------< L A N G U A G E R E M A P P I N G >----------------------
These tables hold language information that is different (correct) from MediaWiki's definitions
For each ['<tag>'] = 'language name' in lang_code_remap{} there must be a matching ['language name'] = {'language name', '<tag>'} in lang_name_remap{}
lang_tag_remap{}:
key is always lowercase ISO 639-1, -2, -3 language tag or a valid lowercase IETF language tag
value is properly spelled and capitalized language name associated with <tag>
only one language name per <tag>;
key/value pair must have matching entry in lang_name_remap{}
lang_name_remap{}:
key is always lowercase language name
value is a table the holds correctly spelled and capitalized language name [1] and associated tag [2] (tag must match a tag key in lang_tag_remap{})
may have multiple keys referring to a common preferred name and tag; For example:
['kolsch'] and ['kölsch'] both refer to 'Kölsch' and 'ksh'
]]
local lang_tag_remap = { -- used for |language= and |script-title= / |script-chapter=
['als'] = 'Tosk Albanian', -- MediaWiki returns Alemannisch
['bh'] = 'Bihari', -- MediaWiki uses 'bh' as a subdomain name for Bhojpuri Wikipedia: bh.wikipedia.org
['bla'] = 'Blackfoot', -- MediaWiki/IANA/ISO 639: Siksika; use en.wiki preferred name
['bn'] = 'Bengali', -- MediaWiki returns Bangla
['ca-valencia'] = 'Valencian', -- IETF variant of Catalan
['fkv'] = 'Kven', -- MediaWiki returns Kvensk
['gsw'] = 'Swiss German',
['ilo'] = 'Ilocano', -- MediaWiki/IANA/ISO 639: Iloko; use en.wiki preferred name
['ksh'] = 'Kölsch', -- MediaWiki: Colognian; use IANA/ISO 639 preferred name
['ksh-x-colog'] = 'Colognian', -- override MediaWiki ksh; no IANA/ISO 639 code for Colognian; IETF private code created at Module:Lang/data
['mis-x-ripuar'] = 'Ripuarian', -- override MediaWiki ksh; no IANA/ISO 639 code for Ripuarian; IETF private code created at Module:Lang/data
['nan-tw'] = 'Taiwanese Hokkien', -- make room for MediaWiki/IANA/ISO 639 nan: Min Nan Chinese and support en.wiki preferred name
['sr-ec'] = 'Serbian (Cyrillic script)', -- MediaWiki returns српски (ћирилица)
['sr-el'] = 'Serbian (Latin script)', -- MediaWiki returns srpski (latinica)
}
local lang_name_remap = { -- used for |language=; names require proper capitalization; tags must be lowercase
['alemannic'] = {'Swiss German', 'gsw'}, -- ISO 639-2, -3 alternate for Swiss German; MediaWiki mediawiki returns Alemannic for gsw; en.wiki preferred name
['alemannisch'] = {'Swiss German', 'gsw'}, -- not an ISO or IANA language name; MediaWiki uses 'als' as a subdomain name for Alemannic Wikipedia: als.wikipedia.org
['bangla'] = {'Bengali', 'bn'}, -- MediaWiki returns Bangla (the endonym) but we want Bengali (the exonym); here we remap
['bengali'] = {'Bengali', 'bn'}, -- MediaWiki doesn't use exonym so here we provide correct language name and 639-1 code
['bhojpuri'] = {'Bhojpuri', 'bho'}, -- MediaWiki uses 'bh' as a subdomain name for Bhojpuri Wikipedia: bh.wikipedia.org
['bihari'] = {'Bihari', 'bh'}, -- MediaWiki replaces 'Bihari' with 'Bhojpuri' so 'Bihari' cannot be found
['blackfoot'] = {'Blackfoot', 'bla'}, -- MediaWiki/IANA/ISO 639: Siksika; use en.wiki preferred name
['colognian'] = {'Colognian', 'ksh-x-colog'}, -- MediaWiki preferred name for ksh
['ilocano'] = {'Ilocano', 'ilo'}, -- MediaWiki/IANA/ISO 639: Iloko; use en.wiki preferred name
['kolsch'] = {'Kölsch', 'ksh'}, -- use IANA/ISO 639 preferred name (use non-diacritical o instead of umlaut ö)
['kölsch'] = {'Kölsch', 'ksh'}, -- use IANA/ISO 639 preferred name
['kven'] = {'Kven', 'fkv'}, -- Unicode CLDR have decided not to support English language name for these two...
['kvensk'] = {'Kven', 'fkv'}, -- ...they say to refer to IANA registry for English names
['ripuarian'] = {'Ripuarian', 'mis-x-ripuar'}, -- group of dialects; no code in MediaWiki or in IANA/ISO 639
['serbian (cyrillic script)'] = {'Serbian (Cyrillic script)', 'sr-cyrl'}, -- special case to get correct tag when |language=sr-ec
['serbian (latin script)'] = {'Serbian (Latin script)', 'sr-latn'}, -- special case to get correct tag when |language=sr-el
['swiss german'] = {'Swiss German', 'gsw'},
['taiwanese hokkien'] = {'Taiwanese Hokkien', 'nan-tw'}, -- make room for MediaWiki/IANA/ISO 639 nan: Min Nan Chinese
['tosk albanian'] = {'Tosk Albanian', 'als'}, -- MediaWiki replaces 'Tosk Albanian' with 'Alemannisch' so 'Tosk Albanian' cannot be found
['valencian'] = {'Valencian', 'ca-valencia'}, -- variant of Catalan; categorizes as Valencian
}
--[[---------------< P R O P E R T I E S _ C A T E G O R I E S >----------------
Properties categories. These are used for investigating qualities of citations.
]]
local prop_cats = {
['foreign-lang-source'] = 'CS1 $1-language sources ($2)', -- |language= categories; $1 is foreign-language name, $2 is ISO639-1 code
['foreign-lang-source-2'] = 'CS1 foreign language sources (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 interproject-linked names|$1', -- any author, editor, etc that has an interproject link; $1 is interproject tag used as a sort key
['interwiki-linked-name'] = 'CS1 interwiki-linked names|$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 $1-language sources ($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 location test',
['long-vol'] = 'CS1: long volume value', -- probably temporary cat to identify scope of |volume= values longer than 4 characters
['script'] = 'CS1 uses $1-language script ($2)', -- |script-title=xx: has matching category; $1 is language name, $2 is language tag
['tracked-param'] = 'CS1 tracked parameter: $1', -- $1 is base (enumerators removed) parameter name
['unfit'] = 'CS1: unfit URL', -- |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: abbreviated year range', -- 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 = '<code class="cs1-code">|access-date=</code> requires <code class="cs1-code">|url=</code>',
anchor = 'accessdate_missing_url',
category = 'CS1 errors: access-date without URL',
hidden = false
},
err_apostrophe_markup = {
message = 'Italic or bold markup not allowed in: <code class="cs1-code">|$1=</code>', -- $1 is parameter name
anchor = 'apostrophe_markup',
category = 'CS1 errors: markup',
hidden = false
},
err_archive_date_missing_url = {
message = '<code class="cs1-code">|archive-date=</code> requires <code class="cs1-code">|archive-url=</code>',
anchor = 'archive_date_missing_url',
category = 'CS1 errors: archive-url',
hidden = false
},
err_archive_date_url_ts_mismatch = {
message = '<code class="cs1-code">|archive-date=</code> / <code class="cs1-code">|archive-url=</code> timestamp mismatch; $1 suggested',
anchor = 'archive_date_url_ts_mismatch',
category = 'CS1 errors: archive-url',
hidden = false
},
err_archive_missing_date = {
message = '<code class="cs1-code">|archive-url=</code> requires <code class="cs1-code">|archive-date=</code>',
anchor = 'archive_missing_date',
category = 'CS1 errors: archive-url',
hidden = false
},
err_archive_missing_url = {
message = '<code class="cs1-code">|archive-url=</code> requires <code class="cs1-code">|url=</code>',
anchor = 'archive_missing_url',
category = 'CS1 errors: archive-url',
hidden = false
},
err_archive_url = {
message = '<code class="cs1-code">|archive-url=</code> is malformed: $1', -- $1 is error message detail
anchor = 'archive_url',
category = 'CS1 errors: archive-url',
hidden = false
},
err_arxiv_missing = {
message = '<code class="cs1-code">|arxiv=</code> required',
anchor = 'arxiv_missing',
category = 'CS1 errors: arXiv', -- same as bad arxiv
hidden = false
},
err_asintld_missing_asin = {
message = '<code class="cs1-code">|$1=</code> requires <code class="cs1-code">|asin=</code>', -- $1 is parameter name
anchor = 'asintld_missing_asin',
category = 'CS1 errors: ASIN TLD',
hidden = false
},
err_bad_arxiv = {
message = 'Check <code class="cs1-code">|arxiv=</code> value',
anchor = 'bad_arxiv',
category = 'CS1 errors: arXiv',
hidden = false
},
err_bad_asin = {
message = 'Check <code class="cs1-code">|asin=</code> value',
anchor = 'bad_asin',
category ='CS1 errors: ASIN',
hidden = false
},
err_bad_asin_tld = {
message = 'Check <code class="cs1-code">|asin-tld=</code> value',
anchor = 'bad_asin_tld',
category ='CS1 errors: ASIN TLD',
hidden = false
},
err_bad_bibcode = {
message = 'Check <code class="cs1-code">|bibcode=</code> $1', -- $1 is error message detail
anchor = 'bad_bibcode',
category = 'CS1 errors: bibcode',
hidden = false
},
err_bad_biorxiv = {
message = 'Check <code class="cs1-code">|biorxiv=</code> value',
anchor = 'bad_biorxiv',
category = 'CS1 errors: bioRxiv',
hidden = false
},
err_bad_citeseerx = {
message = 'Check <code class="cs1-code">|citeseerx=</code> value',
anchor = 'bad_citeseerx',
category = 'CS1 errors: citeseerx',
hidden = false
},
err_bad_date = {
message = 'Check date values in: $1', -- $1 is a parameter name list
anchor = 'bad_date',
category = 'CS1 errors: dates',
hidden = false
},
err_bad_doi = {
message = 'Check <code class="cs1-code">|doi=</code> value',
anchor = 'bad_doi',
category = 'CS1 errors: DOI',
hidden = false
},
err_bad_hdl = {
message = 'Check <code class="cs1-code">|hdl=</code> value',
anchor = 'bad_hdl',
category = 'CS1 errors: HDL',
hidden = false
},
err_bad_isbn = {
message = 'Check <code class="cs1-code">|isbn=</code> value: $1', -- $1 is error message detail
anchor = 'bad_isbn',
category = 'CS1 errors: ISBN',
hidden = false
},
err_bad_ismn = {
message = 'Check <code class="cs1-code">|ismn=</code> value',
anchor = 'bad_ismn',
category = 'CS1 errors: ISMN',
hidden = false
},
err_bad_issn = {
message = 'Check <code class="cs1-code">|$1issn=</code> value', -- $1 is 'e' or '' for eissn or issn
anchor = 'bad_issn',
category = 'CS1 errors: ISSN',
hidden = false
},
err_bad_jfm = {
message = 'Check <code class="cs1-code">|jfm=</code> value',
anchor = 'bad_jfm',
category = 'CS1 errors: JFM',
hidden = false
},
err_bad_jstor = {
message = 'Check <code class="cs1-code">|jstor=</code> value',
anchor = 'bad_jstor',
category = 'CS1 errors: JSTOR',
hidden = false
},
err_bad_lccn = {
message = 'Check <code class="cs1-code">|lccn=</code> value',
anchor = 'bad_lccn',
category = 'CS1 errors: LCCN',
hidden = false
},
err_bad_medrxiv = {
message = 'Check <code class="cs1-code">|medrxiv=</code> value',
anchor = 'bad_medrxiv',
category = 'CS1 errors: medRxiv',
hidden = false
},
err_bad_mr = {
message = 'Check <code class="cs1-code">|mr=</code> value',
anchor = 'bad_mr',
category = 'CS1 errors: MR',
hidden = false
},
err_bad_oclc = {
message = 'Check <code class="cs1-code">|oclc=</code> value',
anchor = 'bad_oclc',
category = 'CS1 errors: OCLC',
hidden = false
},
err_bad_ol = {
message = 'Check <code class="cs1-code">|ol=</code> value',
anchor = 'bad_ol',
category = 'CS1 errors: OL',
hidden = false
},
err_bad_osti = {
message = 'Check <code class="cs1-code">|osti=</code> value',
anchor = 'bad_osti',
category = 'CS1 errors: OSTI',
hidden = false
},
err_bad_paramlink = { -- for |title-link=, |author/editor/translator-link=, |series-link=, |episode-link=
message = 'Check <code class="cs1-code">|$1=</code> value', -- $1 is parameter name
anchor = 'bad_paramlink',
category = 'CS1 errors: parameter link',
hidden = false
},
err_bad_pmc = {
message = 'Check <code class="cs1-code">|pmc=</code> value',
anchor = 'bad_pmc',
category = 'CS1 errors: PMC',
hidden = false
},
err_bad_pmid = {
message = 'Check <code class="cs1-code">|pmid=</code> value',
anchor = 'bad_pmid',
category = 'CS1 errors: PMID',
hidden = false
},
err_bad_rfc = {
message = 'Check <code class="cs1-code">|rfc=</code> value',
anchor = 'bad_rfc',
category = 'CS1 errors: RFC',
hidden = false
},
err_bad_s2cid = {
message = 'Check <code class="cs1-code">|s2cid=</code> value',
anchor = 'bad_s2cid',
category = 'CS1 errors: S2CID',
hidden = false
},
err_bad_sbn = {
message = 'Check <code class="cs1-code">|sbn=</code> value: $1', -- $1 is error message detail
anchor = 'bad_sbn',
category = 'CS1 errors: SBN',
hidden = false
},
err_bad_ssrn = {
message = 'Check <code class="cs1-code">|ssrn=</code> value',
anchor = 'bad_ssrn',
category = 'CS1 errors: SSRN',
hidden = false
},
err_bad_url = {
message = 'Check $1 value', -- $1 is parameter name
anchor = 'bad_url',
category = 'CS1 errors: URL',
hidden = false
},
err_bad_usenet_id = {
message = 'Check <code class="cs1-code">|message-id=</code> value',
anchor = 'bad_message_id',
category = 'CS1 errors: message-id',
hidden = false
},
err_bad_zbl = {
message = 'Check <code class="cs1-code">|zbl=</code> value',
anchor = 'bad_zbl',
category = 'CS1 errors: Zbl',
hidden = false
},
err_bare_url_missing_title = {
message = '$1 missing title', -- $1 is parameter name
anchor = 'bare_url_missing_title',
category = 'CS1 errors: bare URL',
hidden = false
},
err_biorxiv_missing = {
message = '<code class="cs1-code">|biorxiv=</code> required',
anchor = 'biorxiv_missing',
category = 'CS1 errors: bioRxiv', -- same as bad bioRxiv
hidden = false
},
err_chapter_ignored = {
message = '<code class="cs1-code">|$1=</code> ignored', -- $1 is parameter name
anchor = 'chapter_ignored',
category = 'CS1 errors: chapter ignored',
hidden = false
},
err_citation_missing_title = {
message = 'Missing or empty <code class="cs1-code">|$1=</code>', -- $1 is parameter name
anchor = 'citation_missing_title',
category = 'CS1 errors: missing title',
hidden = false
},
err_citeseerx_missing = {
message = '<code class="cs1-code">|citeseerx=</code> required',
anchor = 'citeseerx_missing',
category = 'CS1 errors: citeseerx', -- same as bad citeseerx
hidden = false
},
err_cite_web_url = { -- this error applies to cite web and to cite podcast
message = 'Missing or empty <code class="cs1-code">|url=</code>',
anchor = 'cite_web_url',
category = 'CS1 errors: requires URL',
hidden = false
},
err_class_ignored = {
message = '<code class="cs1-code">|class=</code> ignored',
anchor = 'class_ignored',
category = 'CS1 errors: class',
hidden = false
},
err_contributor_ignored = {
message = '<code class="cs1-code">|contributor=</code> ignored',
anchor = 'contributor_ignored',
category = 'CS1 errors: contributor',
hidden = false
},
err_contributor_missing_required_param = {
message = '<code class="cs1-code">|contributor=</code> requires <code class="cs1-code">|$1=</code>', -- $1 is parameter name
anchor = 'contributor_missing_required_param',
category = 'CS1 errors: contributor',
hidden = false
},
err_deprecated_params = {
message = 'Cite uses deprecated parameter <code class="cs1-code">|$1=</code>', -- $1 is parameter name
anchor = 'deprecated_params',
category = 'CS1 errors: deprecated parameters',
hidden = false
},
err_disp_name = {
message = 'Invalid <code class="cs1-code">|$1=$2</code>', -- $1 is parameter name; $2 is the assigned value
anchor = 'disp_name',
category = 'CS1 errors: display-names',
hidden = false,
},
err_doibroken_missing_doi = {
message = '<code class="cs1-code">|$1=</code> requires <code class="cs1-code">|doi=</code>', -- $1 is parameter name
anchor = 'doibroken_missing_doi',
category = 'CS1 errors: DOI',
hidden = false
},
err_embargo_missing_pmc = {
message = '<code class="cs1-code">|$1=</code> requires <code class="cs1-code">|pmc=</code>', -- $1 is parameter name
anchor = 'embargo_missing_pmc',
category = 'CS1 errors: PMC embargo',
hidden = false
},
err_empty_citation = {
message = 'Empty citation',
anchor = 'empty_citation',
category = 'CS1 errors: empty citation',
hidden = false
},
err_etal = {
message = 'Explicit use of et al. in: <code class="cs1-code">|$1=</code>', -- $1 is parameter name
anchor = 'explicit_et_al',
category = 'CS1 errors: explicit use of et al.',
hidden = false
},
err_extra_text_edition = {
message = '<code class="cs1-code">|edition=</code> has extra text',
anchor = 'extra_text_edition',
category = 'CS1 errors: extra text: edition',
hidden = false,
},
err_extra_text_issue = {
message = '<code class="cs1-code">|$1=</code> has extra text', -- $1 is parameter name
anchor = 'extra_text_issue',
category = 'CS1 errors: extra text: issue',
hidden = false,
},
err_extra_text_pages = {
message = '<code class="cs1-code">|$1=</code> has extra text', -- $1 is parameter name
anchor = 'extra_text_pages',
category = 'CS1 errors: extra text: pages',
hidden = false,
},
err_extra_text_volume = {
message = '<code class="cs1-code">|$1=</code> has extra text', -- $1 is parameter name
anchor = 'extra_text_volume',
category = 'CS1 errors: extra text: volume',
hidden = false,
},
err_first_missing_last = {
message = '<code class="cs1-code">|$1=</code> missing <code class="cs1-code">|$2=</code>', -- $1 is first alias, $2 is matching last alias
anchor = 'first_missing_last',
category = 'CS1 errors: missing name', -- author, contributor, editor, interviewer, translator
hidden = false
},
err_format_missing_url = {
message = '<code class="cs1-code">|$1=</code> requires <code class="cs1-code">|$2=</code>', -- $1 is format parameter $2 is url parameter
anchor = 'format_missing_url',
category = 'CS1 errors: format without URL',
hidden = false
},
err_generic_name = {
message = '<code class="cs1-code">|$1=</code> has generic name', -- $1 is parameter name
anchor = 'generic_name',
category = 'CS1 errors: generic name',
hidden = false,
},
err_generic_title = {
message = 'Cite uses generic title',
anchor = 'generic_title',
category = 'CS1 errors: generic title',
hidden = false,
},
err_invalid_isbn_date = {
message = 'ISBN / Date incompatibility',
anchor = 'invalid_isbn_date',
category = 'CS1 errors: ISBN date',
hidden = true
},
err_invalid_param_val = {
message = 'Invalid <code class="cs1-code">|$1=$2</code>', -- $1 is parameter name $2 is parameter value
anchor = 'invalid_param_val',
category = 'CS1 errors: invalid parameter value',
hidden = false
},
err_invisible_char = {
message = '$1 in $2 at position $3', -- $1 is invisible char $2 is parameter name $3 is position number
anchor = 'invisible_char',
category = 'CS1 errors: invisible characters',
hidden = false
},
err_medrxiv_missing = {
message = '<code class="cs1-code">|medrxiv=</code> required',
anchor = 'medrxiv_missing',
category = 'CS1 errors: medRxiv', -- same as bad medRxiv
hidden = false
},
err_missing_name = {
message = 'Missing <code class="cs1-code">|$1$2=</code>', -- $1 is modified NameList; $2 is enumerator
anchor = 'missing_name',
category = 'CS1 errors: missing name', -- author, contributor, editor, interviewer, translator
hidden = false
},
err_missing_periodical = {
message = 'Cite $1 requires <code class="cs1-code">|$2=</code>', -- $1 is cs1 template name; $2 is canonical periodical parameter name for cite $1
anchor = 'missing_periodical',
category = 'CS1 errors: missing periodical',
hidden = false
},
err_missing_pipe = {
message = 'Missing pipe in: <code class="cs1-code">|$1=</code>', -- $1 is parameter name
anchor = 'missing_pipe',
category = 'CS1 errors: missing pipe',
hidden = false
},
err_missing_publisher = {
message = 'Cite $1 requires <code class="cs1-code">|$2=</code>', -- $1 is cs1 template name; $2 is canonical publisher parameter name for cite $1
anchor = 'missing_publisher',
category = 'CS1 errors: missing publisher',
hidden = false
},
err_numeric_names = {
message = '<code class="cs1-code">|$1=</code> has numeric name', -- $1 is parameter name',
anchor = 'numeric_names',
category = 'CS1 errors: numeric name',
hidden = false,
},
err_param_access_requires_param = {
message = '<code class="cs1-code">|$1-access=</code> requires <code class="cs1-code">|$1=</code>', -- $1 is parameter name
anchor = 'param_access_requires_param',
category = 'CS1 errors: param-access',
hidden = false
},
err_param_has_ext_link = {
message = 'External link in <code class="cs1-code">$1</code>', -- $1 is parameter name
anchor = 'param_has_ext_link',
category = 'CS1 errors: external links',
hidden = false
},
err_param_has_twl_url = {
message = 'Wikipedia Library link in <code class="cs1-code">$1</code>', -- $1 is parameter name
anchor = 'param_has_twl_url',
category = 'CS1 errors: URL',
hidden = false
},
err_parameter_ignored = {
message = 'Unknown parameter <code class="cs1-code">|$1=</code> ignored', -- $1 is parameter name
anchor = 'parameter_ignored',
category = 'CS1 errors: unsupported parameter',
hidden = false
},
err_parameter_ignored_suggest = {
message = 'Unknown parameter <code class="cs1-code">|$1=</code> ignored (<code class="cs1-code">|$2=</code> suggested)', -- $1 is unknown parameter $2 is suggested parameter name
anchor = 'parameter_ignored_suggest',
category = 'CS1 errors: unsupported parameter',
hidden = false
},
err_periodical_ignored = {
message = '<code class="cs1-code">|$1=</code> ignored', -- $1 is parameter name
anchor = 'periodical_ignored',
category = 'CS1 errors: periodical ignored',
hidden = false
},
err_redundant_parameters = {
message = 'More than one of $1 specified', -- $1 is error message detail
anchor = 'redundant_parameters',
category = 'CS1 errors: redundant parameter',
hidden = false
},
err_script_parameter = {
message = 'Invalid <code class="cs1-code">|$1=</code>: $2', -- $1 is parameter name $2 is script language code or error detail
anchor = 'script_parameter',
category = 'CS1 errors: script parameters',
hidden = false
},
err_ssrn_missing = {
message = '<code class="cs1-code">|ssrn=</code> required',
anchor = 'ssrn_missing',
category = 'CS1 errors: SSRN',
hidden = false
},
err_text_ignored = {
message = 'Text "$1" ignored', -- $1 is ignored text
anchor = 'text_ignored',
category = 'CS1 errors: unrecognized parameter',
hidden = false
},
err_trans_missing_title = {
message = '<code class="cs1-code">|trans-$1=</code> requires <code class="cs1-code">|$1=</code> or <code class="cs1-code">|script-$1=</code>', -- $1 is base parameter name
anchor = 'trans_missing_title',
category = 'CS1 errors: translated title',
hidden = false
},
err_param_unknown_empty = {
message = 'Cite has empty unknown parameter$1: $2', -- $1 is 's' or empty space; $2 is emty unknown param list
anchor = 'param_unknown_empty',
category = 'CS1 errors: empty unknown parameters',
hidden = false
},
err_vancouver = {
message = 'Vancouver style error: $1 in name $2', -- $1 is error detail, $2 is the nth name
anchor = 'vancouver',
category = 'CS1 errors: Vancouver style',
hidden = false
},
err_wikilink_in_url = {
message = 'URL–wikilink conflict', -- uses ndash
anchor = 'wikilink_in_url',
category = 'CS1 errors: URL–wikilink conflict', -- 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 = 'CS1 maint: archived copy as title',
hidden = true,
},
maint_bibcode = {
message = nil,
anchor = 'bibcode',
category = 'CS1 maint: 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 = 'CS1 maint: location missing publisher',
hidden = true,
},
maint_bot_unknown = {
message = nil,
anchor = 'bot:_unknown',
category = 'CS1 maint: bot: original URL status unknown',
hidden = true,
},
maint_date_auto_xlated = { -- date auto-translation not supported by en.wiki
message = nil,
anchor = 'date_auto_xlated',
category = 'CS1 maint: date auto-translated',
hidden = true,
},
maint_date_format = {
message = nil,
anchor = 'date_format',
category = 'CS1 maint: date format',
hidden = true,
},
maint_date_year = {
message = nil,
anchor = 'date_year',
category = 'CS1 maint: date and year',
hidden = true,
},
maint_doi_ignore = {
message = nil,
anchor = 'doi_ignore',
category = 'CS1 maint: ignored DOI errors',
hidden = true,
},
maint_doi_inactive = {
message = nil,
anchor = 'doi_inactive',
category = 'CS1 maint: DOI inactive',
hidden = true,
},
maint_doi_inactive_dated = {
message = nil,
anchor = 'doi_inactive_dated',
category = 'CS1 maint: DOI inactive as of $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 = 'CS1 maint: unflagged free DOI',
hidden = true,
},
maint_extra_punct = {
message = nil,
anchor = 'extra_punct',
category = 'CS1 maint: extra punctuation',
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 = 'CS1 maint: ID limit load fail',
hidden = true,
},
maint_isbn_ignore = {
message = nil,
anchor = 'ignore_isbn_err',
category = 'CS1 maint: ignored ISBN errors',
hidden = true,
},
maint_issn_ignore = {
message = nil,
anchor = 'ignore_issn',
category = 'CS1 maint: ignored ISSN errors',
hidden = true,
},
maint_jfm_format = {
message = nil,
anchor = 'jfm_format',
category = 'CS1 maint: JFM format',
hidden = true,
},
maint_location = {
message = nil,
anchor = 'location',
category = 'CS1 maint: location',
hidden = true,
},
maint_mr_format = {
message = nil,
anchor = 'mr_format',
category = 'CS1 maint: MR format',
hidden = true,
},
maint_mult_names = {
message = nil,
anchor = 'mult_names',
category = 'CS1 maint: multiple names: $1', -- $1 is '<name>s list'; gets value from special_case_translation table
hidden = true,
},
maint_numeric_names = {
message = nil,
anchor = 'numeric_names',
category = 'CS1 maint: numeric names: $1', -- $1 is '<name>s list'; gets value from special_case_translation table
hidden = true,
},
maint_others = {
message = nil,
anchor = 'others',
category = 'CS1 maint: others',
hidden = true,
},
maint_others_avm = {
message = nil,
anchor = 'others_avm',
category = 'CS1 maint: others in cite AV media (notes)',
hidden = true,
},
maint_overridden_setting = {
message = nil,
anchor = 'overridden',
category = 'CS1 maint: overridden setting',
hidden = true,
},
maint_pmc_embargo = {
message = nil,
anchor = 'embargo',
category = 'CS1 maint: PMC embargo expired',
hidden = true,
},
maint_pmc_format = {
message = nil,
anchor = 'pmc_format',
category = 'CS1 maint: PMC format',
hidden = true,
},
maint_postscript = {
message = nil,
anchor = 'postscript',
category = 'CS1 maint: postscript',
hidden = true,
},
maint_publisher_location = {
message = nil,
anchor = 'publisher_location',
category = 'CS1 maint: publisher location',
hidden = true,
},
maint_ref_duplicates_default = {
message = nil,
anchor = 'ref_default',
category = 'CS1 maint: ref duplicates default',
hidden = true,
},
maint_unknown_lang = {
message = nil,
anchor = 'unknown_lang',
category = 'CS1 maint: unrecognized language',
hidden = true,
},
maint_untitled = {
message = nil,
anchor = 'untitled',
category = 'CS1 maint: untitled periodical',
hidden = true,
},
maint_url_status = {
message = nil,
anchor = 'url_status',
category = 'CS1 maint: url-status',
hidden = true,
},
maint_year= {
message = nil,
anchor = 'year',
category = 'CS1 maint: year',
hidden = true,
},
maint_zbl = {
message = nil,
anchor = 'zbl',
category = 'CS1 maint: 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 (identifier)',
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 = 'Amazon Standard Identification Number',
redirect = 'ASIN (identifier)',
q = 'Q1753278',
label = 'ASIN',
prefix = 'https://www.amazon.',
COinS = 'url',
separator = ' ',
encode = false;
},
['BIBCODE'] = {
parameters = {'bibcode'},
link = 'Bibcode',
redirect = 'Bibcode (identifier)',
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 (identifier)',
q = 'Q19835482',
label = 'bioRxiv',
prefix = 'https://doi.org/',
COinS = 'pre', -- use prefix value
access = 'free', -- free to read
encode = true,
separator = ' ',
},
['CITESEERX'] = {
parameters = {'citeseerx'},
link = 'CiteSeerX',
redirect = 'CiteSeerX (identifier)',
q = 'Q2715061',
label = 'CiteSeerX',
prefix = 'https://citeseerx.ist.psu.edu/viewdoc/summary?doi=',
COinS = 'pre', -- use prefix value
access = 'free', -- free to read
encode = true,
separator = ' ',
},
['DOI'] = { -- Used by InternetArchiveBot
parameters = { 'doi', 'DOI'},
link = 'Digital object identifier',
redirect = 'doi (identifier)',
q = 'Q25670',
label = 'doi',
prefix = 'https://doi.org/',
COinS = 'info:doi',
separator = ':',
encode = true,
custom_access = 'doi-access',
},
['EISSN'] = {
parameters = {'eissn', 'EISSN'},
link = 'International Standard Serial Number#Electronic ISSN',
redirect = 'eISSN (identifier)',
q = 'Q46339674',
label = 'eISSN',
prefix = 'https://search.worldcat.org/issn/',
COinS = 'rft.eissn',
encode = false,
separator = ' ',
},
['HDL'] = {
parameters = { 'hdl', 'HDL' },
link = 'Handle System',
redirect = 'hdl (identifier)',
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 = 'International Standard Book Number',
redirect = 'ISBN (identifier)',
q = 'Q33057',
label = 'ISBN',
prefix = 'Special:BookSources/',
COinS = 'rft.isbn',
separator = ' ',
},
['ISMN'] = {
parameters = {'ismn', 'ISMN'},
link = 'International Standard Music Number',
redirect = 'ISMN (identifier)',
q = 'Q1666938',
label = 'ISMN',
prefix = '', -- not currently used;
COinS = nil, -- nil because we can't use pre or rft or info:
separator = ' ',
},
['ISSN'] = {
parameters = {'issn', 'ISSN'},
link = 'International Standard Serial Number',
redirect = 'ISSN (identifier)',
q = 'Q131276',
label = 'ISSN',
prefix = 'https://search.worldcat.org/issn/',
COinS = 'rft.issn',
encode = false,
separator = ' ',
},
['JFM'] = {
parameters = {'jfm', 'JFM'},
link = 'Jahrbuch über die Fortschritte der Mathematik',
redirect = 'JFM (identifier)',
q = '',
label = 'JFM',
prefix = 'https://zbmath.org/?format=complete&q=an:',
COinS = 'pre', -- use prefix value
encode = true,
separator = ' ',
},
['JSTOR'] = {
parameters = {'jstor', 'JSTOR'},
link = 'JSTOR',
redirect = 'JSTOR (identifier)',
q = 'Q1420342',
label = 'JSTOR',
prefix = 'https://www.jstor.org/stable/',
COinS = 'pre', -- use prefix value
encode = false,
separator = ' ',
custom_access = 'jstor-access',
},
['LCCN'] = {
parameters = {'lccn', 'LCCN'},
link = 'Library of Congress Control Number',
redirect = 'LCCN (identifier)',
q = 'Q620946',
label = 'LCCN',
prefix = 'https://lccn.loc.gov/',
COinS = 'info:lccn',
encode = false,
separator = ' ',
},
['MEDRXIV'] = {
parameters = {'medrxiv'},
link = 'medRxiv',
redirect = 'medRxiv (identifier)',
q = 'Q58465838',
label = 'medRxiv',
prefix = 'https://www.medrxiv.org/content/',
COinS = 'pre', -- use prefix value
access = 'free', -- free to read
encode = false,
separator = ' ',
},
['MR'] = {
parameters = {'mr', 'MR'},
link = 'Mathematical Reviews',
redirect = 'MR (identifier)',
q = 'Q211172',
label = 'MR',
prefix = 'https://mathscinet.ams.org/mathscinet-getitem?mr=',
COinS = 'pre', -- use prefix value
encode = true,
separator = ' ',
},
['OCLC'] = {
parameters = {'oclc', 'OCLC'},
link = 'OCLC',
redirect = 'OCLC (identifier)',
q = 'Q190593',
label = 'OCLC',
prefix = 'https://search.worldcat.org/oclc/',
COinS = 'info:oclcnum',
encode = true,
separator = ' ',
id_limit = id_limits_data_t.OCLC or 0,
},
['OL'] = {
parameters = { 'ol', 'OL' },
link = 'Open Library',
redirect = 'OL (identifier)',
q = 'Q1201876',
label = 'OL',
prefix = 'https://openlibrary.org/',
COinS = 'url',
separator = ' ',
encode = true,
custom_access = 'ol-access',
},
['OSTI'] = {
parameters = {'osti', 'OSTI'},
link = 'Office of Scientific and Technical Information',
redirect = 'OSTI (identifier)',
q = 'Q2015776',
label = 'OSTI',
prefix = 'https://www.osti.gov/biblio/',
COinS = 'pre', -- use prefix value
encode = true,
separator = ' ',
id_limit = id_limits_data_t.OSTI or 0,
custom_access = 'osti-access',
},
['PMC'] = {
parameters = {'pmc', 'PMC'},
link = 'PubMed Central',
redirect = 'PMC (identifier)',
q = 'Q229883',
label = 'PMC',
prefix = 'https://www.ncbi.nlm.nih.gov/pmc/articles/PMC',
suffix = '',
COinS = 'pre', -- use prefix value
encode = true,
separator = ' ',
id_limit = id_limits_data_t.PMC or 0,
access = 'free', -- free to read
},
['PMID'] = {
parameters = {'pmid', 'PMID'},
link = 'PubMed Identifier',
redirect = 'PMID (identifier)',
q = 'Q2082879',
label = 'PMID',
prefix = 'https://pubmed.ncbi.nlm.nih.gov/',
COinS = 'info:pmid',
encode = false,
separator = ' ',
id_limit = id_limits_data_t.PMID or 0,
},
['RFC'] = {
parameters = {'rfc', 'RFC'},
link = 'Request for Comments',
redirect = 'RFC (identifier)',
q = 'Q212971',
label = 'RFC',
prefix = 'https://tools.ietf.org/html/rfc',
COinS = 'pre', -- use prefix value
encode = false,
separator = ' ',
id_limit = id_limits_data_t.RFC or 0,
access = 'free', -- free to read
},
['SBN'] = {
parameters = {'sbn', 'SBN'},
link = 'Standard Book Number', -- redirect to International_Standard_Book_Number#History
redirect = 'SBN (identifier)',
label = 'SBN',
prefix = 'Special:BookSources/0-', -- prefix has leading zero necessary to make 9-digit sbn a 10-digit isbn
COinS = nil, -- nil because we can't use pre or rft or info:
separator = ' ',
},
['SSRN'] = {
parameters = {'ssrn', 'SSRN'},
link = 'Social Science Research Network',
redirect = 'SSRN (identifier)',
q = 'Q7550801',
label = 'SSRN',
prefix = 'https://papers.ssrn.com/sol3/papers.cfm?abstract_id=',
COinS = 'pre', -- use prefix value
encode = true,
separator = ' ',
id_limit = id_limits_data_t.SSRN or 0,
custom_access = 'ssrn-access',
},
['S2CID'] = {
parameters = {'s2cid', 'S2CID'},
link = 'Semantic Scholar',
redirect = 'S2CID (identifier)',
q = 'Q22908627',
label = 'S2CID',
prefix = 'https://api.semanticscholar.org/CorpusID:',
COinS = 'pre', -- use prefix value
encode = false,
separator = ' ',
id_limit = id_limits_data_t.S2CID or 0,
custom_access = 's2cid-access',
},
['USENETID'] = {
parameters = {'message-id'},
link = 'Usenet',
redirect = 'Usenet (identifier)',
q = 'Q193162',
label = 'Usenet:',
prefix = 'news:',
encode = false,
COinS = 'pre', -- use prefix value
separator = ' ',
},
['ZBL'] = {
parameters = {'zbl', 'ZBL' },
link = 'Zentralblatt MATH',
redirect = 'Zbl (identifier)',
q = 'Q190269',
label = 'Zbl',
prefix = 'https://zbmath.org/?format=complete&q=an:',
COinS = 'pre', -- use prefix value
encode = true,
separator = ' ',
},
}
--[[--------------------------< E X P O R T S >---------------------------------
]]
return {
use_identifier_redirects = use_identifier_redirects, -- booleans defined in the settings at the top of this module
local_lang_cat_enable = local_lang_cat_enable,
date_name_auto_xlate_enable = date_name_auto_xlate_enable,
date_digit_auto_xlate_enable = date_digit_auto_xlate_enable,
enable_sort_keys = enable_sort_keys,
-- tables and variables created when this module is loaded
global_df = get_date_format (), -- this line can be replaced with "global_df = 'dmy-all'," to have all dates auto translated to dmy format.
global_cs1_config_t = global_cs1_config_t, -- global settings from {{cs1 config}}
punct_skip = build_skip_table (punct_skip, punct_meta_params),
url_skip = build_skip_table (url_skip, url_meta_params),
known_free_doi_registrants_t = build_free_doi_registrants_table(),
id_limits_data_load_fail = id_limits_data_load_fail, -- true when commons tabular identifier-limit data fails to load
name_space_sort_keys = name_space_sort_keys,
aliases = aliases,
special_case_translation = special_case_translation,
date_names = date_names,
err_msg_supl = err_msg_supl,
error_conditions = error_conditions,
editor_markup_patterns = editor_markup_patterns,
et_al_patterns = et_al_patterns,
extended_registrants_t = extended_registrants_t,
id_handlers = id_handlers,
keywords_lists = keywords_lists,
keywords_xlate = keywords_xlate,
stripmarkers = stripmarkers,
invisible_chars = invisible_chars,
invisible_defs = invisible_defs,
indic_script = indic_script,
emoji_t = emoji_t,
maint_cats = maint_cats,
messages = messages,
presentation = presentation,
prop_cats = prop_cats,
script_lang_codes = script_lang_codes,
lang_tag_remap = lang_tag_remap,
lang_name_remap = lang_name_remap,
this_wiki_code = this_wiki_code,
title_types = title_types,
uncategorized_namespaces = uncategorized_namespaces_t,
uncategorized_subpages = uncategorized_subpages,
templates_using_volume = templates_using_volume,
templates_using_issue = templates_using_issue,
templates_not_using_page = templates_not_using_page,
vol_iss_pg_patterns = vol_iss_pg_patterns,
single_letter_2nd_lvl_domains_t = single_letter_2nd_lvl_domains_t,
inter_wiki_map = inter_wiki_map,
mw_languages_by_tag_t = mw_languages_by_tag_t,
mw_languages_by_name_t = mw_languages_by_name_t,
citation_class_map_t = citation_class_map_t,
citation_issue_t = citation_issue_t,
citation_no_volume_t = citation_no_volume_t,
}
1qtnsk18jlmk0vxedax9rhtzltma8m9
886424
886345
2025-06-14T05:13:30Z
KartikMistry
10383
ભાષાંતર.
886424
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 = {'/[Ss]andbox', '/[Tt]estcases', '/[^/]*[Ll]og', '/[Aa]rchive'}; -- 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'] = '$1 માંથી $2 પર સંગ્રહિત',
['archived-live'] = 'મૂળ $1 માંથી $2 પર સંગ્રહિત',
['archived-unfit'] = 'મૂળમાંથી અહીં સંગ્રહિત ',
['archived'] = 'સંગ્રહિત',
['by'] = 'વડે', -- contributions to authored works: introduction, foreword, afterword
['cartography'] = '$1 વડે નકશો',
['editor'] = 'સંપાદક',
['editors'] = 'સંપાદકો',
['edition'] = '($1 આવૃત્તિ)',
['episode'] = 'પ્રકરણ $1',
['et al'] = 'et al.',
['in'] = 'In', -- edited works
['inactive'] = 'અસક્રિય',
['inset'] = '$1 inset',
['interview'] = '$1 દ્વારા ઇન્ટરવ્યુ',
['mismatch'] = '<code class="cs1-code">|$1=</code> / <code class="cs1-code">|$2=</code> mismatch', -- $1 is year param name; $2 is date param name
['newsgroup'] = '[[Usenet newsgroup|Newsgroup]]: $1',
['notitle'] = 'શીર્ષક નથી', -- for |title=(()) and (in the future) |title=none
['original'] = 'મૂળ',
['origdate'] = ' [$1]',
['published'] = ' (પ્રકાશિત $1)',
['retrieved'] = 'મેળવેલ $1',
['season'] = 'શ્રેણી $1',
['section'] = '§ $1',
['sections'] = '§§ $1',
['series'] = '$1 $2', -- $1 is sepc, $2 is series
['seriesnum'] = 'શ્રેણી $1',
['translated'] = '$1 વડે અનુવાદિત',
['type'] = ' ($1)', -- for titletype
['written'] = '$1 વડે લખાયેલ',
['vol'] = '$1 ખંડ $2', -- $1 is sepc; bold journal style volume is in presentation{}
['vol-no'] = '$1 ખંડ $2, ક્રમાંક $3', -- sepc, volume, issue (alternatively insert $1 after $2, but then we'd also have to change capitalization)
['issue'] = '$1 ક્રમાંક $2', -- $1 is sepc
['art'] = '$1 Art. $2', -- $1 is sepc; for {{cite conference}} only
['vol-art'] = '$1 ખંડ. $2, art. $3', -- sepc, volume, article-number; for {{cite conference}} only
['j-vol'] = '$1 $2', -- sepc, volume; bold journal volume is in presentation{}
['j-issue'] = ' ($1)',
['j-article-num'] = ' $1', -- TODO: any punctuation here? static text?
['nopp'] = '$1 $2'; -- page(s) without prefix; $1 is sepc
['p-prefix'] = "$1 p. $2", -- $1 is sepc
['pp-prefix'] = "$1 pp. $2", -- $1 is sepc
['j-page(s)'] = ': $1', -- same for page and pages
['sheet'] = '$1 Sheet $2', -- $1 is sepc
['sheets'] = '$1 Sheets $2', -- $1 is sepc
['j-sheet'] = ': Sheet $1',
['j-sheets'] = ': Sheets $1',
['language'] = '($1માં)',
['via'] = " – $1 દ્વારા",
['event'] = 'ઘટના આ સમયે બની',
['minutes'] = 'મિનિટમાં',
-- Determines the location of the help page
['help page link'] = 'Help:CS1 errors',
['help page label'] = 'મદદ',
-- categories
['cat wikilink'] = '[[Category:$1]]', -- $1 is the category name
['cat wikilink sk'] = '[[Category:$1|$2]]', -- $1 is the category name; $2 is namespace sort key
[':cat wikilink'] = '[[:Category:$1|link]]', -- category name as maintenance message wikilink; $1 is the category name
-- Internal errors (should only occur if configuration is bad)
['undefined_error'] = 'Called with an undefined error condition',
['unknown_ID_key'] = 'Unrecognized ID key: ', -- an ID key in id_handlers not found in ~/Identifiers func_map{}
['unknown_ID_access'] = 'Unrecognized ID access keyword: ', -- an ID access keyword in id_handlers not found in keywords_lists['id-access']{}
['unknown_argument_map'] = 'Argument map not defined for this variable',
['bare_url_no_origin'] = 'Bare URL found but origin indicator is nil or empty',
['warning_msg_e'] = '<span style="color:#d33">One or more <code style="color: inherit; background: inherit; border: none; padding: inherit;">{{$1}}</code> templates have errors</span>; messages may be hidden ([[Help:CS1_errors#Controlling_error_message_display|help]]).'; -- $1 is template link
['warning_msg_m'] = '<span style="color:#3a3">One or more <code style="color: inherit; background: inherit; border: none; padding: inherit;">{{$1}}</code> templates have maintenance messages</span>; messages may be hidden ([[Help:CS1_errors#Controlling_error_message_display|help]]).'; -- $1 is template link
}
--[[--------------------------< C I T A T I O N _ C L A S S _ M A P >------------------------------------------
this table maps the value assigned to |CitationClass= in the cs1|2 templates to the canonical template name when
the value assigned to |CitationClass= is different from the canonical template name. |CitationClass= values are
used as class attributes in the <cite> tag that encloses the citation so these names may not contain spaces while
the canonical template name may. These names are used in warning_msg_e and warning_msg_m to create links to the
template's documentation when an article is displayed in preview mode.
Most cs1|2 template |CitationClass= values at en.wiki match their canonical template names so are not listed here.
]]
local citation_class_map_t = { -- TODO: if kept, these and all other config.CitationClass 'names' require some sort of i18n
['arxiv'] = 'arXiv',
['audio-visual'] = 'AV media',
['AV-media-notes'] = 'AV media notes',
['biorxiv'] = 'bioRxiv',
['citeseerx'] = 'CiteSeerX',
['encyclopaedia'] = 'encyclopedia',
['mailinglist'] = 'mailing list',
['medrxiv'] = 'medRxiv',
['pressrelease'] = 'press release',
['ssrn'] = 'SSRN',
['techreport'] = 'tech report',
}
--[=[-------------------------< E T _ A L _ P A T T E R N S >--------------------------------------------------
This table provides Lua patterns for the phrase "et al" and variants in name text
(author, editor, etc.). The main module uses these to identify and emit the 'etal' message.
]=]
local et_al_patterns = {
"[;,]? *[\"']*%f[%a][Ee][Tt]%.? *[Aa][Ll][%.;,\"']*$", -- variations on the 'et al' theme
"[;,]? *[\"']*%f[%a][Ee][Tt]%.? *[Aa][Ll][Ii][AaIi][Ee]?[%.;,\"']*$", -- variations on the 'et alia', 'et alii' and 'et aliae' themes (false positive 'et aliie' unlikely to match)
"[;,]? *%f[%a]and [Oo]thers", -- an alternative to et al.
"%[%[ *[Ee][Tt]%.? *[Aa][Ll]%.? *%]%]", -- a wikilinked form
"%(%( *[Ee][Tt]%.? *[Aa][Ll]%.? *%)%)", -- a double-bracketed form (to counter partial removal of ((...)) syntax)
"[%(%[] *[Ee][Tt]%.? *[Aa][Ll]%.? *[%)%]]", -- a bracketed form
}
--[[--------------------------< P R E S E N T A T I O N >------------------------
Fixed presentation markup. Originally part of citation_config.messages it has
been moved into its own, more semantically correct place.
]]
local presentation =
{
-- .citation-comment class is specified at Help:CS1_errors#Controlling_error_message_display
['hidden-error'] = '<span class="cs1-hidden-error citation-comment">$1</span>',
['visible-error'] = '<span class="cs1-visible-error citation-comment">$1</span>',
['hidden-maint'] = '<span class="cs1-maint citation-comment">$1</span>',
['accessdate'] = '<span class="reference-accessdate">$1$2</span>', -- to allow editors to hide accessdate using personal CSS
['bdi'] = '<bdi$1>$2</bdi>', -- bidirectional isolation used with |script-title= and the like
['cite'] = '<cite class="$1">$2</cite>'; -- for use when citation does not have a namelist and |ref= not set so no id="..." attribute
['cite-id'] = '<cite id="$1" class="$2">$3</cite>'; -- for use when when |ref= is set or when citation has a namelist
['format'] = ' <span class="cs1-format">($1)</span>', -- for |format=, |chapter-format=, etc.
['interwiki'] = ' <span class="cs1-format">[in $1]</span>', -- for interwiki-language-linked author, editor, etc
['interproj'] = ' <span class="cs1-format">[at $1]</span>', -- for interwiki-project-linked author, editor, etc (:d: and :s: supported; :w: ignored)
-- various access levels, for |access=, |doi-access=, |arxiv=, ...
-- narrow no-break space   may work better than nowrap CSS. Or not? Browser support?
['ext-link-access-signal'] = '<span class="$1" title="$2">$3</span>', -- external link with appropriate lock icon
['free'] = {class='id-lock-free', title='Freely accessible'}, -- classes defined in Module:Citation/CS1/styles.css
['registration'] = {class='id-lock-registration', title='Free registration required'},
['limited'] = {class='id-lock-limited', title='Free access subject to limited trial, subscription normally required'},
['subscription'] = {class='id-lock-subscription', title='Paid subscription required'},
['interwiki-icon'] = '<span class="$1" title="$2">$3</span>',
['class-wikisource'] = 'cs1-ws-icon',
['italic-title'] = "''$1''",
['kern-left'] = '<span class="cs1-kern-left"></span>$1', -- spacing to use when title contains leading single or double quote mark
['kern-right'] = '$1<span class="cs1-kern-right"></span>', -- spacing to use when title contains trailing single or double quote mark
['nowrap1'] = '<span class="nowrap">$1</span>', -- for nowrapping an item: <span ...>yyyy-mm-dd</span>
['nowrap2'] = '<span class="nowrap">$1</span> $2', -- for nowrapping portions of an item: <span ...>dd mmmm</span> yyyy (note white space)
['ocins'] = '<span title="$1" class="Z3988"></span>',
['parameter'] = '<code class="cs1-code">|$1=</code>',
['ps_cs1'] = '.'; -- CS1 style postscript (terminal) character
['ps_cs2'] = ''; -- CS2 style postscript (terminal) character (empty string)
['quoted-text'] = '<q>$1</q>', -- for wrapping |quote= content
['quoted-title'] = '"$1"',
['sep_cs1'] = '.', -- CS1 element separator
['sep_cs2'] = ',', -- CS2 separator
['sep_nl'] = ';', -- CS1|2 style name-list separator between names is a semicolon
['sep_nl_and'] = ' and ', -- used as last nl sep when |name-list-style=and and list has 2 items
['sep_nl_end'] = '; and ', -- used as last nl sep when |name-list-style=and and list has 3+ names
['sep_name'] = ', ', -- CS1|2 style last/first separator is <comma><space>
['sep_nl_vanc'] = ',', -- Vancouver style name-list separator between authors is a comma
['sep_name_vanc'] = ' ', -- Vancouver style last/first separator is a space
['sep_list'] = ', ', -- used for |language= when list has 3+ items except for last sep which uses sep_list_end
['sep_list_pair'] = ' and ', -- used for |language= when list has 2 items
['sep_list_end'] = ', and ', -- used as last list sep for |language= when list has 3+ items
['trans-italic-title'] = "[''$1'']",
['trans-quoted-title'] = "[$1]", -- for |trans-title= and |trans-quote=
['vol-bold'] = '$1 <b>$2</b>', -- sepc, volume; for bold journal cites; for other cites ['vol'] in messages{}
}
--[[--------------------------< A L I A S E S >---------------------------------
Aliases table for commonly passed parameters.
Parameter names on the right side in the assignments in this table must have been
defined in the Whitelist before they will be recognized as valid parameter names
]]
local aliases = {
['AccessDate'] = {'access-date', 'accessdate'}, -- Used by InternetArchiveBot
['Agency'] = 'agency',
['ArchiveDate'] = {'archive-date', 'archivedate'}, -- Used by InternetArchiveBot
['ArchiveFormat'] = 'archive-format',
['ArchiveURL'] = {'archive-url', 'archiveurl'}, -- Used by InternetArchiveBot
['ArticleNumber'] = 'article-number',
['ASINTLD'] = 'asin-tld',
['At'] = 'at', -- Used by InternetArchiveBot
['Authors'] = {'people', 'credits'},
['BookTitle'] = {'book-title', 'booktitle'},
['Cartography'] = 'cartography',
['Chapter'] = {'chapter', 'contribution', 'entry', 'article', 'section'},
['ChapterFormat'] = {'chapter-format', 'contribution-format', 'entry-format',
'article-format', 'section-format'};
['ChapterURL'] = {'chapter-url', 'contribution-url', 'entry-url', 'article-url', 'section-url'}, -- Used by InternetArchiveBot
['ChapterUrlAccess'] = {'chapter-url-access', 'contribution-url-access',
'entry-url-access', 'article-url-access', 'section-url-access'}, -- Used by InternetArchiveBot
['Class'] = 'class', -- cite arxiv and arxiv identifier
['Collaboration'] = 'collaboration',
['Conference'] = {'conference', 'event'},
['ConferenceFormat'] = 'conference-format',
['ConferenceURL'] = 'conference-url', -- Used by InternetArchiveBot
['Date'] = {'date', 'air-date', 'airdate'}, -- air-date and airdate for cite episode and cite serial only
['Degree'] = 'degree',
['DF'] = 'df',
['DisplayAuthors'] = {'display-authors', 'display-subjects'},
['DisplayContributors'] = 'display-contributors',
['DisplayEditors'] = 'display-editors',
['DisplayInterviewers'] = 'display-interviewers',
['DisplayTranslators'] = 'display-translators',
['Docket'] = 'docket',
['DoiBroken'] = 'doi-broken-date',
['Edition'] = 'edition',
['Embargo'] = 'pmc-embargo-date',
['Encyclopedia'] = {'encyclopedia', 'encyclopaedia', 'dictionary'}, -- cite encyclopedia only
['Episode'] = 'episode', -- cite serial only TODO: make available to cite episode?
['Format'] = 'format',
['ID'] = {'id', 'ID'},
['Inset'] = 'inset',
['Issue'] = {'issue', 'number'},
['Language'] = {'language', 'lang'},
['MailingList'] = {'mailing-list', 'mailinglist'}, -- cite mailing list only
['Map'] = 'map', -- cite map only
['MapFormat'] = 'map-format', -- cite map only
['MapURL'] = {'map-url', 'mapurl'}, -- cite map only -- Used by InternetArchiveBot
['MapUrlAccess'] = 'map-url-access', -- cite map only -- Used by InternetArchiveBot
['Minutes'] = 'minutes',
['Mode'] = 'mode',
['NameListStyle'] = 'name-list-style',
['Network'] = 'network',
['Newsgroup'] = 'newsgroup', -- cite newsgroup only
['NoPP'] = {'no-pp', 'nopp'},
['NoTracking'] = {'no-tracking', 'template-doc-demo'},
['Number'] = 'number', -- this case only for cite techreport
['OrigDate'] = {'orig-date', 'orig-year', 'origyear'},
['Others'] = 'others',
['Page'] = {'page', 'p'}, -- Used by InternetArchiveBot
['Pages'] = {'pages', 'pp'}, -- Used by InternetArchiveBot
['Periodical'] = {'journal', 'magazine', 'newspaper', 'periodical', 'website', 'work'},
['Place'] = {'place', 'location'},
['PostScript'] = 'postscript',
['PublicationDate'] = {'publication-date', 'publicationdate'},
['PublicationPlace'] = {'publication-place', 'publicationplace'},
['PublisherName'] = {'publisher', 'institution'},
['Quote'] = {'quote', 'quotation'},
['QuotePage'] = 'quote-page',
['QuotePages'] = 'quote-pages',
['Ref'] = 'ref',
['Scale'] = 'scale',
['ScriptChapter'] = {'script-chapter', 'script-contribution', 'script-entry',
'script-article', 'script-section'},
['ScriptEncyclopedia'] = {'script-encyclopedia', 'script-encyclopaedia'}, -- cite encyclopedia only
['ScriptMap'] = 'script-map',
['ScriptPeriodical'] = {'script-journal', 'script-magazine', 'script-newspaper',
'script-periodical', 'script-website', 'script-work'},
['ScriptQuote'] = 'script-quote',
['ScriptTitle'] = 'script-title', -- Used by InternetArchiveBot
['Season'] = 'season',
['Sections'] = 'sections', -- cite map only
['Series'] = {'series', 'version'},
['SeriesLink'] = {'series-link', 'serieslink'},
['SeriesNumber'] = {'series-number', 'series-no'},
['Sheet'] = 'sheet', -- cite map only
['Sheets'] = 'sheets', -- cite map only
['Station'] = 'station',
['Time'] = 'time',
['TimeCaption'] = 'time-caption',
['Title'] = 'title', -- Used by InternetArchiveBot
['TitleLink'] = {'title-link', 'episode-link', 'episodelink'}, -- Used by InternetArchiveBot
['TitleNote'] = {'title-note', 'department'},
['TitleType'] = {'type', 'medium'},
['TransChapter'] = {'trans-article', 'trans-chapter', 'trans-contribution',
'trans-entry', 'trans-section'},
['Transcript'] = 'transcript',
['TranscriptFormat'] = 'transcript-format',
['TranscriptURL'] = 'transcript-url', -- Used by InternetArchiveBot
['TransEncyclopedia'] = {'trans-encyclopedia', 'trans-encyclopaedia'}, -- cite encyclopedia only
['TransMap'] = 'trans-map', -- cite map only
['TransPeriodical'] = {'trans-journal', 'trans-magazine', 'trans-newspaper',
'trans-periodical', 'trans-website', 'trans-work'},
['TransQuote'] = 'trans-quote',
['TransTitle'] = 'trans-title', -- Used by InternetArchiveBot
['URL'] = {'url', 'URL'}, -- Used by InternetArchiveBot
['UrlAccess'] = 'url-access', -- Used by InternetArchiveBot
['UrlStatus'] = 'url-status', -- Used by InternetArchiveBot
['Vauthors'] = 'vauthors',
['Veditors'] = 'veditors',
['Via'] = 'via',
['Volume'] = 'volume',
['Year'] = 'year',
['AuthorList-First'] = {"first#", "author-first#", "author#-first", "author-given#", "author#-given",
"subject-first#", "subject#-first", "subject-given#", "subject#-given",
"given#"},
['AuthorList-Last'] = {"last#", "author-last#", "author#-last", "author-surname#", "author#-surname",
"subject-last#", "subject#-last", "subject-surname#", "subject#-surname",
"author#", 'host#', "subject#", "surname#"},
['AuthorList-Link'] = {"author-link#", "author#-link", "subject-link#",
"subject#-link", "authorlink#", "author#link"},
['AuthorList-Mask'] = {"author-mask#", "author#-mask", "subject-mask#", "subject#-mask"},
['ContributorList-First'] = {'contributor-first#', 'contributor#-first',
'contributor-given#', 'contributor#-given'},
['ContributorList-Last'] = {'contributor-last#', 'contributor#-last',
'contributor-surname#', 'contributor#-surname', 'contributor#'},
['ContributorList-Link'] = {'contributor-link#', 'contributor#-link'},
['ContributorList-Mask'] = {'contributor-mask#', 'contributor#-mask'},
['EditorList-First'] = {"editor-first#", "editor#-first", "editor-given#", "editor#-given"},
['EditorList-Last'] = {"editor-last#", "editor#-last", "editor-surname#",
"editor#-surname", "editor#"},
['EditorList-Link'] = {"editor-link#", "editor#-link"},
['EditorList-Mask'] = {"editor-mask#", "editor#-mask"},
['InterviewerList-First'] = {'interviewer-first#', 'interviewer#-first',
'interviewer-given#', 'interviewer#-given'},
['InterviewerList-Last'] = {'interviewer-last#', 'interviewer#-last',
'interviewer-surname#', 'interviewer#-surname', 'interviewer#'},
['InterviewerList-Link'] = {'interviewer-link#', 'interviewer#-link'},
['InterviewerList-Mask'] = {'interviewer-mask#', 'interviewer#-mask'},
['TranslatorList-First'] = {'translator-first#', 'translator#-first',
'translator-given#', 'translator#-given'},
['TranslatorList-Last'] = {'translator-last#', 'translator#-last',
'translator-surname#', 'translator#-surname', 'translator#'},
['TranslatorList-Link'] = {'translator-link#', 'translator#-link'},
['TranslatorList-Mask'] = {'translator-mask#', 'translator#-mask'},
}
--[[--------------------------< P U N C T _ S K I P >---------------------------
builds a table of parameter names that the extraneous terminal punctuation check should not check.
]]
local punct_meta_params = { -- table of aliases[] keys (meta parameters); each key has a table of parameter names for a value
'BookTitle', 'Chapter', 'ScriptChapter', 'ScriptTitle', 'Title', 'TransChapter', 'Transcript', 'TransMap', 'TransTitle', -- title-holding parameters
'AuthorList-Mask', 'ContributorList-Mask', 'EditorList-Mask', 'InterviewerList-Mask', 'TranslatorList-Mask', -- name-list mask may have name separators
'PostScript', 'Quote', 'ScriptQuote', 'TransQuote', 'Ref', -- miscellaneous
'ArchiveURL', 'ChapterURL', 'ConferenceURL', 'MapURL', 'TranscriptURL', 'URL', -- URL-holding parameters
}
local url_meta_params = { -- table of aliases[] keys (meta parameters); each key has a table of parameter names for a value
'ArchiveURL', 'ChapterURL', 'ConferenceURL', 'ID', 'MapURL', 'TranscriptURL', 'URL', -- parameters allowed to hold urls
'Page', 'Pages', 'At', 'QuotePage', 'QuotePages', -- insource locators allowed to hold urls
}
local function build_skip_table (skip_t, meta_params)
for _, meta_param in ipairs (meta_params) do -- for each meta parameter key
local params = aliases[meta_param]; -- get the parameter or the table of parameters associated with the meta parameter name
if 'string' == type (params) then
skip_t[params] = 1; -- just a single parameter
else
for _, param in ipairs (params) do -- get the parameter name
skip_t[param] = 1; -- add the parameter name to the skip table
local count;
param, count = param:gsub ('#', ''); -- remove enumerator marker from enumerated parameters
if 0 ~= count then -- if removed
skip_t[param] = 1; -- add param name without enumerator marker
end
end
end
end
return skip_t;
end
local punct_skip = {};
local url_skip = {};
--[[--------------------------< S I N G L E - L E T T E R S E C O N D - L E V E L D O M A I N S >----------
this is a list of tlds that are known to have single-letter second-level domain names. This list does not include
ccTLDs which are accepted in is_domain_name().
]]
local single_letter_2nd_lvl_domains_t = {'cash', 'company', 'foundation', 'media', 'org', 'today'};
--[[-----------< S P E C I A L C A S E T R A N S L A T I O N S >------------
This table is primarily here to support internationalization. Translations in
this table are used, for example, when an error message, category name, etc.,
is extracted from the English alias key. There may be other cases where
this translation table may be useful.
]]
local is_Latn = 'A-Za-z\195\128-\195\150\195\152-\195\182\195\184-\198\191\199\132-\201\143\225\184\128-\225\187\191';
local special_case_translation = {
['AuthorList'] = 'authors list', -- used to assemble maintenance category names
['ContributorList'] = 'contributors list', -- translation of these names plus translation of the base maintenance category names in maint_cats{} table below
['EditorList'] = 'editors list', -- must match the names of the actual categories
['InterviewerList'] = 'interviewers list', -- this group or translations used by name_has_ed_markup() and name_has_mult_names()
['TranslatorList'] = 'translators list',
-- 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'] = nil, -- 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'] = nil},
{['en'] = {'page not found', true}, ['local'] = nil},
{['en'] = {'subscribe to read', true}, ['local'] = nil},
{['en'] = {'^[%(%[{<]?unknown[>}%]%)]?$', false}, ['local'] = nil},
{['en'] = {'website is for sale', true}, ['local'] = nil},
{['en'] = {'^404', false}, ['local'] = nil},
{['en'] = {'error[ %-]404', false}, ['local'] = nil},
{['en'] = {'internet archive wayback machine', true}, ['local'] = nil},
{['en'] = {'log into facebook', true}, ['local'] = nil},
{['en'] = {'login • instagram', true}, ['local'] = nil},
{['en'] = {'redirecting...', true}, ['local'] = nil},
{['en'] = {'usurped title', true}, ['local'] = nil}, -- 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'] = nil},
},
['reject'] = {
{['en'] = {'about us', true}, ['local'] = nil},
{['en'] = {'%f[%a][Aa]dvisor%f[%A]', false}, ['local'] = nil},
{['en'] = {'allmusic', true}, ['local'] = nil},
{['en'] = {'%f[%a][Aa]uthor%f[%A]', false}, ['local'] = nil},
{['en'] = {'^[Bb]ureau$', false}, ['local'] = nil},
{['en'] = {'business', true}, ['local'] = nil},
{['en'] = {'cnn', true}, ['local'] = nil},
{['en'] = {'collaborator', true}, ['local'] = nil},
{['en'] = {'^[Cc]ompany$', false}, ['local'] = nil},
{['en'] = {'contributor', true}, ['local'] = nil},
{['en'] = {'contact us', true}, ['local'] = nil},
{['en'] = {'correspondent', true}, ['local'] = nil},
{['en'] = {'^[Dd]esk$', false}, ['local'] = nil},
{['en'] = {'directory', true}, ['local'] = nil},
{['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'] = nil},
{['en'] = {'%f[%a][Ee]ditors?%f[%A]', false}, ['local'] = nil},
{['en'] = {'%f[%a][Ee]mail%f[%A]', false}, ['local'] = nil},
{['en'] = {'facebook', true}, ['local'] = nil},
{['en'] = {'google', true}, ['local'] = nil},
{['en'] = {'^[Gg]roup$', false}, ['local'] = nil},
{['en'] = {'home page', true}, ['local'] = nil},
{['en'] = {'^[Ii]nc%.?$', false}, ['local'] = nil},
{['en'] = {'instagram', true}, ['local'] = nil},
{['en'] = {'interviewer', true}, ['local'] = nil},
{['en'] = {'^[Ll]imited$', false}, ['local'] = nil},
{['en'] = {'linkedIn', true}, ['local'] = nil},
{['en'] = {'^[Nn]ews$', false}, ['local'] = nil},
{['en'] = {'[Nn]ews[ %-]?[Rr]oom', false}, ['local'] = nil},
{['en'] = {'pinterest', true}, ['local'] = nil},
{['en'] = {'policy', true}, ['local'] = nil},
{['en'] = {'privacy', true}, ['local'] = nil},
{['en'] = {'reuters', true}, ['local'] = nil},
{['en'] = {'translator', true}, ['local'] = nil},
{['en'] = {'tumblr', true}, ['local'] = nil},
{['en'] = {'twitter', true}, ['local'] = nil},
{['en'] = {'site name', true}, ['local'] = nil},
{['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'] = nil},
{['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'] = {['જાન્યુઆરી'] = 1, ['ફેબ્રુઆરી'] = 2, ['માર્ચ'] = 3, ['એપ્રિલ'] = 4, ['મે'] = 5, ['જૂન'] = 6, ['જુલાઇ'] = 7, ['ઓગસ્ટ'] = 8, ['સપ્ટેમ્બર'] = 9, ['ઓક્ટોબર'] = 10, ['નવેમ્બર'] = 11, ['ડિસેમ્બર'] = 12},
['short'] = {['જાન'] = 1, ['ફેબ્રુ'] = 2, ['માર્ચ'] = 3, ['એપ્રિ'] = 4, ['મે'] = 5, ['જૂન'] = 6, ['જુલ'] = 7, ['ઓગ'] = 8, ['સપ્ટે'] = 9, ['ઓક્ટ'] = 10, ['નવે'] = 11, ['ડિસે'] = 12},
['quarter'] = {['First Quarter'] = 33, ['Second Quarter'] = 34, ['Third Quarter'] = 35, ['Fourth Quarter'] = 36},
['season'] = {['શિયાળો'] = 24, ['વસંત'] = 21, ['ઉનાળો'] = 22, ['પાનખર'] = 23, ['શિશિર'] = 23},
['named'] = {['ઇસ્ટર'] = 98, ['ક્રિસમસ'] = 99},
},
['inv_local_long'] = {}, -- used in date reformatting & translation; copy of date_names['local'].long where k/v are inverted: [1]='<local name>' etc.
['inv_local_short'] = {}, -- used in date reformatting & translation; copy of date_names['local'].short where k/v are inverted: [1]='<local name>' etc.
['inv_local_quarter'] = {}, -- used in date translation; copy of date_names['local'].quarter where k/v are inverted: [1]='<local name>' etc.
['inv_local_season'] = {}, -- used in date translation; copy of date_names['local'].season where k/v are inverted: [1]='<local name>' etc.
['inv_local_named'] = {}, -- used in date translation; copy of date_names['local'].named where k/v are inverted: [1]='<local name>' etc.
['local_digits'] = {['0'] = '0', ['1'] = '1', ['2'] = '2', ['3'] = '3', ['4'] = '4', ['5'] = '5', ['6'] = '6', ['7'] = '7', ['8'] = '8', ['9'] = '9'}, -- used to convert local language digits to Western 0-9
['xlate_digits'] = {},
}
if local_date_names_from_mediawiki then -- if fetching local month names from MediaWiki is enabled
local long_t = {};
local short_t = {};
for i=1, 12 do -- loop 12x and
local name = lang_obj:formatDate('F', '2022-' .. i .. '-1'); -- get long month name for each i
long_t[name] = i; -- save it
name = lang_obj:formatDate('M', '2022-' .. i .. '-1'); -- get short month name for each i
short_t[name] = i; -- save it
end
date_names['local']['long'] = long_t; -- write the long table – overwrites manual translation
date_names['local']['short'] = short_t; -- write the short table – overwrites manual translation
end
-- create inverted date-name tables for reformatting and/or translation
for _, invert_t in pairs {{'long', 'inv_local_long'}, {'short', 'inv_local_short'}, {'quarter', 'inv_local_quarter'}, {'season', 'inv_local_season'}, {'named', 'inv_local_named'}} do
for name, i in pairs (date_names['local'][invert_t[1]]) do -- this table is ['name'] = i
date_names[invert_t[2]][i] = name; -- invert to get [i] = 'name' for conversions from ymd
end
end
if local_digits_from_mediawiki then -- if fetching local digits from MediaWiki is enabled
local digits_t = {};
for i=0, 9 do -- loop 10x and
digits_t [lang_obj:formatNum (i)] = tostring (i); -- format the loop indexer as local lang table index and assign loop indexer (a string) as the value
end
date_names['local_digits'] = digits_t;
end
for ld, ed in pairs (date_names.local_digits) do -- make a digit translation table for simple date translation from en to local language using local_digits table
date_names.xlate_digits [ed] = ld; -- en digit becomes index with local digit as the value
end
local df_template_patterns = { -- table of redirects to {{Use dmy dates}} and {{Use mdy dates}}
'{{ *[Uu]se +(dmy) +dates *[|}]', -- 1159k -- sorted by approximate transclusion count
'{{ *[Uu]se +(mdy) +dates *[|}]', -- 212k
'{{ *[Uu]se +(MDY) +dates *[|}]', -- 788
'{{ *[Uu]se +(DMY) +dates *[|}]', -- 343
'{{ *([Mm]dy) *[|}]', -- 176
'{{ *[Uu]se *(dmy) *[|}]', -- 156 + 18
'{{ *[Uu]se *(mdy) *[|}]', -- 149 + 11
'{{ *([Dd]my) *[|}]', -- 56
'{{ *[Uu]se +(MDY) *[|}]', -- 5
'{{ *([Dd]MY) *[|}]', -- 3
'{{ *[Uu]se(mdy)dates *[|}]', -- 1
'{{ *[Uu]se +(DMY) *[|}]', -- 0
'{{ *([Mm]DY) *[|}]', -- 0
}
local title_object = mw.title.getCurrentTitle();
local content; -- done this way so that unused templates appear in unused-template-reports; self-transcluded makes them look like they are used
if 10 ~= title_object.namespace then -- all namespaces except Template
content = title_object:getContent() or ''; -- get the content of the article or ''; new pages edited w/ve do not have 'content' until saved; ve does not preview; phab:T221625
end
local function get_date_format ()
if not content then -- nil content when we're in template
return nil; -- auto-formatting does not work in Template space so don't set global_df
end
for _, pattern in ipairs (df_template_patterns) do -- loop through the patterns looking for {{Use dmy dates}} or {{Use mdy dates}} or any of their redirects
local start, _, match = content:find(pattern); -- match is the three letters indicating desired date format
if match then
local use_dates_template = content:match ('%b{}', start); -- get the whole template
if use_dates_template:match ('| *cs1%-dates *= *[lsy][sy]?') then -- look for |cs1-dates=publication date length access-/archive-date length
return match:lower() .. '-' .. use_dates_template:match ('| *cs1%-dates *= *([lsy][sy]?)');
else
return match:lower() .. '-all'; -- no |cs1-dates= k/v pair; return value appropriate for use in |df=
end
end
end
end
local global_df; -- TODO: add this to <global_cs1_config_t>?
--[[-----------------< V O L U M E , I S S U E , P A G E S >------------------
These tables hold cite class values (from the template invocation) and identify those templates that support
|volume=, |issue=, and |page(s)= parameters. Cite conference and cite map require further qualification which
is handled in the main module.
]]
local templates_using_volume = {'citation', 'audio-visual', 'book', 'conference', 'encyclopaedia', 'interview', 'journal', 'magazine', 'map', 'news', 'report', 'techreport', 'thesis'}
local templates_using_issue = {'citation', 'conference', 'episode', 'interview', 'journal', 'magazine', 'map', 'news', 'podcast'}
local templates_not_using_page = {'audio-visual', 'episode', 'mailinglist', 'newsgroup', 'podcast', 'serial', 'sign', 'speech'}
--[[
These tables control when it is appropriate for {{citation}} to render |volume= and/or |issue=. The parameter
names in the tables constrain {{citation}} so that its renderings match the renderings of the equivalent cs1
templates. For example, {{cite web}} does not support |volume= so the equivalent {{citation |website=...}} must
not support |volume=.
]]
local citation_no_volume_t = { -- {{citation}} does not render |volume= when these parameters are used
'website', 'mailinglist', 'script-website',
}
local citation_issue_t = { -- {{citation}} may render |issue= when these parameters are used
'journal', 'magazine', 'newspaper', 'periodical', 'work',
'script-journal', 'script-magazine', 'script-newspaper', 'script-periodical', 'script-work',
}
--[[
Patterns for finding extra text in |volume=, |issue=, |page=, |pages=
]]
local vol_iss_pg_patterns = {
good_ppattern = '^P[^%.PpGg]', -- OK to begin with uppercase P: P7 (page 7 of section P), but not p123 (page 123); TODO: this allows 'Pages' which it should not
bad_ppatterns = { -- patterns for |page= and |pages=
'^[Pp][PpGg]?%.?[ %d]',
'^[Pp][Pp]?%. ', -- from {{p.}} and {{pp.}} templates
'^[Pp]ages?',
'^[Pp]gs.?',
},
vi_patterns_t = { -- combined to catch volume-like text in |issue= and issue-like text in |volume=
'^volumes?', -- volume-like text
'^vols?[%.:=]?',
'^issues?', --issue-like text
'^iss[%.:=]?',
'^numbers?',
'^nos?%A', -- don't match 'november' or 'nostradamus'
'^nr[%.:=]?',
'^n[%.:= ]', -- might be a valid issue without separator (space char is sep char here)
'^n°', -- 'n' with degree sign (U+00B0)
'^№', -- precomposed unicode numero character (U+2116)
},
}
--[[--------------------------< K E Y W O R D S >-------------------------------
These tables hold keywords for those parameters that have defined sets of acceptable keywords.
]]
--[[-------------------< K E Y W O R D S T A B L E >--------------------------
this is a list of keywords; each key in the list is associated with a table of
synonymous keywords possibly from different languages.
for I18N: add local-language keywords to value table; do not change the key.
For example, adding the German keyword 'ja':
['affirmative'] = {'yes', 'true', 'y', 'ja'},
Because CS1|2 templates from en.wiki articles are often copied to other local wikis,
it is recommended that the English keywords remain in these tables.
]]
local keywords = {
['amp'] = {'&', 'amp', 'ampersand'}, -- |name-list-style=
['and'] = {'and', 'serial'}, -- |name-list-style=
['affirmative'] = {'yes', 'true', 'y'}, -- |no-tracking=, |no-pp= -- Used by InternetArchiveBot
['afterword'] = {'afterword'}, -- |contribution=
['bot: unknown'] = {'bot: unknown'}, -- |url-status= -- Used by InternetArchiveBot
['cs1'] = {'cs1'}, -- |mode=
['cs2'] = {'cs2'}, -- |mode=
['dead'] = {'dead', 'deviated'}, -- |url-status= -- Used by InternetArchiveBot
['dmy'] = {'dmy'}, -- |df=
['dmy-all'] = {'dmy-all'}, -- |df=
['foreword'] = {'foreword'}, -- |contribution=
['free'] = {'free'}, -- |<id>-access= -- Used by InternetArchiveBot
['harv'] = {'harv'}, -- |ref=; this no longer supported; is_valid_parameter_value() called with <invert> = true
['introduction'] = {'introduction'}, -- |contribution=
['limited'] = {'limited'}, -- |url-access= -- Used by InternetArchiveBot
['live'] = {'live'}, -- |url-status= -- Used by InternetArchiveBot
['mdy'] = {'mdy'}, -- |df=
['mdy-all'] = {'mdy-all'}, -- |df=
['none'] = {'none'}, -- |postscript=, |ref=, |title=, |type= -- Used by InternetArchiveBot
['off'] = {'off'}, -- |title= (potentially also: |title-link=, |postscript=, |ref=, |type=)
['preface'] = {'preface'}, -- |contribution=
['registration'] = {'registration'}, -- |url-access= -- Used by InternetArchiveBot
['subscription'] = {'subscription'}, -- |url-access= -- Used by InternetArchiveBot
['unfit'] = {'unfit'}, -- |url-status= -- Used by InternetArchiveBot
['usurped'] = {'usurped'}, -- |url-status= -- Used by InternetArchiveBot
['vanc'] = {'vanc'}, -- |name-list-style=
['ymd'] = {'ymd'}, -- |df=
['ymd-all'] = {'ymd-all'}, -- |df=
-- ['yMd'] = {'yMd'}, -- |df=; not supported at en.wiki
-- ['yMd-all'] = {'yMd-all'}, -- |df=; not supported at en.wiki
}
--[[------------------------< X L A T E _ K E Y W O R D S >---------------------
this function builds a list, keywords_xlate{}, of the keywords found in keywords{} where the values from keywords{}
become the keys in keywords_xlate{} and the keys from keywords{} become the values in keywords_xlate{}:
['affirmative'] = {'yes', 'true', 'y'}, -- in keywords{}
becomes
['yes'] = 'affirmative', -- in keywords_xlate{}
['true'] = 'affirmative',
['y'] = 'affirmative',
the purpose of this function is to act as a translator between a non-English keyword and its English equivalent
that may be used in other modules of this suite
]]
local function xlate_keywords ()
local out_table = {}; -- output goes here
for k, keywords_t in pairs (keywords) do -- spin through the keywords table
for _, keyword in ipairs (keywords_t) do -- for each keyword
out_table[keyword] = k; -- create an entry in the output table where keyword is the key
end
end
return out_table;
end
local keywords_xlate = xlate_keywords (); -- the list of translated keywords
--[[----------------< M A K E _ K E Y W O R D S _ L I S T >---------------------
this function assembles, for parameter-value validation, the list of keywords appropriate to that parameter.
keywords_lists{}, is a table of tables from keywords{}
]]
local function make_keywords_list (keywords_lists)
local out_table = {}; -- output goes here
for _, keyword_list in ipairs (keywords_lists) do -- spin through keywords_lists{} and get a table of keywords
for _, keyword in ipairs (keyword_list) do -- spin through keyword_list{} and add each keyword, ...
table.insert (out_table, keyword); -- ... as plain text, to the output list
end
end
return out_table;
end
--[[----------------< K E Y W O R D S _ L I S T S >-----------------------------
this is a list of lists of valid keywords for the various parameters in [key].
Generally the keys in this table are the canonical en.wiki parameter names though
some are contrived because of use in multiple differently named parameters:
['yes_true_y'], ['id-access'].
The function make_keywords_list() extracts the individual keywords from the
appropriate list in keywords{}.
The lists in this table are used to validate the keyword assignment for the
parameters named in this table's keys.
]]
local keywords_lists = {
['yes_true_y'] = make_keywords_list ({keywords.affirmative}),
['contribution'] = make_keywords_list ({keywords.afterword, keywords.foreword, keywords.introduction, keywords.preface}),
['df'] = make_keywords_list ({keywords.dmy, keywords['dmy-all'], keywords.mdy, keywords['mdy-all'], keywords.ymd, keywords['ymd-all']}),
-- ['df'] = make_keywords_list ({keywords.dmy, keywords['dmy-all'], keywords.mdy, keywords['mdy-all'], keywords.ymd, keywords['ymd-all'], keywords.yMd, keywords['yMd-all']}), -- not supported at en.wiki
['mode'] = make_keywords_list ({keywords.cs1, keywords.cs2}),
['name-list-style'] = make_keywords_list ({keywords.amp, keywords['and'], keywords.vanc}),
['ref'] = make_keywords_list ({keywords.harv}), -- inverted check; |ref=harv no longer supported
['url-access'] = make_keywords_list ({keywords.subscription, keywords.limited, keywords.registration}),
['url-status'] = make_keywords_list ({keywords.dead, keywords.live, keywords.unfit, keywords.usurped, keywords['bot: unknown']}),
['id-access'] = make_keywords_list ({keywords.free}),
}
--[[--------------------------< C S 1 _ C O N F I G _ G E T >--------------------------------------------------
fetch and validate values from {{cs1 config}} template to fill <global_cs1_config_t>
no error messages; when errors are detected, the parameter value from {{cs1 config}} is blanked.
Supports all parameters and aliases associated with the metaparameters: DisplayAuthors, DisplayContributors,
DisplayEditors, DisplayInterviewers, DisplayTranslators, NameListStyle, and Mode. The DisplayWhatever metaparameters
accept numeric values only (|display-authors=etal and the like is not supported).
]]
local global_cs1_config_t = {}; -- TODO: add value returned from get_date_format() to this table?
local function get_cs1_config ()
if not content then -- nil content when we're in template
return nil; -- auto-formatting does not work in Template space so don't set global_df
end
local start = content:find('{{ *[Cc][Ss]1 config *[|}]'); -- <start> is offset into <content> when {{cs1 config}} found; nil else
if start then
local cs1_config_template = content:match ('%b{}', start); -- get the whole template
if not cs1_config_template then
return nil;
end
local params_t = mw.text.split (cs1_config_template:gsub ('^{{%s*', ''):gsub ('%s*}}$', ''), '%s*|%s*'); -- remove '{{' and '}}'; make a sequence of parameter/value pairs (split on the pipe)
table.remove (params_t, 1); -- remove the template name because it isn't a parameter/value pair
local config_meta_params_t = {'DisplayAuthors', 'DisplayContributors', 'DisplayEditors', 'DisplayInterviewers', 'DisplayTranslators', 'NameListStyle', 'Mode'};
local meta_param_map_t = {}; -- list of accepted parameter names usable in {{cs1 config}} goes here
for _, meta_param in ipairs (config_meta_params_t) do -- for i18n using <config_meta_params_t>, map template parameter names to their metaparameter equivalents
if 'table' == type (aliases[meta_param]) then -- if <meta_param> is a sequence,
for _, param in ipairs (aliases[meta_param]) do -- extract its contents
meta_param_map_t[param] = meta_param; -- and add to <meta_param_map_t>
end
else
meta_param_map_t[aliases[meta_param]] = meta_param; -- not a sequence so just add the parameter to <meta_param_map_t>
end
end
local keywords_t = {}; -- map valid keywords to their associate metaparameter; reverse form of <keyword_lists[key] for these metaparameters
for _, metaparam_t in ipairs ({{'NameListStyle', 'name-list-style'}, {'Mode', 'mode'}}) do -- only these metaparameter / keywords_lists key pairs
for _, keyword in ipairs (keywords_lists[metaparam_t[2]]) do -- spin through the list of keywords
keywords_t[keyword] = metaparam_t[1]; -- add [keyword] = metaparameter to the map
end
end
for _, param in ipairs (params_t) do -- spin through the {{cs1 config}} parameters and fill <global_cs1_config_t>
local k, v = param:match ('([^=]-)%s*=%s*(.+)'); -- <k> is the parameter name; <v> is parameter's assigned value
if k then
if k:find ('^display') then -- if <k> is one of the |display-<namelist>= parameters
if v:match ('%d+') then -- the assigned value must be digits; doesn't accept 'etal'
global_cs1_config_t[meta_param_map_t[k]]=v; -- add the display param and its value to globals table
end
else
if keywords_t[v] == meta_param_map_t[k] then -- keywords_t[v] returns nil or the metaparam name; these must be the same
global_cs1_config_t[meta_param_map_t[k]]=v; -- add the parameter and its value to globals table
end
end
end
end
end
end
get_cs1_config (); -- fill <global_cs1_config_t>
--[[---------------------< S T R I P M A R K E R S >----------------------------
Common pattern definition location for stripmarkers so that we don't have to go
hunting for them if (when) MediaWiki changes their form.
]]
local stripmarkers = {
['any'] = '\127[^\127]*UNIQ%-%-(%a+)%-[%a%d]+%-QINU[^\127]*\127', -- capture returns name of stripmarker
['math'] = '\127[^\127]*UNIQ%-%-math%-[%a%d]+%-QINU[^\127]*\127' -- math stripmarkers used in coins_cleanup() and coins_replace_math_stripmarker()
}
--[[------------< I N V I S I B L E _ C H A R A C T E R S >---------------------
This table holds non-printing or invisible characters indexed either by name or
by Unicode group. Values are decimal representations of UTF-8 codes. The table
is organized as a table of tables because the Lua pairs keyword returns table
data in an arbitrary order. Here, we want to process the table from top to bottom
because the entries at the top of the table are also found in the ranges specified
by the entries at the bottom of the table.
Also here is a pattern that recognizes stripmarkers that begin and end with the
delete characters. The nowiki stripmarker is not an error but some others are
because the parameter values that include them become part of the template's
metadata before stripmarker replacement.
]]
local invisible_defs = {
del = '\127', -- used to distinguish between stripmarker and del char
zwj = '\226\128\141', -- used with capture because zwj may be allowed
}
local invisible_chars = {
{'replacement', '\239\191\189'}, -- U+FFFD, EF BF BD
{'zero width joiner', '('.. invisible_defs.zwj .. ')'}, -- U+200D, E2 80 8D; capture because zwj may be allowed
{'zero width space', '\226\128\139'}, -- U+200B, E2 80 8B
{'hair space', '\226\128\138'}, -- U+200A, E2 80 8A
{'soft hyphen', '\194\173'}, -- U+00AD, C2 AD
{'horizontal tab', '\009'}, -- U+0009 (HT), 09
{'line feed', '\010'}, -- U+000A (LF), 0A
{'no-break space', '\194\160'}, -- U+00A0 (NBSP), C2 A0
{'carriage return', '\013'}, -- U+000D (CR), 0D
{'stripmarker', stripmarkers.any}, -- stripmarker; may or may not be an error; capture returns the stripmaker type
{'delete', '('.. invisible_defs.del .. ')'}, -- U+007F (DEL), 7F; must be done after stripmarker test; capture to distinguish isolated del chars not part of stripmarker
{'C0 control', '[\000-\008\011\012\014-\031]'}, -- U+0000–U+001F (NULL–US), 00–1F (except HT, LF, CR (09, 0A, 0D))
{'C1 control', '[\194\128-\194\159]'}, -- U+0080–U+009F (XXX–APC), C2 80 – C2 9F
-- {'Specials', '[\239\191\185-\239\191\191]'}, -- U+FFF9-U+FFFF, EF BF B9 – EF BF BF
-- {'Private use area', '[\238\128\128-\239\163\191]'}, -- U+E000–U+F8FF, EE 80 80 – EF A3 BF
-- {'Supplementary Private Use Area-A', '[\243\176\128\128-\243\191\191\189]'}, -- U+F0000–U+FFFFD, F3 B0 80 80 – F3 BF BF BD
-- {'Supplementary Private Use Area-B', '[\244\128\128\128-\244\143\191\189]'}, -- U+100000–U+10FFFD, F4 80 80 80 – F4 8F BF BD
}
--[[
Indic script makes use of zero width joiner as a character modifier so zwj
characters must be left in. This pattern covers all of the unicode characters
for these languages:
Devanagari 0900–097F – https://unicode.org/charts/PDF/U0900.pdf
Devanagari extended A8E0–A8FF – https://unicode.org/charts/PDF/UA8E0.pdf
Bengali 0980–09FF – https://unicode.org/charts/PDF/U0980.pdf
Gurmukhi 0A00–0A7F – https://unicode.org/charts/PDF/U0A00.pdf
Gujarati 0A80–0AFF – https://unicode.org/charts/PDF/U0A80.pdf
Oriya 0B00–0B7F – https://unicode.org/charts/PDF/U0B00.pdf
Tamil 0B80–0BFF – https://unicode.org/charts/PDF/U0B80.pdf
Telugu 0C00–0C7F – https://unicode.org/charts/PDF/U0C00.pdf
Kannada 0C80–0CFF – https://unicode.org/charts/PDF/U0C80.pdf
Malayalam 0D00–0D7F – https://unicode.org/charts/PDF/U0D00.pdf
plus the not-necessarily Indic scripts for Sinhala and Burmese:
Sinhala 0D80-0DFF - https://unicode.org/charts/PDF/U0D80.pdf
Myanmar 1000-109F - https://unicode.org/charts/PDF/U1000.pdf
Myanmar extended A AA60-AA7F - https://unicode.org/charts/PDF/UAA60.pdf
Myanmar extended B A9E0-A9FF - https://unicode.org/charts/PDF/UA9E0.pdf
the pattern is used by has_invisible_chars() and coins_cleanup()
]]
local indic_script = '[\224\164\128-\224\181\191\224\163\160-\224\183\191\225\128\128-\225\130\159\234\167\160-\234\167\191\234\169\160-\234\169\191]';
-- list of emoji that use a zwj character (U+200D) to combine with another emoji
-- from: https://unicode.org/Public/emoji/16.0/emoji-zwj-sequences.txt; version: 16.0; 2024-08-14
-- table created by: [[:en:Module:Make emoji zwj table]]
local emoji_t = { -- indexes are decimal forms of the hex values in U+xxxx
[8596] = true, -- U+2194 ↔ left right arrow
[8597] = true, -- U+2195 ↕ up down arrow
[9760] = true, -- U+2620 ☠ skull and crossbones
[9792] = true, -- U+2640 ♀ female sign
[9794] = true, -- U+2642 ♂ male sign
[9877] = true, -- U+2695 ⚕ staff of aesculapius
[9878] = true, -- U+2696 ⚖ scales
[9895] = true, -- U+26A7 ⚧ male with stroke and male and female sign
[9992] = true, -- U+2708 ✈ airplane
[10052] = true, -- U+2744 ❄ snowflake
[10084] = true, -- U+2764 ❤ heavy black heart
[10145] = true, -- U+27A1 ➡ black rightwards arrow
[11035] = true, -- U+2B1B ⬛ black large square
[127752] = true, -- U+1F308 🌈 rainbow
[127787] = true, -- U+1F32B 🌫 fog
[127806] = true, -- U+1F33E 🌾 ear of rice
[127859] = true, -- U+1F373 🍳 cooking
[127868] = true, -- U+1F37C 🍼 baby bottle
[127876] = true, -- U+1F384 🎄 christmas tree
[127891] = true, -- U+1F393 🎓 graduation cap
[127908] = true, -- U+1F3A4 🎤 microphone
[127912] = true, -- U+1F3A8 🎨 artist palette
[127979] = true, -- U+1F3EB 🏫 school
[127981] = true, -- U+1F3ED 🏭 factory
[128102] = true, -- U+1F466 👦 boy
[128103] = true, -- U+1F467 👧 girl
[128104] = true, -- U+1F468 👨 man
[128105] = true, -- U+1F469 👩 woman
[128139] = true, -- U+1F48B 💋 kiss mark
[128165] = true, -- U+1F4A5 💥 collision symbol
[128168] = true, -- U+1F4A8 💨 dash symbol
[128171] = true, -- U+1F4AB 💫 dizzy symbol
[128187] = true, -- U+1F4BB 💻 personal computer
[128188] = true, -- U+1F4BC 💼 brief case
[128293] = true, -- U+1F525 🔥 fire
[128295] = true, -- U+1F527 🔧 wrench
[128300] = true, -- U+1F52C 🔬 microscope
[128488] = true, -- U+1F5E8 🗨 left speech bubble
[128640] = true, -- U+1F680 🚀 rocket
[128658] = true, -- U+1F692 🚒 fire engine
[129001] = true, -- U+1F7E9 🟩 large green square
[129003] = true, -- U+1F7EB 🟫 large brown square
[129309] = true, -- U+1F91D 🤝 handshake
[129455] = true, -- U+1F9AF 🦯 probing cane
[129456] = true, -- U+1F9B0 🦰 emoji component red hair
[129457] = true, -- U+1F9B1 🦱 emoji component curly hair
[129458] = true, -- U+1F9B2 🦲 emoji component bald
[129459] = true, -- U+1F9B3 🦳 emoji component white hair
[129466] = true, -- U+1F9BA 🦺 safety vest
[129468] = true, -- U+1F9BC 🦼 motorized wheelchair
[129469] = true, -- U+1F9BD 🦽 manual wheelchair
[129489] = true, -- U+1F9D1 🧑 adult
[129490] = true, -- U+1F9D2 🧒 child
[129657] = true, -- U+1FA79 🩹 adhesive bandage
[129778] = true, -- U+1FAF2 🫲 leftwards hand
}
--[[----------------------< L A N G U A G E S U P P O R T >-------------------
These tables and constants support various language-specific functionality.
]]
--local this_wiki_code = mw.getContentLanguage():getCode(); -- get this wiki's language code
local this_wiki_code = lang_obj:getCode(); -- get this wiki's language code
if string.match (mw.site.server, 'wikidata') then
this_wiki_code = mw.getCurrentFrame():callParserFunction('int', {'lang'}); -- on Wikidata so use interface language setting instead
end
local mw_languages_by_tag_t = mw.language.fetchLanguageNames (this_wiki_code, 'all'); -- get a table of language tag/name pairs known to Wikimedia; used for interwiki tests
local mw_languages_by_name_t = {};
for k, v in pairs (mw_languages_by_tag_t) do -- build a 'reversed' table name/tag language pairs know to MediaWiki; used for |language=
v = mw.ustring.lower (v); -- lowercase for tag fetch; get name's proper case from mw_languages_by_tag_t[<tag>]
if mw_languages_by_name_t[v] then -- when name already in the table
if 2 == #k or 3 == #k then -- if tag does not have subtags
mw_languages_by_name_t[v] = k; -- prefer the shortest tag for this name
end
else -- here when name not in the table
mw_languages_by_name_t[v] = k; -- so add name and matching tag
end
end
local inter_wiki_map = {}; -- map of interwiki prefixes that are language-code prefixes
for k, v in pairs (mw.site.interwikiMap ('local')) do -- spin through the base interwiki map (limited to local)
if mw_languages_by_tag_t[v["prefix"]] then -- if the prefix matches a known language tag
inter_wiki_map[v["prefix"]] = true; -- add it to our local map
end
end
--[[--------------------< S C R I P T _ L A N G _ C O D E S >-------------------
This table is used to hold ISO 639-1 two-character and ISO 639-3 three-character
language codes that apply only to |script-title= and |script-chapter=
]]
local script_lang_codes = {
'ab', 'am', 'ar', 'az', 'be', 'bg', 'bn', 'bo', 'bs', 'ce', 'chr', 'dv', 'dz',
'el', 'fa', 'grc', 'gu', 'he', 'hi', 'hy', 'ja', 'ka', 'kk', 'km', 'kn', 'ko',
'ku', 'ky', 'lo', 'mk', 'ml', 'mn', 'mni', 'mr', 'my', 'ne', 'or', 'ota',
'pa', 'ps', 'ru', 'sd', 'si', 'sr', 'syc', 'ta', 'te', 'tg', 'th', 'ti', 'tt',
'ug', 'uk', 'ur', 'uz', 'yi', 'yue', 'zh', 'zgh'
};
--[[---------------< L A N G U A G E R E M A P P I N G >----------------------
These tables hold language information that is different (correct) from MediaWiki's definitions
For each ['<tag>'] = 'language name' in lang_code_remap{} there must be a matching ['language name'] = {'language name', '<tag>'} in lang_name_remap{}
lang_tag_remap{}:
key is always lowercase ISO 639-1, -2, -3 language tag or a valid lowercase IETF language tag
value is properly spelled and capitalized language name associated with <tag>
only one language name per <tag>;
key/value pair must have matching entry in lang_name_remap{}
lang_name_remap{}:
key is always lowercase language name
value is a table the holds correctly spelled and capitalized language name [1] and associated tag [2] (tag must match a tag key in lang_tag_remap{})
may have multiple keys referring to a common preferred name and tag; For example:
['kolsch'] and ['kölsch'] both refer to 'Kölsch' and 'ksh'
]]
local lang_tag_remap = { -- used for |language= and |script-title= / |script-chapter=
['als'] = 'Tosk Albanian', -- MediaWiki returns Alemannisch
['bh'] = 'Bihari', -- MediaWiki uses 'bh' as a subdomain name for Bhojpuri Wikipedia: bh.wikipedia.org
['bla'] = 'Blackfoot', -- MediaWiki/IANA/ISO 639: Siksika; use en.wiki preferred name
['bn'] = 'Bengali', -- MediaWiki returns Bangla
['ca-valencia'] = 'Valencian', -- IETF variant of Catalan
['fkv'] = 'Kven', -- MediaWiki returns Kvensk
['gsw'] = 'Swiss German',
['ilo'] = 'Ilocano', -- MediaWiki/IANA/ISO 639: Iloko; use en.wiki preferred name
['ksh'] = 'Kölsch', -- MediaWiki: Colognian; use IANA/ISO 639 preferred name
['ksh-x-colog'] = 'Colognian', -- override MediaWiki ksh; no IANA/ISO 639 code for Colognian; IETF private code created at Module:Lang/data
['mis-x-ripuar'] = 'Ripuarian', -- override MediaWiki ksh; no IANA/ISO 639 code for Ripuarian; IETF private code created at Module:Lang/data
['nan-tw'] = 'Taiwanese Hokkien', -- make room for MediaWiki/IANA/ISO 639 nan: Min Nan Chinese and support en.wiki preferred name
['sr-ec'] = 'Serbian (Cyrillic script)', -- MediaWiki returns српски (ћирилица)
['sr-el'] = 'Serbian (Latin script)', -- MediaWiki returns srpski (latinica)
}
local lang_name_remap = { -- used for |language=; names require proper capitalization; tags must be lowercase
['alemannic'] = {'Swiss German', 'gsw'}, -- ISO 639-2, -3 alternate for Swiss German; MediaWiki mediawiki returns Alemannic for gsw; en.wiki preferred name
['alemannisch'] = {'Swiss German', 'gsw'}, -- not an ISO or IANA language name; MediaWiki uses 'als' as a subdomain name for Alemannic Wikipedia: als.wikipedia.org
['bangla'] = {'Bengali', 'bn'}, -- MediaWiki returns Bangla (the endonym) but we want Bengali (the exonym); here we remap
['bengali'] = {'Bengali', 'bn'}, -- MediaWiki doesn't use exonym so here we provide correct language name and 639-1 code
['bhojpuri'] = {'Bhojpuri', 'bho'}, -- MediaWiki uses 'bh' as a subdomain name for Bhojpuri Wikipedia: bh.wikipedia.org
['bihari'] = {'Bihari', 'bh'}, -- MediaWiki replaces 'Bihari' with 'Bhojpuri' so 'Bihari' cannot be found
['blackfoot'] = {'Blackfoot', 'bla'}, -- MediaWiki/IANA/ISO 639: Siksika; use en.wiki preferred name
['colognian'] = {'Colognian', 'ksh-x-colog'}, -- MediaWiki preferred name for ksh
['ilocano'] = {'Ilocano', 'ilo'}, -- MediaWiki/IANA/ISO 639: Iloko; use en.wiki preferred name
['kolsch'] = {'Kölsch', 'ksh'}, -- use IANA/ISO 639 preferred name (use non-diacritical o instead of umlaut ö)
['kölsch'] = {'Kölsch', 'ksh'}, -- use IANA/ISO 639 preferred name
['kven'] = {'Kven', 'fkv'}, -- Unicode CLDR have decided not to support English language name for these two...
['kvensk'] = {'Kven', 'fkv'}, -- ...they say to refer to IANA registry for English names
['ripuarian'] = {'Ripuarian', 'mis-x-ripuar'}, -- group of dialects; no code in MediaWiki or in IANA/ISO 639
['serbian (cyrillic script)'] = {'Serbian (Cyrillic script)', 'sr-cyrl'}, -- special case to get correct tag when |language=sr-ec
['serbian (latin script)'] = {'Serbian (Latin script)', 'sr-latn'}, -- special case to get correct tag when |language=sr-el
['swiss german'] = {'Swiss German', 'gsw'},
['taiwanese hokkien'] = {'Taiwanese Hokkien', 'nan-tw'}, -- make room for MediaWiki/IANA/ISO 639 nan: Min Nan Chinese
['tosk albanian'] = {'Tosk Albanian', 'als'}, -- MediaWiki replaces 'Tosk Albanian' with 'Alemannisch' so 'Tosk Albanian' cannot be found
['valencian'] = {'Valencian', 'ca-valencia'}, -- variant of Catalan; categorizes as Valencian
}
--[[---------------< P R O P E R T I E S _ C A T E G O R I E S >----------------
Properties categories. These are used for investigating qualities of citations.
]]
local prop_cats = {
['foreign-lang-source'] = 'CS1 $1-language sources ($2)', -- |language= categories; $1 is foreign-language name, $2 is ISO639-1 code
['foreign-lang-source-2'] = 'CS1 foreign language sources (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 interproject-linked names|$1', -- any author, editor, etc that has an interproject link; $1 is interproject tag used as a sort key
['interwiki-linked-name'] = 'CS1 interwiki-linked names|$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 $1-language sources ($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 location test',
['long-vol'] = 'CS1: long volume value', -- probably temporary cat to identify scope of |volume= values longer than 4 characters
['script'] = 'CS1 uses $1-language script ($2)', -- |script-title=xx: has matching category; $1 is language name, $2 is language tag
['tracked-param'] = 'CS1 tracked parameter: $1', -- $1 is base (enumerators removed) parameter name
['unfit'] = 'CS1: unfit URL', -- |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: abbreviated year range', -- 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 = '<code class="cs1-code">|access-date=</code> requires <code class="cs1-code">|url=</code>',
anchor = 'accessdate_missing_url',
category = 'CS1 errors: access-date without URL',
hidden = false
},
err_apostrophe_markup = {
message = 'Italic or bold markup not allowed in: <code class="cs1-code">|$1=</code>', -- $1 is parameter name
anchor = 'apostrophe_markup',
category = 'CS1 errors: markup',
hidden = false
},
err_archive_date_missing_url = {
message = '<code class="cs1-code">|archive-date=</code> requires <code class="cs1-code">|archive-url=</code>',
anchor = 'archive_date_missing_url',
category = 'CS1 errors: archive-url',
hidden = false
},
err_archive_date_url_ts_mismatch = {
message = '<code class="cs1-code">|archive-date=</code> / <code class="cs1-code">|archive-url=</code> timestamp mismatch; $1 suggested',
anchor = 'archive_date_url_ts_mismatch',
category = 'CS1 errors: archive-url',
hidden = false
},
err_archive_missing_date = {
message = '<code class="cs1-code">|archive-url=</code> requires <code class="cs1-code">|archive-date=</code>',
anchor = 'archive_missing_date',
category = 'CS1 errors: archive-url',
hidden = false
},
err_archive_missing_url = {
message = '<code class="cs1-code">|archive-url=</code> requires <code class="cs1-code">|url=</code>',
anchor = 'archive_missing_url',
category = 'CS1 errors: archive-url',
hidden = false
},
err_archive_url = {
message = '<code class="cs1-code">|archive-url=</code> is malformed: $1', -- $1 is error message detail
anchor = 'archive_url',
category = 'CS1 errors: archive-url',
hidden = false
},
err_arxiv_missing = {
message = '<code class="cs1-code">|arxiv=</code> required',
anchor = 'arxiv_missing',
category = 'CS1 errors: arXiv', -- same as bad arxiv
hidden = false
},
err_asintld_missing_asin = {
message = '<code class="cs1-code">|$1=</code> requires <code class="cs1-code">|asin=</code>', -- $1 is parameter name
anchor = 'asintld_missing_asin',
category = 'CS1 errors: ASIN TLD',
hidden = false
},
err_bad_arxiv = {
message = 'Check <code class="cs1-code">|arxiv=</code> value',
anchor = 'bad_arxiv',
category = 'CS1 errors: arXiv',
hidden = false
},
err_bad_asin = {
message = 'Check <code class="cs1-code">|asin=</code> value',
anchor = 'bad_asin',
category ='CS1 errors: ASIN',
hidden = false
},
err_bad_asin_tld = {
message = 'Check <code class="cs1-code">|asin-tld=</code> value',
anchor = 'bad_asin_tld',
category ='CS1 errors: ASIN TLD',
hidden = false
},
err_bad_bibcode = {
message = 'Check <code class="cs1-code">|bibcode=</code> $1', -- $1 is error message detail
anchor = 'bad_bibcode',
category = 'CS1 errors: bibcode',
hidden = false
},
err_bad_biorxiv = {
message = 'Check <code class="cs1-code">|biorxiv=</code> value',
anchor = 'bad_biorxiv',
category = 'CS1 errors: bioRxiv',
hidden = false
},
err_bad_citeseerx = {
message = 'Check <code class="cs1-code">|citeseerx=</code> value',
anchor = 'bad_citeseerx',
category = 'CS1 errors: citeseerx',
hidden = false
},
err_bad_date = {
message = 'Check date values in: $1', -- $1 is a parameter name list
anchor = 'bad_date',
category = 'CS1 errors: dates',
hidden = false
},
err_bad_doi = {
message = 'Check <code class="cs1-code">|doi=</code> value',
anchor = 'bad_doi',
category = 'CS1 errors: DOI',
hidden = false
},
err_bad_hdl = {
message = 'Check <code class="cs1-code">|hdl=</code> value',
anchor = 'bad_hdl',
category = 'CS1 errors: HDL',
hidden = false
},
err_bad_isbn = {
message = 'Check <code class="cs1-code">|isbn=</code> value: $1', -- $1 is error message detail
anchor = 'bad_isbn',
category = 'CS1 errors: ISBN',
hidden = false
},
err_bad_ismn = {
message = 'Check <code class="cs1-code">|ismn=</code> value',
anchor = 'bad_ismn',
category = 'CS1 errors: ISMN',
hidden = false
},
err_bad_issn = {
message = 'Check <code class="cs1-code">|$1issn=</code> value', -- $1 is 'e' or '' for eissn or issn
anchor = 'bad_issn',
category = 'CS1 errors: ISSN',
hidden = false
},
err_bad_jfm = {
message = 'Check <code class="cs1-code">|jfm=</code> value',
anchor = 'bad_jfm',
category = 'CS1 errors: JFM',
hidden = false
},
err_bad_jstor = {
message = 'Check <code class="cs1-code">|jstor=</code> value',
anchor = 'bad_jstor',
category = 'CS1 errors: JSTOR',
hidden = false
},
err_bad_lccn = {
message = 'Check <code class="cs1-code">|lccn=</code> value',
anchor = 'bad_lccn',
category = 'CS1 errors: LCCN',
hidden = false
},
err_bad_medrxiv = {
message = 'Check <code class="cs1-code">|medrxiv=</code> value',
anchor = 'bad_medrxiv',
category = 'CS1 errors: medRxiv',
hidden = false
},
err_bad_mr = {
message = 'Check <code class="cs1-code">|mr=</code> value',
anchor = 'bad_mr',
category = 'CS1 errors: MR',
hidden = false
},
err_bad_oclc = {
message = 'Check <code class="cs1-code">|oclc=</code> value',
anchor = 'bad_oclc',
category = 'CS1 errors: OCLC',
hidden = false
},
err_bad_ol = {
message = 'Check <code class="cs1-code">|ol=</code> value',
anchor = 'bad_ol',
category = 'CS1 errors: OL',
hidden = false
},
err_bad_osti = {
message = 'Check <code class="cs1-code">|osti=</code> value',
anchor = 'bad_osti',
category = 'CS1 errors: OSTI',
hidden = false
},
err_bad_paramlink = { -- for |title-link=, |author/editor/translator-link=, |series-link=, |episode-link=
message = 'Check <code class="cs1-code">|$1=</code> value', -- $1 is parameter name
anchor = 'bad_paramlink',
category = 'CS1 errors: parameter link',
hidden = false
},
err_bad_pmc = {
message = 'Check <code class="cs1-code">|pmc=</code> value',
anchor = 'bad_pmc',
category = 'CS1 errors: PMC',
hidden = false
},
err_bad_pmid = {
message = 'Check <code class="cs1-code">|pmid=</code> value',
anchor = 'bad_pmid',
category = 'CS1 errors: PMID',
hidden = false
},
err_bad_rfc = {
message = 'Check <code class="cs1-code">|rfc=</code> value',
anchor = 'bad_rfc',
category = 'CS1 errors: RFC',
hidden = false
},
err_bad_s2cid = {
message = 'Check <code class="cs1-code">|s2cid=</code> value',
anchor = 'bad_s2cid',
category = 'CS1 errors: S2CID',
hidden = false
},
err_bad_sbn = {
message = 'Check <code class="cs1-code">|sbn=</code> value: $1', -- $1 is error message detail
anchor = 'bad_sbn',
category = 'CS1 errors: SBN',
hidden = false
},
err_bad_ssrn = {
message = 'Check <code class="cs1-code">|ssrn=</code> value',
anchor = 'bad_ssrn',
category = 'CS1 errors: SSRN',
hidden = false
},
err_bad_url = {
message = 'Check $1 value', -- $1 is parameter name
anchor = 'bad_url',
category = 'CS1 errors: URL',
hidden = false
},
err_bad_usenet_id = {
message = 'Check <code class="cs1-code">|message-id=</code> value',
anchor = 'bad_message_id',
category = 'CS1 errors: message-id',
hidden = false
},
err_bad_zbl = {
message = 'Check <code class="cs1-code">|zbl=</code> value',
anchor = 'bad_zbl',
category = 'CS1 errors: Zbl',
hidden = false
},
err_bare_url_missing_title = {
message = '$1 missing title', -- $1 is parameter name
anchor = 'bare_url_missing_title',
category = 'CS1 errors: bare URL',
hidden = false
},
err_biorxiv_missing = {
message = '<code class="cs1-code">|biorxiv=</code> required',
anchor = 'biorxiv_missing',
category = 'CS1 errors: bioRxiv', -- same as bad bioRxiv
hidden = false
},
err_chapter_ignored = {
message = '<code class="cs1-code">|$1=</code> ignored', -- $1 is parameter name
anchor = 'chapter_ignored',
category = 'CS1 errors: chapter ignored',
hidden = false
},
err_citation_missing_title = {
message = 'Missing or empty <code class="cs1-code">|$1=</code>', -- $1 is parameter name
anchor = 'citation_missing_title',
category = 'CS1 errors: missing title',
hidden = false
},
err_citeseerx_missing = {
message = '<code class="cs1-code">|citeseerx=</code> required',
anchor = 'citeseerx_missing',
category = 'CS1 errors: citeseerx', -- same as bad citeseerx
hidden = false
},
err_cite_web_url = { -- this error applies to cite web and to cite podcast
message = 'Missing or empty <code class="cs1-code">|url=</code>',
anchor = 'cite_web_url',
category = 'CS1 errors: requires URL',
hidden = false
},
err_class_ignored = {
message = '<code class="cs1-code">|class=</code> ignored',
anchor = 'class_ignored',
category = 'CS1 errors: class',
hidden = false
},
err_contributor_ignored = {
message = '<code class="cs1-code">|contributor=</code> ignored',
anchor = 'contributor_ignored',
category = 'CS1 errors: contributor',
hidden = false
},
err_contributor_missing_required_param = {
message = '<code class="cs1-code">|contributor=</code> requires <code class="cs1-code">|$1=</code>', -- $1 is parameter name
anchor = 'contributor_missing_required_param',
category = 'CS1 errors: contributor',
hidden = false
},
err_deprecated_params = {
message = 'Cite uses deprecated parameter <code class="cs1-code">|$1=</code>', -- $1 is parameter name
anchor = 'deprecated_params',
category = 'CS1 errors: deprecated parameters',
hidden = false
},
err_disp_name = {
message = 'Invalid <code class="cs1-code">|$1=$2</code>', -- $1 is parameter name; $2 is the assigned value
anchor = 'disp_name',
category = 'CS1 errors: display-names',
hidden = false,
},
err_doibroken_missing_doi = {
message = '<code class="cs1-code">|$1=</code> requires <code class="cs1-code">|doi=</code>', -- $1 is parameter name
anchor = 'doibroken_missing_doi',
category = 'CS1 errors: DOI',
hidden = false
},
err_embargo_missing_pmc = {
message = '<code class="cs1-code">|$1=</code> requires <code class="cs1-code">|pmc=</code>', -- $1 is parameter name
anchor = 'embargo_missing_pmc',
category = 'CS1 errors: PMC embargo',
hidden = false
},
err_empty_citation = {
message = 'Empty citation',
anchor = 'empty_citation',
category = 'CS1 errors: empty citation',
hidden = false
},
err_etal = {
message = 'Explicit use of et al. in: <code class="cs1-code">|$1=</code>', -- $1 is parameter name
anchor = 'explicit_et_al',
category = 'CS1 errors: explicit use of et al.',
hidden = false
},
err_extra_text_edition = {
message = '<code class="cs1-code">|edition=</code> has extra text',
anchor = 'extra_text_edition',
category = 'CS1 errors: extra text: edition',
hidden = false,
},
err_extra_text_issue = {
message = '<code class="cs1-code">|$1=</code> has extra text', -- $1 is parameter name
anchor = 'extra_text_issue',
category = 'CS1 errors: extra text: issue',
hidden = false,
},
err_extra_text_pages = {
message = '<code class="cs1-code">|$1=</code> has extra text', -- $1 is parameter name
anchor = 'extra_text_pages',
category = 'CS1 errors: extra text: pages',
hidden = false,
},
err_extra_text_volume = {
message = '<code class="cs1-code">|$1=</code> has extra text', -- $1 is parameter name
anchor = 'extra_text_volume',
category = 'CS1 errors: extra text: volume',
hidden = false,
},
err_first_missing_last = {
message = '<code class="cs1-code">|$1=</code> missing <code class="cs1-code">|$2=</code>', -- $1 is first alias, $2 is matching last alias
anchor = 'first_missing_last',
category = 'CS1 errors: missing name', -- author, contributor, editor, interviewer, translator
hidden = false
},
err_format_missing_url = {
message = '<code class="cs1-code">|$1=</code> requires <code class="cs1-code">|$2=</code>', -- $1 is format parameter $2 is url parameter
anchor = 'format_missing_url',
category = 'CS1 errors: format without URL',
hidden = false
},
err_generic_name = {
message = '<code class="cs1-code">|$1=</code> has generic name', -- $1 is parameter name
anchor = 'generic_name',
category = 'CS1 errors: generic name',
hidden = false,
},
err_generic_title = {
message = 'Cite uses generic title',
anchor = 'generic_title',
category = 'CS1 errors: generic title',
hidden = false,
},
err_invalid_isbn_date = {
message = 'ISBN / Date incompatibility',
anchor = 'invalid_isbn_date',
category = 'CS1 errors: ISBN date',
hidden = true
},
err_invalid_param_val = {
message = 'Invalid <code class="cs1-code">|$1=$2</code>', -- $1 is parameter name $2 is parameter value
anchor = 'invalid_param_val',
category = 'CS1 errors: invalid parameter value',
hidden = false
},
err_invisible_char = {
message = '$1 in $2 at position $3', -- $1 is invisible char $2 is parameter name $3 is position number
anchor = 'invisible_char',
category = 'CS1 errors: invisible characters',
hidden = false
},
err_medrxiv_missing = {
message = '<code class="cs1-code">|medrxiv=</code> required',
anchor = 'medrxiv_missing',
category = 'CS1 errors: medRxiv', -- same as bad medRxiv
hidden = false
},
err_missing_name = {
message = 'Missing <code class="cs1-code">|$1$2=</code>', -- $1 is modified NameList; $2 is enumerator
anchor = 'missing_name',
category = 'CS1 errors: missing name', -- author, contributor, editor, interviewer, translator
hidden = false
},
err_missing_periodical = {
message = 'Cite $1 requires <code class="cs1-code">|$2=</code>', -- $1 is cs1 template name; $2 is canonical periodical parameter name for cite $1
anchor = 'missing_periodical',
category = 'CS1 errors: missing periodical',
hidden = false
},
err_missing_pipe = {
message = 'Missing pipe in: <code class="cs1-code">|$1=</code>', -- $1 is parameter name
anchor = 'missing_pipe',
category = 'CS1 errors: missing pipe',
hidden = false
},
err_missing_publisher = {
message = 'Cite $1 requires <code class="cs1-code">|$2=</code>', -- $1 is cs1 template name; $2 is canonical publisher parameter name for cite $1
anchor = 'missing_publisher',
category = 'CS1 errors: missing publisher',
hidden = false
},
err_numeric_names = {
message = '<code class="cs1-code">|$1=</code> has numeric name', -- $1 is parameter name',
anchor = 'numeric_names',
category = 'CS1 errors: numeric name',
hidden = false,
},
err_param_access_requires_param = {
message = '<code class="cs1-code">|$1-access=</code> requires <code class="cs1-code">|$1=</code>', -- $1 is parameter name
anchor = 'param_access_requires_param',
category = 'CS1 errors: param-access',
hidden = false
},
err_param_has_ext_link = {
message = 'External link in <code class="cs1-code">$1</code>', -- $1 is parameter name
anchor = 'param_has_ext_link',
category = 'CS1 errors: external links',
hidden = false
},
err_param_has_twl_url = {
message = 'Wikipedia Library link in <code class="cs1-code">$1</code>', -- $1 is parameter name
anchor = 'param_has_twl_url',
category = 'CS1 errors: URL',
hidden = false
},
err_parameter_ignored = {
message = 'Unknown parameter <code class="cs1-code">|$1=</code> ignored', -- $1 is parameter name
anchor = 'parameter_ignored',
category = 'CS1 errors: unsupported parameter',
hidden = false
},
err_parameter_ignored_suggest = {
message = 'Unknown parameter <code class="cs1-code">|$1=</code> ignored (<code class="cs1-code">|$2=</code> suggested)', -- $1 is unknown parameter $2 is suggested parameter name
anchor = 'parameter_ignored_suggest',
category = 'CS1 errors: unsupported parameter',
hidden = false
},
err_periodical_ignored = {
message = '<code class="cs1-code">|$1=</code> ignored', -- $1 is parameter name
anchor = 'periodical_ignored',
category = 'CS1 errors: periodical ignored',
hidden = false
},
err_redundant_parameters = {
message = 'More than one of $1 specified', -- $1 is error message detail
anchor = 'redundant_parameters',
category = 'CS1 errors: redundant parameter',
hidden = false
},
err_script_parameter = {
message = 'Invalid <code class="cs1-code">|$1=</code>: $2', -- $1 is parameter name $2 is script language code or error detail
anchor = 'script_parameter',
category = 'CS1 errors: script parameters',
hidden = false
},
err_ssrn_missing = {
message = '<code class="cs1-code">|ssrn=</code> required',
anchor = 'ssrn_missing',
category = 'CS1 errors: SSRN',
hidden = false
},
err_text_ignored = {
message = 'Text "$1" ignored', -- $1 is ignored text
anchor = 'text_ignored',
category = 'CS1 errors: unrecognized parameter',
hidden = false
},
err_trans_missing_title = {
message = '<code class="cs1-code">|trans-$1=</code> requires <code class="cs1-code">|$1=</code> or <code class="cs1-code">|script-$1=</code>', -- $1 is base parameter name
anchor = 'trans_missing_title',
category = 'CS1 errors: translated title',
hidden = false
},
err_param_unknown_empty = {
message = 'Cite has empty unknown parameter$1: $2', -- $1 is 's' or empty space; $2 is emty unknown param list
anchor = 'param_unknown_empty',
category = 'CS1 errors: empty unknown parameters',
hidden = false
},
err_vancouver = {
message = 'Vancouver style error: $1 in name $2', -- $1 is error detail, $2 is the nth name
anchor = 'vancouver',
category = 'CS1 errors: Vancouver style',
hidden = false
},
err_wikilink_in_url = {
message = 'URL–wikilink conflict', -- uses ndash
anchor = 'wikilink_in_url',
category = 'CS1 errors: URL–wikilink conflict', -- 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 = 'CS1 maint: archived copy as title',
hidden = true,
},
maint_bibcode = {
message = nil,
anchor = 'bibcode',
category = 'CS1 maint: 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 = 'CS1 maint: location missing publisher',
hidden = true,
},
maint_bot_unknown = {
message = nil,
anchor = 'bot:_unknown',
category = 'CS1 maint: bot: original URL status unknown',
hidden = true,
},
maint_date_auto_xlated = { -- date auto-translation not supported by en.wiki
message = nil,
anchor = 'date_auto_xlated',
category = 'CS1 maint: date auto-translated',
hidden = true,
},
maint_date_format = {
message = nil,
anchor = 'date_format',
category = 'CS1 maint: date format',
hidden = true,
},
maint_date_year = {
message = nil,
anchor = 'date_year',
category = 'CS1 maint: date and year',
hidden = true,
},
maint_doi_ignore = {
message = nil,
anchor = 'doi_ignore',
category = 'CS1 maint: ignored DOI errors',
hidden = true,
},
maint_doi_inactive = {
message = nil,
anchor = 'doi_inactive',
category = 'CS1 maint: DOI inactive',
hidden = true,
},
maint_doi_inactive_dated = {
message = nil,
anchor = 'doi_inactive_dated',
category = 'CS1 maint: DOI inactive as of $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 = 'CS1 maint: unflagged free DOI',
hidden = true,
},
maint_extra_punct = {
message = nil,
anchor = 'extra_punct',
category = 'CS1 maint: extra punctuation',
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 = 'CS1 maint: ID limit load fail',
hidden = true,
},
maint_isbn_ignore = {
message = nil,
anchor = 'ignore_isbn_err',
category = 'CS1 maint: ignored ISBN errors',
hidden = true,
},
maint_issn_ignore = {
message = nil,
anchor = 'ignore_issn',
category = 'CS1 maint: ignored ISSN errors',
hidden = true,
},
maint_jfm_format = {
message = nil,
anchor = 'jfm_format',
category = 'CS1 maint: JFM format',
hidden = true,
},
maint_location = {
message = nil,
anchor = 'location',
category = 'CS1 maint: location',
hidden = true,
},
maint_mr_format = {
message = nil,
anchor = 'mr_format',
category = 'CS1 maint: MR format',
hidden = true,
},
maint_mult_names = {
message = nil,
anchor = 'mult_names',
category = 'CS1 maint: multiple names: $1', -- $1 is '<name>s list'; gets value from special_case_translation table
hidden = true,
},
maint_numeric_names = {
message = nil,
anchor = 'numeric_names',
category = 'CS1 maint: numeric names: $1', -- $1 is '<name>s list'; gets value from special_case_translation table
hidden = true,
},
maint_others = {
message = nil,
anchor = 'others',
category = 'CS1 maint: others',
hidden = true,
},
maint_others_avm = {
message = nil,
anchor = 'others_avm',
category = 'CS1 maint: others in cite AV media (notes)',
hidden = true,
},
maint_overridden_setting = {
message = nil,
anchor = 'overridden',
category = 'CS1 maint: overridden setting',
hidden = true,
},
maint_pmc_embargo = {
message = nil,
anchor = 'embargo',
category = 'CS1 maint: PMC embargo expired',
hidden = true,
},
maint_pmc_format = {
message = nil,
anchor = 'pmc_format',
category = 'CS1 maint: PMC format',
hidden = true,
},
maint_postscript = {
message = nil,
anchor = 'postscript',
category = 'CS1 maint: postscript',
hidden = true,
},
maint_publisher_location = {
message = nil,
anchor = 'publisher_location',
category = 'CS1 maint: publisher location',
hidden = true,
},
maint_ref_duplicates_default = {
message = nil,
anchor = 'ref_default',
category = 'CS1 maint: ref duplicates default',
hidden = true,
},
maint_unknown_lang = {
message = nil,
anchor = 'unknown_lang',
category = 'CS1 maint: unrecognized language',
hidden = true,
},
maint_untitled = {
message = nil,
anchor = 'untitled',
category = 'CS1 maint: untitled periodical',
hidden = true,
},
maint_url_status = {
message = nil,
anchor = 'url_status',
category = 'CS1 maint: url-status',
hidden = true,
},
maint_year= {
message = nil,
anchor = 'year',
category = 'CS1 maint: year',
hidden = true,
},
maint_zbl = {
message = nil,
anchor = 'zbl',
category = 'CS1 maint: 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 (identifier)',
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 = 'Amazon Standard Identification Number',
redirect = 'ASIN (identifier)',
q = 'Q1753278',
label = 'ASIN',
prefix = 'https://www.amazon.',
COinS = 'url',
separator = ' ',
encode = false;
},
['BIBCODE'] = {
parameters = {'bibcode'},
link = 'Bibcode',
redirect = 'Bibcode (identifier)',
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 (identifier)',
q = 'Q19835482',
label = 'bioRxiv',
prefix = 'https://doi.org/',
COinS = 'pre', -- use prefix value
access = 'free', -- free to read
encode = true,
separator = ' ',
},
['CITESEERX'] = {
parameters = {'citeseerx'},
link = 'CiteSeerX',
redirect = 'CiteSeerX (identifier)',
q = 'Q2715061',
label = 'CiteSeerX',
prefix = 'https://citeseerx.ist.psu.edu/viewdoc/summary?doi=',
COinS = 'pre', -- use prefix value
access = 'free', -- free to read
encode = true,
separator = ' ',
},
['DOI'] = { -- Used by InternetArchiveBot
parameters = { 'doi', 'DOI'},
link = 'Digital object identifier',
redirect = 'doi (identifier)',
q = 'Q25670',
label = 'doi',
prefix = 'https://doi.org/',
COinS = 'info:doi',
separator = ':',
encode = true,
custom_access = 'doi-access',
},
['EISSN'] = {
parameters = {'eissn', 'EISSN'},
link = 'International Standard Serial Number#Electronic ISSN',
redirect = 'eISSN (identifier)',
q = 'Q46339674',
label = 'eISSN',
prefix = 'https://search.worldcat.org/issn/',
COinS = 'rft.eissn',
encode = false,
separator = ' ',
},
['HDL'] = {
parameters = { 'hdl', 'HDL' },
link = 'Handle System',
redirect = 'hdl (identifier)',
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 = 'International Standard Book Number',
redirect = 'ISBN (identifier)',
q = 'Q33057',
label = 'ISBN',
prefix = 'Special:BookSources/',
COinS = 'rft.isbn',
separator = ' ',
},
['ISMN'] = {
parameters = {'ismn', 'ISMN'},
link = 'International Standard Music Number',
redirect = 'ISMN (identifier)',
q = 'Q1666938',
label = 'ISMN',
prefix = '', -- not currently used;
COinS = nil, -- nil because we can't use pre or rft or info:
separator = ' ',
},
['ISSN'] = {
parameters = {'issn', 'ISSN'},
link = 'International Standard Serial Number',
redirect = 'ISSN (identifier)',
q = 'Q131276',
label = 'ISSN',
prefix = 'https://search.worldcat.org/issn/',
COinS = 'rft.issn',
encode = false,
separator = ' ',
},
['JFM'] = {
parameters = {'jfm', 'JFM'},
link = 'Jahrbuch über die Fortschritte der Mathematik',
redirect = 'JFM (identifier)',
q = '',
label = 'JFM',
prefix = 'https://zbmath.org/?format=complete&q=an:',
COinS = 'pre', -- use prefix value
encode = true,
separator = ' ',
},
['JSTOR'] = {
parameters = {'jstor', 'JSTOR'},
link = 'JSTOR',
redirect = 'JSTOR (identifier)',
q = 'Q1420342',
label = 'JSTOR',
prefix = 'https://www.jstor.org/stable/',
COinS = 'pre', -- use prefix value
encode = false,
separator = ' ',
custom_access = 'jstor-access',
},
['LCCN'] = {
parameters = {'lccn', 'LCCN'},
link = 'Library of Congress Control Number',
redirect = 'LCCN (identifier)',
q = 'Q620946',
label = 'LCCN',
prefix = 'https://lccn.loc.gov/',
COinS = 'info:lccn',
encode = false,
separator = ' ',
},
['MEDRXIV'] = {
parameters = {'medrxiv'},
link = 'medRxiv',
redirect = 'medRxiv (identifier)',
q = 'Q58465838',
label = 'medRxiv',
prefix = 'https://www.medrxiv.org/content/',
COinS = 'pre', -- use prefix value
access = 'free', -- free to read
encode = false,
separator = ' ',
},
['MR'] = {
parameters = {'mr', 'MR'},
link = 'Mathematical Reviews',
redirect = 'MR (identifier)',
q = 'Q211172',
label = 'MR',
prefix = 'https://mathscinet.ams.org/mathscinet-getitem?mr=',
COinS = 'pre', -- use prefix value
encode = true,
separator = ' ',
},
['OCLC'] = {
parameters = {'oclc', 'OCLC'},
link = 'OCLC',
redirect = 'OCLC (identifier)',
q = 'Q190593',
label = 'OCLC',
prefix = 'https://search.worldcat.org/oclc/',
COinS = 'info:oclcnum',
encode = true,
separator = ' ',
id_limit = id_limits_data_t.OCLC or 0,
},
['OL'] = {
parameters = { 'ol', 'OL' },
link = 'Open Library',
redirect = 'OL (identifier)',
q = 'Q1201876',
label = 'OL',
prefix = 'https://openlibrary.org/',
COinS = 'url',
separator = ' ',
encode = true,
custom_access = 'ol-access',
},
['OSTI'] = {
parameters = {'osti', 'OSTI'},
link = 'Office of Scientific and Technical Information',
redirect = 'OSTI (identifier)',
q = 'Q2015776',
label = 'OSTI',
prefix = 'https://www.osti.gov/biblio/',
COinS = 'pre', -- use prefix value
encode = true,
separator = ' ',
id_limit = id_limits_data_t.OSTI or 0,
custom_access = 'osti-access',
},
['PMC'] = {
parameters = {'pmc', 'PMC'},
link = 'PubMed Central',
redirect = 'PMC (identifier)',
q = 'Q229883',
label = 'PMC',
prefix = 'https://www.ncbi.nlm.nih.gov/pmc/articles/PMC',
suffix = '',
COinS = 'pre', -- use prefix value
encode = true,
separator = ' ',
id_limit = id_limits_data_t.PMC or 0,
access = 'free', -- free to read
},
['PMID'] = {
parameters = {'pmid', 'PMID'},
link = 'PubMed Identifier',
redirect = 'PMID (identifier)',
q = 'Q2082879',
label = 'PMID',
prefix = 'https://pubmed.ncbi.nlm.nih.gov/',
COinS = 'info:pmid',
encode = false,
separator = ' ',
id_limit = id_limits_data_t.PMID or 0,
},
['RFC'] = {
parameters = {'rfc', 'RFC'},
link = 'Request for Comments',
redirect = 'RFC (identifier)',
q = 'Q212971',
label = 'RFC',
prefix = 'https://tools.ietf.org/html/rfc',
COinS = 'pre', -- use prefix value
encode = false,
separator = ' ',
id_limit = id_limits_data_t.RFC or 0,
access = 'free', -- free to read
},
['SBN'] = {
parameters = {'sbn', 'SBN'},
link = 'Standard Book Number', -- redirect to International_Standard_Book_Number#History
redirect = 'SBN (identifier)',
label = 'SBN',
prefix = 'Special:BookSources/0-', -- prefix has leading zero necessary to make 9-digit sbn a 10-digit isbn
COinS = nil, -- nil because we can't use pre or rft or info:
separator = ' ',
},
['SSRN'] = {
parameters = {'ssrn', 'SSRN'},
link = 'Social Science Research Network',
redirect = 'SSRN (identifier)',
q = 'Q7550801',
label = 'SSRN',
prefix = 'https://papers.ssrn.com/sol3/papers.cfm?abstract_id=',
COinS = 'pre', -- use prefix value
encode = true,
separator = ' ',
id_limit = id_limits_data_t.SSRN or 0,
custom_access = 'ssrn-access',
},
['S2CID'] = {
parameters = {'s2cid', 'S2CID'},
link = 'Semantic Scholar',
redirect = 'S2CID (identifier)',
q = 'Q22908627',
label = 'S2CID',
prefix = 'https://api.semanticscholar.org/CorpusID:',
COinS = 'pre', -- use prefix value
encode = false,
separator = ' ',
id_limit = id_limits_data_t.S2CID or 0,
custom_access = 's2cid-access',
},
['USENETID'] = {
parameters = {'message-id'},
link = 'Usenet',
redirect = 'Usenet (identifier)',
q = 'Q193162',
label = 'Usenet:',
prefix = 'news:',
encode = false,
COinS = 'pre', -- use prefix value
separator = ' ',
},
['ZBL'] = {
parameters = {'zbl', 'ZBL' },
link = 'Zentralblatt MATH',
redirect = 'Zbl (identifier)',
q = 'Q190269',
label = 'Zbl',
prefix = 'https://zbmath.org/?format=complete&q=an:',
COinS = 'pre', -- use prefix value
encode = true,
separator = ' ',
},
}
--[[--------------------------< E X P O R T S >---------------------------------
]]
return {
use_identifier_redirects = use_identifier_redirects, -- booleans defined in the settings at the top of this module
local_lang_cat_enable = local_lang_cat_enable,
date_name_auto_xlate_enable = date_name_auto_xlate_enable,
date_digit_auto_xlate_enable = date_digit_auto_xlate_enable,
enable_sort_keys = enable_sort_keys,
-- tables and variables created when this module is loaded
global_df = get_date_format (), -- this line can be replaced with "global_df = 'dmy-all'," to have all dates auto translated to dmy format.
global_cs1_config_t = global_cs1_config_t, -- global settings from {{cs1 config}}
punct_skip = build_skip_table (punct_skip, punct_meta_params),
url_skip = build_skip_table (url_skip, url_meta_params),
known_free_doi_registrants_t = build_free_doi_registrants_table(),
id_limits_data_load_fail = id_limits_data_load_fail, -- true when commons tabular identifier-limit data fails to load
name_space_sort_keys = name_space_sort_keys,
aliases = aliases,
special_case_translation = special_case_translation,
date_names = date_names,
err_msg_supl = err_msg_supl,
error_conditions = error_conditions,
editor_markup_patterns = editor_markup_patterns,
et_al_patterns = et_al_patterns,
extended_registrants_t = extended_registrants_t,
id_handlers = id_handlers,
keywords_lists = keywords_lists,
keywords_xlate = keywords_xlate,
stripmarkers = stripmarkers,
invisible_chars = invisible_chars,
invisible_defs = invisible_defs,
indic_script = indic_script,
emoji_t = emoji_t,
maint_cats = maint_cats,
messages = messages,
presentation = presentation,
prop_cats = prop_cats,
script_lang_codes = script_lang_codes,
lang_tag_remap = lang_tag_remap,
lang_name_remap = lang_name_remap,
this_wiki_code = this_wiki_code,
title_types = title_types,
uncategorized_namespaces = uncategorized_namespaces_t,
uncategorized_subpages = uncategorized_subpages,
templates_using_volume = templates_using_volume,
templates_using_issue = templates_using_issue,
templates_not_using_page = templates_not_using_page,
vol_iss_pg_patterns = vol_iss_pg_patterns,
single_letter_2nd_lvl_domains_t = single_letter_2nd_lvl_domains_t,
inter_wiki_map = inter_wiki_map,
mw_languages_by_tag_t = mw_languages_by_tag_t,
mw_languages_by_name_t = mw_languages_by_name_t,
citation_class_map_t = citation_class_map_t,
citation_issue_t = citation_issue_t,
citation_no_volume_t = citation_no_volume_t,
}
oaa9fcjcdnkkr85fxl20bvn6x8qwh7t
886425
886424
2025-06-14T05:17:14Z
KartikMistry
10383
આંકડાઓ
886425
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 = {'/[Ss]andbox', '/[Tt]estcases', '/[^/]*[Ll]og', '/[Aa]rchive'}; -- 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'] = '$1 માંથી $2 પર સંગ્રહિત',
['archived-live'] = 'મૂળ $1 માંથી $2 પર સંગ્રહિત',
['archived-unfit'] = 'મૂળમાંથી અહીં સંગ્રહિત ',
['archived'] = 'સંગ્રહિત',
['by'] = 'વડે', -- contributions to authored works: introduction, foreword, afterword
['cartography'] = '$1 વડે નકશો',
['editor'] = 'સંપાદક',
['editors'] = 'સંપાદકો',
['edition'] = '($1 આવૃત્તિ)',
['episode'] = 'પ્રકરણ $1',
['et al'] = 'et al.',
['in'] = 'In', -- edited works
['inactive'] = 'અસક્રિય',
['inset'] = '$1 inset',
['interview'] = '$1 દ્વારા ઇન્ટરવ્યુ',
['mismatch'] = '<code class="cs1-code">|$1=</code> / <code class="cs1-code">|$2=</code> mismatch', -- $1 is year param name; $2 is date param name
['newsgroup'] = '[[Usenet newsgroup|Newsgroup]]: $1',
['notitle'] = 'શીર્ષક નથી', -- for |title=(()) and (in the future) |title=none
['original'] = 'મૂળ',
['origdate'] = ' [$1]',
['published'] = ' (પ્રકાશિત $1)',
['retrieved'] = 'મેળવેલ $1',
['season'] = 'શ્રેણી $1',
['section'] = '§ $1',
['sections'] = '§§ $1',
['series'] = '$1 $2', -- $1 is sepc, $2 is series
['seriesnum'] = 'શ્રેણી $1',
['translated'] = '$1 વડે અનુવાદિત',
['type'] = ' ($1)', -- for titletype
['written'] = '$1 વડે લખાયેલ',
['vol'] = '$1 ખંડ $2', -- $1 is sepc; bold journal style volume is in presentation{}
['vol-no'] = '$1 ખંડ $2, ક્રમાંક $3', -- sepc, volume, issue (alternatively insert $1 after $2, but then we'd also have to change capitalization)
['issue'] = '$1 ક્રમાંક $2', -- $1 is sepc
['art'] = '$1 Art. $2', -- $1 is sepc; for {{cite conference}} only
['vol-art'] = '$1 ખંડ. $2, art. $3', -- sepc, volume, article-number; for {{cite conference}} only
['j-vol'] = '$1 $2', -- sepc, volume; bold journal volume is in presentation{}
['j-issue'] = ' ($1)',
['j-article-num'] = ' $1', -- TODO: any punctuation here? static text?
['nopp'] = '$1 $2'; -- page(s) without prefix; $1 is sepc
['p-prefix'] = "$1 p. $2", -- $1 is sepc
['pp-prefix'] = "$1 pp. $2", -- $1 is sepc
['j-page(s)'] = ': $1', -- same for page and pages
['sheet'] = '$1 Sheet $2', -- $1 is sepc
['sheets'] = '$1 Sheets $2', -- $1 is sepc
['j-sheet'] = ': Sheet $1',
['j-sheets'] = ': Sheets $1',
['language'] = '($1માં)',
['via'] = " – $1 દ્વારા",
['event'] = 'ઘટના આ સમયે બની',
['minutes'] = 'મિનિટમાં',
-- Determines the location of the help page
['help page link'] = 'Help:CS1 errors',
['help page label'] = 'મદદ',
-- categories
['cat wikilink'] = '[[Category:$1]]', -- $1 is the category name
['cat wikilink sk'] = '[[Category:$1|$2]]', -- $1 is the category name; $2 is namespace sort key
[':cat wikilink'] = '[[:Category:$1|link]]', -- category name as maintenance message wikilink; $1 is the category name
-- Internal errors (should only occur if configuration is bad)
['undefined_error'] = 'Called with an undefined error condition',
['unknown_ID_key'] = 'Unrecognized ID key: ', -- an ID key in id_handlers not found in ~/Identifiers func_map{}
['unknown_ID_access'] = 'Unrecognized ID access keyword: ', -- an ID access keyword in id_handlers not found in keywords_lists['id-access']{}
['unknown_argument_map'] = 'Argument map not defined for this variable',
['bare_url_no_origin'] = 'Bare URL found but origin indicator is nil or empty',
['warning_msg_e'] = '<span style="color:#d33">One or more <code style="color: inherit; background: inherit; border: none; padding: inherit;">{{$1}}</code> templates have errors</span>; messages may be hidden ([[Help:CS1_errors#Controlling_error_message_display|help]]).'; -- $1 is template link
['warning_msg_m'] = '<span style="color:#3a3">One or more <code style="color: inherit; background: inherit; border: none; padding: inherit;">{{$1}}</code> templates have maintenance messages</span>; messages may be hidden ([[Help:CS1_errors#Controlling_error_message_display|help]]).'; -- $1 is template link
}
--[[--------------------------< C I T A T I O N _ C L A S S _ M A P >------------------------------------------
this table maps the value assigned to |CitationClass= in the cs1|2 templates to the canonical template name when
the value assigned to |CitationClass= is different from the canonical template name. |CitationClass= values are
used as class attributes in the <cite> tag that encloses the citation so these names may not contain spaces while
the canonical template name may. These names are used in warning_msg_e and warning_msg_m to create links to the
template's documentation when an article is displayed in preview mode.
Most cs1|2 template |CitationClass= values at en.wiki match their canonical template names so are not listed here.
]]
local citation_class_map_t = { -- TODO: if kept, these and all other config.CitationClass 'names' require some sort of i18n
['arxiv'] = 'arXiv',
['audio-visual'] = 'AV media',
['AV-media-notes'] = 'AV media notes',
['biorxiv'] = 'bioRxiv',
['citeseerx'] = 'CiteSeerX',
['encyclopaedia'] = 'encyclopedia',
['mailinglist'] = 'mailing list',
['medrxiv'] = 'medRxiv',
['pressrelease'] = 'press release',
['ssrn'] = 'SSRN',
['techreport'] = 'tech report',
}
--[=[-------------------------< E T _ A L _ P A T T E R N S >--------------------------------------------------
This table provides Lua patterns for the phrase "et al" and variants in name text
(author, editor, etc.). The main module uses these to identify and emit the 'etal' message.
]=]
local et_al_patterns = {
"[;,]? *[\"']*%f[%a][Ee][Tt]%.? *[Aa][Ll][%.;,\"']*$", -- variations on the 'et al' theme
"[;,]? *[\"']*%f[%a][Ee][Tt]%.? *[Aa][Ll][Ii][AaIi][Ee]?[%.;,\"']*$", -- variations on the 'et alia', 'et alii' and 'et aliae' themes (false positive 'et aliie' unlikely to match)
"[;,]? *%f[%a]and [Oo]thers", -- an alternative to et al.
"%[%[ *[Ee][Tt]%.? *[Aa][Ll]%.? *%]%]", -- a wikilinked form
"%(%( *[Ee][Tt]%.? *[Aa][Ll]%.? *%)%)", -- a double-bracketed form (to counter partial removal of ((...)) syntax)
"[%(%[] *[Ee][Tt]%.? *[Aa][Ll]%.? *[%)%]]", -- a bracketed form
}
--[[--------------------------< P R E S E N T A T I O N >------------------------
Fixed presentation markup. Originally part of citation_config.messages it has
been moved into its own, more semantically correct place.
]]
local presentation =
{
-- .citation-comment class is specified at Help:CS1_errors#Controlling_error_message_display
['hidden-error'] = '<span class="cs1-hidden-error citation-comment">$1</span>',
['visible-error'] = '<span class="cs1-visible-error citation-comment">$1</span>',
['hidden-maint'] = '<span class="cs1-maint citation-comment">$1</span>',
['accessdate'] = '<span class="reference-accessdate">$1$2</span>', -- to allow editors to hide accessdate using personal CSS
['bdi'] = '<bdi$1>$2</bdi>', -- bidirectional isolation used with |script-title= and the like
['cite'] = '<cite class="$1">$2</cite>'; -- for use when citation does not have a namelist and |ref= not set so no id="..." attribute
['cite-id'] = '<cite id="$1" class="$2">$3</cite>'; -- for use when when |ref= is set or when citation has a namelist
['format'] = ' <span class="cs1-format">($1)</span>', -- for |format=, |chapter-format=, etc.
['interwiki'] = ' <span class="cs1-format">[in $1]</span>', -- for interwiki-language-linked author, editor, etc
['interproj'] = ' <span class="cs1-format">[at $1]</span>', -- for interwiki-project-linked author, editor, etc (:d: and :s: supported; :w: ignored)
-- various access levels, for |access=, |doi-access=, |arxiv=, ...
-- narrow no-break space   may work better than nowrap CSS. Or not? Browser support?
['ext-link-access-signal'] = '<span class="$1" title="$2">$3</span>', -- external link with appropriate lock icon
['free'] = {class='id-lock-free', title='Freely accessible'}, -- classes defined in Module:Citation/CS1/styles.css
['registration'] = {class='id-lock-registration', title='Free registration required'},
['limited'] = {class='id-lock-limited', title='Free access subject to limited trial, subscription normally required'},
['subscription'] = {class='id-lock-subscription', title='Paid subscription required'},
['interwiki-icon'] = '<span class="$1" title="$2">$3</span>',
['class-wikisource'] = 'cs1-ws-icon',
['italic-title'] = "''$1''",
['kern-left'] = '<span class="cs1-kern-left"></span>$1', -- spacing to use when title contains leading single or double quote mark
['kern-right'] = '$1<span class="cs1-kern-right"></span>', -- spacing to use when title contains trailing single or double quote mark
['nowrap1'] = '<span class="nowrap">$1</span>', -- for nowrapping an item: <span ...>yyyy-mm-dd</span>
['nowrap2'] = '<span class="nowrap">$1</span> $2', -- for nowrapping portions of an item: <span ...>dd mmmm</span> yyyy (note white space)
['ocins'] = '<span title="$1" class="Z3988"></span>',
['parameter'] = '<code class="cs1-code">|$1=</code>',
['ps_cs1'] = '.'; -- CS1 style postscript (terminal) character
['ps_cs2'] = ''; -- CS2 style postscript (terminal) character (empty string)
['quoted-text'] = '<q>$1</q>', -- for wrapping |quote= content
['quoted-title'] = '"$1"',
['sep_cs1'] = '.', -- CS1 element separator
['sep_cs2'] = ',', -- CS2 separator
['sep_nl'] = ';', -- CS1|2 style name-list separator between names is a semicolon
['sep_nl_and'] = ' and ', -- used as last nl sep when |name-list-style=and and list has 2 items
['sep_nl_end'] = '; and ', -- used as last nl sep when |name-list-style=and and list has 3+ names
['sep_name'] = ', ', -- CS1|2 style last/first separator is <comma><space>
['sep_nl_vanc'] = ',', -- Vancouver style name-list separator between authors is a comma
['sep_name_vanc'] = ' ', -- Vancouver style last/first separator is a space
['sep_list'] = ', ', -- used for |language= when list has 3+ items except for last sep which uses sep_list_end
['sep_list_pair'] = ' and ', -- used for |language= when list has 2 items
['sep_list_end'] = ', and ', -- used as last list sep for |language= when list has 3+ items
['trans-italic-title'] = "[''$1'']",
['trans-quoted-title'] = "[$1]", -- for |trans-title= and |trans-quote=
['vol-bold'] = '$1 <b>$2</b>', -- sepc, volume; for bold journal cites; for other cites ['vol'] in messages{}
}
--[[--------------------------< A L I A S E S >---------------------------------
Aliases table for commonly passed parameters.
Parameter names on the right side in the assignments in this table must have been
defined in the Whitelist before they will be recognized as valid parameter names
]]
local aliases = {
['AccessDate'] = {'access-date', 'accessdate'}, -- Used by InternetArchiveBot
['Agency'] = 'agency',
['ArchiveDate'] = {'archive-date', 'archivedate'}, -- Used by InternetArchiveBot
['ArchiveFormat'] = 'archive-format',
['ArchiveURL'] = {'archive-url', 'archiveurl'}, -- Used by InternetArchiveBot
['ArticleNumber'] = 'article-number',
['ASINTLD'] = 'asin-tld',
['At'] = 'at', -- Used by InternetArchiveBot
['Authors'] = {'people', 'credits'},
['BookTitle'] = {'book-title', 'booktitle'},
['Cartography'] = 'cartography',
['Chapter'] = {'chapter', 'contribution', 'entry', 'article', 'section'},
['ChapterFormat'] = {'chapter-format', 'contribution-format', 'entry-format',
'article-format', 'section-format'};
['ChapterURL'] = {'chapter-url', 'contribution-url', 'entry-url', 'article-url', 'section-url'}, -- Used by InternetArchiveBot
['ChapterUrlAccess'] = {'chapter-url-access', 'contribution-url-access',
'entry-url-access', 'article-url-access', 'section-url-access'}, -- Used by InternetArchiveBot
['Class'] = 'class', -- cite arxiv and arxiv identifier
['Collaboration'] = 'collaboration',
['Conference'] = {'conference', 'event'},
['ConferenceFormat'] = 'conference-format',
['ConferenceURL'] = 'conference-url', -- Used by InternetArchiveBot
['Date'] = {'date', 'air-date', 'airdate'}, -- air-date and airdate for cite episode and cite serial only
['Degree'] = 'degree',
['DF'] = 'df',
['DisplayAuthors'] = {'display-authors', 'display-subjects'},
['DisplayContributors'] = 'display-contributors',
['DisplayEditors'] = 'display-editors',
['DisplayInterviewers'] = 'display-interviewers',
['DisplayTranslators'] = 'display-translators',
['Docket'] = 'docket',
['DoiBroken'] = 'doi-broken-date',
['Edition'] = 'edition',
['Embargo'] = 'pmc-embargo-date',
['Encyclopedia'] = {'encyclopedia', 'encyclopaedia', 'dictionary'}, -- cite encyclopedia only
['Episode'] = 'episode', -- cite serial only TODO: make available to cite episode?
['Format'] = 'format',
['ID'] = {'id', 'ID'},
['Inset'] = 'inset',
['Issue'] = {'issue', 'number'},
['Language'] = {'language', 'lang'},
['MailingList'] = {'mailing-list', 'mailinglist'}, -- cite mailing list only
['Map'] = 'map', -- cite map only
['MapFormat'] = 'map-format', -- cite map only
['MapURL'] = {'map-url', 'mapurl'}, -- cite map only -- Used by InternetArchiveBot
['MapUrlAccess'] = 'map-url-access', -- cite map only -- Used by InternetArchiveBot
['Minutes'] = 'minutes',
['Mode'] = 'mode',
['NameListStyle'] = 'name-list-style',
['Network'] = 'network',
['Newsgroup'] = 'newsgroup', -- cite newsgroup only
['NoPP'] = {'no-pp', 'nopp'},
['NoTracking'] = {'no-tracking', 'template-doc-demo'},
['Number'] = 'number', -- this case only for cite techreport
['OrigDate'] = {'orig-date', 'orig-year', 'origyear'},
['Others'] = 'others',
['Page'] = {'page', 'p'}, -- Used by InternetArchiveBot
['Pages'] = {'pages', 'pp'}, -- Used by InternetArchiveBot
['Periodical'] = {'journal', 'magazine', 'newspaper', 'periodical', 'website', 'work'},
['Place'] = {'place', 'location'},
['PostScript'] = 'postscript',
['PublicationDate'] = {'publication-date', 'publicationdate'},
['PublicationPlace'] = {'publication-place', 'publicationplace'},
['PublisherName'] = {'publisher', 'institution'},
['Quote'] = {'quote', 'quotation'},
['QuotePage'] = 'quote-page',
['QuotePages'] = 'quote-pages',
['Ref'] = 'ref',
['Scale'] = 'scale',
['ScriptChapter'] = {'script-chapter', 'script-contribution', 'script-entry',
'script-article', 'script-section'},
['ScriptEncyclopedia'] = {'script-encyclopedia', 'script-encyclopaedia'}, -- cite encyclopedia only
['ScriptMap'] = 'script-map',
['ScriptPeriodical'] = {'script-journal', 'script-magazine', 'script-newspaper',
'script-periodical', 'script-website', 'script-work'},
['ScriptQuote'] = 'script-quote',
['ScriptTitle'] = 'script-title', -- Used by InternetArchiveBot
['Season'] = 'season',
['Sections'] = 'sections', -- cite map only
['Series'] = {'series', 'version'},
['SeriesLink'] = {'series-link', 'serieslink'},
['SeriesNumber'] = {'series-number', 'series-no'},
['Sheet'] = 'sheet', -- cite map only
['Sheets'] = 'sheets', -- cite map only
['Station'] = 'station',
['Time'] = 'time',
['TimeCaption'] = 'time-caption',
['Title'] = 'title', -- Used by InternetArchiveBot
['TitleLink'] = {'title-link', 'episode-link', 'episodelink'}, -- Used by InternetArchiveBot
['TitleNote'] = {'title-note', 'department'},
['TitleType'] = {'type', 'medium'},
['TransChapter'] = {'trans-article', 'trans-chapter', 'trans-contribution',
'trans-entry', 'trans-section'},
['Transcript'] = 'transcript',
['TranscriptFormat'] = 'transcript-format',
['TranscriptURL'] = 'transcript-url', -- Used by InternetArchiveBot
['TransEncyclopedia'] = {'trans-encyclopedia', 'trans-encyclopaedia'}, -- cite encyclopedia only
['TransMap'] = 'trans-map', -- cite map only
['TransPeriodical'] = {'trans-journal', 'trans-magazine', 'trans-newspaper',
'trans-periodical', 'trans-website', 'trans-work'},
['TransQuote'] = 'trans-quote',
['TransTitle'] = 'trans-title', -- Used by InternetArchiveBot
['URL'] = {'url', 'URL'}, -- Used by InternetArchiveBot
['UrlAccess'] = 'url-access', -- Used by InternetArchiveBot
['UrlStatus'] = 'url-status', -- Used by InternetArchiveBot
['Vauthors'] = 'vauthors',
['Veditors'] = 'veditors',
['Via'] = 'via',
['Volume'] = 'volume',
['Year'] = 'year',
['AuthorList-First'] = {"first#", "author-first#", "author#-first", "author-given#", "author#-given",
"subject-first#", "subject#-first", "subject-given#", "subject#-given",
"given#"},
['AuthorList-Last'] = {"last#", "author-last#", "author#-last", "author-surname#", "author#-surname",
"subject-last#", "subject#-last", "subject-surname#", "subject#-surname",
"author#", 'host#', "subject#", "surname#"},
['AuthorList-Link'] = {"author-link#", "author#-link", "subject-link#",
"subject#-link", "authorlink#", "author#link"},
['AuthorList-Mask'] = {"author-mask#", "author#-mask", "subject-mask#", "subject#-mask"},
['ContributorList-First'] = {'contributor-first#', 'contributor#-first',
'contributor-given#', 'contributor#-given'},
['ContributorList-Last'] = {'contributor-last#', 'contributor#-last',
'contributor-surname#', 'contributor#-surname', 'contributor#'},
['ContributorList-Link'] = {'contributor-link#', 'contributor#-link'},
['ContributorList-Mask'] = {'contributor-mask#', 'contributor#-mask'},
['EditorList-First'] = {"editor-first#", "editor#-first", "editor-given#", "editor#-given"},
['EditorList-Last'] = {"editor-last#", "editor#-last", "editor-surname#",
"editor#-surname", "editor#"},
['EditorList-Link'] = {"editor-link#", "editor#-link"},
['EditorList-Mask'] = {"editor-mask#", "editor#-mask"},
['InterviewerList-First'] = {'interviewer-first#', 'interviewer#-first',
'interviewer-given#', 'interviewer#-given'},
['InterviewerList-Last'] = {'interviewer-last#', 'interviewer#-last',
'interviewer-surname#', 'interviewer#-surname', 'interviewer#'},
['InterviewerList-Link'] = {'interviewer-link#', 'interviewer#-link'},
['InterviewerList-Mask'] = {'interviewer-mask#', 'interviewer#-mask'},
['TranslatorList-First'] = {'translator-first#', 'translator#-first',
'translator-given#', 'translator#-given'},
['TranslatorList-Last'] = {'translator-last#', 'translator#-last',
'translator-surname#', 'translator#-surname', 'translator#'},
['TranslatorList-Link'] = {'translator-link#', 'translator#-link'},
['TranslatorList-Mask'] = {'translator-mask#', 'translator#-mask'},
}
--[[--------------------------< P U N C T _ S K I P >---------------------------
builds a table of parameter names that the extraneous terminal punctuation check should not check.
]]
local punct_meta_params = { -- table of aliases[] keys (meta parameters); each key has a table of parameter names for a value
'BookTitle', 'Chapter', 'ScriptChapter', 'ScriptTitle', 'Title', 'TransChapter', 'Transcript', 'TransMap', 'TransTitle', -- title-holding parameters
'AuthorList-Mask', 'ContributorList-Mask', 'EditorList-Mask', 'InterviewerList-Mask', 'TranslatorList-Mask', -- name-list mask may have name separators
'PostScript', 'Quote', 'ScriptQuote', 'TransQuote', 'Ref', -- miscellaneous
'ArchiveURL', 'ChapterURL', 'ConferenceURL', 'MapURL', 'TranscriptURL', 'URL', -- URL-holding parameters
}
local url_meta_params = { -- table of aliases[] keys (meta parameters); each key has a table of parameter names for a value
'ArchiveURL', 'ChapterURL', 'ConferenceURL', 'ID', 'MapURL', 'TranscriptURL', 'URL', -- parameters allowed to hold urls
'Page', 'Pages', 'At', 'QuotePage', 'QuotePages', -- insource locators allowed to hold urls
}
local function build_skip_table (skip_t, meta_params)
for _, meta_param in ipairs (meta_params) do -- for each meta parameter key
local params = aliases[meta_param]; -- get the parameter or the table of parameters associated with the meta parameter name
if 'string' == type (params) then
skip_t[params] = 1; -- just a single parameter
else
for _, param in ipairs (params) do -- get the parameter name
skip_t[param] = 1; -- add the parameter name to the skip table
local count;
param, count = param:gsub ('#', ''); -- remove enumerator marker from enumerated parameters
if 0 ~= count then -- if removed
skip_t[param] = 1; -- add param name without enumerator marker
end
end
end
end
return skip_t;
end
local punct_skip = {};
local url_skip = {};
--[[--------------------------< S I N G L E - L E T T E R S E C O N D - L E V E L D O M A I N S >----------
this is a list of tlds that are known to have single-letter second-level domain names. This list does not include
ccTLDs which are accepted in is_domain_name().
]]
local single_letter_2nd_lvl_domains_t = {'cash', 'company', 'foundation', 'media', 'org', 'today'};
--[[-----------< S P E C I A L C A S E T R A N S L A T I O N S >------------
This table is primarily here to support internationalization. Translations in
this table are used, for example, when an error message, category name, etc.,
is extracted from the English alias key. There may be other cases where
this translation table may be useful.
]]
local is_Latn = 'A-Za-z\195\128-\195\150\195\152-\195\182\195\184-\198\191\199\132-\201\143\225\184\128-\225\187\191';
local special_case_translation = {
['AuthorList'] = 'authors list', -- used to assemble maintenance category names
['ContributorList'] = 'contributors list', -- translation of these names plus translation of the base maintenance category names in maint_cats{} table below
['EditorList'] = 'editors list', -- must match the names of the actual categories
['InterviewerList'] = 'interviewers list', -- this group or translations used by name_has_ed_markup() and name_has_mult_names()
['TranslatorList'] = 'translators list',
-- 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'] = nil, -- 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'] = nil},
{['en'] = {'page not found', true}, ['local'] = nil},
{['en'] = {'subscribe to read', true}, ['local'] = nil},
{['en'] = {'^[%(%[{<]?unknown[>}%]%)]?$', false}, ['local'] = nil},
{['en'] = {'website is for sale', true}, ['local'] = nil},
{['en'] = {'^404', false}, ['local'] = nil},
{['en'] = {'error[ %-]404', false}, ['local'] = nil},
{['en'] = {'internet archive wayback machine', true}, ['local'] = nil},
{['en'] = {'log into facebook', true}, ['local'] = nil},
{['en'] = {'login • instagram', true}, ['local'] = nil},
{['en'] = {'redirecting...', true}, ['local'] = nil},
{['en'] = {'usurped title', true}, ['local'] = nil}, -- 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'] = nil},
},
['reject'] = {
{['en'] = {'about us', true}, ['local'] = nil},
{['en'] = {'%f[%a][Aa]dvisor%f[%A]', false}, ['local'] = nil},
{['en'] = {'allmusic', true}, ['local'] = nil},
{['en'] = {'%f[%a][Aa]uthor%f[%A]', false}, ['local'] = nil},
{['en'] = {'^[Bb]ureau$', false}, ['local'] = nil},
{['en'] = {'business', true}, ['local'] = nil},
{['en'] = {'cnn', true}, ['local'] = nil},
{['en'] = {'collaborator', true}, ['local'] = nil},
{['en'] = {'^[Cc]ompany$', false}, ['local'] = nil},
{['en'] = {'contributor', true}, ['local'] = nil},
{['en'] = {'contact us', true}, ['local'] = nil},
{['en'] = {'correspondent', true}, ['local'] = nil},
{['en'] = {'^[Dd]esk$', false}, ['local'] = nil},
{['en'] = {'directory', true}, ['local'] = nil},
{['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'] = nil},
{['en'] = {'%f[%a][Ee]ditors?%f[%A]', false}, ['local'] = nil},
{['en'] = {'%f[%a][Ee]mail%f[%A]', false}, ['local'] = nil},
{['en'] = {'facebook', true}, ['local'] = nil},
{['en'] = {'google', true}, ['local'] = nil},
{['en'] = {'^[Gg]roup$', false}, ['local'] = nil},
{['en'] = {'home page', true}, ['local'] = nil},
{['en'] = {'^[Ii]nc%.?$', false}, ['local'] = nil},
{['en'] = {'instagram', true}, ['local'] = nil},
{['en'] = {'interviewer', true}, ['local'] = nil},
{['en'] = {'^[Ll]imited$', false}, ['local'] = nil},
{['en'] = {'linkedIn', true}, ['local'] = nil},
{['en'] = {'^[Nn]ews$', false}, ['local'] = nil},
{['en'] = {'[Nn]ews[ %-]?[Rr]oom', false}, ['local'] = nil},
{['en'] = {'pinterest', true}, ['local'] = nil},
{['en'] = {'policy', true}, ['local'] = nil},
{['en'] = {'privacy', true}, ['local'] = nil},
{['en'] = {'reuters', true}, ['local'] = nil},
{['en'] = {'translator', true}, ['local'] = nil},
{['en'] = {'tumblr', true}, ['local'] = nil},
{['en'] = {'twitter', true}, ['local'] = nil},
{['en'] = {'site name', true}, ['local'] = nil},
{['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'] = nil},
{['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'] = {['જાન્યુઆરી'] = 1, ['ફેબ્રુઆરી'] = 2, ['માર્ચ'] = 3, ['એપ્રિલ'] = 4, ['મે'] = 5, ['જૂન'] = 6, ['જુલાઇ'] = 7, ['ઓગસ્ટ'] = 8, ['સપ્ટેમ્બર'] = 9, ['ઓક્ટોબર'] = 10, ['નવેમ્બર'] = 11, ['ડિસેમ્બર'] = 12},
['short'] = {['જાન'] = 1, ['ફેબ્રુ'] = 2, ['માર્ચ'] = 3, ['એપ્રિ'] = 4, ['મે'] = 5, ['જૂન'] = 6, ['જુલ'] = 7, ['ઓગ'] = 8, ['સપ્ટે'] = 9, ['ઓક્ટ'] = 10, ['નવે'] = 11, ['ડિસે'] = 12},
['quarter'] = {['First Quarter'] = 33, ['Second Quarter'] = 34, ['Third Quarter'] = 35, ['Fourth Quarter'] = 36},
['season'] = {['શિયાળો'] = 24, ['વસંત'] = 21, ['ઉનાળો'] = 22, ['પાનખર'] = 23, ['શિશિર'] = 23},
['named'] = {['ઇસ્ટર'] = 98, ['ક્રિસમસ'] = 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'] = '૦', ['1'] = '૧', ['2'] = '૨', ['3'] = '૩', ['4'] = '૪', ['5'] = '૫', ['6'] = '૬', ['7'] = '૭', ['8'] = '૮', ['9'] = '૯'}, -- used to convert local language digits to Western 0-9
['xlate_digits'] = {},
}
if local_date_names_from_mediawiki then -- if fetching local month names from MediaWiki is enabled
local long_t = {};
local short_t = {};
for i=1, 12 do -- loop 12x and
local name = lang_obj:formatDate('F', '2022-' .. i .. '-1'); -- get long month name for each i
long_t[name] = i; -- save it
name = lang_obj:formatDate('M', '2022-' .. i .. '-1'); -- get short month name for each i
short_t[name] = i; -- save it
end
date_names['local']['long'] = long_t; -- write the long table – overwrites manual translation
date_names['local']['short'] = short_t; -- write the short table – overwrites manual translation
end
-- create inverted date-name tables for reformatting and/or translation
for _, invert_t in pairs {{'long', 'inv_local_long'}, {'short', 'inv_local_short'}, {'quarter', 'inv_local_quarter'}, {'season', 'inv_local_season'}, {'named', 'inv_local_named'}} do
for name, i in pairs (date_names['local'][invert_t[1]]) do -- this table is ['name'] = i
date_names[invert_t[2]][i] = name; -- invert to get [i] = 'name' for conversions from ymd
end
end
if local_digits_from_mediawiki then -- if fetching local digits from MediaWiki is enabled
local digits_t = {};
for i=0, 9 do -- loop 10x and
digits_t [lang_obj:formatNum (i)] = tostring (i); -- format the loop indexer as local lang table index and assign loop indexer (a string) as the value
end
date_names['local_digits'] = digits_t;
end
for ld, ed in pairs (date_names.local_digits) do -- make a digit translation table for simple date translation from en to local language using local_digits table
date_names.xlate_digits [ed] = ld; -- en digit becomes index with local digit as the value
end
local df_template_patterns = { -- table of redirects to {{Use dmy dates}} and {{Use mdy dates}}
'{{ *[Uu]se +(dmy) +dates *[|}]', -- 1159k -- sorted by approximate transclusion count
'{{ *[Uu]se +(mdy) +dates *[|}]', -- 212k
'{{ *[Uu]se +(MDY) +dates *[|}]', -- 788
'{{ *[Uu]se +(DMY) +dates *[|}]', -- 343
'{{ *([Mm]dy) *[|}]', -- 176
'{{ *[Uu]se *(dmy) *[|}]', -- 156 + 18
'{{ *[Uu]se *(mdy) *[|}]', -- 149 + 11
'{{ *([Dd]my) *[|}]', -- 56
'{{ *[Uu]se +(MDY) *[|}]', -- 5
'{{ *([Dd]MY) *[|}]', -- 3
'{{ *[Uu]se(mdy)dates *[|}]', -- 1
'{{ *[Uu]se +(DMY) *[|}]', -- 0
'{{ *([Mm]DY) *[|}]', -- 0
}
local title_object = mw.title.getCurrentTitle();
local content; -- done this way so that unused templates appear in unused-template-reports; self-transcluded makes them look like they are used
if 10 ~= title_object.namespace then -- all namespaces except Template
content = title_object:getContent() or ''; -- get the content of the article or ''; new pages edited w/ve do not have 'content' until saved; ve does not preview; phab:T221625
end
local function get_date_format ()
if not content then -- nil content when we're in template
return nil; -- auto-formatting does not work in Template space so don't set global_df
end
for _, pattern in ipairs (df_template_patterns) do -- loop through the patterns looking for {{Use dmy dates}} or {{Use mdy dates}} or any of their redirects
local start, _, match = content:find(pattern); -- match is the three letters indicating desired date format
if match then
local use_dates_template = content:match ('%b{}', start); -- get the whole template
if use_dates_template:match ('| *cs1%-dates *= *[lsy][sy]?') then -- look for |cs1-dates=publication date length access-/archive-date length
return match:lower() .. '-' .. use_dates_template:match ('| *cs1%-dates *= *([lsy][sy]?)');
else
return match:lower() .. '-all'; -- no |cs1-dates= k/v pair; return value appropriate for use in |df=
end
end
end
end
local global_df; -- TODO: add this to <global_cs1_config_t>?
--[[-----------------< V O L U M E , I S S U E , P A G E S >------------------
These tables hold cite class values (from the template invocation) and identify those templates that support
|volume=, |issue=, and |page(s)= parameters. Cite conference and cite map require further qualification which
is handled in the main module.
]]
local templates_using_volume = {'citation', 'audio-visual', 'book', 'conference', 'encyclopaedia', 'interview', 'journal', 'magazine', 'map', 'news', 'report', 'techreport', 'thesis'}
local templates_using_issue = {'citation', 'conference', 'episode', 'interview', 'journal', 'magazine', 'map', 'news', 'podcast'}
local templates_not_using_page = {'audio-visual', 'episode', 'mailinglist', 'newsgroup', 'podcast', 'serial', 'sign', 'speech'}
--[[
These tables control when it is appropriate for {{citation}} to render |volume= and/or |issue=. The parameter
names in the tables constrain {{citation}} so that its renderings match the renderings of the equivalent cs1
templates. For example, {{cite web}} does not support |volume= so the equivalent {{citation |website=...}} must
not support |volume=.
]]
local citation_no_volume_t = { -- {{citation}} does not render |volume= when these parameters are used
'website', 'mailinglist', 'script-website',
}
local citation_issue_t = { -- {{citation}} may render |issue= when these parameters are used
'journal', 'magazine', 'newspaper', 'periodical', 'work',
'script-journal', 'script-magazine', 'script-newspaper', 'script-periodical', 'script-work',
}
--[[
Patterns for finding extra text in |volume=, |issue=, |page=, |pages=
]]
local vol_iss_pg_patterns = {
good_ppattern = '^P[^%.PpGg]', -- OK to begin with uppercase P: P7 (page 7 of section P), but not p123 (page 123); TODO: this allows 'Pages' which it should not
bad_ppatterns = { -- patterns for |page= and |pages=
'^[Pp][PpGg]?%.?[ %d]',
'^[Pp][Pp]?%. ', -- from {{p.}} and {{pp.}} templates
'^[Pp]ages?',
'^[Pp]gs.?',
},
vi_patterns_t = { -- combined to catch volume-like text in |issue= and issue-like text in |volume=
'^volumes?', -- volume-like text
'^vols?[%.:=]?',
'^issues?', --issue-like text
'^iss[%.:=]?',
'^numbers?',
'^nos?%A', -- don't match 'november' or 'nostradamus'
'^nr[%.:=]?',
'^n[%.:= ]', -- might be a valid issue without separator (space char is sep char here)
'^n°', -- 'n' with degree sign (U+00B0)
'^№', -- precomposed unicode numero character (U+2116)
},
}
--[[--------------------------< K E Y W O R D S >-------------------------------
These tables hold keywords for those parameters that have defined sets of acceptable keywords.
]]
--[[-------------------< K E Y W O R D S T A B L E >--------------------------
this is a list of keywords; each key in the list is associated with a table of
synonymous keywords possibly from different languages.
for I18N: add local-language keywords to value table; do not change the key.
For example, adding the German keyword 'ja':
['affirmative'] = {'yes', 'true', 'y', 'ja'},
Because CS1|2 templates from en.wiki articles are often copied to other local wikis,
it is recommended that the English keywords remain in these tables.
]]
local keywords = {
['amp'] = {'&', 'amp', 'ampersand'}, -- |name-list-style=
['and'] = {'and', 'serial'}, -- |name-list-style=
['affirmative'] = {'yes', 'true', 'y'}, -- |no-tracking=, |no-pp= -- Used by InternetArchiveBot
['afterword'] = {'afterword'}, -- |contribution=
['bot: unknown'] = {'bot: unknown'}, -- |url-status= -- Used by InternetArchiveBot
['cs1'] = {'cs1'}, -- |mode=
['cs2'] = {'cs2'}, -- |mode=
['dead'] = {'dead', 'deviated'}, -- |url-status= -- Used by InternetArchiveBot
['dmy'] = {'dmy'}, -- |df=
['dmy-all'] = {'dmy-all'}, -- |df=
['foreword'] = {'foreword'}, -- |contribution=
['free'] = {'free'}, -- |<id>-access= -- Used by InternetArchiveBot
['harv'] = {'harv'}, -- |ref=; this no longer supported; is_valid_parameter_value() called with <invert> = true
['introduction'] = {'introduction'}, -- |contribution=
['limited'] = {'limited'}, -- |url-access= -- Used by InternetArchiveBot
['live'] = {'live'}, -- |url-status= -- Used by InternetArchiveBot
['mdy'] = {'mdy'}, -- |df=
['mdy-all'] = {'mdy-all'}, -- |df=
['none'] = {'none'}, -- |postscript=, |ref=, |title=, |type= -- Used by InternetArchiveBot
['off'] = {'off'}, -- |title= (potentially also: |title-link=, |postscript=, |ref=, |type=)
['preface'] = {'preface'}, -- |contribution=
['registration'] = {'registration'}, -- |url-access= -- Used by InternetArchiveBot
['subscription'] = {'subscription'}, -- |url-access= -- Used by InternetArchiveBot
['unfit'] = {'unfit'}, -- |url-status= -- Used by InternetArchiveBot
['usurped'] = {'usurped'}, -- |url-status= -- Used by InternetArchiveBot
['vanc'] = {'vanc'}, -- |name-list-style=
['ymd'] = {'ymd'}, -- |df=
['ymd-all'] = {'ymd-all'}, -- |df=
-- ['yMd'] = {'yMd'}, -- |df=; not supported at en.wiki
-- ['yMd-all'] = {'yMd-all'}, -- |df=; not supported at en.wiki
}
--[[------------------------< X L A T E _ K E Y W O R D S >---------------------
this function builds a list, keywords_xlate{}, of the keywords found in keywords{} where the values from keywords{}
become the keys in keywords_xlate{} and the keys from keywords{} become the values in keywords_xlate{}:
['affirmative'] = {'yes', 'true', 'y'}, -- in keywords{}
becomes
['yes'] = 'affirmative', -- in keywords_xlate{}
['true'] = 'affirmative',
['y'] = 'affirmative',
the purpose of this function is to act as a translator between a non-English keyword and its English equivalent
that may be used in other modules of this suite
]]
local function xlate_keywords ()
local out_table = {}; -- output goes here
for k, keywords_t in pairs (keywords) do -- spin through the keywords table
for _, keyword in ipairs (keywords_t) do -- for each keyword
out_table[keyword] = k; -- create an entry in the output table where keyword is the key
end
end
return out_table;
end
local keywords_xlate = xlate_keywords (); -- the list of translated keywords
--[[----------------< M A K E _ K E Y W O R D S _ L I S T >---------------------
this function assembles, for parameter-value validation, the list of keywords appropriate to that parameter.
keywords_lists{}, is a table of tables from keywords{}
]]
local function make_keywords_list (keywords_lists)
local out_table = {}; -- output goes here
for _, keyword_list in ipairs (keywords_lists) do -- spin through keywords_lists{} and get a table of keywords
for _, keyword in ipairs (keyword_list) do -- spin through keyword_list{} and add each keyword, ...
table.insert (out_table, keyword); -- ... as plain text, to the output list
end
end
return out_table;
end
--[[----------------< K E Y W O R D S _ L I S T S >-----------------------------
this is a list of lists of valid keywords for the various parameters in [key].
Generally the keys in this table are the canonical en.wiki parameter names though
some are contrived because of use in multiple differently named parameters:
['yes_true_y'], ['id-access'].
The function make_keywords_list() extracts the individual keywords from the
appropriate list in keywords{}.
The lists in this table are used to validate the keyword assignment for the
parameters named in this table's keys.
]]
local keywords_lists = {
['yes_true_y'] = make_keywords_list ({keywords.affirmative}),
['contribution'] = make_keywords_list ({keywords.afterword, keywords.foreword, keywords.introduction, keywords.preface}),
['df'] = make_keywords_list ({keywords.dmy, keywords['dmy-all'], keywords.mdy, keywords['mdy-all'], keywords.ymd, keywords['ymd-all']}),
-- ['df'] = make_keywords_list ({keywords.dmy, keywords['dmy-all'], keywords.mdy, keywords['mdy-all'], keywords.ymd, keywords['ymd-all'], keywords.yMd, keywords['yMd-all']}), -- not supported at en.wiki
['mode'] = make_keywords_list ({keywords.cs1, keywords.cs2}),
['name-list-style'] = make_keywords_list ({keywords.amp, keywords['and'], keywords.vanc}),
['ref'] = make_keywords_list ({keywords.harv}), -- inverted check; |ref=harv no longer supported
['url-access'] = make_keywords_list ({keywords.subscription, keywords.limited, keywords.registration}),
['url-status'] = make_keywords_list ({keywords.dead, keywords.live, keywords.unfit, keywords.usurped, keywords['bot: unknown']}),
['id-access'] = make_keywords_list ({keywords.free}),
}
--[[--------------------------< C S 1 _ C O N F I G _ G E T >--------------------------------------------------
fetch and validate values from {{cs1 config}} template to fill <global_cs1_config_t>
no error messages; when errors are detected, the parameter value from {{cs1 config}} is blanked.
Supports all parameters and aliases associated with the metaparameters: DisplayAuthors, DisplayContributors,
DisplayEditors, DisplayInterviewers, DisplayTranslators, NameListStyle, and Mode. The DisplayWhatever metaparameters
accept numeric values only (|display-authors=etal and the like is not supported).
]]
local global_cs1_config_t = {}; -- TODO: add value returned from get_date_format() to this table?
local function get_cs1_config ()
if not content then -- nil content when we're in template
return nil; -- auto-formatting does not work in Template space so don't set global_df
end
local start = content:find('{{ *[Cc][Ss]1 config *[|}]'); -- <start> is offset into <content> when {{cs1 config}} found; nil else
if start then
local cs1_config_template = content:match ('%b{}', start); -- get the whole template
if not cs1_config_template then
return nil;
end
local params_t = mw.text.split (cs1_config_template:gsub ('^{{%s*', ''):gsub ('%s*}}$', ''), '%s*|%s*'); -- remove '{{' and '}}'; make a sequence of parameter/value pairs (split on the pipe)
table.remove (params_t, 1); -- remove the template name because it isn't a parameter/value pair
local config_meta_params_t = {'DisplayAuthors', 'DisplayContributors', 'DisplayEditors', 'DisplayInterviewers', 'DisplayTranslators', 'NameListStyle', 'Mode'};
local meta_param_map_t = {}; -- list of accepted parameter names usable in {{cs1 config}} goes here
for _, meta_param in ipairs (config_meta_params_t) do -- for i18n using <config_meta_params_t>, map template parameter names to their metaparameter equivalents
if 'table' == type (aliases[meta_param]) then -- if <meta_param> is a sequence,
for _, param in ipairs (aliases[meta_param]) do -- extract its contents
meta_param_map_t[param] = meta_param; -- and add to <meta_param_map_t>
end
else
meta_param_map_t[aliases[meta_param]] = meta_param; -- not a sequence so just add the parameter to <meta_param_map_t>
end
end
local keywords_t = {}; -- map valid keywords to their associate metaparameter; reverse form of <keyword_lists[key] for these metaparameters
for _, metaparam_t in ipairs ({{'NameListStyle', 'name-list-style'}, {'Mode', 'mode'}}) do -- only these metaparameter / keywords_lists key pairs
for _, keyword in ipairs (keywords_lists[metaparam_t[2]]) do -- spin through the list of keywords
keywords_t[keyword] = metaparam_t[1]; -- add [keyword] = metaparameter to the map
end
end
for _, param in ipairs (params_t) do -- spin through the {{cs1 config}} parameters and fill <global_cs1_config_t>
local k, v = param:match ('([^=]-)%s*=%s*(.+)'); -- <k> is the parameter name; <v> is parameter's assigned value
if k then
if k:find ('^display') then -- if <k> is one of the |display-<namelist>= parameters
if v:match ('%d+') then -- the assigned value must be digits; doesn't accept 'etal'
global_cs1_config_t[meta_param_map_t[k]]=v; -- add the display param and its value to globals table
end
else
if keywords_t[v] == meta_param_map_t[k] then -- keywords_t[v] returns nil or the metaparam name; these must be the same
global_cs1_config_t[meta_param_map_t[k]]=v; -- add the parameter and its value to globals table
end
end
end
end
end
end
get_cs1_config (); -- fill <global_cs1_config_t>
--[[---------------------< S T R I P M A R K E R S >----------------------------
Common pattern definition location for stripmarkers so that we don't have to go
hunting for them if (when) MediaWiki changes their form.
]]
local stripmarkers = {
['any'] = '\127[^\127]*UNIQ%-%-(%a+)%-[%a%d]+%-QINU[^\127]*\127', -- capture returns name of stripmarker
['math'] = '\127[^\127]*UNIQ%-%-math%-[%a%d]+%-QINU[^\127]*\127' -- math stripmarkers used in coins_cleanup() and coins_replace_math_stripmarker()
}
--[[------------< I N V I S I B L E _ C H A R A C T E R S >---------------------
This table holds non-printing or invisible characters indexed either by name or
by Unicode group. Values are decimal representations of UTF-8 codes. The table
is organized as a table of tables because the Lua pairs keyword returns table
data in an arbitrary order. Here, we want to process the table from top to bottom
because the entries at the top of the table are also found in the ranges specified
by the entries at the bottom of the table.
Also here is a pattern that recognizes stripmarkers that begin and end with the
delete characters. The nowiki stripmarker is not an error but some others are
because the parameter values that include them become part of the template's
metadata before stripmarker replacement.
]]
local invisible_defs = {
del = '\127', -- used to distinguish between stripmarker and del char
zwj = '\226\128\141', -- used with capture because zwj may be allowed
}
local invisible_chars = {
{'replacement', '\239\191\189'}, -- U+FFFD, EF BF BD
{'zero width joiner', '('.. invisible_defs.zwj .. ')'}, -- U+200D, E2 80 8D; capture because zwj may be allowed
{'zero width space', '\226\128\139'}, -- U+200B, E2 80 8B
{'hair space', '\226\128\138'}, -- U+200A, E2 80 8A
{'soft hyphen', '\194\173'}, -- U+00AD, C2 AD
{'horizontal tab', '\009'}, -- U+0009 (HT), 09
{'line feed', '\010'}, -- U+000A (LF), 0A
{'no-break space', '\194\160'}, -- U+00A0 (NBSP), C2 A0
{'carriage return', '\013'}, -- U+000D (CR), 0D
{'stripmarker', stripmarkers.any}, -- stripmarker; may or may not be an error; capture returns the stripmaker type
{'delete', '('.. invisible_defs.del .. ')'}, -- U+007F (DEL), 7F; must be done after stripmarker test; capture to distinguish isolated del chars not part of stripmarker
{'C0 control', '[\000-\008\011\012\014-\031]'}, -- U+0000–U+001F (NULL–US), 00–1F (except HT, LF, CR (09, 0A, 0D))
{'C1 control', '[\194\128-\194\159]'}, -- U+0080–U+009F (XXX–APC), C2 80 – C2 9F
-- {'Specials', '[\239\191\185-\239\191\191]'}, -- U+FFF9-U+FFFF, EF BF B9 – EF BF BF
-- {'Private use area', '[\238\128\128-\239\163\191]'}, -- U+E000–U+F8FF, EE 80 80 – EF A3 BF
-- {'Supplementary Private Use Area-A', '[\243\176\128\128-\243\191\191\189]'}, -- U+F0000–U+FFFFD, F3 B0 80 80 – F3 BF BF BD
-- {'Supplementary Private Use Area-B', '[\244\128\128\128-\244\143\191\189]'}, -- U+100000–U+10FFFD, F4 80 80 80 – F4 8F BF BD
}
--[[
Indic script makes use of zero width joiner as a character modifier so zwj
characters must be left in. This pattern covers all of the unicode characters
for these languages:
Devanagari 0900–097F – https://unicode.org/charts/PDF/U0900.pdf
Devanagari extended A8E0–A8FF – https://unicode.org/charts/PDF/UA8E0.pdf
Bengali 0980–09FF – https://unicode.org/charts/PDF/U0980.pdf
Gurmukhi 0A00–0A7F – https://unicode.org/charts/PDF/U0A00.pdf
Gujarati 0A80–0AFF – https://unicode.org/charts/PDF/U0A80.pdf
Oriya 0B00–0B7F – https://unicode.org/charts/PDF/U0B00.pdf
Tamil 0B80–0BFF – https://unicode.org/charts/PDF/U0B80.pdf
Telugu 0C00–0C7F – https://unicode.org/charts/PDF/U0C00.pdf
Kannada 0C80–0CFF – https://unicode.org/charts/PDF/U0C80.pdf
Malayalam 0D00–0D7F – https://unicode.org/charts/PDF/U0D00.pdf
plus the not-necessarily Indic scripts for Sinhala and Burmese:
Sinhala 0D80-0DFF - https://unicode.org/charts/PDF/U0D80.pdf
Myanmar 1000-109F - https://unicode.org/charts/PDF/U1000.pdf
Myanmar extended A AA60-AA7F - https://unicode.org/charts/PDF/UAA60.pdf
Myanmar extended B A9E0-A9FF - https://unicode.org/charts/PDF/UA9E0.pdf
the pattern is used by has_invisible_chars() and coins_cleanup()
]]
local indic_script = '[\224\164\128-\224\181\191\224\163\160-\224\183\191\225\128\128-\225\130\159\234\167\160-\234\167\191\234\169\160-\234\169\191]';
-- list of emoji that use a zwj character (U+200D) to combine with another emoji
-- from: https://unicode.org/Public/emoji/16.0/emoji-zwj-sequences.txt; version: 16.0; 2024-08-14
-- table created by: [[:en:Module:Make emoji zwj table]]
local emoji_t = { -- indexes are decimal forms of the hex values in U+xxxx
[8596] = true, -- U+2194 ↔ left right arrow
[8597] = true, -- U+2195 ↕ up down arrow
[9760] = true, -- U+2620 ☠ skull and crossbones
[9792] = true, -- U+2640 ♀ female sign
[9794] = true, -- U+2642 ♂ male sign
[9877] = true, -- U+2695 ⚕ staff of aesculapius
[9878] = true, -- U+2696 ⚖ scales
[9895] = true, -- U+26A7 ⚧ male with stroke and male and female sign
[9992] = true, -- U+2708 ✈ airplane
[10052] = true, -- U+2744 ❄ snowflake
[10084] = true, -- U+2764 ❤ heavy black heart
[10145] = true, -- U+27A1 ➡ black rightwards arrow
[11035] = true, -- U+2B1B ⬛ black large square
[127752] = true, -- U+1F308 🌈 rainbow
[127787] = true, -- U+1F32B 🌫 fog
[127806] = true, -- U+1F33E 🌾 ear of rice
[127859] = true, -- U+1F373 🍳 cooking
[127868] = true, -- U+1F37C 🍼 baby bottle
[127876] = true, -- U+1F384 🎄 christmas tree
[127891] = true, -- U+1F393 🎓 graduation cap
[127908] = true, -- U+1F3A4 🎤 microphone
[127912] = true, -- U+1F3A8 🎨 artist palette
[127979] = true, -- U+1F3EB 🏫 school
[127981] = true, -- U+1F3ED 🏭 factory
[128102] = true, -- U+1F466 👦 boy
[128103] = true, -- U+1F467 👧 girl
[128104] = true, -- U+1F468 👨 man
[128105] = true, -- U+1F469 👩 woman
[128139] = true, -- U+1F48B 💋 kiss mark
[128165] = true, -- U+1F4A5 💥 collision symbol
[128168] = true, -- U+1F4A8 💨 dash symbol
[128171] = true, -- U+1F4AB 💫 dizzy symbol
[128187] = true, -- U+1F4BB 💻 personal computer
[128188] = true, -- U+1F4BC 💼 brief case
[128293] = true, -- U+1F525 🔥 fire
[128295] = true, -- U+1F527 🔧 wrench
[128300] = true, -- U+1F52C 🔬 microscope
[128488] = true, -- U+1F5E8 🗨 left speech bubble
[128640] = true, -- U+1F680 🚀 rocket
[128658] = true, -- U+1F692 🚒 fire engine
[129001] = true, -- U+1F7E9 🟩 large green square
[129003] = true, -- U+1F7EB 🟫 large brown square
[129309] = true, -- U+1F91D 🤝 handshake
[129455] = true, -- U+1F9AF 🦯 probing cane
[129456] = true, -- U+1F9B0 🦰 emoji component red hair
[129457] = true, -- U+1F9B1 🦱 emoji component curly hair
[129458] = true, -- U+1F9B2 🦲 emoji component bald
[129459] = true, -- U+1F9B3 🦳 emoji component white hair
[129466] = true, -- U+1F9BA 🦺 safety vest
[129468] = true, -- U+1F9BC 🦼 motorized wheelchair
[129469] = true, -- U+1F9BD 🦽 manual wheelchair
[129489] = true, -- U+1F9D1 🧑 adult
[129490] = true, -- U+1F9D2 🧒 child
[129657] = true, -- U+1FA79 🩹 adhesive bandage
[129778] = true, -- U+1FAF2 🫲 leftwards hand
}
--[[----------------------< L A N G U A G E S U P P O R T >-------------------
These tables and constants support various language-specific functionality.
]]
--local this_wiki_code = mw.getContentLanguage():getCode(); -- get this wiki's language code
local this_wiki_code = lang_obj:getCode(); -- get this wiki's language code
if string.match (mw.site.server, 'wikidata') then
this_wiki_code = mw.getCurrentFrame():callParserFunction('int', {'lang'}); -- on Wikidata so use interface language setting instead
end
local mw_languages_by_tag_t = mw.language.fetchLanguageNames (this_wiki_code, 'all'); -- get a table of language tag/name pairs known to Wikimedia; used for interwiki tests
local mw_languages_by_name_t = {};
for k, v in pairs (mw_languages_by_tag_t) do -- build a 'reversed' table name/tag language pairs know to MediaWiki; used for |language=
v = mw.ustring.lower (v); -- lowercase for tag fetch; get name's proper case from mw_languages_by_tag_t[<tag>]
if mw_languages_by_name_t[v] then -- when name already in the table
if 2 == #k or 3 == #k then -- if tag does not have subtags
mw_languages_by_name_t[v] = k; -- prefer the shortest tag for this name
end
else -- here when name not in the table
mw_languages_by_name_t[v] = k; -- so add name and matching tag
end
end
local inter_wiki_map = {}; -- map of interwiki prefixes that are language-code prefixes
for k, v in pairs (mw.site.interwikiMap ('local')) do -- spin through the base interwiki map (limited to local)
if mw_languages_by_tag_t[v["prefix"]] then -- if the prefix matches a known language tag
inter_wiki_map[v["prefix"]] = true; -- add it to our local map
end
end
--[[--------------------< S C R I P T _ L A N G _ C O D E S >-------------------
This table is used to hold ISO 639-1 two-character and ISO 639-3 three-character
language codes that apply only to |script-title= and |script-chapter=
]]
local script_lang_codes = {
'ab', 'am', 'ar', 'az', 'be', 'bg', 'bn', 'bo', 'bs', 'ce', 'chr', 'dv', 'dz',
'el', 'fa', 'grc', 'gu', 'he', 'hi', 'hy', 'ja', 'ka', 'kk', 'km', 'kn', 'ko',
'ku', 'ky', 'lo', 'mk', 'ml', 'mn', 'mni', 'mr', 'my', 'ne', 'or', 'ota',
'pa', 'ps', 'ru', 'sd', 'si', 'sr', 'syc', 'ta', 'te', 'tg', 'th', 'ti', 'tt',
'ug', 'uk', 'ur', 'uz', 'yi', 'yue', 'zh', 'zgh'
};
--[[---------------< L A N G U A G E R E M A P P I N G >----------------------
These tables hold language information that is different (correct) from MediaWiki's definitions
For each ['<tag>'] = 'language name' in lang_code_remap{} there must be a matching ['language name'] = {'language name', '<tag>'} in lang_name_remap{}
lang_tag_remap{}:
key is always lowercase ISO 639-1, -2, -3 language tag or a valid lowercase IETF language tag
value is properly spelled and capitalized language name associated with <tag>
only one language name per <tag>;
key/value pair must have matching entry in lang_name_remap{}
lang_name_remap{}:
key is always lowercase language name
value is a table the holds correctly spelled and capitalized language name [1] and associated tag [2] (tag must match a tag key in lang_tag_remap{})
may have multiple keys referring to a common preferred name and tag; For example:
['kolsch'] and ['kölsch'] both refer to 'Kölsch' and 'ksh'
]]
local lang_tag_remap = { -- used for |language= and |script-title= / |script-chapter=
['als'] = 'Tosk Albanian', -- MediaWiki returns Alemannisch
['bh'] = 'Bihari', -- MediaWiki uses 'bh' as a subdomain name for Bhojpuri Wikipedia: bh.wikipedia.org
['bla'] = 'Blackfoot', -- MediaWiki/IANA/ISO 639: Siksika; use en.wiki preferred name
['bn'] = 'Bengali', -- MediaWiki returns Bangla
['ca-valencia'] = 'Valencian', -- IETF variant of Catalan
['fkv'] = 'Kven', -- MediaWiki returns Kvensk
['gsw'] = 'Swiss German',
['ilo'] = 'Ilocano', -- MediaWiki/IANA/ISO 639: Iloko; use en.wiki preferred name
['ksh'] = 'Kölsch', -- MediaWiki: Colognian; use IANA/ISO 639 preferred name
['ksh-x-colog'] = 'Colognian', -- override MediaWiki ksh; no IANA/ISO 639 code for Colognian; IETF private code created at Module:Lang/data
['mis-x-ripuar'] = 'Ripuarian', -- override MediaWiki ksh; no IANA/ISO 639 code for Ripuarian; IETF private code created at Module:Lang/data
['nan-tw'] = 'Taiwanese Hokkien', -- make room for MediaWiki/IANA/ISO 639 nan: Min Nan Chinese and support en.wiki preferred name
['sr-ec'] = 'Serbian (Cyrillic script)', -- MediaWiki returns српски (ћирилица)
['sr-el'] = 'Serbian (Latin script)', -- MediaWiki returns srpski (latinica)
}
local lang_name_remap = { -- used for |language=; names require proper capitalization; tags must be lowercase
['alemannic'] = {'Swiss German', 'gsw'}, -- ISO 639-2, -3 alternate for Swiss German; MediaWiki mediawiki returns Alemannic for gsw; en.wiki preferred name
['alemannisch'] = {'Swiss German', 'gsw'}, -- not an ISO or IANA language name; MediaWiki uses 'als' as a subdomain name for Alemannic Wikipedia: als.wikipedia.org
['bangla'] = {'Bengali', 'bn'}, -- MediaWiki returns Bangla (the endonym) but we want Bengali (the exonym); here we remap
['bengali'] = {'Bengali', 'bn'}, -- MediaWiki doesn't use exonym so here we provide correct language name and 639-1 code
['bhojpuri'] = {'Bhojpuri', 'bho'}, -- MediaWiki uses 'bh' as a subdomain name for Bhojpuri Wikipedia: bh.wikipedia.org
['bihari'] = {'Bihari', 'bh'}, -- MediaWiki replaces 'Bihari' with 'Bhojpuri' so 'Bihari' cannot be found
['blackfoot'] = {'Blackfoot', 'bla'}, -- MediaWiki/IANA/ISO 639: Siksika; use en.wiki preferred name
['colognian'] = {'Colognian', 'ksh-x-colog'}, -- MediaWiki preferred name for ksh
['ilocano'] = {'Ilocano', 'ilo'}, -- MediaWiki/IANA/ISO 639: Iloko; use en.wiki preferred name
['kolsch'] = {'Kölsch', 'ksh'}, -- use IANA/ISO 639 preferred name (use non-diacritical o instead of umlaut ö)
['kölsch'] = {'Kölsch', 'ksh'}, -- use IANA/ISO 639 preferred name
['kven'] = {'Kven', 'fkv'}, -- Unicode CLDR have decided not to support English language name for these two...
['kvensk'] = {'Kven', 'fkv'}, -- ...they say to refer to IANA registry for English names
['ripuarian'] = {'Ripuarian', 'mis-x-ripuar'}, -- group of dialects; no code in MediaWiki or in IANA/ISO 639
['serbian (cyrillic script)'] = {'Serbian (Cyrillic script)', 'sr-cyrl'}, -- special case to get correct tag when |language=sr-ec
['serbian (latin script)'] = {'Serbian (Latin script)', 'sr-latn'}, -- special case to get correct tag when |language=sr-el
['swiss german'] = {'Swiss German', 'gsw'},
['taiwanese hokkien'] = {'Taiwanese Hokkien', 'nan-tw'}, -- make room for MediaWiki/IANA/ISO 639 nan: Min Nan Chinese
['tosk albanian'] = {'Tosk Albanian', 'als'}, -- MediaWiki replaces 'Tosk Albanian' with 'Alemannisch' so 'Tosk Albanian' cannot be found
['valencian'] = {'Valencian', 'ca-valencia'}, -- variant of Catalan; categorizes as Valencian
}
--[[---------------< P R O P E R T I E S _ C A T E G O R I E S >----------------
Properties categories. These are used for investigating qualities of citations.
]]
local prop_cats = {
['foreign-lang-source'] = 'CS1 $1-language sources ($2)', -- |language= categories; $1 is foreign-language name, $2 is ISO639-1 code
['foreign-lang-source-2'] = 'CS1 foreign language sources (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 interproject-linked names|$1', -- any author, editor, etc that has an interproject link; $1 is interproject tag used as a sort key
['interwiki-linked-name'] = 'CS1 interwiki-linked names|$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 $1-language sources ($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 location test',
['long-vol'] = 'CS1: long volume value', -- probably temporary cat to identify scope of |volume= values longer than 4 characters
['script'] = 'CS1 uses $1-language script ($2)', -- |script-title=xx: has matching category; $1 is language name, $2 is language tag
['tracked-param'] = 'CS1 tracked parameter: $1', -- $1 is base (enumerators removed) parameter name
['unfit'] = 'CS1: unfit URL', -- |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: abbreviated year range', -- 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 = '<code class="cs1-code">|access-date=</code> requires <code class="cs1-code">|url=</code>',
anchor = 'accessdate_missing_url',
category = 'CS1 errors: access-date without URL',
hidden = false
},
err_apostrophe_markup = {
message = 'Italic or bold markup not allowed in: <code class="cs1-code">|$1=</code>', -- $1 is parameter name
anchor = 'apostrophe_markup',
category = 'CS1 errors: markup',
hidden = false
},
err_archive_date_missing_url = {
message = '<code class="cs1-code">|archive-date=</code> requires <code class="cs1-code">|archive-url=</code>',
anchor = 'archive_date_missing_url',
category = 'CS1 errors: archive-url',
hidden = false
},
err_archive_date_url_ts_mismatch = {
message = '<code class="cs1-code">|archive-date=</code> / <code class="cs1-code">|archive-url=</code> timestamp mismatch; $1 suggested',
anchor = 'archive_date_url_ts_mismatch',
category = 'CS1 errors: archive-url',
hidden = false
},
err_archive_missing_date = {
message = '<code class="cs1-code">|archive-url=</code> requires <code class="cs1-code">|archive-date=</code>',
anchor = 'archive_missing_date',
category = 'CS1 errors: archive-url',
hidden = false
},
err_archive_missing_url = {
message = '<code class="cs1-code">|archive-url=</code> requires <code class="cs1-code">|url=</code>',
anchor = 'archive_missing_url',
category = 'CS1 errors: archive-url',
hidden = false
},
err_archive_url = {
message = '<code class="cs1-code">|archive-url=</code> is malformed: $1', -- $1 is error message detail
anchor = 'archive_url',
category = 'CS1 errors: archive-url',
hidden = false
},
err_arxiv_missing = {
message = '<code class="cs1-code">|arxiv=</code> required',
anchor = 'arxiv_missing',
category = 'CS1 errors: arXiv', -- same as bad arxiv
hidden = false
},
err_asintld_missing_asin = {
message = '<code class="cs1-code">|$1=</code> requires <code class="cs1-code">|asin=</code>', -- $1 is parameter name
anchor = 'asintld_missing_asin',
category = 'CS1 errors: ASIN TLD',
hidden = false
},
err_bad_arxiv = {
message = 'Check <code class="cs1-code">|arxiv=</code> value',
anchor = 'bad_arxiv',
category = 'CS1 errors: arXiv',
hidden = false
},
err_bad_asin = {
message = 'Check <code class="cs1-code">|asin=</code> value',
anchor = 'bad_asin',
category ='CS1 errors: ASIN',
hidden = false
},
err_bad_asin_tld = {
message = 'Check <code class="cs1-code">|asin-tld=</code> value',
anchor = 'bad_asin_tld',
category ='CS1 errors: ASIN TLD',
hidden = false
},
err_bad_bibcode = {
message = 'Check <code class="cs1-code">|bibcode=</code> $1', -- $1 is error message detail
anchor = 'bad_bibcode',
category = 'CS1 errors: bibcode',
hidden = false
},
err_bad_biorxiv = {
message = 'Check <code class="cs1-code">|biorxiv=</code> value',
anchor = 'bad_biorxiv',
category = 'CS1 errors: bioRxiv',
hidden = false
},
err_bad_citeseerx = {
message = 'Check <code class="cs1-code">|citeseerx=</code> value',
anchor = 'bad_citeseerx',
category = 'CS1 errors: citeseerx',
hidden = false
},
err_bad_date = {
message = 'Check date values in: $1', -- $1 is a parameter name list
anchor = 'bad_date',
category = 'CS1 errors: dates',
hidden = false
},
err_bad_doi = {
message = 'Check <code class="cs1-code">|doi=</code> value',
anchor = 'bad_doi',
category = 'CS1 errors: DOI',
hidden = false
},
err_bad_hdl = {
message = 'Check <code class="cs1-code">|hdl=</code> value',
anchor = 'bad_hdl',
category = 'CS1 errors: HDL',
hidden = false
},
err_bad_isbn = {
message = 'Check <code class="cs1-code">|isbn=</code> value: $1', -- $1 is error message detail
anchor = 'bad_isbn',
category = 'CS1 errors: ISBN',
hidden = false
},
err_bad_ismn = {
message = 'Check <code class="cs1-code">|ismn=</code> value',
anchor = 'bad_ismn',
category = 'CS1 errors: ISMN',
hidden = false
},
err_bad_issn = {
message = 'Check <code class="cs1-code">|$1issn=</code> value', -- $1 is 'e' or '' for eissn or issn
anchor = 'bad_issn',
category = 'CS1 errors: ISSN',
hidden = false
},
err_bad_jfm = {
message = 'Check <code class="cs1-code">|jfm=</code> value',
anchor = 'bad_jfm',
category = 'CS1 errors: JFM',
hidden = false
},
err_bad_jstor = {
message = 'Check <code class="cs1-code">|jstor=</code> value',
anchor = 'bad_jstor',
category = 'CS1 errors: JSTOR',
hidden = false
},
err_bad_lccn = {
message = 'Check <code class="cs1-code">|lccn=</code> value',
anchor = 'bad_lccn',
category = 'CS1 errors: LCCN',
hidden = false
},
err_bad_medrxiv = {
message = 'Check <code class="cs1-code">|medrxiv=</code> value',
anchor = 'bad_medrxiv',
category = 'CS1 errors: medRxiv',
hidden = false
},
err_bad_mr = {
message = 'Check <code class="cs1-code">|mr=</code> value',
anchor = 'bad_mr',
category = 'CS1 errors: MR',
hidden = false
},
err_bad_oclc = {
message = 'Check <code class="cs1-code">|oclc=</code> value',
anchor = 'bad_oclc',
category = 'CS1 errors: OCLC',
hidden = false
},
err_bad_ol = {
message = 'Check <code class="cs1-code">|ol=</code> value',
anchor = 'bad_ol',
category = 'CS1 errors: OL',
hidden = false
},
err_bad_osti = {
message = 'Check <code class="cs1-code">|osti=</code> value',
anchor = 'bad_osti',
category = 'CS1 errors: OSTI',
hidden = false
},
err_bad_paramlink = { -- for |title-link=, |author/editor/translator-link=, |series-link=, |episode-link=
message = 'Check <code class="cs1-code">|$1=</code> value', -- $1 is parameter name
anchor = 'bad_paramlink',
category = 'CS1 errors: parameter link',
hidden = false
},
err_bad_pmc = {
message = 'Check <code class="cs1-code">|pmc=</code> value',
anchor = 'bad_pmc',
category = 'CS1 errors: PMC',
hidden = false
},
err_bad_pmid = {
message = 'Check <code class="cs1-code">|pmid=</code> value',
anchor = 'bad_pmid',
category = 'CS1 errors: PMID',
hidden = false
},
err_bad_rfc = {
message = 'Check <code class="cs1-code">|rfc=</code> value',
anchor = 'bad_rfc',
category = 'CS1 errors: RFC',
hidden = false
},
err_bad_s2cid = {
message = 'Check <code class="cs1-code">|s2cid=</code> value',
anchor = 'bad_s2cid',
category = 'CS1 errors: S2CID',
hidden = false
},
err_bad_sbn = {
message = 'Check <code class="cs1-code">|sbn=</code> value: $1', -- $1 is error message detail
anchor = 'bad_sbn',
category = 'CS1 errors: SBN',
hidden = false
},
err_bad_ssrn = {
message = 'Check <code class="cs1-code">|ssrn=</code> value',
anchor = 'bad_ssrn',
category = 'CS1 errors: SSRN',
hidden = false
},
err_bad_url = {
message = 'Check $1 value', -- $1 is parameter name
anchor = 'bad_url',
category = 'CS1 errors: URL',
hidden = false
},
err_bad_usenet_id = {
message = 'Check <code class="cs1-code">|message-id=</code> value',
anchor = 'bad_message_id',
category = 'CS1 errors: message-id',
hidden = false
},
err_bad_zbl = {
message = 'Check <code class="cs1-code">|zbl=</code> value',
anchor = 'bad_zbl',
category = 'CS1 errors: Zbl',
hidden = false
},
err_bare_url_missing_title = {
message = '$1 missing title', -- $1 is parameter name
anchor = 'bare_url_missing_title',
category = 'CS1 errors: bare URL',
hidden = false
},
err_biorxiv_missing = {
message = '<code class="cs1-code">|biorxiv=</code> required',
anchor = 'biorxiv_missing',
category = 'CS1 errors: bioRxiv', -- same as bad bioRxiv
hidden = false
},
err_chapter_ignored = {
message = '<code class="cs1-code">|$1=</code> ignored', -- $1 is parameter name
anchor = 'chapter_ignored',
category = 'CS1 errors: chapter ignored',
hidden = false
},
err_citation_missing_title = {
message = 'Missing or empty <code class="cs1-code">|$1=</code>', -- $1 is parameter name
anchor = 'citation_missing_title',
category = 'CS1 errors: missing title',
hidden = false
},
err_citeseerx_missing = {
message = '<code class="cs1-code">|citeseerx=</code> required',
anchor = 'citeseerx_missing',
category = 'CS1 errors: citeseerx', -- same as bad citeseerx
hidden = false
},
err_cite_web_url = { -- this error applies to cite web and to cite podcast
message = 'Missing or empty <code class="cs1-code">|url=</code>',
anchor = 'cite_web_url',
category = 'CS1 errors: requires URL',
hidden = false
},
err_class_ignored = {
message = '<code class="cs1-code">|class=</code> ignored',
anchor = 'class_ignored',
category = 'CS1 errors: class',
hidden = false
},
err_contributor_ignored = {
message = '<code class="cs1-code">|contributor=</code> ignored',
anchor = 'contributor_ignored',
category = 'CS1 errors: contributor',
hidden = false
},
err_contributor_missing_required_param = {
message = '<code class="cs1-code">|contributor=</code> requires <code class="cs1-code">|$1=</code>', -- $1 is parameter name
anchor = 'contributor_missing_required_param',
category = 'CS1 errors: contributor',
hidden = false
},
err_deprecated_params = {
message = 'Cite uses deprecated parameter <code class="cs1-code">|$1=</code>', -- $1 is parameter name
anchor = 'deprecated_params',
category = 'CS1 errors: deprecated parameters',
hidden = false
},
err_disp_name = {
message = 'Invalid <code class="cs1-code">|$1=$2</code>', -- $1 is parameter name; $2 is the assigned value
anchor = 'disp_name',
category = 'CS1 errors: display-names',
hidden = false,
},
err_doibroken_missing_doi = {
message = '<code class="cs1-code">|$1=</code> requires <code class="cs1-code">|doi=</code>', -- $1 is parameter name
anchor = 'doibroken_missing_doi',
category = 'CS1 errors: DOI',
hidden = false
},
err_embargo_missing_pmc = {
message = '<code class="cs1-code">|$1=</code> requires <code class="cs1-code">|pmc=</code>', -- $1 is parameter name
anchor = 'embargo_missing_pmc',
category = 'CS1 errors: PMC embargo',
hidden = false
},
err_empty_citation = {
message = 'Empty citation',
anchor = 'empty_citation',
category = 'CS1 errors: empty citation',
hidden = false
},
err_etal = {
message = 'Explicit use of et al. in: <code class="cs1-code">|$1=</code>', -- $1 is parameter name
anchor = 'explicit_et_al',
category = 'CS1 errors: explicit use of et al.',
hidden = false
},
err_extra_text_edition = {
message = '<code class="cs1-code">|edition=</code> has extra text',
anchor = 'extra_text_edition',
category = 'CS1 errors: extra text: edition',
hidden = false,
},
err_extra_text_issue = {
message = '<code class="cs1-code">|$1=</code> has extra text', -- $1 is parameter name
anchor = 'extra_text_issue',
category = 'CS1 errors: extra text: issue',
hidden = false,
},
err_extra_text_pages = {
message = '<code class="cs1-code">|$1=</code> has extra text', -- $1 is parameter name
anchor = 'extra_text_pages',
category = 'CS1 errors: extra text: pages',
hidden = false,
},
err_extra_text_volume = {
message = '<code class="cs1-code">|$1=</code> has extra text', -- $1 is parameter name
anchor = 'extra_text_volume',
category = 'CS1 errors: extra text: volume',
hidden = false,
},
err_first_missing_last = {
message = '<code class="cs1-code">|$1=</code> missing <code class="cs1-code">|$2=</code>', -- $1 is first alias, $2 is matching last alias
anchor = 'first_missing_last',
category = 'CS1 errors: missing name', -- author, contributor, editor, interviewer, translator
hidden = false
},
err_format_missing_url = {
message = '<code class="cs1-code">|$1=</code> requires <code class="cs1-code">|$2=</code>', -- $1 is format parameter $2 is url parameter
anchor = 'format_missing_url',
category = 'CS1 errors: format without URL',
hidden = false
},
err_generic_name = {
message = '<code class="cs1-code">|$1=</code> has generic name', -- $1 is parameter name
anchor = 'generic_name',
category = 'CS1 errors: generic name',
hidden = false,
},
err_generic_title = {
message = 'Cite uses generic title',
anchor = 'generic_title',
category = 'CS1 errors: generic title',
hidden = false,
},
err_invalid_isbn_date = {
message = 'ISBN / Date incompatibility',
anchor = 'invalid_isbn_date',
category = 'CS1 errors: ISBN date',
hidden = true
},
err_invalid_param_val = {
message = 'Invalid <code class="cs1-code">|$1=$2</code>', -- $1 is parameter name $2 is parameter value
anchor = 'invalid_param_val',
category = 'CS1 errors: invalid parameter value',
hidden = false
},
err_invisible_char = {
message = '$1 in $2 at position $3', -- $1 is invisible char $2 is parameter name $3 is position number
anchor = 'invisible_char',
category = 'CS1 errors: invisible characters',
hidden = false
},
err_medrxiv_missing = {
message = '<code class="cs1-code">|medrxiv=</code> required',
anchor = 'medrxiv_missing',
category = 'CS1 errors: medRxiv', -- same as bad medRxiv
hidden = false
},
err_missing_name = {
message = 'Missing <code class="cs1-code">|$1$2=</code>', -- $1 is modified NameList; $2 is enumerator
anchor = 'missing_name',
category = 'CS1 errors: missing name', -- author, contributor, editor, interviewer, translator
hidden = false
},
err_missing_periodical = {
message = 'Cite $1 requires <code class="cs1-code">|$2=</code>', -- $1 is cs1 template name; $2 is canonical periodical parameter name for cite $1
anchor = 'missing_periodical',
category = 'CS1 errors: missing periodical',
hidden = false
},
err_missing_pipe = {
message = 'Missing pipe in: <code class="cs1-code">|$1=</code>', -- $1 is parameter name
anchor = 'missing_pipe',
category = 'CS1 errors: missing pipe',
hidden = false
},
err_missing_publisher = {
message = 'Cite $1 requires <code class="cs1-code">|$2=</code>', -- $1 is cs1 template name; $2 is canonical publisher parameter name for cite $1
anchor = 'missing_publisher',
category = 'CS1 errors: missing publisher',
hidden = false
},
err_numeric_names = {
message = '<code class="cs1-code">|$1=</code> has numeric name', -- $1 is parameter name',
anchor = 'numeric_names',
category = 'CS1 errors: numeric name',
hidden = false,
},
err_param_access_requires_param = {
message = '<code class="cs1-code">|$1-access=</code> requires <code class="cs1-code">|$1=</code>', -- $1 is parameter name
anchor = 'param_access_requires_param',
category = 'CS1 errors: param-access',
hidden = false
},
err_param_has_ext_link = {
message = 'External link in <code class="cs1-code">$1</code>', -- $1 is parameter name
anchor = 'param_has_ext_link',
category = 'CS1 errors: external links',
hidden = false
},
err_param_has_twl_url = {
message = 'Wikipedia Library link in <code class="cs1-code">$1</code>', -- $1 is parameter name
anchor = 'param_has_twl_url',
category = 'CS1 errors: URL',
hidden = false
},
err_parameter_ignored = {
message = 'Unknown parameter <code class="cs1-code">|$1=</code> ignored', -- $1 is parameter name
anchor = 'parameter_ignored',
category = 'CS1 errors: unsupported parameter',
hidden = false
},
err_parameter_ignored_suggest = {
message = 'Unknown parameter <code class="cs1-code">|$1=</code> ignored (<code class="cs1-code">|$2=</code> suggested)', -- $1 is unknown parameter $2 is suggested parameter name
anchor = 'parameter_ignored_suggest',
category = 'CS1 errors: unsupported parameter',
hidden = false
},
err_periodical_ignored = {
message = '<code class="cs1-code">|$1=</code> ignored', -- $1 is parameter name
anchor = 'periodical_ignored',
category = 'CS1 errors: periodical ignored',
hidden = false
},
err_redundant_parameters = {
message = 'More than one of $1 specified', -- $1 is error message detail
anchor = 'redundant_parameters',
category = 'CS1 errors: redundant parameter',
hidden = false
},
err_script_parameter = {
message = 'Invalid <code class="cs1-code">|$1=</code>: $2', -- $1 is parameter name $2 is script language code or error detail
anchor = 'script_parameter',
category = 'CS1 errors: script parameters',
hidden = false
},
err_ssrn_missing = {
message = '<code class="cs1-code">|ssrn=</code> required',
anchor = 'ssrn_missing',
category = 'CS1 errors: SSRN',
hidden = false
},
err_text_ignored = {
message = 'Text "$1" ignored', -- $1 is ignored text
anchor = 'text_ignored',
category = 'CS1 errors: unrecognized parameter',
hidden = false
},
err_trans_missing_title = {
message = '<code class="cs1-code">|trans-$1=</code> requires <code class="cs1-code">|$1=</code> or <code class="cs1-code">|script-$1=</code>', -- $1 is base parameter name
anchor = 'trans_missing_title',
category = 'CS1 errors: translated title',
hidden = false
},
err_param_unknown_empty = {
message = 'Cite has empty unknown parameter$1: $2', -- $1 is 's' or empty space; $2 is emty unknown param list
anchor = 'param_unknown_empty',
category = 'CS1 errors: empty unknown parameters',
hidden = false
},
err_vancouver = {
message = 'Vancouver style error: $1 in name $2', -- $1 is error detail, $2 is the nth name
anchor = 'vancouver',
category = 'CS1 errors: Vancouver style',
hidden = false
},
err_wikilink_in_url = {
message = 'URL–wikilink conflict', -- uses ndash
anchor = 'wikilink_in_url',
category = 'CS1 errors: URL–wikilink conflict', -- 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 = 'CS1 maint: archived copy as title',
hidden = true,
},
maint_bibcode = {
message = nil,
anchor = 'bibcode',
category = 'CS1 maint: 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 = 'CS1 maint: location missing publisher',
hidden = true,
},
maint_bot_unknown = {
message = nil,
anchor = 'bot:_unknown',
category = 'CS1 maint: bot: original URL status unknown',
hidden = true,
},
maint_date_auto_xlated = { -- date auto-translation not supported by en.wiki
message = nil,
anchor = 'date_auto_xlated',
category = 'CS1 maint: date auto-translated',
hidden = true,
},
maint_date_format = {
message = nil,
anchor = 'date_format',
category = 'CS1 maint: date format',
hidden = true,
},
maint_date_year = {
message = nil,
anchor = 'date_year',
category = 'CS1 maint: date and year',
hidden = true,
},
maint_doi_ignore = {
message = nil,
anchor = 'doi_ignore',
category = 'CS1 maint: ignored DOI errors',
hidden = true,
},
maint_doi_inactive = {
message = nil,
anchor = 'doi_inactive',
category = 'CS1 maint: DOI inactive',
hidden = true,
},
maint_doi_inactive_dated = {
message = nil,
anchor = 'doi_inactive_dated',
category = 'CS1 maint: DOI inactive as of $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 = 'CS1 maint: unflagged free DOI',
hidden = true,
},
maint_extra_punct = {
message = nil,
anchor = 'extra_punct',
category = 'CS1 maint: extra punctuation',
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 = 'CS1 maint: ID limit load fail',
hidden = true,
},
maint_isbn_ignore = {
message = nil,
anchor = 'ignore_isbn_err',
category = 'CS1 maint: ignored ISBN errors',
hidden = true,
},
maint_issn_ignore = {
message = nil,
anchor = 'ignore_issn',
category = 'CS1 maint: ignored ISSN errors',
hidden = true,
},
maint_jfm_format = {
message = nil,
anchor = 'jfm_format',
category = 'CS1 maint: JFM format',
hidden = true,
},
maint_location = {
message = nil,
anchor = 'location',
category = 'CS1 maint: location',
hidden = true,
},
maint_mr_format = {
message = nil,
anchor = 'mr_format',
category = 'CS1 maint: MR format',
hidden = true,
},
maint_mult_names = {
message = nil,
anchor = 'mult_names',
category = 'CS1 maint: multiple names: $1', -- $1 is '<name>s list'; gets value from special_case_translation table
hidden = true,
},
maint_numeric_names = {
message = nil,
anchor = 'numeric_names',
category = 'CS1 maint: numeric names: $1', -- $1 is '<name>s list'; gets value from special_case_translation table
hidden = true,
},
maint_others = {
message = nil,
anchor = 'others',
category = 'CS1 maint: others',
hidden = true,
},
maint_others_avm = {
message = nil,
anchor = 'others_avm',
category = 'CS1 maint: others in cite AV media (notes)',
hidden = true,
},
maint_overridden_setting = {
message = nil,
anchor = 'overridden',
category = 'CS1 maint: overridden setting',
hidden = true,
},
maint_pmc_embargo = {
message = nil,
anchor = 'embargo',
category = 'CS1 maint: PMC embargo expired',
hidden = true,
},
maint_pmc_format = {
message = nil,
anchor = 'pmc_format',
category = 'CS1 maint: PMC format',
hidden = true,
},
maint_postscript = {
message = nil,
anchor = 'postscript',
category = 'CS1 maint: postscript',
hidden = true,
},
maint_publisher_location = {
message = nil,
anchor = 'publisher_location',
category = 'CS1 maint: publisher location',
hidden = true,
},
maint_ref_duplicates_default = {
message = nil,
anchor = 'ref_default',
category = 'CS1 maint: ref duplicates default',
hidden = true,
},
maint_unknown_lang = {
message = nil,
anchor = 'unknown_lang',
category = 'CS1 maint: unrecognized language',
hidden = true,
},
maint_untitled = {
message = nil,
anchor = 'untitled',
category = 'CS1 maint: untitled periodical',
hidden = true,
},
maint_url_status = {
message = nil,
anchor = 'url_status',
category = 'CS1 maint: url-status',
hidden = true,
},
maint_year= {
message = nil,
anchor = 'year',
category = 'CS1 maint: year',
hidden = true,
},
maint_zbl = {
message = nil,
anchor = 'zbl',
category = 'CS1 maint: 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 (identifier)',
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 = 'Amazon Standard Identification Number',
redirect = 'ASIN (identifier)',
q = 'Q1753278',
label = 'ASIN',
prefix = 'https://www.amazon.',
COinS = 'url',
separator = ' ',
encode = false;
},
['BIBCODE'] = {
parameters = {'bibcode'},
link = 'Bibcode',
redirect = 'Bibcode (identifier)',
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 (identifier)',
q = 'Q19835482',
label = 'bioRxiv',
prefix = 'https://doi.org/',
COinS = 'pre', -- use prefix value
access = 'free', -- free to read
encode = true,
separator = ' ',
},
['CITESEERX'] = {
parameters = {'citeseerx'},
link = 'CiteSeerX',
redirect = 'CiteSeerX (identifier)',
q = 'Q2715061',
label = 'CiteSeerX',
prefix = 'https://citeseerx.ist.psu.edu/viewdoc/summary?doi=',
COinS = 'pre', -- use prefix value
access = 'free', -- free to read
encode = true,
separator = ' ',
},
['DOI'] = { -- Used by InternetArchiveBot
parameters = { 'doi', 'DOI'},
link = 'Digital object identifier',
redirect = 'doi (identifier)',
q = 'Q25670',
label = 'doi',
prefix = 'https://doi.org/',
COinS = 'info:doi',
separator = ':',
encode = true,
custom_access = 'doi-access',
},
['EISSN'] = {
parameters = {'eissn', 'EISSN'},
link = 'International Standard Serial Number#Electronic ISSN',
redirect = 'eISSN (identifier)',
q = 'Q46339674',
label = 'eISSN',
prefix = 'https://search.worldcat.org/issn/',
COinS = 'rft.eissn',
encode = false,
separator = ' ',
},
['HDL'] = {
parameters = { 'hdl', 'HDL' },
link = 'Handle System',
redirect = 'hdl (identifier)',
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 = 'International Standard Book Number',
redirect = 'ISBN (identifier)',
q = 'Q33057',
label = 'ISBN',
prefix = 'Special:BookSources/',
COinS = 'rft.isbn',
separator = ' ',
},
['ISMN'] = {
parameters = {'ismn', 'ISMN'},
link = 'International Standard Music Number',
redirect = 'ISMN (identifier)',
q = 'Q1666938',
label = 'ISMN',
prefix = '', -- not currently used;
COinS = nil, -- nil because we can't use pre or rft or info:
separator = ' ',
},
['ISSN'] = {
parameters = {'issn', 'ISSN'},
link = 'International Standard Serial Number',
redirect = 'ISSN (identifier)',
q = 'Q131276',
label = 'ISSN',
prefix = 'https://search.worldcat.org/issn/',
COinS = 'rft.issn',
encode = false,
separator = ' ',
},
['JFM'] = {
parameters = {'jfm', 'JFM'},
link = 'Jahrbuch über die Fortschritte der Mathematik',
redirect = 'JFM (identifier)',
q = '',
label = 'JFM',
prefix = 'https://zbmath.org/?format=complete&q=an:',
COinS = 'pre', -- use prefix value
encode = true,
separator = ' ',
},
['JSTOR'] = {
parameters = {'jstor', 'JSTOR'},
link = 'JSTOR',
redirect = 'JSTOR (identifier)',
q = 'Q1420342',
label = 'JSTOR',
prefix = 'https://www.jstor.org/stable/',
COinS = 'pre', -- use prefix value
encode = false,
separator = ' ',
custom_access = 'jstor-access',
},
['LCCN'] = {
parameters = {'lccn', 'LCCN'},
link = 'Library of Congress Control Number',
redirect = 'LCCN (identifier)',
q = 'Q620946',
label = 'LCCN',
prefix = 'https://lccn.loc.gov/',
COinS = 'info:lccn',
encode = false,
separator = ' ',
},
['MEDRXIV'] = {
parameters = {'medrxiv'},
link = 'medRxiv',
redirect = 'medRxiv (identifier)',
q = 'Q58465838',
label = 'medRxiv',
prefix = 'https://www.medrxiv.org/content/',
COinS = 'pre', -- use prefix value
access = 'free', -- free to read
encode = false,
separator = ' ',
},
['MR'] = {
parameters = {'mr', 'MR'},
link = 'Mathematical Reviews',
redirect = 'MR (identifier)',
q = 'Q211172',
label = 'MR',
prefix = 'https://mathscinet.ams.org/mathscinet-getitem?mr=',
COinS = 'pre', -- use prefix value
encode = true,
separator = ' ',
},
['OCLC'] = {
parameters = {'oclc', 'OCLC'},
link = 'OCLC',
redirect = 'OCLC (identifier)',
q = 'Q190593',
label = 'OCLC',
prefix = 'https://search.worldcat.org/oclc/',
COinS = 'info:oclcnum',
encode = true,
separator = ' ',
id_limit = id_limits_data_t.OCLC or 0,
},
['OL'] = {
parameters = { 'ol', 'OL' },
link = 'Open Library',
redirect = 'OL (identifier)',
q = 'Q1201876',
label = 'OL',
prefix = 'https://openlibrary.org/',
COinS = 'url',
separator = ' ',
encode = true,
custom_access = 'ol-access',
},
['OSTI'] = {
parameters = {'osti', 'OSTI'},
link = 'Office of Scientific and Technical Information',
redirect = 'OSTI (identifier)',
q = 'Q2015776',
label = 'OSTI',
prefix = 'https://www.osti.gov/biblio/',
COinS = 'pre', -- use prefix value
encode = true,
separator = ' ',
id_limit = id_limits_data_t.OSTI or 0,
custom_access = 'osti-access',
},
['PMC'] = {
parameters = {'pmc', 'PMC'},
link = 'PubMed Central',
redirect = 'PMC (identifier)',
q = 'Q229883',
label = 'PMC',
prefix = 'https://www.ncbi.nlm.nih.gov/pmc/articles/PMC',
suffix = '',
COinS = 'pre', -- use prefix value
encode = true,
separator = ' ',
id_limit = id_limits_data_t.PMC or 0,
access = 'free', -- free to read
},
['PMID'] = {
parameters = {'pmid', 'PMID'},
link = 'PubMed Identifier',
redirect = 'PMID (identifier)',
q = 'Q2082879',
label = 'PMID',
prefix = 'https://pubmed.ncbi.nlm.nih.gov/',
COinS = 'info:pmid',
encode = false,
separator = ' ',
id_limit = id_limits_data_t.PMID or 0,
},
['RFC'] = {
parameters = {'rfc', 'RFC'},
link = 'Request for Comments',
redirect = 'RFC (identifier)',
q = 'Q212971',
label = 'RFC',
prefix = 'https://tools.ietf.org/html/rfc',
COinS = 'pre', -- use prefix value
encode = false,
separator = ' ',
id_limit = id_limits_data_t.RFC or 0,
access = 'free', -- free to read
},
['SBN'] = {
parameters = {'sbn', 'SBN'},
link = 'Standard Book Number', -- redirect to International_Standard_Book_Number#History
redirect = 'SBN (identifier)',
label = 'SBN',
prefix = 'Special:BookSources/0-', -- prefix has leading zero necessary to make 9-digit sbn a 10-digit isbn
COinS = nil, -- nil because we can't use pre or rft or info:
separator = ' ',
},
['SSRN'] = {
parameters = {'ssrn', 'SSRN'},
link = 'Social Science Research Network',
redirect = 'SSRN (identifier)',
q = 'Q7550801',
label = 'SSRN',
prefix = 'https://papers.ssrn.com/sol3/papers.cfm?abstract_id=',
COinS = 'pre', -- use prefix value
encode = true,
separator = ' ',
id_limit = id_limits_data_t.SSRN or 0,
custom_access = 'ssrn-access',
},
['S2CID'] = {
parameters = {'s2cid', 'S2CID'},
link = 'Semantic Scholar',
redirect = 'S2CID (identifier)',
q = 'Q22908627',
label = 'S2CID',
prefix = 'https://api.semanticscholar.org/CorpusID:',
COinS = 'pre', -- use prefix value
encode = false,
separator = ' ',
id_limit = id_limits_data_t.S2CID or 0,
custom_access = 's2cid-access',
},
['USENETID'] = {
parameters = {'message-id'},
link = 'Usenet',
redirect = 'Usenet (identifier)',
q = 'Q193162',
label = 'Usenet:',
prefix = 'news:',
encode = false,
COinS = 'pre', -- use prefix value
separator = ' ',
},
['ZBL'] = {
parameters = {'zbl', 'ZBL' },
link = 'Zentralblatt MATH',
redirect = 'Zbl (identifier)',
q = 'Q190269',
label = 'Zbl',
prefix = 'https://zbmath.org/?format=complete&q=an:',
COinS = 'pre', -- use prefix value
encode = true,
separator = ' ',
},
}
--[[--------------------------< E X P O R T S >---------------------------------
]]
return {
use_identifier_redirects = use_identifier_redirects, -- booleans defined in the settings at the top of this module
local_lang_cat_enable = local_lang_cat_enable,
date_name_auto_xlate_enable = date_name_auto_xlate_enable,
date_digit_auto_xlate_enable = date_digit_auto_xlate_enable,
enable_sort_keys = enable_sort_keys,
-- tables and variables created when this module is loaded
global_df = get_date_format (), -- this line can be replaced with "global_df = 'dmy-all'," to have all dates auto translated to dmy format.
global_cs1_config_t = global_cs1_config_t, -- global settings from {{cs1 config}}
punct_skip = build_skip_table (punct_skip, punct_meta_params),
url_skip = build_skip_table (url_skip, url_meta_params),
known_free_doi_registrants_t = build_free_doi_registrants_table(),
id_limits_data_load_fail = id_limits_data_load_fail, -- true when commons tabular identifier-limit data fails to load
name_space_sort_keys = name_space_sort_keys,
aliases = aliases,
special_case_translation = special_case_translation,
date_names = date_names,
err_msg_supl = err_msg_supl,
error_conditions = error_conditions,
editor_markup_patterns = editor_markup_patterns,
et_al_patterns = et_al_patterns,
extended_registrants_t = extended_registrants_t,
id_handlers = id_handlers,
keywords_lists = keywords_lists,
keywords_xlate = keywords_xlate,
stripmarkers = stripmarkers,
invisible_chars = invisible_chars,
invisible_defs = invisible_defs,
indic_script = indic_script,
emoji_t = emoji_t,
maint_cats = maint_cats,
messages = messages,
presentation = presentation,
prop_cats = prop_cats,
script_lang_codes = script_lang_codes,
lang_tag_remap = lang_tag_remap,
lang_name_remap = lang_name_remap,
this_wiki_code = this_wiki_code,
title_types = title_types,
uncategorized_namespaces = uncategorized_namespaces_t,
uncategorized_subpages = uncategorized_subpages,
templates_using_volume = templates_using_volume,
templates_using_issue = templates_using_issue,
templates_not_using_page = templates_not_using_page,
vol_iss_pg_patterns = vol_iss_pg_patterns,
single_letter_2nd_lvl_domains_t = single_letter_2nd_lvl_domains_t,
inter_wiki_map = inter_wiki_map,
mw_languages_by_tag_t = mw_languages_by_tag_t,
mw_languages_by_name_t = mw_languages_by_name_t,
citation_class_map_t = citation_class_map_t,
citation_issue_t = citation_issue_t,
citation_no_volume_t = citation_no_volume_t,
}
8ilknlj3na7t4ipyqyyt1zokyb2n9zr
886428
886425
2025-06-14T08:26:50Z
KartikMistry
10383
CS1 errors: dates
886428
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 = {'/[Ss]andbox', '/[Tt]estcases', '/[^/]*[Ll]og', '/[Aa]rchive'}; -- 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'] = '$1 માંથી $2 પર સંગ્રહિત',
['archived-live'] = 'મૂળ $1 માંથી $2 પર સંગ્રહિત',
['archived-unfit'] = 'મૂળમાંથી અહીં સંગ્રહિત ',
['archived'] = 'સંગ્રહિત',
['by'] = 'વડે', -- contributions to authored works: introduction, foreword, afterword
['cartography'] = '$1 વડે નકશો',
['editor'] = 'સંપાદક',
['editors'] = 'સંપાદકો',
['edition'] = '($1 આવૃત્તિ)',
['episode'] = 'પ્રકરણ $1',
['et al'] = 'et al.',
['in'] = 'In', -- edited works
['inactive'] = 'અસક્રિય',
['inset'] = '$1 inset',
['interview'] = '$1 દ્વારા ઇન્ટરવ્યુ',
['mismatch'] = '<code class="cs1-code">|$1=</code> / <code class="cs1-code">|$2=</code> mismatch', -- $1 is year param name; $2 is date param name
['newsgroup'] = '[[Usenet newsgroup|Newsgroup]]: $1',
['notitle'] = 'શીર્ષક નથી', -- for |title=(()) and (in the future) |title=none
['original'] = 'મૂળ',
['origdate'] = ' [$1]',
['published'] = ' (પ્રકાશિત $1)',
['retrieved'] = 'મેળવેલ $1',
['season'] = 'શ્રેણી $1',
['section'] = '§ $1',
['sections'] = '§§ $1',
['series'] = '$1 $2', -- $1 is sepc, $2 is series
['seriesnum'] = 'શ્રેણી $1',
['translated'] = '$1 વડે અનુવાદિત',
['type'] = ' ($1)', -- for titletype
['written'] = '$1 વડે લખાયેલ',
['vol'] = '$1 ખંડ $2', -- $1 is sepc; bold journal style volume is in presentation{}
['vol-no'] = '$1 ખંડ $2, ક્રમાંક $3', -- sepc, volume, issue (alternatively insert $1 after $2, but then we'd also have to change capitalization)
['issue'] = '$1 ક્રમાંક $2', -- $1 is sepc
['art'] = '$1 Art. $2', -- $1 is sepc; for {{cite conference}} only
['vol-art'] = '$1 ખંડ. $2, art. $3', -- sepc, volume, article-number; for {{cite conference}} only
['j-vol'] = '$1 $2', -- sepc, volume; bold journal volume is in presentation{}
['j-issue'] = ' ($1)',
['j-article-num'] = ' $1', -- TODO: any punctuation here? static text?
['nopp'] = '$1 $2'; -- page(s) without prefix; $1 is sepc
['p-prefix'] = "$1 p. $2", -- $1 is sepc
['pp-prefix'] = "$1 pp. $2", -- $1 is sepc
['j-page(s)'] = ': $1', -- same for page and pages
['sheet'] = '$1 Sheet $2', -- $1 is sepc
['sheets'] = '$1 Sheets $2', -- $1 is sepc
['j-sheet'] = ': Sheet $1',
['j-sheets'] = ': Sheets $1',
['language'] = '($1માં)',
['via'] = " – $1 દ્વારા",
['event'] = 'ઘટના આ સમયે બની',
['minutes'] = 'મિનિટમાં',
-- Determines the location of the help page
['help page link'] = 'Help:CS1 errors',
['help page label'] = 'મદદ',
-- categories
['cat wikilink'] = '[[Category:$1]]', -- $1 is the category name
['cat wikilink sk'] = '[[Category:$1|$2]]', -- $1 is the category name; $2 is namespace sort key
[':cat wikilink'] = '[[:Category:$1|link]]', -- category name as maintenance message wikilink; $1 is the category name
-- Internal errors (should only occur if configuration is bad)
['undefined_error'] = 'Called with an undefined error condition',
['unknown_ID_key'] = 'Unrecognized ID key: ', -- an ID key in id_handlers not found in ~/Identifiers func_map{}
['unknown_ID_access'] = 'Unrecognized ID access keyword: ', -- an ID access keyword in id_handlers not found in keywords_lists['id-access']{}
['unknown_argument_map'] = 'Argument map not defined for this variable',
['bare_url_no_origin'] = 'Bare URL found but origin indicator is nil or empty',
['warning_msg_e'] = '<span style="color:#d33">One or more <code style="color: inherit; background: inherit; border: none; padding: inherit;">{{$1}}</code> templates have errors</span>; messages may be hidden ([[Help:CS1_errors#Controlling_error_message_display|help]]).'; -- $1 is template link
['warning_msg_m'] = '<span style="color:#3a3">One or more <code style="color: inherit; background: inherit; border: none; padding: inherit;">{{$1}}</code> templates have maintenance messages</span>; messages may be hidden ([[Help:CS1_errors#Controlling_error_message_display|help]]).'; -- $1 is template link
}
--[[--------------------------< C I T A T I O N _ C L A S S _ M A P >------------------------------------------
this table maps the value assigned to |CitationClass= in the cs1|2 templates to the canonical template name when
the value assigned to |CitationClass= is different from the canonical template name. |CitationClass= values are
used as class attributes in the <cite> tag that encloses the citation so these names may not contain spaces while
the canonical template name may. These names are used in warning_msg_e and warning_msg_m to create links to the
template's documentation when an article is displayed in preview mode.
Most cs1|2 template |CitationClass= values at en.wiki match their canonical template names so are not listed here.
]]
local citation_class_map_t = { -- TODO: if kept, these and all other config.CitationClass 'names' require some sort of i18n
['arxiv'] = 'arXiv',
['audio-visual'] = 'AV media',
['AV-media-notes'] = 'AV media notes',
['biorxiv'] = 'bioRxiv',
['citeseerx'] = 'CiteSeerX',
['encyclopaedia'] = 'encyclopedia',
['mailinglist'] = 'mailing list',
['medrxiv'] = 'medRxiv',
['pressrelease'] = 'press release',
['ssrn'] = 'SSRN',
['techreport'] = 'tech report',
}
--[=[-------------------------< E T _ A L _ P A T T E R N S >--------------------------------------------------
This table provides Lua patterns for the phrase "et al" and variants in name text
(author, editor, etc.). The main module uses these to identify and emit the 'etal' message.
]=]
local et_al_patterns = {
"[;,]? *[\"']*%f[%a][Ee][Tt]%.? *[Aa][Ll][%.;,\"']*$", -- variations on the 'et al' theme
"[;,]? *[\"']*%f[%a][Ee][Tt]%.? *[Aa][Ll][Ii][AaIi][Ee]?[%.;,\"']*$", -- variations on the 'et alia', 'et alii' and 'et aliae' themes (false positive 'et aliie' unlikely to match)
"[;,]? *%f[%a]and [Oo]thers", -- an alternative to et al.
"%[%[ *[Ee][Tt]%.? *[Aa][Ll]%.? *%]%]", -- a wikilinked form
"%(%( *[Ee][Tt]%.? *[Aa][Ll]%.? *%)%)", -- a double-bracketed form (to counter partial removal of ((...)) syntax)
"[%(%[] *[Ee][Tt]%.? *[Aa][Ll]%.? *[%)%]]", -- a bracketed form
}
--[[--------------------------< P R E S E N T A T I O N >------------------------
Fixed presentation markup. Originally part of citation_config.messages it has
been moved into its own, more semantically correct place.
]]
local presentation =
{
-- .citation-comment class is specified at Help:CS1_errors#Controlling_error_message_display
['hidden-error'] = '<span class="cs1-hidden-error citation-comment">$1</span>',
['visible-error'] = '<span class="cs1-visible-error citation-comment">$1</span>',
['hidden-maint'] = '<span class="cs1-maint citation-comment">$1</span>',
['accessdate'] = '<span class="reference-accessdate">$1$2</span>', -- to allow editors to hide accessdate using personal CSS
['bdi'] = '<bdi$1>$2</bdi>', -- bidirectional isolation used with |script-title= and the like
['cite'] = '<cite class="$1">$2</cite>'; -- for use when citation does not have a namelist and |ref= not set so no id="..." attribute
['cite-id'] = '<cite id="$1" class="$2">$3</cite>'; -- for use when when |ref= is set or when citation has a namelist
['format'] = ' <span class="cs1-format">($1)</span>', -- for |format=, |chapter-format=, etc.
['interwiki'] = ' <span class="cs1-format">[in $1]</span>', -- for interwiki-language-linked author, editor, etc
['interproj'] = ' <span class="cs1-format">[at $1]</span>', -- for interwiki-project-linked author, editor, etc (:d: and :s: supported; :w: ignored)
-- various access levels, for |access=, |doi-access=, |arxiv=, ...
-- narrow no-break space   may work better than nowrap CSS. Or not? Browser support?
['ext-link-access-signal'] = '<span class="$1" title="$2">$3</span>', -- external link with appropriate lock icon
['free'] = {class='id-lock-free', title='Freely accessible'}, -- classes defined in Module:Citation/CS1/styles.css
['registration'] = {class='id-lock-registration', title='Free registration required'},
['limited'] = {class='id-lock-limited', title='Free access subject to limited trial, subscription normally required'},
['subscription'] = {class='id-lock-subscription', title='Paid subscription required'},
['interwiki-icon'] = '<span class="$1" title="$2">$3</span>',
['class-wikisource'] = 'cs1-ws-icon',
['italic-title'] = "''$1''",
['kern-left'] = '<span class="cs1-kern-left"></span>$1', -- spacing to use when title contains leading single or double quote mark
['kern-right'] = '$1<span class="cs1-kern-right"></span>', -- spacing to use when title contains trailing single or double quote mark
['nowrap1'] = '<span class="nowrap">$1</span>', -- for nowrapping an item: <span ...>yyyy-mm-dd</span>
['nowrap2'] = '<span class="nowrap">$1</span> $2', -- for nowrapping portions of an item: <span ...>dd mmmm</span> yyyy (note white space)
['ocins'] = '<span title="$1" class="Z3988"></span>',
['parameter'] = '<code class="cs1-code">|$1=</code>',
['ps_cs1'] = '.'; -- CS1 style postscript (terminal) character
['ps_cs2'] = ''; -- CS2 style postscript (terminal) character (empty string)
['quoted-text'] = '<q>$1</q>', -- for wrapping |quote= content
['quoted-title'] = '"$1"',
['sep_cs1'] = '.', -- CS1 element separator
['sep_cs2'] = ',', -- CS2 separator
['sep_nl'] = ';', -- CS1|2 style name-list separator between names is a semicolon
['sep_nl_and'] = ' and ', -- used as last nl sep when |name-list-style=and and list has 2 items
['sep_nl_end'] = '; and ', -- used as last nl sep when |name-list-style=and and list has 3+ names
['sep_name'] = ', ', -- CS1|2 style last/first separator is <comma><space>
['sep_nl_vanc'] = ',', -- Vancouver style name-list separator between authors is a comma
['sep_name_vanc'] = ' ', -- Vancouver style last/first separator is a space
['sep_list'] = ', ', -- used for |language= when list has 3+ items except for last sep which uses sep_list_end
['sep_list_pair'] = ' and ', -- used for |language= when list has 2 items
['sep_list_end'] = ', and ', -- used as last list sep for |language= when list has 3+ items
['trans-italic-title'] = "[''$1'']",
['trans-quoted-title'] = "[$1]", -- for |trans-title= and |trans-quote=
['vol-bold'] = '$1 <b>$2</b>', -- sepc, volume; for bold journal cites; for other cites ['vol'] in messages{}
}
--[[--------------------------< A L I A S E S >---------------------------------
Aliases table for commonly passed parameters.
Parameter names on the right side in the assignments in this table must have been
defined in the Whitelist before they will be recognized as valid parameter names
]]
local aliases = {
['AccessDate'] = {'access-date', 'accessdate'}, -- Used by InternetArchiveBot
['Agency'] = 'agency',
['ArchiveDate'] = {'archive-date', 'archivedate'}, -- Used by InternetArchiveBot
['ArchiveFormat'] = 'archive-format',
['ArchiveURL'] = {'archive-url', 'archiveurl'}, -- Used by InternetArchiveBot
['ArticleNumber'] = 'article-number',
['ASINTLD'] = 'asin-tld',
['At'] = 'at', -- Used by InternetArchiveBot
['Authors'] = {'people', 'credits'},
['BookTitle'] = {'book-title', 'booktitle'},
['Cartography'] = 'cartography',
['Chapter'] = {'chapter', 'contribution', 'entry', 'article', 'section'},
['ChapterFormat'] = {'chapter-format', 'contribution-format', 'entry-format',
'article-format', 'section-format'};
['ChapterURL'] = {'chapter-url', 'contribution-url', 'entry-url', 'article-url', 'section-url'}, -- Used by InternetArchiveBot
['ChapterUrlAccess'] = {'chapter-url-access', 'contribution-url-access',
'entry-url-access', 'article-url-access', 'section-url-access'}, -- Used by InternetArchiveBot
['Class'] = 'class', -- cite arxiv and arxiv identifier
['Collaboration'] = 'collaboration',
['Conference'] = {'conference', 'event'},
['ConferenceFormat'] = 'conference-format',
['ConferenceURL'] = 'conference-url', -- Used by InternetArchiveBot
['Date'] = {'date', 'air-date', 'airdate'}, -- air-date and airdate for cite episode and cite serial only
['Degree'] = 'degree',
['DF'] = 'df',
['DisplayAuthors'] = {'display-authors', 'display-subjects'},
['DisplayContributors'] = 'display-contributors',
['DisplayEditors'] = 'display-editors',
['DisplayInterviewers'] = 'display-interviewers',
['DisplayTranslators'] = 'display-translators',
['Docket'] = 'docket',
['DoiBroken'] = 'doi-broken-date',
['Edition'] = 'edition',
['Embargo'] = 'pmc-embargo-date',
['Encyclopedia'] = {'encyclopedia', 'encyclopaedia', 'dictionary'}, -- cite encyclopedia only
['Episode'] = 'episode', -- cite serial only TODO: make available to cite episode?
['Format'] = 'format',
['ID'] = {'id', 'ID'},
['Inset'] = 'inset',
['Issue'] = {'issue', 'number'},
['Language'] = {'language', 'lang'},
['MailingList'] = {'mailing-list', 'mailinglist'}, -- cite mailing list only
['Map'] = 'map', -- cite map only
['MapFormat'] = 'map-format', -- cite map only
['MapURL'] = {'map-url', 'mapurl'}, -- cite map only -- Used by InternetArchiveBot
['MapUrlAccess'] = 'map-url-access', -- cite map only -- Used by InternetArchiveBot
['Minutes'] = 'minutes',
['Mode'] = 'mode',
['NameListStyle'] = 'name-list-style',
['Network'] = 'network',
['Newsgroup'] = 'newsgroup', -- cite newsgroup only
['NoPP'] = {'no-pp', 'nopp'},
['NoTracking'] = {'no-tracking', 'template-doc-demo'},
['Number'] = 'number', -- this case only for cite techreport
['OrigDate'] = {'orig-date', 'orig-year', 'origyear'},
['Others'] = 'others',
['Page'] = {'page', 'p'}, -- Used by InternetArchiveBot
['Pages'] = {'pages', 'pp'}, -- Used by InternetArchiveBot
['Periodical'] = {'journal', 'magazine', 'newspaper', 'periodical', 'website', 'work'},
['Place'] = {'place', 'location'},
['PostScript'] = 'postscript',
['PublicationDate'] = {'publication-date', 'publicationdate'},
['PublicationPlace'] = {'publication-place', 'publicationplace'},
['PublisherName'] = {'publisher', 'institution'},
['Quote'] = {'quote', 'quotation'},
['QuotePage'] = 'quote-page',
['QuotePages'] = 'quote-pages',
['Ref'] = 'ref',
['Scale'] = 'scale',
['ScriptChapter'] = {'script-chapter', 'script-contribution', 'script-entry',
'script-article', 'script-section'},
['ScriptEncyclopedia'] = {'script-encyclopedia', 'script-encyclopaedia'}, -- cite encyclopedia only
['ScriptMap'] = 'script-map',
['ScriptPeriodical'] = {'script-journal', 'script-magazine', 'script-newspaper',
'script-periodical', 'script-website', 'script-work'},
['ScriptQuote'] = 'script-quote',
['ScriptTitle'] = 'script-title', -- Used by InternetArchiveBot
['Season'] = 'season',
['Sections'] = 'sections', -- cite map only
['Series'] = {'series', 'version'},
['SeriesLink'] = {'series-link', 'serieslink'},
['SeriesNumber'] = {'series-number', 'series-no'},
['Sheet'] = 'sheet', -- cite map only
['Sheets'] = 'sheets', -- cite map only
['Station'] = 'station',
['Time'] = 'time',
['TimeCaption'] = 'time-caption',
['Title'] = 'title', -- Used by InternetArchiveBot
['TitleLink'] = {'title-link', 'episode-link', 'episodelink'}, -- Used by InternetArchiveBot
['TitleNote'] = {'title-note', 'department'},
['TitleType'] = {'type', 'medium'},
['TransChapter'] = {'trans-article', 'trans-chapter', 'trans-contribution',
'trans-entry', 'trans-section'},
['Transcript'] = 'transcript',
['TranscriptFormat'] = 'transcript-format',
['TranscriptURL'] = 'transcript-url', -- Used by InternetArchiveBot
['TransEncyclopedia'] = {'trans-encyclopedia', 'trans-encyclopaedia'}, -- cite encyclopedia only
['TransMap'] = 'trans-map', -- cite map only
['TransPeriodical'] = {'trans-journal', 'trans-magazine', 'trans-newspaper',
'trans-periodical', 'trans-website', 'trans-work'},
['TransQuote'] = 'trans-quote',
['TransTitle'] = 'trans-title', -- Used by InternetArchiveBot
['URL'] = {'url', 'URL'}, -- Used by InternetArchiveBot
['UrlAccess'] = 'url-access', -- Used by InternetArchiveBot
['UrlStatus'] = 'url-status', -- Used by InternetArchiveBot
['Vauthors'] = 'vauthors',
['Veditors'] = 'veditors',
['Via'] = 'via',
['Volume'] = 'volume',
['Year'] = 'year',
['AuthorList-First'] = {"first#", "author-first#", "author#-first", "author-given#", "author#-given",
"subject-first#", "subject#-first", "subject-given#", "subject#-given",
"given#"},
['AuthorList-Last'] = {"last#", "author-last#", "author#-last", "author-surname#", "author#-surname",
"subject-last#", "subject#-last", "subject-surname#", "subject#-surname",
"author#", 'host#', "subject#", "surname#"},
['AuthorList-Link'] = {"author-link#", "author#-link", "subject-link#",
"subject#-link", "authorlink#", "author#link"},
['AuthorList-Mask'] = {"author-mask#", "author#-mask", "subject-mask#", "subject#-mask"},
['ContributorList-First'] = {'contributor-first#', 'contributor#-first',
'contributor-given#', 'contributor#-given'},
['ContributorList-Last'] = {'contributor-last#', 'contributor#-last',
'contributor-surname#', 'contributor#-surname', 'contributor#'},
['ContributorList-Link'] = {'contributor-link#', 'contributor#-link'},
['ContributorList-Mask'] = {'contributor-mask#', 'contributor#-mask'},
['EditorList-First'] = {"editor-first#", "editor#-first", "editor-given#", "editor#-given"},
['EditorList-Last'] = {"editor-last#", "editor#-last", "editor-surname#",
"editor#-surname", "editor#"},
['EditorList-Link'] = {"editor-link#", "editor#-link"},
['EditorList-Mask'] = {"editor-mask#", "editor#-mask"},
['InterviewerList-First'] = {'interviewer-first#', 'interviewer#-first',
'interviewer-given#', 'interviewer#-given'},
['InterviewerList-Last'] = {'interviewer-last#', 'interviewer#-last',
'interviewer-surname#', 'interviewer#-surname', 'interviewer#'},
['InterviewerList-Link'] = {'interviewer-link#', 'interviewer#-link'},
['InterviewerList-Mask'] = {'interviewer-mask#', 'interviewer#-mask'},
['TranslatorList-First'] = {'translator-first#', 'translator#-first',
'translator-given#', 'translator#-given'},
['TranslatorList-Last'] = {'translator-last#', 'translator#-last',
'translator-surname#', 'translator#-surname', 'translator#'},
['TranslatorList-Link'] = {'translator-link#', 'translator#-link'},
['TranslatorList-Mask'] = {'translator-mask#', 'translator#-mask'},
}
--[[--------------------------< P U N C T _ S K I P >---------------------------
builds a table of parameter names that the extraneous terminal punctuation check should not check.
]]
local punct_meta_params = { -- table of aliases[] keys (meta parameters); each key has a table of parameter names for a value
'BookTitle', 'Chapter', 'ScriptChapter', 'ScriptTitle', 'Title', 'TransChapter', 'Transcript', 'TransMap', 'TransTitle', -- title-holding parameters
'AuthorList-Mask', 'ContributorList-Mask', 'EditorList-Mask', 'InterviewerList-Mask', 'TranslatorList-Mask', -- name-list mask may have name separators
'PostScript', 'Quote', 'ScriptQuote', 'TransQuote', 'Ref', -- miscellaneous
'ArchiveURL', 'ChapterURL', 'ConferenceURL', 'MapURL', 'TranscriptURL', 'URL', -- URL-holding parameters
}
local url_meta_params = { -- table of aliases[] keys (meta parameters); each key has a table of parameter names for a value
'ArchiveURL', 'ChapterURL', 'ConferenceURL', 'ID', 'MapURL', 'TranscriptURL', 'URL', -- parameters allowed to hold urls
'Page', 'Pages', 'At', 'QuotePage', 'QuotePages', -- insource locators allowed to hold urls
}
local function build_skip_table (skip_t, meta_params)
for _, meta_param in ipairs (meta_params) do -- for each meta parameter key
local params = aliases[meta_param]; -- get the parameter or the table of parameters associated with the meta parameter name
if 'string' == type (params) then
skip_t[params] = 1; -- just a single parameter
else
for _, param in ipairs (params) do -- get the parameter name
skip_t[param] = 1; -- add the parameter name to the skip table
local count;
param, count = param:gsub ('#', ''); -- remove enumerator marker from enumerated parameters
if 0 ~= count then -- if removed
skip_t[param] = 1; -- add param name without enumerator marker
end
end
end
end
return skip_t;
end
local punct_skip = {};
local url_skip = {};
--[[--------------------------< S I N G L E - L E T T E R S E C O N D - L E V E L D O M A I N S >----------
this is a list of tlds that are known to have single-letter second-level domain names. This list does not include
ccTLDs which are accepted in is_domain_name().
]]
local single_letter_2nd_lvl_domains_t = {'cash', 'company', 'foundation', 'media', 'org', 'today'};
--[[-----------< S P E C I A L C A S E T R A N S L A T I O N S >------------
This table is primarily here to support internationalization. Translations in
this table are used, for example, when an error message, category name, etc.,
is extracted from the English alias key. There may be other cases where
this translation table may be useful.
]]
local is_Latn = 'A-Za-z\195\128-\195\150\195\152-\195\182\195\184-\198\191\199\132-\201\143\225\184\128-\225\187\191';
local special_case_translation = {
['AuthorList'] = 'authors list', -- used to assemble maintenance category names
['ContributorList'] = 'contributors list', -- translation of these names plus translation of the base maintenance category names in maint_cats{} table below
['EditorList'] = 'editors list', -- must match the names of the actual categories
['InterviewerList'] = 'interviewers list', -- this group or translations used by name_has_ed_markup() and name_has_mult_names()
['TranslatorList'] = 'translators list',
-- 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'] = nil, -- 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'] = nil},
{['en'] = {'page not found', true}, ['local'] = nil},
{['en'] = {'subscribe to read', true}, ['local'] = nil},
{['en'] = {'^[%(%[{<]?unknown[>}%]%)]?$', false}, ['local'] = nil},
{['en'] = {'website is for sale', true}, ['local'] = nil},
{['en'] = {'^404', false}, ['local'] = nil},
{['en'] = {'error[ %-]404', false}, ['local'] = nil},
{['en'] = {'internet archive wayback machine', true}, ['local'] = nil},
{['en'] = {'log into facebook', true}, ['local'] = nil},
{['en'] = {'login • instagram', true}, ['local'] = nil},
{['en'] = {'redirecting...', true}, ['local'] = nil},
{['en'] = {'usurped title', true}, ['local'] = nil}, -- 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'] = nil},
},
['reject'] = {
{['en'] = {'about us', true}, ['local'] = nil},
{['en'] = {'%f[%a][Aa]dvisor%f[%A]', false}, ['local'] = nil},
{['en'] = {'allmusic', true}, ['local'] = nil},
{['en'] = {'%f[%a][Aa]uthor%f[%A]', false}, ['local'] = nil},
{['en'] = {'^[Bb]ureau$', false}, ['local'] = nil},
{['en'] = {'business', true}, ['local'] = nil},
{['en'] = {'cnn', true}, ['local'] = nil},
{['en'] = {'collaborator', true}, ['local'] = nil},
{['en'] = {'^[Cc]ompany$', false}, ['local'] = nil},
{['en'] = {'contributor', true}, ['local'] = nil},
{['en'] = {'contact us', true}, ['local'] = nil},
{['en'] = {'correspondent', true}, ['local'] = nil},
{['en'] = {'^[Dd]esk$', false}, ['local'] = nil},
{['en'] = {'directory', true}, ['local'] = nil},
{['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'] = nil},
{['en'] = {'%f[%a][Ee]ditors?%f[%A]', false}, ['local'] = nil},
{['en'] = {'%f[%a][Ee]mail%f[%A]', false}, ['local'] = nil},
{['en'] = {'facebook', true}, ['local'] = nil},
{['en'] = {'google', true}, ['local'] = nil},
{['en'] = {'^[Gg]roup$', false}, ['local'] = nil},
{['en'] = {'home page', true}, ['local'] = nil},
{['en'] = {'^[Ii]nc%.?$', false}, ['local'] = nil},
{['en'] = {'instagram', true}, ['local'] = nil},
{['en'] = {'interviewer', true}, ['local'] = nil},
{['en'] = {'^[Ll]imited$', false}, ['local'] = nil},
{['en'] = {'linkedIn', true}, ['local'] = nil},
{['en'] = {'^[Nn]ews$', false}, ['local'] = nil},
{['en'] = {'[Nn]ews[ %-]?[Rr]oom', false}, ['local'] = nil},
{['en'] = {'pinterest', true}, ['local'] = nil},
{['en'] = {'policy', true}, ['local'] = nil},
{['en'] = {'privacy', true}, ['local'] = nil},
{['en'] = {'reuters', true}, ['local'] = nil},
{['en'] = {'translator', true}, ['local'] = nil},
{['en'] = {'tumblr', true}, ['local'] = nil},
{['en'] = {'twitter', true}, ['local'] = nil},
{['en'] = {'site name', true}, ['local'] = nil},
{['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'] = nil},
{['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'] = {['જાન્યુઆરી'] = 1, ['ફેબ્રુઆરી'] = 2, ['માર્ચ'] = 3, ['એપ્રિલ'] = 4, ['મે'] = 5, ['જૂન'] = 6, ['જુલાઇ'] = 7, ['ઓગસ્ટ'] = 8, ['સપ્ટેમ્બર'] = 9, ['ઓક્ટોબર'] = 10, ['નવેમ્બર'] = 11, ['ડિસેમ્બર'] = 12},
['short'] = {['જાન'] = 1, ['ફેબ્રુ'] = 2, ['માર્ચ'] = 3, ['એપ્રિ'] = 4, ['મે'] = 5, ['જૂન'] = 6, ['જુલ'] = 7, ['ઓગ'] = 8, ['સપ્ટે'] = 9, ['ઓક્ટ'] = 10, ['નવે'] = 11, ['ડિસે'] = 12},
['quarter'] = {['First Quarter'] = 33, ['Second Quarter'] = 34, ['Third Quarter'] = 35, ['Fourth Quarter'] = 36},
['season'] = {['શિયાળો'] = 24, ['વસંત'] = 21, ['ઉનાળો'] = 22, ['પાનખર'] = 23, ['શિશિર'] = 23},
['named'] = {['ઇસ્ટર'] = 98, ['ક્રિસમસ'] = 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'] = '૦', ['1'] = '૧', ['2'] = '૨', ['3'] = '૩', ['4'] = '૪', ['5'] = '૫', ['6'] = '૬', ['7'] = '૭', ['8'] = '૮', ['9'] = '૯'}, -- used to convert local language digits to Western 0-9
['xlate_digits'] = {},
}
if local_date_names_from_mediawiki then -- if fetching local month names from MediaWiki is enabled
local long_t = {};
local short_t = {};
for i=1, 12 do -- loop 12x and
local name = lang_obj:formatDate('F', '2022-' .. i .. '-1'); -- get long month name for each i
long_t[name] = i; -- save it
name = lang_obj:formatDate('M', '2022-' .. i .. '-1'); -- get short month name for each i
short_t[name] = i; -- save it
end
date_names['local']['long'] = long_t; -- write the long table – overwrites manual translation
date_names['local']['short'] = short_t; -- write the short table – overwrites manual translation
end
-- create inverted date-name tables for reformatting and/or translation
for _, invert_t in pairs {{'long', 'inv_local_long'}, {'short', 'inv_local_short'}, {'quarter', 'inv_local_quarter'}, {'season', 'inv_local_season'}, {'named', 'inv_local_named'}} do
for name, i in pairs (date_names['local'][invert_t[1]]) do -- this table is ['name'] = i
date_names[invert_t[2]][i] = name; -- invert to get [i] = 'name' for conversions from ymd
end
end
if local_digits_from_mediawiki then -- if fetching local digits from MediaWiki is enabled
local digits_t = {};
for i=0, 9 do -- loop 10x and
digits_t [lang_obj:formatNum (i)] = tostring (i); -- format the loop indexer as local lang table index and assign loop indexer (a string) as the value
end
date_names['local_digits'] = digits_t;
end
for ld, ed in pairs (date_names.local_digits) do -- make a digit translation table for simple date translation from en to local language using local_digits table
date_names.xlate_digits [ed] = ld; -- en digit becomes index with local digit as the value
end
local df_template_patterns = { -- table of redirects to {{Use dmy dates}} and {{Use mdy dates}}
'{{ *[Uu]se +(dmy) +dates *[|}]', -- 1159k -- sorted by approximate transclusion count
'{{ *[Uu]se +(mdy) +dates *[|}]', -- 212k
'{{ *[Uu]se +(MDY) +dates *[|}]', -- 788
'{{ *[Uu]se +(DMY) +dates *[|}]', -- 343
'{{ *([Mm]dy) *[|}]', -- 176
'{{ *[Uu]se *(dmy) *[|}]', -- 156 + 18
'{{ *[Uu]se *(mdy) *[|}]', -- 149 + 11
'{{ *([Dd]my) *[|}]', -- 56
'{{ *[Uu]se +(MDY) *[|}]', -- 5
'{{ *([Dd]MY) *[|}]', -- 3
'{{ *[Uu]se(mdy)dates *[|}]', -- 1
'{{ *[Uu]se +(DMY) *[|}]', -- 0
'{{ *([Mm]DY) *[|}]', -- 0
}
local title_object = mw.title.getCurrentTitle();
local content; -- done this way so that unused templates appear in unused-template-reports; self-transcluded makes them look like they are used
if 10 ~= title_object.namespace then -- all namespaces except Template
content = title_object:getContent() or ''; -- get the content of the article or ''; new pages edited w/ve do not have 'content' until saved; ve does not preview; phab:T221625
end
local function get_date_format ()
if not content then -- nil content when we're in template
return nil; -- auto-formatting does not work in Template space so don't set global_df
end
for _, pattern in ipairs (df_template_patterns) do -- loop through the patterns looking for {{Use dmy dates}} or {{Use mdy dates}} or any of their redirects
local start, _, match = content:find(pattern); -- match is the three letters indicating desired date format
if match then
local use_dates_template = content:match ('%b{}', start); -- get the whole template
if use_dates_template:match ('| *cs1%-dates *= *[lsy][sy]?') then -- look for |cs1-dates=publication date length access-/archive-date length
return match:lower() .. '-' .. use_dates_template:match ('| *cs1%-dates *= *([lsy][sy]?)');
else
return match:lower() .. '-all'; -- no |cs1-dates= k/v pair; return value appropriate for use in |df=
end
end
end
end
local global_df; -- TODO: add this to <global_cs1_config_t>?
--[[-----------------< V O L U M E , I S S U E , P A G E S >------------------
These tables hold cite class values (from the template invocation) and identify those templates that support
|volume=, |issue=, and |page(s)= parameters. Cite conference and cite map require further qualification which
is handled in the main module.
]]
local templates_using_volume = {'citation', 'audio-visual', 'book', 'conference', 'encyclopaedia', 'interview', 'journal', 'magazine', 'map', 'news', 'report', 'techreport', 'thesis'}
local templates_using_issue = {'citation', 'conference', 'episode', 'interview', 'journal', 'magazine', 'map', 'news', 'podcast'}
local templates_not_using_page = {'audio-visual', 'episode', 'mailinglist', 'newsgroup', 'podcast', 'serial', 'sign', 'speech'}
--[[
These tables control when it is appropriate for {{citation}} to render |volume= and/or |issue=. The parameter
names in the tables constrain {{citation}} so that its renderings match the renderings of the equivalent cs1
templates. For example, {{cite web}} does not support |volume= so the equivalent {{citation |website=...}} must
not support |volume=.
]]
local citation_no_volume_t = { -- {{citation}} does not render |volume= when these parameters are used
'website', 'mailinglist', 'script-website',
}
local citation_issue_t = { -- {{citation}} may render |issue= when these parameters are used
'journal', 'magazine', 'newspaper', 'periodical', 'work',
'script-journal', 'script-magazine', 'script-newspaper', 'script-periodical', 'script-work',
}
--[[
Patterns for finding extra text in |volume=, |issue=, |page=, |pages=
]]
local vol_iss_pg_patterns = {
good_ppattern = '^P[^%.PpGg]', -- OK to begin with uppercase P: P7 (page 7 of section P), but not p123 (page 123); TODO: this allows 'Pages' which it should not
bad_ppatterns = { -- patterns for |page= and |pages=
'^[Pp][PpGg]?%.?[ %d]',
'^[Pp][Pp]?%. ', -- from {{p.}} and {{pp.}} templates
'^[Pp]ages?',
'^[Pp]gs.?',
},
vi_patterns_t = { -- combined to catch volume-like text in |issue= and issue-like text in |volume=
'^volumes?', -- volume-like text
'^vols?[%.:=]?',
'^issues?', --issue-like text
'^iss[%.:=]?',
'^numbers?',
'^nos?%A', -- don't match 'november' or 'nostradamus'
'^nr[%.:=]?',
'^n[%.:= ]', -- might be a valid issue without separator (space char is sep char here)
'^n°', -- 'n' with degree sign (U+00B0)
'^№', -- precomposed unicode numero character (U+2116)
},
}
--[[--------------------------< K E Y W O R D S >-------------------------------
These tables hold keywords for those parameters that have defined sets of acceptable keywords.
]]
--[[-------------------< K E Y W O R D S T A B L E >--------------------------
this is a list of keywords; each key in the list is associated with a table of
synonymous keywords possibly from different languages.
for I18N: add local-language keywords to value table; do not change the key.
For example, adding the German keyword 'ja':
['affirmative'] = {'yes', 'true', 'y', 'ja'},
Because CS1|2 templates from en.wiki articles are often copied to other local wikis,
it is recommended that the English keywords remain in these tables.
]]
local keywords = {
['amp'] = {'&', 'amp', 'ampersand'}, -- |name-list-style=
['and'] = {'and', 'serial'}, -- |name-list-style=
['affirmative'] = {'yes', 'true', 'y'}, -- |no-tracking=, |no-pp= -- Used by InternetArchiveBot
['afterword'] = {'afterword'}, -- |contribution=
['bot: unknown'] = {'bot: unknown'}, -- |url-status= -- Used by InternetArchiveBot
['cs1'] = {'cs1'}, -- |mode=
['cs2'] = {'cs2'}, -- |mode=
['dead'] = {'dead', 'deviated'}, -- |url-status= -- Used by InternetArchiveBot
['dmy'] = {'dmy'}, -- |df=
['dmy-all'] = {'dmy-all'}, -- |df=
['foreword'] = {'foreword'}, -- |contribution=
['free'] = {'free'}, -- |<id>-access= -- Used by InternetArchiveBot
['harv'] = {'harv'}, -- |ref=; this no longer supported; is_valid_parameter_value() called with <invert> = true
['introduction'] = {'introduction'}, -- |contribution=
['limited'] = {'limited'}, -- |url-access= -- Used by InternetArchiveBot
['live'] = {'live'}, -- |url-status= -- Used by InternetArchiveBot
['mdy'] = {'mdy'}, -- |df=
['mdy-all'] = {'mdy-all'}, -- |df=
['none'] = {'none'}, -- |postscript=, |ref=, |title=, |type= -- Used by InternetArchiveBot
['off'] = {'off'}, -- |title= (potentially also: |title-link=, |postscript=, |ref=, |type=)
['preface'] = {'preface'}, -- |contribution=
['registration'] = {'registration'}, -- |url-access= -- Used by InternetArchiveBot
['subscription'] = {'subscription'}, -- |url-access= -- Used by InternetArchiveBot
['unfit'] = {'unfit'}, -- |url-status= -- Used by InternetArchiveBot
['usurped'] = {'usurped'}, -- |url-status= -- Used by InternetArchiveBot
['vanc'] = {'vanc'}, -- |name-list-style=
['ymd'] = {'ymd'}, -- |df=
['ymd-all'] = {'ymd-all'}, -- |df=
-- ['yMd'] = {'yMd'}, -- |df=; not supported at en.wiki
-- ['yMd-all'] = {'yMd-all'}, -- |df=; not supported at en.wiki
}
--[[------------------------< X L A T E _ K E Y W O R D S >---------------------
this function builds a list, keywords_xlate{}, of the keywords found in keywords{} where the values from keywords{}
become the keys in keywords_xlate{} and the keys from keywords{} become the values in keywords_xlate{}:
['affirmative'] = {'yes', 'true', 'y'}, -- in keywords{}
becomes
['yes'] = 'affirmative', -- in keywords_xlate{}
['true'] = 'affirmative',
['y'] = 'affirmative',
the purpose of this function is to act as a translator between a non-English keyword and its English equivalent
that may be used in other modules of this suite
]]
local function xlate_keywords ()
local out_table = {}; -- output goes here
for k, keywords_t in pairs (keywords) do -- spin through the keywords table
for _, keyword in ipairs (keywords_t) do -- for each keyword
out_table[keyword] = k; -- create an entry in the output table where keyword is the key
end
end
return out_table;
end
local keywords_xlate = xlate_keywords (); -- the list of translated keywords
--[[----------------< M A K E _ K E Y W O R D S _ L I S T >---------------------
this function assembles, for parameter-value validation, the list of keywords appropriate to that parameter.
keywords_lists{}, is a table of tables from keywords{}
]]
local function make_keywords_list (keywords_lists)
local out_table = {}; -- output goes here
for _, keyword_list in ipairs (keywords_lists) do -- spin through keywords_lists{} and get a table of keywords
for _, keyword in ipairs (keyword_list) do -- spin through keyword_list{} and add each keyword, ...
table.insert (out_table, keyword); -- ... as plain text, to the output list
end
end
return out_table;
end
--[[----------------< K E Y W O R D S _ L I S T S >-----------------------------
this is a list of lists of valid keywords for the various parameters in [key].
Generally the keys in this table are the canonical en.wiki parameter names though
some are contrived because of use in multiple differently named parameters:
['yes_true_y'], ['id-access'].
The function make_keywords_list() extracts the individual keywords from the
appropriate list in keywords{}.
The lists in this table are used to validate the keyword assignment for the
parameters named in this table's keys.
]]
local keywords_lists = {
['yes_true_y'] = make_keywords_list ({keywords.affirmative}),
['contribution'] = make_keywords_list ({keywords.afterword, keywords.foreword, keywords.introduction, keywords.preface}),
['df'] = make_keywords_list ({keywords.dmy, keywords['dmy-all'], keywords.mdy, keywords['mdy-all'], keywords.ymd, keywords['ymd-all']}),
-- ['df'] = make_keywords_list ({keywords.dmy, keywords['dmy-all'], keywords.mdy, keywords['mdy-all'], keywords.ymd, keywords['ymd-all'], keywords.yMd, keywords['yMd-all']}), -- not supported at en.wiki
['mode'] = make_keywords_list ({keywords.cs1, keywords.cs2}),
['name-list-style'] = make_keywords_list ({keywords.amp, keywords['and'], keywords.vanc}),
['ref'] = make_keywords_list ({keywords.harv}), -- inverted check; |ref=harv no longer supported
['url-access'] = make_keywords_list ({keywords.subscription, keywords.limited, keywords.registration}),
['url-status'] = make_keywords_list ({keywords.dead, keywords.live, keywords.unfit, keywords.usurped, keywords['bot: unknown']}),
['id-access'] = make_keywords_list ({keywords.free}),
}
--[[--------------------------< C S 1 _ C O N F I G _ G E T >--------------------------------------------------
fetch and validate values from {{cs1 config}} template to fill <global_cs1_config_t>
no error messages; when errors are detected, the parameter value from {{cs1 config}} is blanked.
Supports all parameters and aliases associated with the metaparameters: DisplayAuthors, DisplayContributors,
DisplayEditors, DisplayInterviewers, DisplayTranslators, NameListStyle, and Mode. The DisplayWhatever metaparameters
accept numeric values only (|display-authors=etal and the like is not supported).
]]
local global_cs1_config_t = {}; -- TODO: add value returned from get_date_format() to this table?
local function get_cs1_config ()
if not content then -- nil content when we're in template
return nil; -- auto-formatting does not work in Template space so don't set global_df
end
local start = content:find('{{ *[Cc][Ss]1 config *[|}]'); -- <start> is offset into <content> when {{cs1 config}} found; nil else
if start then
local cs1_config_template = content:match ('%b{}', start); -- get the whole template
if not cs1_config_template then
return nil;
end
local params_t = mw.text.split (cs1_config_template:gsub ('^{{%s*', ''):gsub ('%s*}}$', ''), '%s*|%s*'); -- remove '{{' and '}}'; make a sequence of parameter/value pairs (split on the pipe)
table.remove (params_t, 1); -- remove the template name because it isn't a parameter/value pair
local config_meta_params_t = {'DisplayAuthors', 'DisplayContributors', 'DisplayEditors', 'DisplayInterviewers', 'DisplayTranslators', 'NameListStyle', 'Mode'};
local meta_param_map_t = {}; -- list of accepted parameter names usable in {{cs1 config}} goes here
for _, meta_param in ipairs (config_meta_params_t) do -- for i18n using <config_meta_params_t>, map template parameter names to their metaparameter equivalents
if 'table' == type (aliases[meta_param]) then -- if <meta_param> is a sequence,
for _, param in ipairs (aliases[meta_param]) do -- extract its contents
meta_param_map_t[param] = meta_param; -- and add to <meta_param_map_t>
end
else
meta_param_map_t[aliases[meta_param]] = meta_param; -- not a sequence so just add the parameter to <meta_param_map_t>
end
end
local keywords_t = {}; -- map valid keywords to their associate metaparameter; reverse form of <keyword_lists[key] for these metaparameters
for _, metaparam_t in ipairs ({{'NameListStyle', 'name-list-style'}, {'Mode', 'mode'}}) do -- only these metaparameter / keywords_lists key pairs
for _, keyword in ipairs (keywords_lists[metaparam_t[2]]) do -- spin through the list of keywords
keywords_t[keyword] = metaparam_t[1]; -- add [keyword] = metaparameter to the map
end
end
for _, param in ipairs (params_t) do -- spin through the {{cs1 config}} parameters and fill <global_cs1_config_t>
local k, v = param:match ('([^=]-)%s*=%s*(.+)'); -- <k> is the parameter name; <v> is parameter's assigned value
if k then
if k:find ('^display') then -- if <k> is one of the |display-<namelist>= parameters
if v:match ('%d+') then -- the assigned value must be digits; doesn't accept 'etal'
global_cs1_config_t[meta_param_map_t[k]]=v; -- add the display param and its value to globals table
end
else
if keywords_t[v] == meta_param_map_t[k] then -- keywords_t[v] returns nil or the metaparam name; these must be the same
global_cs1_config_t[meta_param_map_t[k]]=v; -- add the parameter and its value to globals table
end
end
end
end
end
end
get_cs1_config (); -- fill <global_cs1_config_t>
--[[---------------------< S T R I P M A R K E R S >----------------------------
Common pattern definition location for stripmarkers so that we don't have to go
hunting for them if (when) MediaWiki changes their form.
]]
local stripmarkers = {
['any'] = '\127[^\127]*UNIQ%-%-(%a+)%-[%a%d]+%-QINU[^\127]*\127', -- capture returns name of stripmarker
['math'] = '\127[^\127]*UNIQ%-%-math%-[%a%d]+%-QINU[^\127]*\127' -- math stripmarkers used in coins_cleanup() and coins_replace_math_stripmarker()
}
--[[------------< I N V I S I B L E _ C H A R A C T E R S >---------------------
This table holds non-printing or invisible characters indexed either by name or
by Unicode group. Values are decimal representations of UTF-8 codes. The table
is organized as a table of tables because the Lua pairs keyword returns table
data in an arbitrary order. Here, we want to process the table from top to bottom
because the entries at the top of the table are also found in the ranges specified
by the entries at the bottom of the table.
Also here is a pattern that recognizes stripmarkers that begin and end with the
delete characters. The nowiki stripmarker is not an error but some others are
because the parameter values that include them become part of the template's
metadata before stripmarker replacement.
]]
local invisible_defs = {
del = '\127', -- used to distinguish between stripmarker and del char
zwj = '\226\128\141', -- used with capture because zwj may be allowed
}
local invisible_chars = {
{'replacement', '\239\191\189'}, -- U+FFFD, EF BF BD
{'zero width joiner', '('.. invisible_defs.zwj .. ')'}, -- U+200D, E2 80 8D; capture because zwj may be allowed
{'zero width space', '\226\128\139'}, -- U+200B, E2 80 8B
{'hair space', '\226\128\138'}, -- U+200A, E2 80 8A
{'soft hyphen', '\194\173'}, -- U+00AD, C2 AD
{'horizontal tab', '\009'}, -- U+0009 (HT), 09
{'line feed', '\010'}, -- U+000A (LF), 0A
{'no-break space', '\194\160'}, -- U+00A0 (NBSP), C2 A0
{'carriage return', '\013'}, -- U+000D (CR), 0D
{'stripmarker', stripmarkers.any}, -- stripmarker; may or may not be an error; capture returns the stripmaker type
{'delete', '('.. invisible_defs.del .. ')'}, -- U+007F (DEL), 7F; must be done after stripmarker test; capture to distinguish isolated del chars not part of stripmarker
{'C0 control', '[\000-\008\011\012\014-\031]'}, -- U+0000–U+001F (NULL–US), 00–1F (except HT, LF, CR (09, 0A, 0D))
{'C1 control', '[\194\128-\194\159]'}, -- U+0080–U+009F (XXX–APC), C2 80 – C2 9F
-- {'Specials', '[\239\191\185-\239\191\191]'}, -- U+FFF9-U+FFFF, EF BF B9 – EF BF BF
-- {'Private use area', '[\238\128\128-\239\163\191]'}, -- U+E000–U+F8FF, EE 80 80 – EF A3 BF
-- {'Supplementary Private Use Area-A', '[\243\176\128\128-\243\191\191\189]'}, -- U+F0000–U+FFFFD, F3 B0 80 80 – F3 BF BF BD
-- {'Supplementary Private Use Area-B', '[\244\128\128\128-\244\143\191\189]'}, -- U+100000–U+10FFFD, F4 80 80 80 – F4 8F BF BD
}
--[[
Indic script makes use of zero width joiner as a character modifier so zwj
characters must be left in. This pattern covers all of the unicode characters
for these languages:
Devanagari 0900–097F – https://unicode.org/charts/PDF/U0900.pdf
Devanagari extended A8E0–A8FF – https://unicode.org/charts/PDF/UA8E0.pdf
Bengali 0980–09FF – https://unicode.org/charts/PDF/U0980.pdf
Gurmukhi 0A00–0A7F – https://unicode.org/charts/PDF/U0A00.pdf
Gujarati 0A80–0AFF – https://unicode.org/charts/PDF/U0A80.pdf
Oriya 0B00–0B7F – https://unicode.org/charts/PDF/U0B00.pdf
Tamil 0B80–0BFF – https://unicode.org/charts/PDF/U0B80.pdf
Telugu 0C00–0C7F – https://unicode.org/charts/PDF/U0C00.pdf
Kannada 0C80–0CFF – https://unicode.org/charts/PDF/U0C80.pdf
Malayalam 0D00–0D7F – https://unicode.org/charts/PDF/U0D00.pdf
plus the not-necessarily Indic scripts for Sinhala and Burmese:
Sinhala 0D80-0DFF - https://unicode.org/charts/PDF/U0D80.pdf
Myanmar 1000-109F - https://unicode.org/charts/PDF/U1000.pdf
Myanmar extended A AA60-AA7F - https://unicode.org/charts/PDF/UAA60.pdf
Myanmar extended B A9E0-A9FF - https://unicode.org/charts/PDF/UA9E0.pdf
the pattern is used by has_invisible_chars() and coins_cleanup()
]]
local indic_script = '[\224\164\128-\224\181\191\224\163\160-\224\183\191\225\128\128-\225\130\159\234\167\160-\234\167\191\234\169\160-\234\169\191]';
-- list of emoji that use a zwj character (U+200D) to combine with another emoji
-- from: https://unicode.org/Public/emoji/16.0/emoji-zwj-sequences.txt; version: 16.0; 2024-08-14
-- table created by: [[:en:Module:Make emoji zwj table]]
local emoji_t = { -- indexes are decimal forms of the hex values in U+xxxx
[8596] = true, -- U+2194 ↔ left right arrow
[8597] = true, -- U+2195 ↕ up down arrow
[9760] = true, -- U+2620 ☠ skull and crossbones
[9792] = true, -- U+2640 ♀ female sign
[9794] = true, -- U+2642 ♂ male sign
[9877] = true, -- U+2695 ⚕ staff of aesculapius
[9878] = true, -- U+2696 ⚖ scales
[9895] = true, -- U+26A7 ⚧ male with stroke and male and female sign
[9992] = true, -- U+2708 ✈ airplane
[10052] = true, -- U+2744 ❄ snowflake
[10084] = true, -- U+2764 ❤ heavy black heart
[10145] = true, -- U+27A1 ➡ black rightwards arrow
[11035] = true, -- U+2B1B ⬛ black large square
[127752] = true, -- U+1F308 🌈 rainbow
[127787] = true, -- U+1F32B 🌫 fog
[127806] = true, -- U+1F33E 🌾 ear of rice
[127859] = true, -- U+1F373 🍳 cooking
[127868] = true, -- U+1F37C 🍼 baby bottle
[127876] = true, -- U+1F384 🎄 christmas tree
[127891] = true, -- U+1F393 🎓 graduation cap
[127908] = true, -- U+1F3A4 🎤 microphone
[127912] = true, -- U+1F3A8 🎨 artist palette
[127979] = true, -- U+1F3EB 🏫 school
[127981] = true, -- U+1F3ED 🏭 factory
[128102] = true, -- U+1F466 👦 boy
[128103] = true, -- U+1F467 👧 girl
[128104] = true, -- U+1F468 👨 man
[128105] = true, -- U+1F469 👩 woman
[128139] = true, -- U+1F48B 💋 kiss mark
[128165] = true, -- U+1F4A5 💥 collision symbol
[128168] = true, -- U+1F4A8 💨 dash symbol
[128171] = true, -- U+1F4AB 💫 dizzy symbol
[128187] = true, -- U+1F4BB 💻 personal computer
[128188] = true, -- U+1F4BC 💼 brief case
[128293] = true, -- U+1F525 🔥 fire
[128295] = true, -- U+1F527 🔧 wrench
[128300] = true, -- U+1F52C 🔬 microscope
[128488] = true, -- U+1F5E8 🗨 left speech bubble
[128640] = true, -- U+1F680 🚀 rocket
[128658] = true, -- U+1F692 🚒 fire engine
[129001] = true, -- U+1F7E9 🟩 large green square
[129003] = true, -- U+1F7EB 🟫 large brown square
[129309] = true, -- U+1F91D 🤝 handshake
[129455] = true, -- U+1F9AF 🦯 probing cane
[129456] = true, -- U+1F9B0 🦰 emoji component red hair
[129457] = true, -- U+1F9B1 🦱 emoji component curly hair
[129458] = true, -- U+1F9B2 🦲 emoji component bald
[129459] = true, -- U+1F9B3 🦳 emoji component white hair
[129466] = true, -- U+1F9BA 🦺 safety vest
[129468] = true, -- U+1F9BC 🦼 motorized wheelchair
[129469] = true, -- U+1F9BD 🦽 manual wheelchair
[129489] = true, -- U+1F9D1 🧑 adult
[129490] = true, -- U+1F9D2 🧒 child
[129657] = true, -- U+1FA79 🩹 adhesive bandage
[129778] = true, -- U+1FAF2 🫲 leftwards hand
}
--[[----------------------< L A N G U A G E S U P P O R T >-------------------
These tables and constants support various language-specific functionality.
]]
--local this_wiki_code = mw.getContentLanguage():getCode(); -- get this wiki's language code
local this_wiki_code = lang_obj:getCode(); -- get this wiki's language code
if string.match (mw.site.server, 'wikidata') then
this_wiki_code = mw.getCurrentFrame():callParserFunction('int', {'lang'}); -- on Wikidata so use interface language setting instead
end
local mw_languages_by_tag_t = mw.language.fetchLanguageNames (this_wiki_code, 'all'); -- get a table of language tag/name pairs known to Wikimedia; used for interwiki tests
local mw_languages_by_name_t = {};
for k, v in pairs (mw_languages_by_tag_t) do -- build a 'reversed' table name/tag language pairs know to MediaWiki; used for |language=
v = mw.ustring.lower (v); -- lowercase for tag fetch; get name's proper case from mw_languages_by_tag_t[<tag>]
if mw_languages_by_name_t[v] then -- when name already in the table
if 2 == #k or 3 == #k then -- if tag does not have subtags
mw_languages_by_name_t[v] = k; -- prefer the shortest tag for this name
end
else -- here when name not in the table
mw_languages_by_name_t[v] = k; -- so add name and matching tag
end
end
local inter_wiki_map = {}; -- map of interwiki prefixes that are language-code prefixes
for k, v in pairs (mw.site.interwikiMap ('local')) do -- spin through the base interwiki map (limited to local)
if mw_languages_by_tag_t[v["prefix"]] then -- if the prefix matches a known language tag
inter_wiki_map[v["prefix"]] = true; -- add it to our local map
end
end
--[[--------------------< S C R I P T _ L A N G _ C O D E S >-------------------
This table is used to hold ISO 639-1 two-character and ISO 639-3 three-character
language codes that apply only to |script-title= and |script-chapter=
]]
local script_lang_codes = {
'ab', 'am', 'ar', 'az', 'be', 'bg', 'bn', 'bo', 'bs', 'ce', 'chr', 'dv', 'dz',
'el', 'fa', 'grc', 'gu', 'he', 'hi', 'hy', 'ja', 'ka', 'kk', 'km', 'kn', 'ko',
'ku', 'ky', 'lo', 'mk', 'ml', 'mn', 'mni', 'mr', 'my', 'ne', 'or', 'ota',
'pa', 'ps', 'ru', 'sd', 'si', 'sr', 'syc', 'ta', 'te', 'tg', 'th', 'ti', 'tt',
'ug', 'uk', 'ur', 'uz', 'yi', 'yue', 'zh', 'zgh'
};
--[[---------------< L A N G U A G E R E M A P P I N G >----------------------
These tables hold language information that is different (correct) from MediaWiki's definitions
For each ['<tag>'] = 'language name' in lang_code_remap{} there must be a matching ['language name'] = {'language name', '<tag>'} in lang_name_remap{}
lang_tag_remap{}:
key is always lowercase ISO 639-1, -2, -3 language tag or a valid lowercase IETF language tag
value is properly spelled and capitalized language name associated with <tag>
only one language name per <tag>;
key/value pair must have matching entry in lang_name_remap{}
lang_name_remap{}:
key is always lowercase language name
value is a table the holds correctly spelled and capitalized language name [1] and associated tag [2] (tag must match a tag key in lang_tag_remap{})
may have multiple keys referring to a common preferred name and tag; For example:
['kolsch'] and ['kölsch'] both refer to 'Kölsch' and 'ksh'
]]
local lang_tag_remap = { -- used for |language= and |script-title= / |script-chapter=
['als'] = 'Tosk Albanian', -- MediaWiki returns Alemannisch
['bh'] = 'Bihari', -- MediaWiki uses 'bh' as a subdomain name for Bhojpuri Wikipedia: bh.wikipedia.org
['bla'] = 'Blackfoot', -- MediaWiki/IANA/ISO 639: Siksika; use en.wiki preferred name
['bn'] = 'Bengali', -- MediaWiki returns Bangla
['ca-valencia'] = 'Valencian', -- IETF variant of Catalan
['fkv'] = 'Kven', -- MediaWiki returns Kvensk
['gsw'] = 'Swiss German',
['ilo'] = 'Ilocano', -- MediaWiki/IANA/ISO 639: Iloko; use en.wiki preferred name
['ksh'] = 'Kölsch', -- MediaWiki: Colognian; use IANA/ISO 639 preferred name
['ksh-x-colog'] = 'Colognian', -- override MediaWiki ksh; no IANA/ISO 639 code for Colognian; IETF private code created at Module:Lang/data
['mis-x-ripuar'] = 'Ripuarian', -- override MediaWiki ksh; no IANA/ISO 639 code for Ripuarian; IETF private code created at Module:Lang/data
['nan-tw'] = 'Taiwanese Hokkien', -- make room for MediaWiki/IANA/ISO 639 nan: Min Nan Chinese and support en.wiki preferred name
['sr-ec'] = 'Serbian (Cyrillic script)', -- MediaWiki returns српски (ћирилица)
['sr-el'] = 'Serbian (Latin script)', -- MediaWiki returns srpski (latinica)
}
local lang_name_remap = { -- used for |language=; names require proper capitalization; tags must be lowercase
['alemannic'] = {'Swiss German', 'gsw'}, -- ISO 639-2, -3 alternate for Swiss German; MediaWiki mediawiki returns Alemannic for gsw; en.wiki preferred name
['alemannisch'] = {'Swiss German', 'gsw'}, -- not an ISO or IANA language name; MediaWiki uses 'als' as a subdomain name for Alemannic Wikipedia: als.wikipedia.org
['bangla'] = {'Bengali', 'bn'}, -- MediaWiki returns Bangla (the endonym) but we want Bengali (the exonym); here we remap
['bengali'] = {'Bengali', 'bn'}, -- MediaWiki doesn't use exonym so here we provide correct language name and 639-1 code
['bhojpuri'] = {'Bhojpuri', 'bho'}, -- MediaWiki uses 'bh' as a subdomain name for Bhojpuri Wikipedia: bh.wikipedia.org
['bihari'] = {'Bihari', 'bh'}, -- MediaWiki replaces 'Bihari' with 'Bhojpuri' so 'Bihari' cannot be found
['blackfoot'] = {'Blackfoot', 'bla'}, -- MediaWiki/IANA/ISO 639: Siksika; use en.wiki preferred name
['colognian'] = {'Colognian', 'ksh-x-colog'}, -- MediaWiki preferred name for ksh
['ilocano'] = {'Ilocano', 'ilo'}, -- MediaWiki/IANA/ISO 639: Iloko; use en.wiki preferred name
['kolsch'] = {'Kölsch', 'ksh'}, -- use IANA/ISO 639 preferred name (use non-diacritical o instead of umlaut ö)
['kölsch'] = {'Kölsch', 'ksh'}, -- use IANA/ISO 639 preferred name
['kven'] = {'Kven', 'fkv'}, -- Unicode CLDR have decided not to support English language name for these two...
['kvensk'] = {'Kven', 'fkv'}, -- ...they say to refer to IANA registry for English names
['ripuarian'] = {'Ripuarian', 'mis-x-ripuar'}, -- group of dialects; no code in MediaWiki or in IANA/ISO 639
['serbian (cyrillic script)'] = {'Serbian (Cyrillic script)', 'sr-cyrl'}, -- special case to get correct tag when |language=sr-ec
['serbian (latin script)'] = {'Serbian (Latin script)', 'sr-latn'}, -- special case to get correct tag when |language=sr-el
['swiss german'] = {'Swiss German', 'gsw'},
['taiwanese hokkien'] = {'Taiwanese Hokkien', 'nan-tw'}, -- make room for MediaWiki/IANA/ISO 639 nan: Min Nan Chinese
['tosk albanian'] = {'Tosk Albanian', 'als'}, -- MediaWiki replaces 'Tosk Albanian' with 'Alemannisch' so 'Tosk Albanian' cannot be found
['valencian'] = {'Valencian', 'ca-valencia'}, -- variant of Catalan; categorizes as Valencian
}
--[[---------------< P R O P E R T I E S _ C A T E G O R I E S >----------------
Properties categories. These are used for investigating qualities of citations.
]]
local prop_cats = {
['foreign-lang-source'] = 'CS1 $1-language sources ($2)', -- |language= categories; $1 is foreign-language name, $2 is ISO639-1 code
['foreign-lang-source-2'] = 'CS1 foreign language sources (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 interproject-linked names|$1', -- any author, editor, etc that has an interproject link; $1 is interproject tag used as a sort key
['interwiki-linked-name'] = 'CS1 interwiki-linked names|$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 $1-language sources ($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 location test',
['long-vol'] = 'CS1: long volume value', -- probably temporary cat to identify scope of |volume= values longer than 4 characters
['script'] = 'CS1 uses $1-language script ($2)', -- |script-title=xx: has matching category; $1 is language name, $2 is language tag
['tracked-param'] = 'CS1 tracked parameter: $1', -- $1 is base (enumerators removed) parameter name
['unfit'] = 'CS1: unfit URL', -- |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: abbreviated year range', -- 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 = '<code class="cs1-code">|access-date=</code> requires <code class="cs1-code">|url=</code>',
anchor = 'accessdate_missing_url',
category = 'CS1 errors: access-date without URL',
hidden = false
},
err_apostrophe_markup = {
message = 'Italic or bold markup not allowed in: <code class="cs1-code">|$1=</code>', -- $1 is parameter name
anchor = 'apostrophe_markup',
category = 'CS1 errors: markup',
hidden = false
},
err_archive_date_missing_url = {
message = '<code class="cs1-code">|archive-date=</code> requires <code class="cs1-code">|archive-url=</code>',
anchor = 'archive_date_missing_url',
category = 'CS1 errors: archive-url',
hidden = false
},
err_archive_date_url_ts_mismatch = {
message = '<code class="cs1-code">|archive-date=</code> / <code class="cs1-code">|archive-url=</code> timestamp mismatch; $1 suggested',
anchor = 'archive_date_url_ts_mismatch',
category = 'CS1 errors: archive-url',
hidden = false
},
err_archive_missing_date = {
message = '<code class="cs1-code">|archive-url=</code> requires <code class="cs1-code">|archive-date=</code>',
anchor = 'archive_missing_date',
category = 'CS1 errors: archive-url',
hidden = false
},
err_archive_missing_url = {
message = '<code class="cs1-code">|archive-url=</code> requires <code class="cs1-code">|url=</code>',
anchor = 'archive_missing_url',
category = 'CS1 errors: archive-url',
hidden = false
},
err_archive_url = {
message = '<code class="cs1-code">|archive-url=</code> is malformed: $1', -- $1 is error message detail
anchor = 'archive_url',
category = 'CS1 errors: archive-url',
hidden = false
},
err_arxiv_missing = {
message = '<code class="cs1-code">|arxiv=</code> required',
anchor = 'arxiv_missing',
category = 'CS1 errors: arXiv', -- same as bad arxiv
hidden = false
},
err_asintld_missing_asin = {
message = '<code class="cs1-code">|$1=</code> requires <code class="cs1-code">|asin=</code>', -- $1 is parameter name
anchor = 'asintld_missing_asin',
category = 'CS1 errors: ASIN TLD',
hidden = false
},
err_bad_arxiv = {
message = 'Check <code class="cs1-code">|arxiv=</code> value',
anchor = 'bad_arxiv',
category = 'CS1 errors: arXiv',
hidden = false
},
err_bad_asin = {
message = 'Check <code class="cs1-code">|asin=</code> value',
anchor = 'bad_asin',
category ='CS1 errors: ASIN',
hidden = false
},
err_bad_asin_tld = {
message = 'Check <code class="cs1-code">|asin-tld=</code> value',
anchor = 'bad_asin_tld',
category ='CS1 errors: ASIN TLD',
hidden = false
},
err_bad_bibcode = {
message = 'Check <code class="cs1-code">|bibcode=</code> $1', -- $1 is error message detail
anchor = 'bad_bibcode',
category = 'CS1 errors: bibcode',
hidden = false
},
err_bad_biorxiv = {
message = 'Check <code class="cs1-code">|biorxiv=</code> value',
anchor = 'bad_biorxiv',
category = 'CS1 errors: bioRxiv',
hidden = false
},
err_bad_citeseerx = {
message = 'Check <code class="cs1-code">|citeseerx=</code> value',
anchor = 'bad_citeseerx',
category = 'CS1 errors: citeseerx',
hidden = false
},
err_bad_date = {
message = 'Check date values in: $1', -- $1 is a parameter name list
anchor = 'bad_date',
category = 'CS1 errors: dates',
hidden = true
},
err_bad_doi = {
message = 'Check <code class="cs1-code">|doi=</code> value',
anchor = 'bad_doi',
category = 'CS1 errors: DOI',
hidden = false
},
err_bad_hdl = {
message = 'Check <code class="cs1-code">|hdl=</code> value',
anchor = 'bad_hdl',
category = 'CS1 errors: HDL',
hidden = false
},
err_bad_isbn = {
message = 'Check <code class="cs1-code">|isbn=</code> value: $1', -- $1 is error message detail
anchor = 'bad_isbn',
category = 'CS1 errors: ISBN',
hidden = false
},
err_bad_ismn = {
message = 'Check <code class="cs1-code">|ismn=</code> value',
anchor = 'bad_ismn',
category = 'CS1 errors: ISMN',
hidden = false
},
err_bad_issn = {
message = 'Check <code class="cs1-code">|$1issn=</code> value', -- $1 is 'e' or '' for eissn or issn
anchor = 'bad_issn',
category = 'CS1 errors: ISSN',
hidden = false
},
err_bad_jfm = {
message = 'Check <code class="cs1-code">|jfm=</code> value',
anchor = 'bad_jfm',
category = 'CS1 errors: JFM',
hidden = false
},
err_bad_jstor = {
message = 'Check <code class="cs1-code">|jstor=</code> value',
anchor = 'bad_jstor',
category = 'CS1 errors: JSTOR',
hidden = false
},
err_bad_lccn = {
message = 'Check <code class="cs1-code">|lccn=</code> value',
anchor = 'bad_lccn',
category = 'CS1 errors: LCCN',
hidden = false
},
err_bad_medrxiv = {
message = 'Check <code class="cs1-code">|medrxiv=</code> value',
anchor = 'bad_medrxiv',
category = 'CS1 errors: medRxiv',
hidden = false
},
err_bad_mr = {
message = 'Check <code class="cs1-code">|mr=</code> value',
anchor = 'bad_mr',
category = 'CS1 errors: MR',
hidden = false
},
err_bad_oclc = {
message = 'Check <code class="cs1-code">|oclc=</code> value',
anchor = 'bad_oclc',
category = 'CS1 errors: OCLC',
hidden = false
},
err_bad_ol = {
message = 'Check <code class="cs1-code">|ol=</code> value',
anchor = 'bad_ol',
category = 'CS1 errors: OL',
hidden = false
},
err_bad_osti = {
message = 'Check <code class="cs1-code">|osti=</code> value',
anchor = 'bad_osti',
category = 'CS1 errors: OSTI',
hidden = false
},
err_bad_paramlink = { -- for |title-link=, |author/editor/translator-link=, |series-link=, |episode-link=
message = 'Check <code class="cs1-code">|$1=</code> value', -- $1 is parameter name
anchor = 'bad_paramlink',
category = 'CS1 errors: parameter link',
hidden = false
},
err_bad_pmc = {
message = 'Check <code class="cs1-code">|pmc=</code> value',
anchor = 'bad_pmc',
category = 'CS1 errors: PMC',
hidden = false
},
err_bad_pmid = {
message = 'Check <code class="cs1-code">|pmid=</code> value',
anchor = 'bad_pmid',
category = 'CS1 errors: PMID',
hidden = false
},
err_bad_rfc = {
message = 'Check <code class="cs1-code">|rfc=</code> value',
anchor = 'bad_rfc',
category = 'CS1 errors: RFC',
hidden = false
},
err_bad_s2cid = {
message = 'Check <code class="cs1-code">|s2cid=</code> value',
anchor = 'bad_s2cid',
category = 'CS1 errors: S2CID',
hidden = false
},
err_bad_sbn = {
message = 'Check <code class="cs1-code">|sbn=</code> value: $1', -- $1 is error message detail
anchor = 'bad_sbn',
category = 'CS1 errors: SBN',
hidden = false
},
err_bad_ssrn = {
message = 'Check <code class="cs1-code">|ssrn=</code> value',
anchor = 'bad_ssrn',
category = 'CS1 errors: SSRN',
hidden = false
},
err_bad_url = {
message = 'Check $1 value', -- $1 is parameter name
anchor = 'bad_url',
category = 'CS1 errors: URL',
hidden = false
},
err_bad_usenet_id = {
message = 'Check <code class="cs1-code">|message-id=</code> value',
anchor = 'bad_message_id',
category = 'CS1 errors: message-id',
hidden = false
},
err_bad_zbl = {
message = 'Check <code class="cs1-code">|zbl=</code> value',
anchor = 'bad_zbl',
category = 'CS1 errors: Zbl',
hidden = false
},
err_bare_url_missing_title = {
message = '$1 missing title', -- $1 is parameter name
anchor = 'bare_url_missing_title',
category = 'CS1 errors: bare URL',
hidden = false
},
err_biorxiv_missing = {
message = '<code class="cs1-code">|biorxiv=</code> required',
anchor = 'biorxiv_missing',
category = 'CS1 errors: bioRxiv', -- same as bad bioRxiv
hidden = false
},
err_chapter_ignored = {
message = '<code class="cs1-code">|$1=</code> ignored', -- $1 is parameter name
anchor = 'chapter_ignored',
category = 'CS1 errors: chapter ignored',
hidden = false
},
err_citation_missing_title = {
message = 'Missing or empty <code class="cs1-code">|$1=</code>', -- $1 is parameter name
anchor = 'citation_missing_title',
category = 'CS1 errors: missing title',
hidden = false
},
err_citeseerx_missing = {
message = '<code class="cs1-code">|citeseerx=</code> required',
anchor = 'citeseerx_missing',
category = 'CS1 errors: citeseerx', -- same as bad citeseerx
hidden = false
},
err_cite_web_url = { -- this error applies to cite web and to cite podcast
message = 'Missing or empty <code class="cs1-code">|url=</code>',
anchor = 'cite_web_url',
category = 'CS1 errors: requires URL',
hidden = false
},
err_class_ignored = {
message = '<code class="cs1-code">|class=</code> ignored',
anchor = 'class_ignored',
category = 'CS1 errors: class',
hidden = false
},
err_contributor_ignored = {
message = '<code class="cs1-code">|contributor=</code> ignored',
anchor = 'contributor_ignored',
category = 'CS1 errors: contributor',
hidden = false
},
err_contributor_missing_required_param = {
message = '<code class="cs1-code">|contributor=</code> requires <code class="cs1-code">|$1=</code>', -- $1 is parameter name
anchor = 'contributor_missing_required_param',
category = 'CS1 errors: contributor',
hidden = false
},
err_deprecated_params = {
message = 'Cite uses deprecated parameter <code class="cs1-code">|$1=</code>', -- $1 is parameter name
anchor = 'deprecated_params',
category = 'CS1 errors: deprecated parameters',
hidden = false
},
err_disp_name = {
message = 'Invalid <code class="cs1-code">|$1=$2</code>', -- $1 is parameter name; $2 is the assigned value
anchor = 'disp_name',
category = 'CS1 errors: display-names',
hidden = false,
},
err_doibroken_missing_doi = {
message = '<code class="cs1-code">|$1=</code> requires <code class="cs1-code">|doi=</code>', -- $1 is parameter name
anchor = 'doibroken_missing_doi',
category = 'CS1 errors: DOI',
hidden = false
},
err_embargo_missing_pmc = {
message = '<code class="cs1-code">|$1=</code> requires <code class="cs1-code">|pmc=</code>', -- $1 is parameter name
anchor = 'embargo_missing_pmc',
category = 'CS1 errors: PMC embargo',
hidden = false
},
err_empty_citation = {
message = 'Empty citation',
anchor = 'empty_citation',
category = 'CS1 errors: empty citation',
hidden = false
},
err_etal = {
message = 'Explicit use of et al. in: <code class="cs1-code">|$1=</code>', -- $1 is parameter name
anchor = 'explicit_et_al',
category = 'CS1 errors: explicit use of et al.',
hidden = false
},
err_extra_text_edition = {
message = '<code class="cs1-code">|edition=</code> has extra text',
anchor = 'extra_text_edition',
category = 'CS1 errors: extra text: edition',
hidden = false,
},
err_extra_text_issue = {
message = '<code class="cs1-code">|$1=</code> has extra text', -- $1 is parameter name
anchor = 'extra_text_issue',
category = 'CS1 errors: extra text: issue',
hidden = false,
},
err_extra_text_pages = {
message = '<code class="cs1-code">|$1=</code> has extra text', -- $1 is parameter name
anchor = 'extra_text_pages',
category = 'CS1 errors: extra text: pages',
hidden = false,
},
err_extra_text_volume = {
message = '<code class="cs1-code">|$1=</code> has extra text', -- $1 is parameter name
anchor = 'extra_text_volume',
category = 'CS1 errors: extra text: volume',
hidden = false,
},
err_first_missing_last = {
message = '<code class="cs1-code">|$1=</code> missing <code class="cs1-code">|$2=</code>', -- $1 is first alias, $2 is matching last alias
anchor = 'first_missing_last',
category = 'CS1 errors: missing name', -- author, contributor, editor, interviewer, translator
hidden = false
},
err_format_missing_url = {
message = '<code class="cs1-code">|$1=</code> requires <code class="cs1-code">|$2=</code>', -- $1 is format parameter $2 is url parameter
anchor = 'format_missing_url',
category = 'CS1 errors: format without URL',
hidden = false
},
err_generic_name = {
message = '<code class="cs1-code">|$1=</code> has generic name', -- $1 is parameter name
anchor = 'generic_name',
category = 'CS1 errors: generic name',
hidden = false,
},
err_generic_title = {
message = 'Cite uses generic title',
anchor = 'generic_title',
category = 'CS1 errors: generic title',
hidden = false,
},
err_invalid_isbn_date = {
message = 'ISBN / Date incompatibility',
anchor = 'invalid_isbn_date',
category = 'CS1 errors: ISBN date',
hidden = true
},
err_invalid_param_val = {
message = 'Invalid <code class="cs1-code">|$1=$2</code>', -- $1 is parameter name $2 is parameter value
anchor = 'invalid_param_val',
category = 'CS1 errors: invalid parameter value',
hidden = false
},
err_invisible_char = {
message = '$1 in $2 at position $3', -- $1 is invisible char $2 is parameter name $3 is position number
anchor = 'invisible_char',
category = 'CS1 errors: invisible characters',
hidden = false
},
err_medrxiv_missing = {
message = '<code class="cs1-code">|medrxiv=</code> required',
anchor = 'medrxiv_missing',
category = 'CS1 errors: medRxiv', -- same as bad medRxiv
hidden = false
},
err_missing_name = {
message = 'Missing <code class="cs1-code">|$1$2=</code>', -- $1 is modified NameList; $2 is enumerator
anchor = 'missing_name',
category = 'CS1 errors: missing name', -- author, contributor, editor, interviewer, translator
hidden = false
},
err_missing_periodical = {
message = 'Cite $1 requires <code class="cs1-code">|$2=</code>', -- $1 is cs1 template name; $2 is canonical periodical parameter name for cite $1
anchor = 'missing_periodical',
category = 'CS1 errors: missing periodical',
hidden = false
},
err_missing_pipe = {
message = 'Missing pipe in: <code class="cs1-code">|$1=</code>', -- $1 is parameter name
anchor = 'missing_pipe',
category = 'CS1 errors: missing pipe',
hidden = false
},
err_missing_publisher = {
message = 'Cite $1 requires <code class="cs1-code">|$2=</code>', -- $1 is cs1 template name; $2 is canonical publisher parameter name for cite $1
anchor = 'missing_publisher',
category = 'CS1 errors: missing publisher',
hidden = false
},
err_numeric_names = {
message = '<code class="cs1-code">|$1=</code> has numeric name', -- $1 is parameter name',
anchor = 'numeric_names',
category = 'CS1 errors: numeric name',
hidden = false,
},
err_param_access_requires_param = {
message = '<code class="cs1-code">|$1-access=</code> requires <code class="cs1-code">|$1=</code>', -- $1 is parameter name
anchor = 'param_access_requires_param',
category = 'CS1 errors: param-access',
hidden = false
},
err_param_has_ext_link = {
message = 'External link in <code class="cs1-code">$1</code>', -- $1 is parameter name
anchor = 'param_has_ext_link',
category = 'CS1 errors: external links',
hidden = false
},
err_param_has_twl_url = {
message = 'Wikipedia Library link in <code class="cs1-code">$1</code>', -- $1 is parameter name
anchor = 'param_has_twl_url',
category = 'CS1 errors: URL',
hidden = false
},
err_parameter_ignored = {
message = 'Unknown parameter <code class="cs1-code">|$1=</code> ignored', -- $1 is parameter name
anchor = 'parameter_ignored',
category = 'CS1 errors: unsupported parameter',
hidden = false
},
err_parameter_ignored_suggest = {
message = 'Unknown parameter <code class="cs1-code">|$1=</code> ignored (<code class="cs1-code">|$2=</code> suggested)', -- $1 is unknown parameter $2 is suggested parameter name
anchor = 'parameter_ignored_suggest',
category = 'CS1 errors: unsupported parameter',
hidden = false
},
err_periodical_ignored = {
message = '<code class="cs1-code">|$1=</code> ignored', -- $1 is parameter name
anchor = 'periodical_ignored',
category = 'CS1 errors: periodical ignored',
hidden = false
},
err_redundant_parameters = {
message = 'More than one of $1 specified', -- $1 is error message detail
anchor = 'redundant_parameters',
category = 'CS1 errors: redundant parameter',
hidden = false
},
err_script_parameter = {
message = 'Invalid <code class="cs1-code">|$1=</code>: $2', -- $1 is parameter name $2 is script language code or error detail
anchor = 'script_parameter',
category = 'CS1 errors: script parameters',
hidden = false
},
err_ssrn_missing = {
message = '<code class="cs1-code">|ssrn=</code> required',
anchor = 'ssrn_missing',
category = 'CS1 errors: SSRN',
hidden = false
},
err_text_ignored = {
message = 'Text "$1" ignored', -- $1 is ignored text
anchor = 'text_ignored',
category = 'CS1 errors: unrecognized parameter',
hidden = false
},
err_trans_missing_title = {
message = '<code class="cs1-code">|trans-$1=</code> requires <code class="cs1-code">|$1=</code> or <code class="cs1-code">|script-$1=</code>', -- $1 is base parameter name
anchor = 'trans_missing_title',
category = 'CS1 errors: translated title',
hidden = false
},
err_param_unknown_empty = {
message = 'Cite has empty unknown parameter$1: $2', -- $1 is 's' or empty space; $2 is emty unknown param list
anchor = 'param_unknown_empty',
category = 'CS1 errors: empty unknown parameters',
hidden = false
},
err_vancouver = {
message = 'Vancouver style error: $1 in name $2', -- $1 is error detail, $2 is the nth name
anchor = 'vancouver',
category = 'CS1 errors: Vancouver style',
hidden = false
},
err_wikilink_in_url = {
message = 'URL–wikilink conflict', -- uses ndash
anchor = 'wikilink_in_url',
category = 'CS1 errors: URL–wikilink conflict', -- 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 = 'CS1 maint: archived copy as title',
hidden = true,
},
maint_bibcode = {
message = nil,
anchor = 'bibcode',
category = 'CS1 maint: 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 = 'CS1 maint: location missing publisher',
hidden = true,
},
maint_bot_unknown = {
message = nil,
anchor = 'bot:_unknown',
category = 'CS1 maint: bot: original URL status unknown',
hidden = true,
},
maint_date_auto_xlated = { -- date auto-translation not supported by en.wiki
message = nil,
anchor = 'date_auto_xlated',
category = 'CS1 maint: date auto-translated',
hidden = true,
},
maint_date_format = {
message = nil,
anchor = 'date_format',
category = 'CS1 maint: date format',
hidden = true,
},
maint_date_year = {
message = nil,
anchor = 'date_year',
category = 'CS1 maint: date and year',
hidden = true,
},
maint_doi_ignore = {
message = nil,
anchor = 'doi_ignore',
category = 'CS1 maint: ignored DOI errors',
hidden = true,
},
maint_doi_inactive = {
message = nil,
anchor = 'doi_inactive',
category = 'CS1 maint: DOI inactive',
hidden = true,
},
maint_doi_inactive_dated = {
message = nil,
anchor = 'doi_inactive_dated',
category = 'CS1 maint: DOI inactive as of $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 = 'CS1 maint: unflagged free DOI',
hidden = true,
},
maint_extra_punct = {
message = nil,
anchor = 'extra_punct',
category = 'CS1 maint: extra punctuation',
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 = 'CS1 maint: ID limit load fail',
hidden = true,
},
maint_isbn_ignore = {
message = nil,
anchor = 'ignore_isbn_err',
category = 'CS1 maint: ignored ISBN errors',
hidden = true,
},
maint_issn_ignore = {
message = nil,
anchor = 'ignore_issn',
category = 'CS1 maint: ignored ISSN errors',
hidden = true,
},
maint_jfm_format = {
message = nil,
anchor = 'jfm_format',
category = 'CS1 maint: JFM format',
hidden = true,
},
maint_location = {
message = nil,
anchor = 'location',
category = 'CS1 maint: location',
hidden = true,
},
maint_mr_format = {
message = nil,
anchor = 'mr_format',
category = 'CS1 maint: MR format',
hidden = true,
},
maint_mult_names = {
message = nil,
anchor = 'mult_names',
category = 'CS1 maint: multiple names: $1', -- $1 is '<name>s list'; gets value from special_case_translation table
hidden = true,
},
maint_numeric_names = {
message = nil,
anchor = 'numeric_names',
category = 'CS1 maint: numeric names: $1', -- $1 is '<name>s list'; gets value from special_case_translation table
hidden = true,
},
maint_others = {
message = nil,
anchor = 'others',
category = 'CS1 maint: others',
hidden = true,
},
maint_others_avm = {
message = nil,
anchor = 'others_avm',
category = 'CS1 maint: others in cite AV media (notes)',
hidden = true,
},
maint_overridden_setting = {
message = nil,
anchor = 'overridden',
category = 'CS1 maint: overridden setting',
hidden = true,
},
maint_pmc_embargo = {
message = nil,
anchor = 'embargo',
category = 'CS1 maint: PMC embargo expired',
hidden = true,
},
maint_pmc_format = {
message = nil,
anchor = 'pmc_format',
category = 'CS1 maint: PMC format',
hidden = true,
},
maint_postscript = {
message = nil,
anchor = 'postscript',
category = 'CS1 maint: postscript',
hidden = true,
},
maint_publisher_location = {
message = nil,
anchor = 'publisher_location',
category = 'CS1 maint: publisher location',
hidden = true,
},
maint_ref_duplicates_default = {
message = nil,
anchor = 'ref_default',
category = 'CS1 maint: ref duplicates default',
hidden = true,
},
maint_unknown_lang = {
message = nil,
anchor = 'unknown_lang',
category = 'CS1 maint: unrecognized language',
hidden = true,
},
maint_untitled = {
message = nil,
anchor = 'untitled',
category = 'CS1 maint: untitled periodical',
hidden = true,
},
maint_url_status = {
message = nil,
anchor = 'url_status',
category = 'CS1 maint: url-status',
hidden = true,
},
maint_year= {
message = nil,
anchor = 'year',
category = 'CS1 maint: year',
hidden = true,
},
maint_zbl = {
message = nil,
anchor = 'zbl',
category = 'CS1 maint: 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 (identifier)',
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 = 'Amazon Standard Identification Number',
redirect = 'ASIN (identifier)',
q = 'Q1753278',
label = 'ASIN',
prefix = 'https://www.amazon.',
COinS = 'url',
separator = ' ',
encode = false;
},
['BIBCODE'] = {
parameters = {'bibcode'},
link = 'Bibcode',
redirect = 'Bibcode (identifier)',
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 (identifier)',
q = 'Q19835482',
label = 'bioRxiv',
prefix = 'https://doi.org/',
COinS = 'pre', -- use prefix value
access = 'free', -- free to read
encode = true,
separator = ' ',
},
['CITESEERX'] = {
parameters = {'citeseerx'},
link = 'CiteSeerX',
redirect = 'CiteSeerX (identifier)',
q = 'Q2715061',
label = 'CiteSeerX',
prefix = 'https://citeseerx.ist.psu.edu/viewdoc/summary?doi=',
COinS = 'pre', -- use prefix value
access = 'free', -- free to read
encode = true,
separator = ' ',
},
['DOI'] = { -- Used by InternetArchiveBot
parameters = { 'doi', 'DOI'},
link = 'Digital object identifier',
redirect = 'doi (identifier)',
q = 'Q25670',
label = 'doi',
prefix = 'https://doi.org/',
COinS = 'info:doi',
separator = ':',
encode = true,
custom_access = 'doi-access',
},
['EISSN'] = {
parameters = {'eissn', 'EISSN'},
link = 'International Standard Serial Number#Electronic ISSN',
redirect = 'eISSN (identifier)',
q = 'Q46339674',
label = 'eISSN',
prefix = 'https://search.worldcat.org/issn/',
COinS = 'rft.eissn',
encode = false,
separator = ' ',
},
['HDL'] = {
parameters = { 'hdl', 'HDL' },
link = 'Handle System',
redirect = 'hdl (identifier)',
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 = 'International Standard Book Number',
redirect = 'ISBN (identifier)',
q = 'Q33057',
label = 'ISBN',
prefix = 'Special:BookSources/',
COinS = 'rft.isbn',
separator = ' ',
},
['ISMN'] = {
parameters = {'ismn', 'ISMN'},
link = 'International Standard Music Number',
redirect = 'ISMN (identifier)',
q = 'Q1666938',
label = 'ISMN',
prefix = '', -- not currently used;
COinS = nil, -- nil because we can't use pre or rft or info:
separator = ' ',
},
['ISSN'] = {
parameters = {'issn', 'ISSN'},
link = 'International Standard Serial Number',
redirect = 'ISSN (identifier)',
q = 'Q131276',
label = 'ISSN',
prefix = 'https://search.worldcat.org/issn/',
COinS = 'rft.issn',
encode = false,
separator = ' ',
},
['JFM'] = {
parameters = {'jfm', 'JFM'},
link = 'Jahrbuch über die Fortschritte der Mathematik',
redirect = 'JFM (identifier)',
q = '',
label = 'JFM',
prefix = 'https://zbmath.org/?format=complete&q=an:',
COinS = 'pre', -- use prefix value
encode = true,
separator = ' ',
},
['JSTOR'] = {
parameters = {'jstor', 'JSTOR'},
link = 'JSTOR',
redirect = 'JSTOR (identifier)',
q = 'Q1420342',
label = 'JSTOR',
prefix = 'https://www.jstor.org/stable/',
COinS = 'pre', -- use prefix value
encode = false,
separator = ' ',
custom_access = 'jstor-access',
},
['LCCN'] = {
parameters = {'lccn', 'LCCN'},
link = 'Library of Congress Control Number',
redirect = 'LCCN (identifier)',
q = 'Q620946',
label = 'LCCN',
prefix = 'https://lccn.loc.gov/',
COinS = 'info:lccn',
encode = false,
separator = ' ',
},
['MEDRXIV'] = {
parameters = {'medrxiv'},
link = 'medRxiv',
redirect = 'medRxiv (identifier)',
q = 'Q58465838',
label = 'medRxiv',
prefix = 'https://www.medrxiv.org/content/',
COinS = 'pre', -- use prefix value
access = 'free', -- free to read
encode = false,
separator = ' ',
},
['MR'] = {
parameters = {'mr', 'MR'},
link = 'Mathematical Reviews',
redirect = 'MR (identifier)',
q = 'Q211172',
label = 'MR',
prefix = 'https://mathscinet.ams.org/mathscinet-getitem?mr=',
COinS = 'pre', -- use prefix value
encode = true,
separator = ' ',
},
['OCLC'] = {
parameters = {'oclc', 'OCLC'},
link = 'OCLC',
redirect = 'OCLC (identifier)',
q = 'Q190593',
label = 'OCLC',
prefix = 'https://search.worldcat.org/oclc/',
COinS = 'info:oclcnum',
encode = true,
separator = ' ',
id_limit = id_limits_data_t.OCLC or 0,
},
['OL'] = {
parameters = { 'ol', 'OL' },
link = 'Open Library',
redirect = 'OL (identifier)',
q = 'Q1201876',
label = 'OL',
prefix = 'https://openlibrary.org/',
COinS = 'url',
separator = ' ',
encode = true,
custom_access = 'ol-access',
},
['OSTI'] = {
parameters = {'osti', 'OSTI'},
link = 'Office of Scientific and Technical Information',
redirect = 'OSTI (identifier)',
q = 'Q2015776',
label = 'OSTI',
prefix = 'https://www.osti.gov/biblio/',
COinS = 'pre', -- use prefix value
encode = true,
separator = ' ',
id_limit = id_limits_data_t.OSTI or 0,
custom_access = 'osti-access',
},
['PMC'] = {
parameters = {'pmc', 'PMC'},
link = 'PubMed Central',
redirect = 'PMC (identifier)',
q = 'Q229883',
label = 'PMC',
prefix = 'https://www.ncbi.nlm.nih.gov/pmc/articles/PMC',
suffix = '',
COinS = 'pre', -- use prefix value
encode = true,
separator = ' ',
id_limit = id_limits_data_t.PMC or 0,
access = 'free', -- free to read
},
['PMID'] = {
parameters = {'pmid', 'PMID'},
link = 'PubMed Identifier',
redirect = 'PMID (identifier)',
q = 'Q2082879',
label = 'PMID',
prefix = 'https://pubmed.ncbi.nlm.nih.gov/',
COinS = 'info:pmid',
encode = false,
separator = ' ',
id_limit = id_limits_data_t.PMID or 0,
},
['RFC'] = {
parameters = {'rfc', 'RFC'},
link = 'Request for Comments',
redirect = 'RFC (identifier)',
q = 'Q212971',
label = 'RFC',
prefix = 'https://tools.ietf.org/html/rfc',
COinS = 'pre', -- use prefix value
encode = false,
separator = ' ',
id_limit = id_limits_data_t.RFC or 0,
access = 'free', -- free to read
},
['SBN'] = {
parameters = {'sbn', 'SBN'},
link = 'Standard Book Number', -- redirect to International_Standard_Book_Number#History
redirect = 'SBN (identifier)',
label = 'SBN',
prefix = 'Special:BookSources/0-', -- prefix has leading zero necessary to make 9-digit sbn a 10-digit isbn
COinS = nil, -- nil because we can't use pre or rft or info:
separator = ' ',
},
['SSRN'] = {
parameters = {'ssrn', 'SSRN'},
link = 'Social Science Research Network',
redirect = 'SSRN (identifier)',
q = 'Q7550801',
label = 'SSRN',
prefix = 'https://papers.ssrn.com/sol3/papers.cfm?abstract_id=',
COinS = 'pre', -- use prefix value
encode = true,
separator = ' ',
id_limit = id_limits_data_t.SSRN or 0,
custom_access = 'ssrn-access',
},
['S2CID'] = {
parameters = {'s2cid', 'S2CID'},
link = 'Semantic Scholar',
redirect = 'S2CID (identifier)',
q = 'Q22908627',
label = 'S2CID',
prefix = 'https://api.semanticscholar.org/CorpusID:',
COinS = 'pre', -- use prefix value
encode = false,
separator = ' ',
id_limit = id_limits_data_t.S2CID or 0,
custom_access = 's2cid-access',
},
['USENETID'] = {
parameters = {'message-id'},
link = 'Usenet',
redirect = 'Usenet (identifier)',
q = 'Q193162',
label = 'Usenet:',
prefix = 'news:',
encode = false,
COinS = 'pre', -- use prefix value
separator = ' ',
},
['ZBL'] = {
parameters = {'zbl', 'ZBL' },
link = 'Zentralblatt MATH',
redirect = 'Zbl (identifier)',
q = 'Q190269',
label = 'Zbl',
prefix = 'https://zbmath.org/?format=complete&q=an:',
COinS = 'pre', -- use prefix value
encode = true,
separator = ' ',
},
}
--[[--------------------------< E X P O R T S >---------------------------------
]]
return {
use_identifier_redirects = use_identifier_redirects, -- booleans defined in the settings at the top of this module
local_lang_cat_enable = local_lang_cat_enable,
date_name_auto_xlate_enable = date_name_auto_xlate_enable,
date_digit_auto_xlate_enable = date_digit_auto_xlate_enable,
enable_sort_keys = enable_sort_keys,
-- tables and variables created when this module is loaded
global_df = get_date_format (), -- this line can be replaced with "global_df = 'dmy-all'," to have all dates auto translated to dmy format.
global_cs1_config_t = global_cs1_config_t, -- global settings from {{cs1 config}}
punct_skip = build_skip_table (punct_skip, punct_meta_params),
url_skip = build_skip_table (url_skip, url_meta_params),
known_free_doi_registrants_t = build_free_doi_registrants_table(),
id_limits_data_load_fail = id_limits_data_load_fail, -- true when commons tabular identifier-limit data fails to load
name_space_sort_keys = name_space_sort_keys,
aliases = aliases,
special_case_translation = special_case_translation,
date_names = date_names,
err_msg_supl = err_msg_supl,
error_conditions = error_conditions,
editor_markup_patterns = editor_markup_patterns,
et_al_patterns = et_al_patterns,
extended_registrants_t = extended_registrants_t,
id_handlers = id_handlers,
keywords_lists = keywords_lists,
keywords_xlate = keywords_xlate,
stripmarkers = stripmarkers,
invisible_chars = invisible_chars,
invisible_defs = invisible_defs,
indic_script = indic_script,
emoji_t = emoji_t,
maint_cats = maint_cats,
messages = messages,
presentation = presentation,
prop_cats = prop_cats,
script_lang_codes = script_lang_codes,
lang_tag_remap = lang_tag_remap,
lang_name_remap = lang_name_remap,
this_wiki_code = this_wiki_code,
title_types = title_types,
uncategorized_namespaces = uncategorized_namespaces_t,
uncategorized_subpages = uncategorized_subpages,
templates_using_volume = templates_using_volume,
templates_using_issue = templates_using_issue,
templates_not_using_page = templates_not_using_page,
vol_iss_pg_patterns = vol_iss_pg_patterns,
single_letter_2nd_lvl_domains_t = single_letter_2nd_lvl_domains_t,
inter_wiki_map = inter_wiki_map,
mw_languages_by_tag_t = mw_languages_by_tag_t,
mw_languages_by_name_t = mw_languages_by_name_t,
citation_class_map_t = citation_class_map_t,
citation_issue_t = citation_issue_t,
citation_no_volume_t = citation_no_volume_t,
}
l7qsbr5wk96jhtxz6pbcv9miplunyox
વિભાગ:Citation/CS1/Whitelist
828
50996
886346
821289
2024-08-17T18:39:09Z
en>Trappist the monk
0
sync from sandbox;
886346
Scribunto
text/plain
--[[--------------------------< S U P P O R T E D P A R A M E T E R S >--------------------------------------
Because a steady-state signal conveys no useful information, whitelist.basic_arguments[] list items can have three values:
true - these parameters are valid and supported parameters
false - these parameters are deprecated but still supported
tracked - these parameters are valid and supported parameters tracked in an eponymous properties category
nil - these parameters are no longer supported. remove entirely
]]
local basic_arguments_t = {
['accessdate'] = true,
['access-date'] = true,
['agency'] = true,
['archivedate'] = true,
['archive-date'] = true,
['archive-format'] = true,
['archiveurl'] = true,
['archive-url'] = true,
['article'] = true,
['article-format'] = true,
['article-number'] = true, -- {{cite journal}}, {{cite conference}}; {{citation}} when |journal= has a value
['article-url'] = true,
['article-url-access'] = true,
['arxiv'] = true, -- cite arxiv; here because allowed in cite ... as identifier
['asin'] = true,
['ASIN'] = true,
['asin-tld'] = true,
['at'] = true,
['author'] = true,
['author-first'] = true,
['author-given'] = true,
['author-last'] = true,
['author-surname'] = true,
['authorlink'] = true,
['author-link'] = true,
['author-mask'] = true,
['bibcode'] = true,
['bibcode-access'] = true,
['biorxiv'] = true, -- cite biorxiv; here because allowed in cite ... as identifier
['chapter'] = true,
['chapter-format'] = true,
['chapter-url'] = true,
['chapter-url-access'] = true,
['citeseerx'] = true, -- cite citeseerx; here because allowed in cite ... as identifier
['collaboration'] = true,
['contribution'] = true,
['contribution-format'] = true,
['contribution-url'] = true,
['contribution-url-access'] = true,
['contributor'] = true,
['contributor-first'] = true,
['contributor-given'] = true,
['contributor-last'] = true,
['contributor-surname'] = true,
['contributor-link'] = true,
['contributor-mask'] = true,
['date'] = true,
['department'] = true,
['df'] = true,
['dictionary'] = true,
['display-authors'] = true,
['display-contributors'] = true,
['display-editors'] = true,
['display-interviewers'] = true,
['display-subjects'] = true,
['display-translators'] = true,
['doi'] = true,
['DOI'] = true,
['doi-access'] = true,
['doi-broken-date'] = true,
['edition'] = true,
['editor'] = true,
['editor-first'] = true,
['editor-given'] = true,
['editor-last'] = true,
['editor-surname'] = true,
['editor-link'] = true,
['editor-mask'] = true,
['eissn'] = true,
['EISSN'] = true,
['encyclopaedia'] = true,
['encyclopedia'] = true,
['entry'] = true,
['entry-format'] = true,
['entry-url'] = true,
['entry-url-access'] = true,
['eprint'] = true, -- cite arxiv; here because allowed in cite ... as identifier
['first'] = true,
['format'] = true,
['given'] = true,
['hdl'] = true,
['HDL'] = true,
['hdl-access'] = true,
['host'] = true, -- unique to certain templates?
['id'] = true,
['ID'] = true,
['institution'] = true, -- constrain to cite thesis?
['interviewer'] = true,
['interviewer-first'] = true,
['interviewer-given'] = true,
['interviewer-last'] = true,
['interviewer-surname'] = true,
['interviewer-link'] = true,
['interviewer-mask'] = true,
['isbn'] = true,
['ISBN'] = true,
['ismn'] = true,
['ISMN'] = true,
['issn'] = true,
['ISSN'] = true,
['issue'] = true,
['jfm'] = true,
['JFM'] = true,
['journal'] = true,
['jstor'] = true,
['JSTOR'] = true,
['jstor-access'] = true,
['lang'] = true,
['language'] = true,
['last'] = true,
['lccn'] = true,
['LCCN'] = true,
['location'] = true,
['magazine'] = true,
['medium'] = true,
['medrxiv'] = true, -- cite medrxiv; here because allowed in cite ... as identifier
['minutes'] = true, -- constrain to cite AV media and podcast?
['mode'] = true,
['mr'] = true,
['MR'] = true,
['name-list-style'] = true,
['newspaper'] = true,
['no-pp'] = true,
['no-tracking'] = true,
['number'] = true,
['oclc'] = true,
['OCLC'] = true,
['ol'] = true,
['OL'] = true,
['ol-access'] = true,
['orig-date'] = true,
['origyear'] = true,
['orig-year'] = true,
['osti'] = true,
['OSTI'] = true,
['osti-access'] = true,
['others'] = true,
['p'] = true,
['page'] = true,
['pages'] = true,
['periodical'] = true,
['place'] = true,
['pmc'] = true,
['PMC'] = true,
['pmc-embargo-date'] = true,
['pmid'] = true,
['PMID'] = true,
['postscript'] = true,
['pp'] = true,
['publication-date'] = true,
['publication-place'] = true,
['publisher'] = true,
['quotation'] = true,
['quote'] = true,
['quote-page'] = true,
['quote-pages'] = true,
['ref'] = true,
['rfc'] = true,
['RFC'] = true,
['sbn'] = true,
['SBN'] = true,
['scale'] = true,
['script-article'] = true,
['script-chapter'] = true,
['script-contribution'] = true,
['script-encyclopaedia'] = true,
['script-encyclopedia'] = true,
['script-entry'] = true,
['script-journal'] = true,
['script-magazine'] = true,
['script-newspaper'] = true,
['script-periodical'] = true,
['script-quote'] = true,
['script-section'] = true,
['script-title'] = true,
['script-website'] = true,
['script-work'] = true,
['section'] = true,
['section-format'] = true,
['section-url'] = true,
['section-url-access'] = true,
['series'] = true,
['ssrn'] = true, -- cite ssrn; these three here because allowed in cite ... as identifier
['SSRN'] = true,
['ssrn-access'] = true,
['subject'] = true,
['subject-first'] = true,
['subject-given'] = true,
['subject-last'] = true,
['subject-link'] = true,
['subject-mask'] = true,
['subject-surname'] = true,
['surname'] = true,
['s2cid'] = true,
['S2CID'] = true,
['s2cid-access'] = true,
['template-doc-demo'] = true,
['time'] = true, -- constrain to cite av media and podcast?
['time-caption'] = true, -- constrain to cite av media and podcast?
['title'] = true,
['title-link'] = true,
['title-note'] = true,
['translator'] = true,
['translator-first'] = true,
['translator-given'] = true,
['translator-last'] = true,
['translator-surname'] = true,
['translator-link'] = true,
['translator-mask'] = true,
['trans-article'] = true,
['trans-chapter'] = true,
['trans-contribution'] = true,
['trans-encyclopaedia'] = true,
['trans-encyclopedia'] = true,
['trans-entry'] = true,
['trans-journal'] = true,
['trans-magazine'] = true,
['trans-newspaper'] = true,
['trans-periodical'] = true,
['trans-quote'] = true,
['trans-section'] = true,
['trans-title'] = true,
['trans-website'] = true,
['trans-work'] = true,
['type'] = true,
['url'] = true,
['URL'] = true,
['url-access'] = true,
['url-status'] = true,
['vauthors'] = true,
['veditors'] = true,
['version'] = true,
['via'] = true,
['volume'] = true,
['website'] = true,
['work'] = true,
['year'] = true,
['zbl'] = true,
['ZBL'] = true,
}
local numbered_arguments_t = {
['author#'] = true,
['author-first#'] = true,
['author#-first'] = true,
['author-given#'] = true,
['author#-given'] = true,
['author-last#'] = true,
['author#-last'] = true,
['author-surname#'] = true,
['author#-surname'] = true,
['author-link#'] = true,
['author#-link'] = true,
['authorlink#'] = true,
['author#link'] = true,
['author-mask#'] = true,
['author#-mask'] = true,
['contributor#'] = true,
['contributor-first#'] = true,
['contributor#-first'] = true,
['contributor-given#'] = true,
['contributor#-given'] = true,
['contributor-last#'] = true,
['contributor#-last'] = true,
['contributor-surname#'] = true,
['contributor#-surname'] = true,
['contributor-link#'] = true,
['contributor#-link'] = true,
['contributor-mask#'] = true,
['contributor#-mask'] = true,
['editor#'] = true,
['editor-first#'] = true,
['editor#-first'] = true,
['editor-given#'] = true,
['editor#-given'] = true,
['editor-last#'] = true,
['editor#-last'] = true,
['editor-surname#'] = true,
['editor#-surname'] = true,
['editor-link#'] = true,
['editor#-link'] = true,
['editor-mask#'] = true,
['editor#-mask'] = true,
['first#'] = true,
['given#'] = true,
['host#'] = true,
['interviewer#'] = true,
['interviewer-first#'] = true,
['interviewer#-first'] = true,
['interviewer-given#'] = true,
['interviewer#-given'] = true,
['interviewer-last#'] = true,
['interviewer#-last'] = true,
['interviewer-surname#'] = true,
['interviewer#-surname'] = true,
['interviewer-link#'] = true,
['interviewer#-link'] = true,
['interviewer-mask#'] = true,
['interviewer#-mask'] = true,
['last#'] = true,
['subject#'] = true,
['subject-first#'] = true,
['subject#-first'] = true,
['subject-given#'] = true,
['subject#-given'] = true,
['subject-last#'] = true,
['subject#-last'] = true,
['subject-link#'] = true,
['subject#-link'] = true,
['subject-mask#'] = true,
['subject#-mask'] = true,
['subject-surname#'] = true,
['subject#-surname'] = true,
['surname#'] = true,
['translator#'] = true,
['translator-first#'] = true,
['translator#-first'] = true,
['translator-given#'] = true,
['translator#-given'] = true,
['translator-last#'] = true,
['translator#-last'] = true,
['translator-surname#'] = true,
['translator#-surname'] = true,
['translator-link#'] = true,
['translator#-link'] = true,
['translator-mask#'] = true,
['translator#-mask'] = true,
}
--[[--------------------------< P R E P R I N T S U P P O R T E D P A R A M E T E R S >--------------------
Cite arXiv, cite biorxiv, cite citeseerx, cite medrxiv, and cite ssrn are preprint templates that use the limited
set of parameters defined in the limited_basic_arguments and limited_numbered_arguments tables. Those lists are
supplemented with a template-specific list of parameters that are required by the particular template and may be
exclusive to one of the preprint templates. Some of these parameters may also be available to the general cs1|2
templates.
Same conventions for true/false/tracked/nil as above.
]]
local preprint_arguments_t = {
arxiv = {
['arxiv'] = true, -- cite arxiv and arxiv identifiers
['class'] = true,
['eprint'] = true, -- cite arxiv and arxiv identifiers
},
biorxiv = {
['biorxiv'] = true,
},
citeseerx = {
['citeseerx'] = true,
},
medrxiv = {
['medrxiv'] = true,
},
ssrn = {
['ssrn'] = true,
['SSRN'] = true,
['ssrn-access'] = true,
},
}
--[[--------------------------< L I M I T E D S U P P O R T E D P A R A M E T E R S >----------------------
cite arxiv, cite biorxiv, cite citeseerx, cite medrxiv, and cite ssrn templates are preprint templates so are
allowed only a limited subset of parameters allowed to all other cs1|2 templates. The limited subset is defined
here.
This list of parameters also used by {{cite document}}
Same conventions for true/false/tracked/nil as above.
]]
local limited_basic_arguments_t = {
['at'] = true,
['author'] = true,
['author-first'] = true,
['author-given'] = true,
['author-last'] = true,
['author-surname'] = true,
['author-link'] = true,
['authorlink'] = true,
['author-mask'] = true,
['collaboration'] = true,
['date'] = true,
['df'] = true,
['display-authors'] = true,
['first'] = true,
['given'] = true,
['language'] = true,
['last'] = true,
['mode'] = true,
['name-list-style'] = true,
['no-tracking'] = true,
['p'] = true,
['page'] = true,
['pages'] = true,
['postscript'] = true,
['pp'] = true,
['quotation'] = true,
['quote'] = true,
['ref'] = true,
['surname'] = true,
['template-doc-demo'] = true,
['title'] = true,
['trans-title'] = true,
['vauthors'] = true,
['year'] = true,
}
local limited_numbered_arguments_t = {
['author#'] = true,
['author-first#'] = true,
['author#-first'] = true,
['author-given#'] = true,
['author#-given'] = true,
['author-last#'] = true,
['author#-last'] = true,
['author-surname#'] = true,
['author#-surname'] = true,
['author-link#'] = true,
['author#-link'] = true,
['authorlink#'] = true,
['author#link'] = true,
['author-mask#'] = true,
['author#-mask'] = true,
['first#'] = true,
['given#'] = true,
['last#'] = true,
['surname#'] = true,
}
--[[--------------------------< U N I Q U E _ A R G U M E N T S >----------------------------------------------
Some templates have unique parameters. Those templates and their unique parameters are listed here. Keys in this
table are the template's CitationClass parameter value
Same conventions for true/false/tracked/nil as above.
]]
local unique_arguments_t = {
['audio-visual'] = {
['people'] = true,
['transcript'] = true,
['transcript-format'] = true,
['transcript-url'] = true,
},
conference = {
['book-title'] = true,
['conference'] = true,
['conference-format'] = true,
['conference-url'] = true,
['event'] = true,
},
episode = {
['airdate'] = true,
['air-date'] = true,
['credits'] = true,
['episode-link'] = true, -- alias of |title-link=
['network'] = true,
['people'] = true,
['season'] = true,
['series-link'] = true,
['series-no'] = true,
['series-number'] = true,
['station'] = true,
['transcript'] = true,
['transcript-format'] = true,
['transcript-url'] = true,
},
mailinglist = {
['mailing-list'] = true,
},
map = {
['cartography'] = true,
['inset'] = true,
['map'] = true,
['map-format'] = true,
['map-url'] = true,
['map-url-access'] = true,
['script-map'] = true,
['sections'] = true,
['sheet'] = true,
['sheets'] = true,
['trans-map'] = true,
},
newsgroup = {
['message-id'] = true,
['newsgroup'] = true,
},
report = {
['docket'] = true,
},
serial = {
['airdate'] = true,
['air-date'] = true,
['credits'] = true,
['episode'] = true, -- cite serial only TODO: make available to cite episode?
['episode-link'] = true, -- alias of |title-link=
['network'] = true,
['people'] = true,
['series-link'] = true,
['station'] = true,
},
speech = {
['conference'] = true,
['conference-format'] = true,
['conference-url'] = true,
['event'] = true,
},
thesis = {
['degree'] = true,
['docket'] = true,
},
}
--[[--------------------------< C I T E _ D O C U M E N T >----------------------------------------------------
Special case for cite document. This template takes the limited basic and limited enumerated parameters plus
others that are apply only to standalone published sources that cannot be cited any other way; no url, book,
periodical, etc parameters; limited support for name lists and named identifiers.
when validating parameters in {{cite document}} templates, the basic and
]]
local document_arguments_t = {
['bibcode'] = true,
['bibcode-access'] = true,
['doi'] = true,
['DOI'] = true,
['doi-access'] = true,
['doi-broken-date'] = true,
['hdl'] = true,
['HDL'] = true,
['hdl-access'] = true,
['id'] = true,
['ID'] = true,
['jfm'] = true,
['JFM'] = true,
['lang'] = true,
['location'] = true,
['mr'] = true,
['MR'] = true,
['no-pp'] = true,
['orig-date'] = true,
['origyear'] = true,
['orig-year'] = true,
['osti'] = true,
['OSTI'] = true,
['osti-access'] = true,
['place'] = true,
['publisher'] = true,
['quote-page'] = true,
['quote-pages'] = true,
['script-quote'] = true,
['script-title'] = true,
['title-link'] = true,
['translator'] = true,
['translator-first'] = true,
['translator-given'] = true,
['translator-last'] = true,
['translator-surname'] = true,
['translator-link'] = true,
['translator-mask'] = true,
['trans-quote'] = true,
['type'] = true,
['zbl'] = true,
['ZBL'] = true,
}
local document_numbered_arguments_t = {
['translator#'] = true,
['translator-first#'] = true,
['translator#-first'] = true,
['translator-given#'] = true,
['translator#-given'] = true,
['translator-last#'] = true,
['translator#-last'] = true,
['translator-surname#'] = true,
['translator#-surname'] = true,
['translator-link#'] = true,
['translator#-link'] = true,
['translator-mask#'] = true,
['translator#-mask'] = true,
}
--[[--------------------------< L I S T _ C O M B I N E >------------------------------------------------------
makes one table from a list of tables. <lists_t> is a sequence of tables to be combined
]]
local function list_combine (lists_t)
local out_t = {};
for _, list_t in ipairs (lists_t) do -- for each list in <lists_t>
for k, v in pairs (list_t) do -- extract each k/v pair
out_t[k] = v; -- add to <out_t>
end
end
return out_t; -- and done
end
--[[--------------------------< T E M P L A T E _ L I S T _ G E T >--------------------------------------------
gets a list of the templates from table t
]]
local function template_list_get (t)
local out_t = {}; -- a table for output
for k, _ in pairs (t) do -- spin through the table and collect the keys
table.insert (out_t, k) -- add each key to the output table
end
return out_t; -- and done
end
--[[--------------------------< E X P O R T E D T A B L E S >------------------------------------------------
]]
return {
preprint_arguments_t = preprint_arguments_t,
preprint_template_list_t = template_list_get (preprint_arguments_t), -- make a template list from preprint_arguments{} table
unique_arguments_t = unique_arguments_t,
unique_param_template_list_t = template_list_get (unique_arguments_t), -- make a template list from unique_arguments{} table
document_parameters_t = list_combine ({limited_basic_arguments_t, limited_numbered_arguments_t, document_arguments_t, document_numbered_arguments_t});
common_parameters_t = list_combine ({basic_arguments_t, numbered_arguments_t});
limited_parameters_t = list_combine ({limited_basic_arguments_t, limited_numbered_arguments_t});
};
baub5v4d976tff8ebf3konhoua4ojgu
886347
886346
2025-06-13T17:03:24Z
KartikMistry
10383
[[:en:Module:Citation/CS1/Whitelist]] માંથી આયાત કરેલ ૧ પુનરાવર્તન
886346
Scribunto
text/plain
--[[--------------------------< S U P P O R T E D P A R A M E T E R S >--------------------------------------
Because a steady-state signal conveys no useful information, whitelist.basic_arguments[] list items can have three values:
true - these parameters are valid and supported parameters
false - these parameters are deprecated but still supported
tracked - these parameters are valid and supported parameters tracked in an eponymous properties category
nil - these parameters are no longer supported. remove entirely
]]
local basic_arguments_t = {
['accessdate'] = true,
['access-date'] = true,
['agency'] = true,
['archivedate'] = true,
['archive-date'] = true,
['archive-format'] = true,
['archiveurl'] = true,
['archive-url'] = true,
['article'] = true,
['article-format'] = true,
['article-number'] = true, -- {{cite journal}}, {{cite conference}}; {{citation}} when |journal= has a value
['article-url'] = true,
['article-url-access'] = true,
['arxiv'] = true, -- cite arxiv; here because allowed in cite ... as identifier
['asin'] = true,
['ASIN'] = true,
['asin-tld'] = true,
['at'] = true,
['author'] = true,
['author-first'] = true,
['author-given'] = true,
['author-last'] = true,
['author-surname'] = true,
['authorlink'] = true,
['author-link'] = true,
['author-mask'] = true,
['bibcode'] = true,
['bibcode-access'] = true,
['biorxiv'] = true, -- cite biorxiv; here because allowed in cite ... as identifier
['chapter'] = true,
['chapter-format'] = true,
['chapter-url'] = true,
['chapter-url-access'] = true,
['citeseerx'] = true, -- cite citeseerx; here because allowed in cite ... as identifier
['collaboration'] = true,
['contribution'] = true,
['contribution-format'] = true,
['contribution-url'] = true,
['contribution-url-access'] = true,
['contributor'] = true,
['contributor-first'] = true,
['contributor-given'] = true,
['contributor-last'] = true,
['contributor-surname'] = true,
['contributor-link'] = true,
['contributor-mask'] = true,
['date'] = true,
['department'] = true,
['df'] = true,
['dictionary'] = true,
['display-authors'] = true,
['display-contributors'] = true,
['display-editors'] = true,
['display-interviewers'] = true,
['display-subjects'] = true,
['display-translators'] = true,
['doi'] = true,
['DOI'] = true,
['doi-access'] = true,
['doi-broken-date'] = true,
['edition'] = true,
['editor'] = true,
['editor-first'] = true,
['editor-given'] = true,
['editor-last'] = true,
['editor-surname'] = true,
['editor-link'] = true,
['editor-mask'] = true,
['eissn'] = true,
['EISSN'] = true,
['encyclopaedia'] = true,
['encyclopedia'] = true,
['entry'] = true,
['entry-format'] = true,
['entry-url'] = true,
['entry-url-access'] = true,
['eprint'] = true, -- cite arxiv; here because allowed in cite ... as identifier
['first'] = true,
['format'] = true,
['given'] = true,
['hdl'] = true,
['HDL'] = true,
['hdl-access'] = true,
['host'] = true, -- unique to certain templates?
['id'] = true,
['ID'] = true,
['institution'] = true, -- constrain to cite thesis?
['interviewer'] = true,
['interviewer-first'] = true,
['interviewer-given'] = true,
['interviewer-last'] = true,
['interviewer-surname'] = true,
['interviewer-link'] = true,
['interviewer-mask'] = true,
['isbn'] = true,
['ISBN'] = true,
['ismn'] = true,
['ISMN'] = true,
['issn'] = true,
['ISSN'] = true,
['issue'] = true,
['jfm'] = true,
['JFM'] = true,
['journal'] = true,
['jstor'] = true,
['JSTOR'] = true,
['jstor-access'] = true,
['lang'] = true,
['language'] = true,
['last'] = true,
['lccn'] = true,
['LCCN'] = true,
['location'] = true,
['magazine'] = true,
['medium'] = true,
['medrxiv'] = true, -- cite medrxiv; here because allowed in cite ... as identifier
['minutes'] = true, -- constrain to cite AV media and podcast?
['mode'] = true,
['mr'] = true,
['MR'] = true,
['name-list-style'] = true,
['newspaper'] = true,
['no-pp'] = true,
['no-tracking'] = true,
['number'] = true,
['oclc'] = true,
['OCLC'] = true,
['ol'] = true,
['OL'] = true,
['ol-access'] = true,
['orig-date'] = true,
['origyear'] = true,
['orig-year'] = true,
['osti'] = true,
['OSTI'] = true,
['osti-access'] = true,
['others'] = true,
['p'] = true,
['page'] = true,
['pages'] = true,
['periodical'] = true,
['place'] = true,
['pmc'] = true,
['PMC'] = true,
['pmc-embargo-date'] = true,
['pmid'] = true,
['PMID'] = true,
['postscript'] = true,
['pp'] = true,
['publication-date'] = true,
['publication-place'] = true,
['publisher'] = true,
['quotation'] = true,
['quote'] = true,
['quote-page'] = true,
['quote-pages'] = true,
['ref'] = true,
['rfc'] = true,
['RFC'] = true,
['sbn'] = true,
['SBN'] = true,
['scale'] = true,
['script-article'] = true,
['script-chapter'] = true,
['script-contribution'] = true,
['script-encyclopaedia'] = true,
['script-encyclopedia'] = true,
['script-entry'] = true,
['script-journal'] = true,
['script-magazine'] = true,
['script-newspaper'] = true,
['script-periodical'] = true,
['script-quote'] = true,
['script-section'] = true,
['script-title'] = true,
['script-website'] = true,
['script-work'] = true,
['section'] = true,
['section-format'] = true,
['section-url'] = true,
['section-url-access'] = true,
['series'] = true,
['ssrn'] = true, -- cite ssrn; these three here because allowed in cite ... as identifier
['SSRN'] = true,
['ssrn-access'] = true,
['subject'] = true,
['subject-first'] = true,
['subject-given'] = true,
['subject-last'] = true,
['subject-link'] = true,
['subject-mask'] = true,
['subject-surname'] = true,
['surname'] = true,
['s2cid'] = true,
['S2CID'] = true,
['s2cid-access'] = true,
['template-doc-demo'] = true,
['time'] = true, -- constrain to cite av media and podcast?
['time-caption'] = true, -- constrain to cite av media and podcast?
['title'] = true,
['title-link'] = true,
['title-note'] = true,
['translator'] = true,
['translator-first'] = true,
['translator-given'] = true,
['translator-last'] = true,
['translator-surname'] = true,
['translator-link'] = true,
['translator-mask'] = true,
['trans-article'] = true,
['trans-chapter'] = true,
['trans-contribution'] = true,
['trans-encyclopaedia'] = true,
['trans-encyclopedia'] = true,
['trans-entry'] = true,
['trans-journal'] = true,
['trans-magazine'] = true,
['trans-newspaper'] = true,
['trans-periodical'] = true,
['trans-quote'] = true,
['trans-section'] = true,
['trans-title'] = true,
['trans-website'] = true,
['trans-work'] = true,
['type'] = true,
['url'] = true,
['URL'] = true,
['url-access'] = true,
['url-status'] = true,
['vauthors'] = true,
['veditors'] = true,
['version'] = true,
['via'] = true,
['volume'] = true,
['website'] = true,
['work'] = true,
['year'] = true,
['zbl'] = true,
['ZBL'] = true,
}
local numbered_arguments_t = {
['author#'] = true,
['author-first#'] = true,
['author#-first'] = true,
['author-given#'] = true,
['author#-given'] = true,
['author-last#'] = true,
['author#-last'] = true,
['author-surname#'] = true,
['author#-surname'] = true,
['author-link#'] = true,
['author#-link'] = true,
['authorlink#'] = true,
['author#link'] = true,
['author-mask#'] = true,
['author#-mask'] = true,
['contributor#'] = true,
['contributor-first#'] = true,
['contributor#-first'] = true,
['contributor-given#'] = true,
['contributor#-given'] = true,
['contributor-last#'] = true,
['contributor#-last'] = true,
['contributor-surname#'] = true,
['contributor#-surname'] = true,
['contributor-link#'] = true,
['contributor#-link'] = true,
['contributor-mask#'] = true,
['contributor#-mask'] = true,
['editor#'] = true,
['editor-first#'] = true,
['editor#-first'] = true,
['editor-given#'] = true,
['editor#-given'] = true,
['editor-last#'] = true,
['editor#-last'] = true,
['editor-surname#'] = true,
['editor#-surname'] = true,
['editor-link#'] = true,
['editor#-link'] = true,
['editor-mask#'] = true,
['editor#-mask'] = true,
['first#'] = true,
['given#'] = true,
['host#'] = true,
['interviewer#'] = true,
['interviewer-first#'] = true,
['interviewer#-first'] = true,
['interviewer-given#'] = true,
['interviewer#-given'] = true,
['interviewer-last#'] = true,
['interviewer#-last'] = true,
['interviewer-surname#'] = true,
['interviewer#-surname'] = true,
['interviewer-link#'] = true,
['interviewer#-link'] = true,
['interviewer-mask#'] = true,
['interviewer#-mask'] = true,
['last#'] = true,
['subject#'] = true,
['subject-first#'] = true,
['subject#-first'] = true,
['subject-given#'] = true,
['subject#-given'] = true,
['subject-last#'] = true,
['subject#-last'] = true,
['subject-link#'] = true,
['subject#-link'] = true,
['subject-mask#'] = true,
['subject#-mask'] = true,
['subject-surname#'] = true,
['subject#-surname'] = true,
['surname#'] = true,
['translator#'] = true,
['translator-first#'] = true,
['translator#-first'] = true,
['translator-given#'] = true,
['translator#-given'] = true,
['translator-last#'] = true,
['translator#-last'] = true,
['translator-surname#'] = true,
['translator#-surname'] = true,
['translator-link#'] = true,
['translator#-link'] = true,
['translator-mask#'] = true,
['translator#-mask'] = true,
}
--[[--------------------------< P R E P R I N T S U P P O R T E D P A R A M E T E R S >--------------------
Cite arXiv, cite biorxiv, cite citeseerx, cite medrxiv, and cite ssrn are preprint templates that use the limited
set of parameters defined in the limited_basic_arguments and limited_numbered_arguments tables. Those lists are
supplemented with a template-specific list of parameters that are required by the particular template and may be
exclusive to one of the preprint templates. Some of these parameters may also be available to the general cs1|2
templates.
Same conventions for true/false/tracked/nil as above.
]]
local preprint_arguments_t = {
arxiv = {
['arxiv'] = true, -- cite arxiv and arxiv identifiers
['class'] = true,
['eprint'] = true, -- cite arxiv and arxiv identifiers
},
biorxiv = {
['biorxiv'] = true,
},
citeseerx = {
['citeseerx'] = true,
},
medrxiv = {
['medrxiv'] = true,
},
ssrn = {
['ssrn'] = true,
['SSRN'] = true,
['ssrn-access'] = true,
},
}
--[[--------------------------< L I M I T E D S U P P O R T E D P A R A M E T E R S >----------------------
cite arxiv, cite biorxiv, cite citeseerx, cite medrxiv, and cite ssrn templates are preprint templates so are
allowed only a limited subset of parameters allowed to all other cs1|2 templates. The limited subset is defined
here.
This list of parameters also used by {{cite document}}
Same conventions for true/false/tracked/nil as above.
]]
local limited_basic_arguments_t = {
['at'] = true,
['author'] = true,
['author-first'] = true,
['author-given'] = true,
['author-last'] = true,
['author-surname'] = true,
['author-link'] = true,
['authorlink'] = true,
['author-mask'] = true,
['collaboration'] = true,
['date'] = true,
['df'] = true,
['display-authors'] = true,
['first'] = true,
['given'] = true,
['language'] = true,
['last'] = true,
['mode'] = true,
['name-list-style'] = true,
['no-tracking'] = true,
['p'] = true,
['page'] = true,
['pages'] = true,
['postscript'] = true,
['pp'] = true,
['quotation'] = true,
['quote'] = true,
['ref'] = true,
['surname'] = true,
['template-doc-demo'] = true,
['title'] = true,
['trans-title'] = true,
['vauthors'] = true,
['year'] = true,
}
local limited_numbered_arguments_t = {
['author#'] = true,
['author-first#'] = true,
['author#-first'] = true,
['author-given#'] = true,
['author#-given'] = true,
['author-last#'] = true,
['author#-last'] = true,
['author-surname#'] = true,
['author#-surname'] = true,
['author-link#'] = true,
['author#-link'] = true,
['authorlink#'] = true,
['author#link'] = true,
['author-mask#'] = true,
['author#-mask'] = true,
['first#'] = true,
['given#'] = true,
['last#'] = true,
['surname#'] = true,
}
--[[--------------------------< U N I Q U E _ A R G U M E N T S >----------------------------------------------
Some templates have unique parameters. Those templates and their unique parameters are listed here. Keys in this
table are the template's CitationClass parameter value
Same conventions for true/false/tracked/nil as above.
]]
local unique_arguments_t = {
['audio-visual'] = {
['people'] = true,
['transcript'] = true,
['transcript-format'] = true,
['transcript-url'] = true,
},
conference = {
['book-title'] = true,
['conference'] = true,
['conference-format'] = true,
['conference-url'] = true,
['event'] = true,
},
episode = {
['airdate'] = true,
['air-date'] = true,
['credits'] = true,
['episode-link'] = true, -- alias of |title-link=
['network'] = true,
['people'] = true,
['season'] = true,
['series-link'] = true,
['series-no'] = true,
['series-number'] = true,
['station'] = true,
['transcript'] = true,
['transcript-format'] = true,
['transcript-url'] = true,
},
mailinglist = {
['mailing-list'] = true,
},
map = {
['cartography'] = true,
['inset'] = true,
['map'] = true,
['map-format'] = true,
['map-url'] = true,
['map-url-access'] = true,
['script-map'] = true,
['sections'] = true,
['sheet'] = true,
['sheets'] = true,
['trans-map'] = true,
},
newsgroup = {
['message-id'] = true,
['newsgroup'] = true,
},
report = {
['docket'] = true,
},
serial = {
['airdate'] = true,
['air-date'] = true,
['credits'] = true,
['episode'] = true, -- cite serial only TODO: make available to cite episode?
['episode-link'] = true, -- alias of |title-link=
['network'] = true,
['people'] = true,
['series-link'] = true,
['station'] = true,
},
speech = {
['conference'] = true,
['conference-format'] = true,
['conference-url'] = true,
['event'] = true,
},
thesis = {
['degree'] = true,
['docket'] = true,
},
}
--[[--------------------------< C I T E _ D O C U M E N T >----------------------------------------------------
Special case for cite document. This template takes the limited basic and limited enumerated parameters plus
others that are apply only to standalone published sources that cannot be cited any other way; no url, book,
periodical, etc parameters; limited support for name lists and named identifiers.
when validating parameters in {{cite document}} templates, the basic and
]]
local document_arguments_t = {
['bibcode'] = true,
['bibcode-access'] = true,
['doi'] = true,
['DOI'] = true,
['doi-access'] = true,
['doi-broken-date'] = true,
['hdl'] = true,
['HDL'] = true,
['hdl-access'] = true,
['id'] = true,
['ID'] = true,
['jfm'] = true,
['JFM'] = true,
['lang'] = true,
['location'] = true,
['mr'] = true,
['MR'] = true,
['no-pp'] = true,
['orig-date'] = true,
['origyear'] = true,
['orig-year'] = true,
['osti'] = true,
['OSTI'] = true,
['osti-access'] = true,
['place'] = true,
['publisher'] = true,
['quote-page'] = true,
['quote-pages'] = true,
['script-quote'] = true,
['script-title'] = true,
['title-link'] = true,
['translator'] = true,
['translator-first'] = true,
['translator-given'] = true,
['translator-last'] = true,
['translator-surname'] = true,
['translator-link'] = true,
['translator-mask'] = true,
['trans-quote'] = true,
['type'] = true,
['zbl'] = true,
['ZBL'] = true,
}
local document_numbered_arguments_t = {
['translator#'] = true,
['translator-first#'] = true,
['translator#-first'] = true,
['translator-given#'] = true,
['translator#-given'] = true,
['translator-last#'] = true,
['translator#-last'] = true,
['translator-surname#'] = true,
['translator#-surname'] = true,
['translator-link#'] = true,
['translator#-link'] = true,
['translator-mask#'] = true,
['translator#-mask'] = true,
}
--[[--------------------------< L I S T _ C O M B I N E >------------------------------------------------------
makes one table from a list of tables. <lists_t> is a sequence of tables to be combined
]]
local function list_combine (lists_t)
local out_t = {};
for _, list_t in ipairs (lists_t) do -- for each list in <lists_t>
for k, v in pairs (list_t) do -- extract each k/v pair
out_t[k] = v; -- add to <out_t>
end
end
return out_t; -- and done
end
--[[--------------------------< T E M P L A T E _ L I S T _ G E T >--------------------------------------------
gets a list of the templates from table t
]]
local function template_list_get (t)
local out_t = {}; -- a table for output
for k, _ in pairs (t) do -- spin through the table and collect the keys
table.insert (out_t, k) -- add each key to the output table
end
return out_t; -- and done
end
--[[--------------------------< E X P O R T E D T A B L E S >------------------------------------------------
]]
return {
preprint_arguments_t = preprint_arguments_t,
preprint_template_list_t = template_list_get (preprint_arguments_t), -- make a template list from preprint_arguments{} table
unique_arguments_t = unique_arguments_t,
unique_param_template_list_t = template_list_get (unique_arguments_t), -- make a template list from unique_arguments{} table
document_parameters_t = list_combine ({limited_basic_arguments_t, limited_numbered_arguments_t, document_arguments_t, document_numbered_arguments_t});
common_parameters_t = list_combine ({basic_arguments_t, numbered_arguments_t});
limited_parameters_t = list_combine ({limited_basic_arguments_t, limited_numbered_arguments_t});
};
baub5v4d976tff8ebf3konhoua4ojgu
વિભાગ:Message box
828
54034
886340
869211
2025-01-29T21:53:54Z
en>Pppery
0
Copy from sandbox per request
886340
Scribunto
text/plain
require('strict')
local getArgs
local yesno = require('Module:Yesno')
local lang = mw.language.getContentLanguage()
local CONFIG_MODULE = 'Module:Message box/configuration'
local DEMOSPACES = {talk = 'tmbox', image = 'imbox', file = 'imbox', category = 'cmbox', article = 'ambox', main = 'ambox'}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getTitleObject(...)
-- Get the title object, passing the function through pcall
-- in case we are over the expensive function count limit.
local success, title = pcall(mw.title.new, ...)
if success then
return title
end
end
local function union(t1, t2)
-- Returns the union of two arrays.
local vals = {}
for i, v in ipairs(t1) do
vals[v] = true
end
for i, v in ipairs(t2) do
vals[v] = true
end
local ret = {}
for k in pairs(vals) do
table.insert(ret, k)
end
table.sort(ret)
return ret
end
local function getArgNums(args, prefix)
local nums = {}
for k, v in pairs(args) do
local num = mw.ustring.match(tostring(k), '^' .. prefix .. '([1-9]%d*)$')
if num then
table.insert(nums, tonumber(num))
end
end
table.sort(nums)
return nums
end
--------------------------------------------------------------------------------
-- Box class definition
--------------------------------------------------------------------------------
local MessageBox = {}
MessageBox.__index = MessageBox
function MessageBox.new(boxType, args, cfg)
args = args or {}
local obj = {}
-- Set the title object and the namespace.
obj.title = getTitleObject(args.page) or mw.title.getCurrentTitle()
-- Set the config for our box type.
obj.cfg = cfg[boxType]
if not obj.cfg then
local ns = obj.title.namespace
-- boxType is "mbox" or invalid input
if args.demospace and args.demospace ~= '' then
-- implement demospace parameter of mbox
local demospace = string.lower(args.demospace)
if DEMOSPACES[demospace] then
-- use template from DEMOSPACES
obj.cfg = cfg[DEMOSPACES[demospace]]
elseif string.find( demospace, 'talk' ) then
-- demo as a talk page
obj.cfg = cfg.tmbox
else
-- default to ombox
obj.cfg = cfg.ombox
end
elseif ns == 0 then
obj.cfg = cfg.ambox -- main namespace
elseif ns == 6 then
obj.cfg = cfg.imbox -- file namespace
elseif ns == 14 then
obj.cfg = cfg.cmbox -- category namespace
else
local nsTable = mw.site.namespaces[ns]
if nsTable and nsTable.isTalk then
obj.cfg = cfg.tmbox -- any talk namespace
else
obj.cfg = cfg.ombox -- other namespaces or invalid input
end
end
end
-- Set the arguments, and remove all blank arguments except for the ones
-- listed in cfg.allowBlankParams.
do
local newArgs = {}
for k, v in pairs(args) do
if v ~= '' then
newArgs[k] = v
end
end
for i, param in ipairs(obj.cfg.allowBlankParams or {}) do
newArgs[param] = args[param]
end
obj.args = newArgs
end
-- Define internal data structure.
obj.categories = {}
obj.classes = {}
-- For lazy loading of [[Module:Category handler]].
obj.hasCategories = false
return setmetatable(obj, MessageBox)
end
function MessageBox:addCat(ns, cat, sort)
if not cat then
return nil
end
if sort then
cat = string.format('[[Category:%s|%s]]', cat, sort)
else
cat = string.format('[[Category:%s]]', cat)
end
self.hasCategories = true
self.categories[ns] = self.categories[ns] or {}
table.insert(self.categories[ns], cat)
end
function MessageBox:addClass(class)
if not class then
return nil
end
table.insert(self.classes, class)
end
function MessageBox:setParameters()
local args = self.args
local cfg = self.cfg
-- Get type data.
self.type = args.type
local typeData = cfg.types[self.type]
self.invalidTypeError = cfg.showInvalidTypeError
and self.type
and not typeData
typeData = typeData or cfg.types[cfg.default]
self.typeClass = typeData.class
self.typeImage = typeData.image
self.typeImageNeedsLink = typeData.imageNeedsLink
-- Find if the box has been wrongly substituted.
self.isSubstituted = cfg.substCheck and args.subst == 'SUBST'
-- Find whether we are using a small message box.
self.isSmall = cfg.allowSmall and (
cfg.smallParam and args.small == cfg.smallParam
or not cfg.smallParam and yesno(args.small)
)
-- Add attributes, classes and styles.
self.id = args.id
self.name = args.name
if self.name then
self:addClass('box-' .. string.gsub(self.name,' ','_'))
end
if yesno(args.plainlinks) ~= false then
self:addClass('plainlinks')
end
for _, class in ipairs(cfg.classes or {}) do
self:addClass(class)
end
if self.isSmall then
self:addClass(cfg.smallClass or 'mbox-small')
end
self:addClass(self.typeClass)
self:addClass(args.class)
self.style = args.style
self.attrs = args.attrs
-- Set text style.
self.textstyle = args.textstyle
-- Set image classes.
self.imageRightClass = args.imagerightclass or args.imageclass
self.imageLeftClass = args.imageleftclass or args.imageclass
-- Find if we are on the template page or not. This functionality is only
-- used if useCollapsibleTextFields is set, or if both cfg.templateCategory
-- and cfg.templateCategoryRequireName are set.
self.useCollapsibleTextFields = cfg.useCollapsibleTextFields
if self.useCollapsibleTextFields
or cfg.templateCategory
and cfg.templateCategoryRequireName
then
if self.name then
local templateName = mw.ustring.match(
self.name,
'^[tT][eE][mM][pP][lL][aA][tT][eE][%s_]*:[%s_]*(.*)$'
) or self.name
templateName = 'Template:' .. templateName
self.templateTitle = getTitleObject(templateName)
end
self.isTemplatePage = self.templateTitle
and mw.title.equals(self.title, self.templateTitle)
end
-- Process data for collapsible text fields. At the moment these are only
-- used in {{ambox}}.
if self.useCollapsibleTextFields then
-- Get the self.issue value.
if self.isSmall and args.smalltext then
self.issue = args.smalltext
else
local sect
if args.sect == '' then
sect = 'This ' .. (cfg.sectionDefault or 'page')
elseif type(args.sect) == 'string' then
sect = 'This ' .. args.sect
end
local issue = args.issue
issue = type(issue) == 'string' and issue ~= '' and issue or nil
local text = args.text
text = type(text) == 'string' and text or nil
local issues = {}
table.insert(issues, sect)
table.insert(issues, issue)
table.insert(issues, text)
self.issue = table.concat(issues, ' ')
end
-- Get the self.talk value.
local talk = args.talk
-- Show talk links on the template page or template subpages if the talk
-- parameter is blank.
if talk == ''
and self.templateTitle
and (
mw.title.equals(self.templateTitle, self.title)
or self.title:isSubpageOf(self.templateTitle)
)
then
talk = '#'
elseif talk == '' then
talk = nil
end
if talk then
-- If the talk value is a talk page, make a link to that page. Else
-- assume that it's a section heading, and make a link to the talk
-- page of the current page with that section heading.
local talkTitle = getTitleObject(talk)
local talkArgIsTalkPage = true
if not talkTitle or not talkTitle.isTalkPage then
talkArgIsTalkPage = false
talkTitle = getTitleObject(
self.title.text,
mw.site.namespaces[self.title.namespace].talk.id
)
end
if talkTitle and talkTitle.exists then
local talkText
if self.isSmall then
local talkLink = talkArgIsTalkPage and talk or (talkTitle.prefixedText .. (talk == '#' and '' or '#') .. talk)
talkText = string.format('([[%s|talk]])', talkLink)
else
talkText = 'Relevant discussion may be found on'
if talkArgIsTalkPage then
talkText = string.format(
'%s [[%s|%s]].',
talkText,
talk,
talkTitle.prefixedText
)
else
talkText = string.format(
'%s the [[%s' .. (talk == '#' and '' or '#') .. '%s|talk page]].',
talkText,
talkTitle.prefixedText,
talk
)
end
end
self.talk = talkText
end
end
-- Get other values.
self.fix = args.fix ~= '' and args.fix or nil
local date
if args.date and args.date ~= '' then
date = args.date
elseif args.date == '' and self.isTemplatePage then
date = lang:formatDate('F Y')
end
if date then
self.date = string.format(" <span class='date-container'><i>(<span class='date'>%s</span>)</i></span>", date)
end
self.info = args.info
if yesno(args.removalnotice) then
self.removalNotice = cfg.removalNotice
end
end
-- Set the non-collapsible text field. At the moment this is used by all box
-- types other than ambox, and also by ambox when small=yes.
if self.isSmall then
self.text = args.smalltext or args.text
else
self.text = args.text
end
-- Set the below row.
self.below = cfg.below and args.below
-- General image settings.
self.imageCellDiv = not self.isSmall and cfg.imageCellDiv
self.imageEmptyCell = cfg.imageEmptyCell
-- Left image settings.
local imageLeft = self.isSmall and args.smallimage or args.image
if cfg.imageCheckBlank and imageLeft ~= 'blank' and imageLeft ~= 'none'
or not cfg.imageCheckBlank and imageLeft ~= 'none'
then
self.imageLeft = imageLeft
if not imageLeft then
local imageSize = self.isSmall
and (cfg.imageSmallSize or '30x30px')
or '40x40px'
self.imageLeft = string.format('[[File:%s|%s%s|alt=]]', self.typeImage
or 'Information icon4.svg', imageSize, self.typeImageNeedsLink and "" or "|link=" )
end
end
-- Right image settings.
local imageRight = self.isSmall and args.smallimageright or args.imageright
if not (cfg.imageRightNone and imageRight == 'none') then
self.imageRight = imageRight
end
-- set templatestyles
self.base_templatestyles = cfg.templatestyles
self.templatestyles = args.templatestyles
end
function MessageBox:setMainspaceCategories()
local args = self.args
local cfg = self.cfg
if not cfg.allowMainspaceCategories then
return nil
end
local nums = {}
for _, prefix in ipairs{'cat', 'category', 'all'} do
args[prefix .. '1'] = args[prefix]
nums = union(nums, getArgNums(args, prefix))
end
-- The following is roughly equivalent to the old {{Ambox/category}}.
local date = args.date
date = type(date) == 'string' and date
local preposition = 'from'
for _, num in ipairs(nums) do
local mainCat = args['cat' .. tostring(num)]
or args['category' .. tostring(num)]
local allCat = args['all' .. tostring(num)]
mainCat = type(mainCat) == 'string' and mainCat
allCat = type(allCat) == 'string' and allCat
if mainCat and date and date ~= '' then
local catTitle = string.format('%s %s %s', mainCat, preposition, date)
self:addCat(0, catTitle)
catTitle = getTitleObject('Category:' .. catTitle)
if not catTitle or not catTitle.exists then
self:addCat(0, 'Articles with invalid date parameter in template')
end
elseif mainCat and (not date or date == '') then
self:addCat(0, mainCat)
end
if allCat then
self:addCat(0, allCat)
end
end
end
function MessageBox:setTemplateCategories()
local args = self.args
local cfg = self.cfg
-- Add template categories.
if cfg.templateCategory then
if cfg.templateCategoryRequireName then
if self.isTemplatePage then
self:addCat(10, cfg.templateCategory)
end
elseif not self.title.isSubpage then
self:addCat(10, cfg.templateCategory)
end
end
-- Add template error categories.
if cfg.templateErrorCategory then
local templateErrorCategory = cfg.templateErrorCategory
local templateCat, templateSort
if not self.name and not self.title.isSubpage then
templateCat = templateErrorCategory
elseif self.isTemplatePage then
local paramsToCheck = cfg.templateErrorParamsToCheck or {}
local count = 0
for i, param in ipairs(paramsToCheck) do
if not args[param] then
count = count + 1
end
end
if count > 0 then
templateCat = templateErrorCategory
templateSort = tostring(count)
end
if self.categoryNums and #self.categoryNums > 0 then
templateCat = templateErrorCategory
templateSort = 'C'
end
end
self:addCat(10, templateCat, templateSort)
end
end
function MessageBox:setAllNamespaceCategories()
-- Set categories for all namespaces.
if self.invalidTypeError then
local allSort = (self.title.namespace == 0 and 'Main:' or '') .. self.title.prefixedText
self:addCat('all', 'Wikipedia message box parameter needs fixing', allSort)
end
if self.isSubstituted then
self:addCat('all', 'Pages with incorrectly substituted templates')
end
end
function MessageBox:setCategories()
if self.title.namespace == 0 then
self:setMainspaceCategories()
elseif self.title.namespace == 10 then
self:setTemplateCategories()
end
self:setAllNamespaceCategories()
end
function MessageBox:renderCategories()
if not self.hasCategories then
-- No categories added, no need to pass them to Category handler so,
-- if it was invoked, it would return the empty string.
-- So we shortcut and return the empty string.
return ""
end
-- Convert category tables to strings and pass them through
-- [[Module:Category handler]].
return require('Module:Category handler')._main{
main = table.concat(self.categories[0] or {}),
template = table.concat(self.categories[10] or {}),
all = table.concat(self.categories.all or {}),
nocat = self.args.nocat,
page = self.args.page
}
end
function MessageBox:export()
local root = mw.html.create()
-- Add the subst check error.
if self.isSubstituted and self.name then
root:tag('b')
:addClass('error')
:wikitext(string.format(
'Template <code>%s[[Template:%s|%s]]%s</code> has been incorrectly substituted.',
mw.text.nowiki('{{'), self.name, self.name, mw.text.nowiki('}}')
))
end
local frame = mw.getCurrentFrame()
root:wikitext(frame:extensionTag{
name = 'templatestyles',
args = { src = self.base_templatestyles },
})
-- Add support for a single custom templatestyles sheet. Undocumented as
-- need should be limited and many templates using mbox are substed; we
-- don't want to spread templatestyles sheets around to arbitrary places
if self.templatestyles then
root:wikitext(frame:extensionTag{
name = 'templatestyles',
args = { src = self.templatestyles },
})
end
-- Create the box table.
local boxTable = root:tag('table')
boxTable:attr('id', self.id or nil)
for i, class in ipairs(self.classes or {}) do
boxTable:addClass(class or nil)
end
boxTable
:cssText(self.style or nil)
:attr('role', 'presentation')
if self.attrs then
boxTable:attr(self.attrs)
end
-- Add the left-hand image.
local row = boxTable:tag('tr')
if self.imageLeft then
local imageLeftCell = row:tag('td'):addClass('mbox-image')
if self.imageCellDiv then
-- If we are using a div, redefine imageLeftCell so that the image
-- is inside it. Divs use style="width: 52px;", which limits the
-- image width to 52px. If any images in a div are wider than that,
-- they may overlap with the text or cause other display problems.
imageLeftCell = imageLeftCell:tag('div'):addClass('mbox-image-div')
end
imageLeftCell
:addClass(self.imageLeftClass)
:wikitext(self.imageLeft or nil)
elseif self.imageEmptyCell then
-- Some message boxes define an empty cell if no image is specified, and
-- some don't. The old template code in templates where empty cells are
-- specified gives the following hint: "No image. Cell with some width
-- or padding necessary for text cell to have 100% width."
row:tag('td')
:addClass('mbox-empty-cell')
end
-- Add the text.
local textCell = row:tag('td'):addClass('mbox-text')
if self.useCollapsibleTextFields then
-- The message box uses advanced text parameters that allow things to be
-- collapsible. At the moment, only ambox uses this.
textCell:cssText(self.textstyle or nil)
local textCellDiv = textCell:tag('div')
textCellDiv
:addClass('mbox-text-span')
:wikitext(self.issue or nil)
if (self.talk or self.fix) then
textCellDiv:tag('span')
:addClass('hide-when-compact')
:wikitext(self.talk and (' ' .. self.talk) or nil)
:wikitext(self.fix and (' ' .. self.fix) or nil)
end
textCellDiv:wikitext(self.date and (' ' .. self.date) or nil)
if self.info and not self.isSmall then
textCellDiv
:tag('span')
:addClass('hide-when-compact')
:wikitext(self.info and (' ' .. self.info) or nil)
end
if self.removalNotice then
textCellDiv:tag('span')
:addClass('hide-when-compact')
:tag('i')
:wikitext(string.format(" (%s)", self.removalNotice))
end
else
-- Default text formatting - anything goes.
textCell
:cssText(self.textstyle or nil)
:wikitext(self.text or nil)
end
-- Add the right-hand image.
if self.imageRight then
local imageRightCell = row:tag('td'):addClass('mbox-imageright')
if self.imageCellDiv then
-- If we are using a div, redefine imageRightCell so that the image
-- is inside it.
imageRightCell = imageRightCell:tag('div'):addClass('mbox-image-div')
end
imageRightCell
:addClass(self.imageRightClass)
:wikitext(self.imageRight or nil)
end
-- Add the below row.
if self.below then
boxTable:tag('tr')
:tag('td')
:attr('colspan', self.imageRight and '3' or '2')
:addClass('mbox-text')
:cssText(self.textstyle or nil)
:wikitext(self.below or nil)
end
-- Add error message for invalid type parameters.
if self.invalidTypeError then
root:tag('div')
:addClass('mbox-invalid-type')
:wikitext(string.format(
'This message box is using an invalid "type=%s" parameter and needs fixing.',
self.type or ''
))
end
-- Add categories.
root:wikitext(self:renderCategories() or nil)
return tostring(root)
end
--------------------------------------------------------------------------------
-- Exports
--------------------------------------------------------------------------------
local p, mt = {}, {}
function p._exportClasses()
-- For testing.
return {
MessageBox = MessageBox
}
end
function p.main(boxType, args, cfgTables)
local box = MessageBox.new(boxType, args, cfgTables or mw.loadData(CONFIG_MODULE))
box:setParameters()
box:setCategories()
return box:export()
end
function mt.__index(t, k)
return function (frame)
if not getArgs then
getArgs = require('Module:Arguments').getArgs
end
return t.main(k, getArgs(frame, {trim = false, removeBlanks = false}))
end
end
return setmetatable(p, mt)
qjedtq8k5rp7pkaeftelylik87u0bva
886341
886340
2025-06-13T17:03:23Z
KartikMistry
10383
[[:en:Module:Message_box]] માંથી આયાત કરેલ ૧ પુનરાવર્તન
886340
Scribunto
text/plain
require('strict')
local getArgs
local yesno = require('Module:Yesno')
local lang = mw.language.getContentLanguage()
local CONFIG_MODULE = 'Module:Message box/configuration'
local DEMOSPACES = {talk = 'tmbox', image = 'imbox', file = 'imbox', category = 'cmbox', article = 'ambox', main = 'ambox'}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getTitleObject(...)
-- Get the title object, passing the function through pcall
-- in case we are over the expensive function count limit.
local success, title = pcall(mw.title.new, ...)
if success then
return title
end
end
local function union(t1, t2)
-- Returns the union of two arrays.
local vals = {}
for i, v in ipairs(t1) do
vals[v] = true
end
for i, v in ipairs(t2) do
vals[v] = true
end
local ret = {}
for k in pairs(vals) do
table.insert(ret, k)
end
table.sort(ret)
return ret
end
local function getArgNums(args, prefix)
local nums = {}
for k, v in pairs(args) do
local num = mw.ustring.match(tostring(k), '^' .. prefix .. '([1-9]%d*)$')
if num then
table.insert(nums, tonumber(num))
end
end
table.sort(nums)
return nums
end
--------------------------------------------------------------------------------
-- Box class definition
--------------------------------------------------------------------------------
local MessageBox = {}
MessageBox.__index = MessageBox
function MessageBox.new(boxType, args, cfg)
args = args or {}
local obj = {}
-- Set the title object and the namespace.
obj.title = getTitleObject(args.page) or mw.title.getCurrentTitle()
-- Set the config for our box type.
obj.cfg = cfg[boxType]
if not obj.cfg then
local ns = obj.title.namespace
-- boxType is "mbox" or invalid input
if args.demospace and args.demospace ~= '' then
-- implement demospace parameter of mbox
local demospace = string.lower(args.demospace)
if DEMOSPACES[demospace] then
-- use template from DEMOSPACES
obj.cfg = cfg[DEMOSPACES[demospace]]
elseif string.find( demospace, 'talk' ) then
-- demo as a talk page
obj.cfg = cfg.tmbox
else
-- default to ombox
obj.cfg = cfg.ombox
end
elseif ns == 0 then
obj.cfg = cfg.ambox -- main namespace
elseif ns == 6 then
obj.cfg = cfg.imbox -- file namespace
elseif ns == 14 then
obj.cfg = cfg.cmbox -- category namespace
else
local nsTable = mw.site.namespaces[ns]
if nsTable and nsTable.isTalk then
obj.cfg = cfg.tmbox -- any talk namespace
else
obj.cfg = cfg.ombox -- other namespaces or invalid input
end
end
end
-- Set the arguments, and remove all blank arguments except for the ones
-- listed in cfg.allowBlankParams.
do
local newArgs = {}
for k, v in pairs(args) do
if v ~= '' then
newArgs[k] = v
end
end
for i, param in ipairs(obj.cfg.allowBlankParams or {}) do
newArgs[param] = args[param]
end
obj.args = newArgs
end
-- Define internal data structure.
obj.categories = {}
obj.classes = {}
-- For lazy loading of [[Module:Category handler]].
obj.hasCategories = false
return setmetatable(obj, MessageBox)
end
function MessageBox:addCat(ns, cat, sort)
if not cat then
return nil
end
if sort then
cat = string.format('[[Category:%s|%s]]', cat, sort)
else
cat = string.format('[[Category:%s]]', cat)
end
self.hasCategories = true
self.categories[ns] = self.categories[ns] or {}
table.insert(self.categories[ns], cat)
end
function MessageBox:addClass(class)
if not class then
return nil
end
table.insert(self.classes, class)
end
function MessageBox:setParameters()
local args = self.args
local cfg = self.cfg
-- Get type data.
self.type = args.type
local typeData = cfg.types[self.type]
self.invalidTypeError = cfg.showInvalidTypeError
and self.type
and not typeData
typeData = typeData or cfg.types[cfg.default]
self.typeClass = typeData.class
self.typeImage = typeData.image
self.typeImageNeedsLink = typeData.imageNeedsLink
-- Find if the box has been wrongly substituted.
self.isSubstituted = cfg.substCheck and args.subst == 'SUBST'
-- Find whether we are using a small message box.
self.isSmall = cfg.allowSmall and (
cfg.smallParam and args.small == cfg.smallParam
or not cfg.smallParam and yesno(args.small)
)
-- Add attributes, classes and styles.
self.id = args.id
self.name = args.name
if self.name then
self:addClass('box-' .. string.gsub(self.name,' ','_'))
end
if yesno(args.plainlinks) ~= false then
self:addClass('plainlinks')
end
for _, class in ipairs(cfg.classes or {}) do
self:addClass(class)
end
if self.isSmall then
self:addClass(cfg.smallClass or 'mbox-small')
end
self:addClass(self.typeClass)
self:addClass(args.class)
self.style = args.style
self.attrs = args.attrs
-- Set text style.
self.textstyle = args.textstyle
-- Set image classes.
self.imageRightClass = args.imagerightclass or args.imageclass
self.imageLeftClass = args.imageleftclass or args.imageclass
-- Find if we are on the template page or not. This functionality is only
-- used if useCollapsibleTextFields is set, or if both cfg.templateCategory
-- and cfg.templateCategoryRequireName are set.
self.useCollapsibleTextFields = cfg.useCollapsibleTextFields
if self.useCollapsibleTextFields
or cfg.templateCategory
and cfg.templateCategoryRequireName
then
if self.name then
local templateName = mw.ustring.match(
self.name,
'^[tT][eE][mM][pP][lL][aA][tT][eE][%s_]*:[%s_]*(.*)$'
) or self.name
templateName = 'Template:' .. templateName
self.templateTitle = getTitleObject(templateName)
end
self.isTemplatePage = self.templateTitle
and mw.title.equals(self.title, self.templateTitle)
end
-- Process data for collapsible text fields. At the moment these are only
-- used in {{ambox}}.
if self.useCollapsibleTextFields then
-- Get the self.issue value.
if self.isSmall and args.smalltext then
self.issue = args.smalltext
else
local sect
if args.sect == '' then
sect = 'This ' .. (cfg.sectionDefault or 'page')
elseif type(args.sect) == 'string' then
sect = 'This ' .. args.sect
end
local issue = args.issue
issue = type(issue) == 'string' and issue ~= '' and issue or nil
local text = args.text
text = type(text) == 'string' and text or nil
local issues = {}
table.insert(issues, sect)
table.insert(issues, issue)
table.insert(issues, text)
self.issue = table.concat(issues, ' ')
end
-- Get the self.talk value.
local talk = args.talk
-- Show talk links on the template page or template subpages if the talk
-- parameter is blank.
if talk == ''
and self.templateTitle
and (
mw.title.equals(self.templateTitle, self.title)
or self.title:isSubpageOf(self.templateTitle)
)
then
talk = '#'
elseif talk == '' then
talk = nil
end
if talk then
-- If the talk value is a talk page, make a link to that page. Else
-- assume that it's a section heading, and make a link to the talk
-- page of the current page with that section heading.
local talkTitle = getTitleObject(talk)
local talkArgIsTalkPage = true
if not talkTitle or not talkTitle.isTalkPage then
talkArgIsTalkPage = false
talkTitle = getTitleObject(
self.title.text,
mw.site.namespaces[self.title.namespace].talk.id
)
end
if talkTitle and talkTitle.exists then
local talkText
if self.isSmall then
local talkLink = talkArgIsTalkPage and talk or (talkTitle.prefixedText .. (talk == '#' and '' or '#') .. talk)
talkText = string.format('([[%s|talk]])', talkLink)
else
talkText = 'Relevant discussion may be found on'
if talkArgIsTalkPage then
talkText = string.format(
'%s [[%s|%s]].',
talkText,
talk,
talkTitle.prefixedText
)
else
talkText = string.format(
'%s the [[%s' .. (talk == '#' and '' or '#') .. '%s|talk page]].',
talkText,
talkTitle.prefixedText,
talk
)
end
end
self.talk = talkText
end
end
-- Get other values.
self.fix = args.fix ~= '' and args.fix or nil
local date
if args.date and args.date ~= '' then
date = args.date
elseif args.date == '' and self.isTemplatePage then
date = lang:formatDate('F Y')
end
if date then
self.date = string.format(" <span class='date-container'><i>(<span class='date'>%s</span>)</i></span>", date)
end
self.info = args.info
if yesno(args.removalnotice) then
self.removalNotice = cfg.removalNotice
end
end
-- Set the non-collapsible text field. At the moment this is used by all box
-- types other than ambox, and also by ambox when small=yes.
if self.isSmall then
self.text = args.smalltext or args.text
else
self.text = args.text
end
-- Set the below row.
self.below = cfg.below and args.below
-- General image settings.
self.imageCellDiv = not self.isSmall and cfg.imageCellDiv
self.imageEmptyCell = cfg.imageEmptyCell
-- Left image settings.
local imageLeft = self.isSmall and args.smallimage or args.image
if cfg.imageCheckBlank and imageLeft ~= 'blank' and imageLeft ~= 'none'
or not cfg.imageCheckBlank and imageLeft ~= 'none'
then
self.imageLeft = imageLeft
if not imageLeft then
local imageSize = self.isSmall
and (cfg.imageSmallSize or '30x30px')
or '40x40px'
self.imageLeft = string.format('[[File:%s|%s%s|alt=]]', self.typeImage
or 'Information icon4.svg', imageSize, self.typeImageNeedsLink and "" or "|link=" )
end
end
-- Right image settings.
local imageRight = self.isSmall and args.smallimageright or args.imageright
if not (cfg.imageRightNone and imageRight == 'none') then
self.imageRight = imageRight
end
-- set templatestyles
self.base_templatestyles = cfg.templatestyles
self.templatestyles = args.templatestyles
end
function MessageBox:setMainspaceCategories()
local args = self.args
local cfg = self.cfg
if not cfg.allowMainspaceCategories then
return nil
end
local nums = {}
for _, prefix in ipairs{'cat', 'category', 'all'} do
args[prefix .. '1'] = args[prefix]
nums = union(nums, getArgNums(args, prefix))
end
-- The following is roughly equivalent to the old {{Ambox/category}}.
local date = args.date
date = type(date) == 'string' and date
local preposition = 'from'
for _, num in ipairs(nums) do
local mainCat = args['cat' .. tostring(num)]
or args['category' .. tostring(num)]
local allCat = args['all' .. tostring(num)]
mainCat = type(mainCat) == 'string' and mainCat
allCat = type(allCat) == 'string' and allCat
if mainCat and date and date ~= '' then
local catTitle = string.format('%s %s %s', mainCat, preposition, date)
self:addCat(0, catTitle)
catTitle = getTitleObject('Category:' .. catTitle)
if not catTitle or not catTitle.exists then
self:addCat(0, 'Articles with invalid date parameter in template')
end
elseif mainCat and (not date or date == '') then
self:addCat(0, mainCat)
end
if allCat then
self:addCat(0, allCat)
end
end
end
function MessageBox:setTemplateCategories()
local args = self.args
local cfg = self.cfg
-- Add template categories.
if cfg.templateCategory then
if cfg.templateCategoryRequireName then
if self.isTemplatePage then
self:addCat(10, cfg.templateCategory)
end
elseif not self.title.isSubpage then
self:addCat(10, cfg.templateCategory)
end
end
-- Add template error categories.
if cfg.templateErrorCategory then
local templateErrorCategory = cfg.templateErrorCategory
local templateCat, templateSort
if not self.name and not self.title.isSubpage then
templateCat = templateErrorCategory
elseif self.isTemplatePage then
local paramsToCheck = cfg.templateErrorParamsToCheck or {}
local count = 0
for i, param in ipairs(paramsToCheck) do
if not args[param] then
count = count + 1
end
end
if count > 0 then
templateCat = templateErrorCategory
templateSort = tostring(count)
end
if self.categoryNums and #self.categoryNums > 0 then
templateCat = templateErrorCategory
templateSort = 'C'
end
end
self:addCat(10, templateCat, templateSort)
end
end
function MessageBox:setAllNamespaceCategories()
-- Set categories for all namespaces.
if self.invalidTypeError then
local allSort = (self.title.namespace == 0 and 'Main:' or '') .. self.title.prefixedText
self:addCat('all', 'Wikipedia message box parameter needs fixing', allSort)
end
if self.isSubstituted then
self:addCat('all', 'Pages with incorrectly substituted templates')
end
end
function MessageBox:setCategories()
if self.title.namespace == 0 then
self:setMainspaceCategories()
elseif self.title.namespace == 10 then
self:setTemplateCategories()
end
self:setAllNamespaceCategories()
end
function MessageBox:renderCategories()
if not self.hasCategories then
-- No categories added, no need to pass them to Category handler so,
-- if it was invoked, it would return the empty string.
-- So we shortcut and return the empty string.
return ""
end
-- Convert category tables to strings and pass them through
-- [[Module:Category handler]].
return require('Module:Category handler')._main{
main = table.concat(self.categories[0] or {}),
template = table.concat(self.categories[10] or {}),
all = table.concat(self.categories.all or {}),
nocat = self.args.nocat,
page = self.args.page
}
end
function MessageBox:export()
local root = mw.html.create()
-- Add the subst check error.
if self.isSubstituted and self.name then
root:tag('b')
:addClass('error')
:wikitext(string.format(
'Template <code>%s[[Template:%s|%s]]%s</code> has been incorrectly substituted.',
mw.text.nowiki('{{'), self.name, self.name, mw.text.nowiki('}}')
))
end
local frame = mw.getCurrentFrame()
root:wikitext(frame:extensionTag{
name = 'templatestyles',
args = { src = self.base_templatestyles },
})
-- Add support for a single custom templatestyles sheet. Undocumented as
-- need should be limited and many templates using mbox are substed; we
-- don't want to spread templatestyles sheets around to arbitrary places
if self.templatestyles then
root:wikitext(frame:extensionTag{
name = 'templatestyles',
args = { src = self.templatestyles },
})
end
-- Create the box table.
local boxTable = root:tag('table')
boxTable:attr('id', self.id or nil)
for i, class in ipairs(self.classes or {}) do
boxTable:addClass(class or nil)
end
boxTable
:cssText(self.style or nil)
:attr('role', 'presentation')
if self.attrs then
boxTable:attr(self.attrs)
end
-- Add the left-hand image.
local row = boxTable:tag('tr')
if self.imageLeft then
local imageLeftCell = row:tag('td'):addClass('mbox-image')
if self.imageCellDiv then
-- If we are using a div, redefine imageLeftCell so that the image
-- is inside it. Divs use style="width: 52px;", which limits the
-- image width to 52px. If any images in a div are wider than that,
-- they may overlap with the text or cause other display problems.
imageLeftCell = imageLeftCell:tag('div'):addClass('mbox-image-div')
end
imageLeftCell
:addClass(self.imageLeftClass)
:wikitext(self.imageLeft or nil)
elseif self.imageEmptyCell then
-- Some message boxes define an empty cell if no image is specified, and
-- some don't. The old template code in templates where empty cells are
-- specified gives the following hint: "No image. Cell with some width
-- or padding necessary for text cell to have 100% width."
row:tag('td')
:addClass('mbox-empty-cell')
end
-- Add the text.
local textCell = row:tag('td'):addClass('mbox-text')
if self.useCollapsibleTextFields then
-- The message box uses advanced text parameters that allow things to be
-- collapsible. At the moment, only ambox uses this.
textCell:cssText(self.textstyle or nil)
local textCellDiv = textCell:tag('div')
textCellDiv
:addClass('mbox-text-span')
:wikitext(self.issue or nil)
if (self.talk or self.fix) then
textCellDiv:tag('span')
:addClass('hide-when-compact')
:wikitext(self.talk and (' ' .. self.talk) or nil)
:wikitext(self.fix and (' ' .. self.fix) or nil)
end
textCellDiv:wikitext(self.date and (' ' .. self.date) or nil)
if self.info and not self.isSmall then
textCellDiv
:tag('span')
:addClass('hide-when-compact')
:wikitext(self.info and (' ' .. self.info) or nil)
end
if self.removalNotice then
textCellDiv:tag('span')
:addClass('hide-when-compact')
:tag('i')
:wikitext(string.format(" (%s)", self.removalNotice))
end
else
-- Default text formatting - anything goes.
textCell
:cssText(self.textstyle or nil)
:wikitext(self.text or nil)
end
-- Add the right-hand image.
if self.imageRight then
local imageRightCell = row:tag('td'):addClass('mbox-imageright')
if self.imageCellDiv then
-- If we are using a div, redefine imageRightCell so that the image
-- is inside it.
imageRightCell = imageRightCell:tag('div'):addClass('mbox-image-div')
end
imageRightCell
:addClass(self.imageRightClass)
:wikitext(self.imageRight or nil)
end
-- Add the below row.
if self.below then
boxTable:tag('tr')
:tag('td')
:attr('colspan', self.imageRight and '3' or '2')
:addClass('mbox-text')
:cssText(self.textstyle or nil)
:wikitext(self.below or nil)
end
-- Add error message for invalid type parameters.
if self.invalidTypeError then
root:tag('div')
:addClass('mbox-invalid-type')
:wikitext(string.format(
'This message box is using an invalid "type=%s" parameter and needs fixing.',
self.type or ''
))
end
-- Add categories.
root:wikitext(self:renderCategories() or nil)
return tostring(root)
end
--------------------------------------------------------------------------------
-- Exports
--------------------------------------------------------------------------------
local p, mt = {}, {}
function p._exportClasses()
-- For testing.
return {
MessageBox = MessageBox
}
end
function p.main(boxType, args, cfgTables)
local box = MessageBox.new(boxType, args, cfgTables or mw.loadData(CONFIG_MODULE))
box:setParameters()
box:setCategories()
return box:export()
end
function mt.__index(t, k)
return function (frame)
if not getArgs then
getArgs = require('Module:Arguments').getArgs
end
return t.main(k, getArgs(frame, {trim = false, removeBlanks = false}))
end
end
return setmetatable(p, mt)
qjedtq8k5rp7pkaeftelylik87u0bva
વિભાગ:TableTools
828
58064
886362
869061
2024-08-18T04:19:07Z
en>Pppery
0
Update from sandbox per request
886362
Scribunto
text/plain
------------------------------------------------------------------------------------
-- TableTools --
-- --
-- This module includes a number of functions for dealing with Lua tables. --
-- It is a meta-module, meant to be called from other Lua modules, and should not --
-- be called directly from #invoke. --
------------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local p = {}
-- Define often-used variables and functions.
local floor = math.floor
local infinity = math.huge
local checkType = libraryUtil.checkType
local checkTypeMulti = libraryUtil.checkTypeMulti
------------------------------------------------------------------------------------
-- isPositiveInteger
--
-- This function returns true if the given value is a positive integer, and false
-- if not. Although it doesn't operate on tables, it is included here as it is
-- useful for determining whether a given table key is in the array part or the
-- hash part of a table.
------------------------------------------------------------------------------------
function p.isPositiveInteger(v)
return type(v) == 'number' and v >= 1 and floor(v) == v and v < infinity
end
------------------------------------------------------------------------------------
-- isNan
--
-- This function returns true if the given number is a NaN value, and false if
-- not. Although it doesn't operate on tables, it is included here as it is useful
-- for determining whether a value can be a valid table key. Lua will generate an
-- error if a NaN is used as a table key.
------------------------------------------------------------------------------------
function p.isNan(v)
return type(v) == 'number' and v ~= v
end
------------------------------------------------------------------------------------
-- shallowClone
--
-- This returns a clone of a table. The value returned is a new table, but all
-- subtables and functions are shared. Metamethods are respected, but the returned
-- table will have no metatable of its own.
------------------------------------------------------------------------------------
function p.shallowClone(t)
checkType('shallowClone', 1, t, 'table')
local ret = {}
for k, v in pairs(t) do
ret[k] = v
end
return ret
end
------------------------------------------------------------------------------------
-- removeDuplicates
--
-- This removes duplicate values from an array. Non-positive-integer keys are
-- ignored. The earliest value is kept, and all subsequent duplicate values are
-- removed, but otherwise the array order is unchanged.
------------------------------------------------------------------------------------
function p.removeDuplicates(arr)
checkType('removeDuplicates', 1, arr, 'table')
local isNan = p.isNan
local ret, exists = {}, {}
for _, v in ipairs(arr) do
if isNan(v) then
-- NaNs can't be table keys, and they are also unique, so we don't need to check existence.
ret[#ret + 1] = v
elseif not exists[v] then
ret[#ret + 1] = v
exists[v] = true
end
end
return ret
end
------------------------------------------------------------------------------------
-- numKeys
--
-- This takes a table and returns an array containing the numbers of any numerical
-- keys that have non-nil values, sorted in numerical order.
------------------------------------------------------------------------------------
function p.numKeys(t)
checkType('numKeys', 1, t, 'table')
local isPositiveInteger = p.isPositiveInteger
local nums = {}
for k in pairs(t) do
if isPositiveInteger(k) then
nums[#nums + 1] = k
end
end
table.sort(nums)
return nums
end
------------------------------------------------------------------------------------
-- affixNums
--
-- This takes a table and returns an array containing the numbers of keys with the
-- specified prefix and suffix. For example, for the table
-- {a1 = 'foo', a3 = 'bar', a6 = 'baz'} and the prefix "a", affixNums will return
-- {1, 3, 6}.
------------------------------------------------------------------------------------
function p.affixNums(t, prefix, suffix)
checkType('affixNums', 1, t, 'table')
checkType('affixNums', 2, prefix, 'string', true)
checkType('affixNums', 3, suffix, 'string', true)
local function cleanPattern(s)
-- Cleans a pattern so that the magic characters ()%.[]*+-?^$ are interpreted literally.
return s:gsub('([%(%)%%%.%[%]%*%+%-%?%^%$])', '%%%1')
end
prefix = prefix or ''
suffix = suffix or ''
prefix = cleanPattern(prefix)
suffix = cleanPattern(suffix)
local pattern = '^' .. prefix .. '([1-9]%d*)' .. suffix .. '$'
local nums = {}
for k in pairs(t) do
if type(k) == 'string' then
local num = mw.ustring.match(k, pattern)
if num then
nums[#nums + 1] = tonumber(num)
end
end
end
table.sort(nums)
return nums
end
------------------------------------------------------------------------------------
-- numData
--
-- Given a table with keys like {"foo1", "bar1", "foo2", "baz2"}, returns a table
-- of subtables in the format
-- {[1] = {foo = 'text', bar = 'text'}, [2] = {foo = 'text', baz = 'text'}}.
-- Keys that don't end with an integer are stored in a subtable named "other". The
-- compress option compresses the table so that it can be iterated over with
-- ipairs.
------------------------------------------------------------------------------------
function p.numData(t, compress)
checkType('numData', 1, t, 'table')
checkType('numData', 2, compress, 'boolean', true)
local ret = {}
for k, v in pairs(t) do
local prefix, num = mw.ustring.match(tostring(k), '^([^0-9]*)([1-9][0-9]*)$')
if num then
num = tonumber(num)
local subtable = ret[num] or {}
if prefix == '' then
-- Positional parameters match the blank string; put them at the start of the subtable instead.
prefix = 1
end
subtable[prefix] = v
ret[num] = subtable
else
local subtable = ret.other or {}
subtable[k] = v
ret.other = subtable
end
end
if compress then
local other = ret.other
ret = p.compressSparseArray(ret)
ret.other = other
end
return ret
end
------------------------------------------------------------------------------------
-- compressSparseArray
--
-- This takes an array with one or more nil values, and removes the nil values
-- while preserving the order, so that the array can be safely traversed with
-- ipairs.
------------------------------------------------------------------------------------
function p.compressSparseArray(t)
checkType('compressSparseArray', 1, t, 'table')
local ret = {}
local nums = p.numKeys(t)
for _, num in ipairs(nums) do
ret[#ret + 1] = t[num]
end
return ret
end
------------------------------------------------------------------------------------
-- sparseIpairs
--
-- This is an iterator for sparse arrays. It can be used like ipairs, but can
-- handle nil values.
------------------------------------------------------------------------------------
function p.sparseIpairs(t)
checkType('sparseIpairs', 1, t, 'table')
local nums = p.numKeys(t)
local i = 0
local lim = #nums
return function ()
i = i + 1
if i <= lim then
local key = nums[i]
return key, t[key]
else
return nil, nil
end
end
end
------------------------------------------------------------------------------------
-- size
--
-- This returns the size of a key/value pair table. It will also work on arrays,
-- but for arrays it is more efficient to use the # operator.
------------------------------------------------------------------------------------
function p.size(t)
checkType('size', 1, t, 'table')
local i = 0
for _ in pairs(t) do
i = i + 1
end
return i
end
local function defaultKeySort(item1, item2)
-- "number" < "string", so numbers will be sorted before strings.
local type1, type2 = type(item1), type(item2)
if type1 ~= type2 then
return type1 < type2
elseif type1 == 'table' or type1 == 'boolean' or type1 == 'function' then
return tostring(item1) < tostring(item2)
else
return item1 < item2
end
end
------------------------------------------------------------------------------------
-- keysToList
--
-- Returns an array of the keys in a table, sorted using either a default
-- comparison function or a custom keySort function.
------------------------------------------------------------------------------------
function p.keysToList(t, keySort, checked)
if not checked then
checkType('keysToList', 1, t, 'table')
checkTypeMulti('keysToList', 2, keySort, {'function', 'boolean', 'nil'})
end
local arr = {}
local index = 1
for k in pairs(t) do
arr[index] = k
index = index + 1
end
if keySort ~= false then
keySort = type(keySort) == 'function' and keySort or defaultKeySort
table.sort(arr, keySort)
end
return arr
end
------------------------------------------------------------------------------------
-- sortedPairs
--
-- Iterates through a table, with the keys sorted using the keysToList function.
-- If there are only numerical keys, sparseIpairs is probably more efficient.
------------------------------------------------------------------------------------
function p.sortedPairs(t, keySort)
checkType('sortedPairs', 1, t, 'table')
checkType('sortedPairs', 2, keySort, 'function', true)
local arr = p.keysToList(t, keySort, true)
local i = 0
return function ()
i = i + 1
local key = arr[i]
if key ~= nil then
return key, t[key]
else
return nil, nil
end
end
end
------------------------------------------------------------------------------------
-- isArray
--
-- Returns true if the given value is a table and all keys are consecutive
-- integers starting at 1.
------------------------------------------------------------------------------------
function p.isArray(v)
if type(v) ~= 'table' then
return false
end
local i = 0
for _ in pairs(v) do
i = i + 1
if v[i] == nil then
return false
end
end
return true
end
------------------------------------------------------------------------------------
-- isArrayLike
--
-- Returns true if the given value is iterable and all keys are consecutive
-- integers starting at 1.
------------------------------------------------------------------------------------
function p.isArrayLike(v)
if not pcall(pairs, v) then
return false
end
local i = 0
for _ in pairs(v) do
i = i + 1
if v[i] == nil then
return false
end
end
return true
end
------------------------------------------------------------------------------------
-- invert
--
-- Transposes the keys and values in an array. For example, {"a", "b", "c"} ->
-- {a = 1, b = 2, c = 3}. Duplicates are not supported (result values refer to
-- the index of the last duplicate) and NaN values are ignored.
------------------------------------------------------------------------------------
function p.invert(arr)
checkType("invert", 1, arr, "table")
local isNan = p.isNan
local map = {}
for i, v in ipairs(arr) do
if not isNan(v) then
map[v] = i
end
end
return map
end
------------------------------------------------------------------------------------
-- listToSet
--
-- Creates a set from the array part of the table. Indexing the set by any of the
-- values of the array returns true. For example, {"a", "b", "c"} ->
-- {a = true, b = true, c = true}. NaN values are ignored as Lua considers them
-- never equal to any value (including other NaNs or even themselves).
------------------------------------------------------------------------------------
function p.listToSet(arr)
checkType("listToSet", 1, arr, "table")
local isNan = p.isNan
local set = {}
for _, v in ipairs(arr) do
if not isNan(v) then
set[v] = true
end
end
return set
end
------------------------------------------------------------------------------------
-- deepCopy
--
-- Recursive deep copy function. Preserves identities of subtables.
------------------------------------------------------------------------------------
local function _deepCopy(orig, includeMetatable, already_seen)
if type(orig) ~= "table" then
return orig
end
-- already_seen stores copies of tables indexed by the original table.
local copy = already_seen[orig]
if copy ~= nil then
return copy
end
copy = {}
already_seen[orig] = copy -- memoize before any recursion, to avoid infinite loops
for orig_key, orig_value in pairs(orig) do
copy[_deepCopy(orig_key, includeMetatable, already_seen)] = _deepCopy(orig_value, includeMetatable, already_seen)
end
if includeMetatable then
local mt = getmetatable(orig)
if mt ~= nil then
setmetatable(copy, _deepCopy(mt, true, already_seen))
end
end
return copy
end
function p.deepCopy(orig, noMetatable, already_seen)
checkType("deepCopy", 3, already_seen, "table", true)
return _deepCopy(orig, not noMetatable, already_seen or {})
end
------------------------------------------------------------------------------------
-- sparseConcat
--
-- Concatenates all values in the table that are indexed by a number, in order.
-- sparseConcat{a, nil, c, d} => "acd"
-- sparseConcat{nil, b, c, d} => "bcd"
------------------------------------------------------------------------------------
function p.sparseConcat(t, sep, i, j)
local arr = {}
local arr_i = 0
for _, v in p.sparseIpairs(t) do
arr_i = arr_i + 1
arr[arr_i] = v
end
return table.concat(arr, sep, i, j)
end
------------------------------------------------------------------------------------
-- length
--
-- Finds the length of an array, or of a quasi-array with keys such as "data1",
-- "data2", etc., using an exponential search algorithm. It is similar to the
-- operator #, but may return a different value when there are gaps in the array
-- portion of the table. Intended to be used on data loaded with mw.loadData. For
-- other tables, use #.
-- Note: #frame.args in frame object always be set to 0, regardless of the number
-- of unnamed template parameters, so use this function for frame.args.
------------------------------------------------------------------------------------
function p.length(t, prefix)
-- requiring module inline so that [[Module:Exponential search]] which is
-- only needed by this one function doesn't get millions of transclusions
local expSearch = require("Module:Exponential search")
checkType('length', 1, t, 'table')
checkType('length', 2, prefix, 'string', true)
return expSearch(function (i)
local key
if prefix then
key = prefix .. tostring(i)
else
key = i
end
return t[key] ~= nil
end) or 0
end
------------------------------------------------------------------------------------
-- inArray
--
-- Returns true if searchElement is a member of the array, and false otherwise.
-- Equivalent to JavaScript array.includes(searchElement) or
-- array.includes(searchElement, fromIndex), except fromIndex is 1 indexed
------------------------------------------------------------------------------------
function p.inArray(array, searchElement, fromIndex)
checkType("inArray", 1, array, "table")
-- if searchElement is nil, error?
fromIndex = tonumber(fromIndex)
if fromIndex then
if (fromIndex < 0) then
fromIndex = #array + fromIndex + 1
end
if fromIndex < 1 then fromIndex = 1 end
for _, v in ipairs({unpack(array, fromIndex)}) do
if v == searchElement then
return true
end
end
else
for _, v in pairs(array) do
if v == searchElement then
return true
end
end
end
return false
end
------------------------------------------------------------------------------------
-- merge
--
-- Given the arrays, returns an array containing the elements of each input array
-- in sequence.
------------------------------------------------------------------------------------
function p.merge(...)
local arrays = {...}
local ret = {}
for i, arr in ipairs(arrays) do
checkType('merge', i, arr, 'table')
for _, v in ipairs(arr) do
ret[#ret + 1] = v
end
end
return ret
end
------------------------------------------------------------------------------------
-- extend
--
-- Extends the first array in place by appending all elements from the second
-- array.
------------------------------------------------------------------------------------
function p.extend(arr1, arr2)
checkType('extend', 1, arr1, 'table')
checkType('extend', 2, arr2, 'table')
for _, v in ipairs(arr2) do
arr1[#arr1 + 1] = v
end
end
return p
4n03zk6kcoeg4gz82mieeh94c1szcjy
886363
886362
2025-06-13T17:03:25Z
KartikMistry
10383
[[:en:Module:TableTools]] માંથી આયાત કરેલ ૧ પુનરાવર્તન
886362
Scribunto
text/plain
------------------------------------------------------------------------------------
-- TableTools --
-- --
-- This module includes a number of functions for dealing with Lua tables. --
-- It is a meta-module, meant to be called from other Lua modules, and should not --
-- be called directly from #invoke. --
------------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local p = {}
-- Define often-used variables and functions.
local floor = math.floor
local infinity = math.huge
local checkType = libraryUtil.checkType
local checkTypeMulti = libraryUtil.checkTypeMulti
------------------------------------------------------------------------------------
-- isPositiveInteger
--
-- This function returns true if the given value is a positive integer, and false
-- if not. Although it doesn't operate on tables, it is included here as it is
-- useful for determining whether a given table key is in the array part or the
-- hash part of a table.
------------------------------------------------------------------------------------
function p.isPositiveInteger(v)
return type(v) == 'number' and v >= 1 and floor(v) == v and v < infinity
end
------------------------------------------------------------------------------------
-- isNan
--
-- This function returns true if the given number is a NaN value, and false if
-- not. Although it doesn't operate on tables, it is included here as it is useful
-- for determining whether a value can be a valid table key. Lua will generate an
-- error if a NaN is used as a table key.
------------------------------------------------------------------------------------
function p.isNan(v)
return type(v) == 'number' and v ~= v
end
------------------------------------------------------------------------------------
-- shallowClone
--
-- This returns a clone of a table. The value returned is a new table, but all
-- subtables and functions are shared. Metamethods are respected, but the returned
-- table will have no metatable of its own.
------------------------------------------------------------------------------------
function p.shallowClone(t)
checkType('shallowClone', 1, t, 'table')
local ret = {}
for k, v in pairs(t) do
ret[k] = v
end
return ret
end
------------------------------------------------------------------------------------
-- removeDuplicates
--
-- This removes duplicate values from an array. Non-positive-integer keys are
-- ignored. The earliest value is kept, and all subsequent duplicate values are
-- removed, but otherwise the array order is unchanged.
------------------------------------------------------------------------------------
function p.removeDuplicates(arr)
checkType('removeDuplicates', 1, arr, 'table')
local isNan = p.isNan
local ret, exists = {}, {}
for _, v in ipairs(arr) do
if isNan(v) then
-- NaNs can't be table keys, and they are also unique, so we don't need to check existence.
ret[#ret + 1] = v
elseif not exists[v] then
ret[#ret + 1] = v
exists[v] = true
end
end
return ret
end
------------------------------------------------------------------------------------
-- numKeys
--
-- This takes a table and returns an array containing the numbers of any numerical
-- keys that have non-nil values, sorted in numerical order.
------------------------------------------------------------------------------------
function p.numKeys(t)
checkType('numKeys', 1, t, 'table')
local isPositiveInteger = p.isPositiveInteger
local nums = {}
for k in pairs(t) do
if isPositiveInteger(k) then
nums[#nums + 1] = k
end
end
table.sort(nums)
return nums
end
------------------------------------------------------------------------------------
-- affixNums
--
-- This takes a table and returns an array containing the numbers of keys with the
-- specified prefix and suffix. For example, for the table
-- {a1 = 'foo', a3 = 'bar', a6 = 'baz'} and the prefix "a", affixNums will return
-- {1, 3, 6}.
------------------------------------------------------------------------------------
function p.affixNums(t, prefix, suffix)
checkType('affixNums', 1, t, 'table')
checkType('affixNums', 2, prefix, 'string', true)
checkType('affixNums', 3, suffix, 'string', true)
local function cleanPattern(s)
-- Cleans a pattern so that the magic characters ()%.[]*+-?^$ are interpreted literally.
return s:gsub('([%(%)%%%.%[%]%*%+%-%?%^%$])', '%%%1')
end
prefix = prefix or ''
suffix = suffix or ''
prefix = cleanPattern(prefix)
suffix = cleanPattern(suffix)
local pattern = '^' .. prefix .. '([1-9]%d*)' .. suffix .. '$'
local nums = {}
for k in pairs(t) do
if type(k) == 'string' then
local num = mw.ustring.match(k, pattern)
if num then
nums[#nums + 1] = tonumber(num)
end
end
end
table.sort(nums)
return nums
end
------------------------------------------------------------------------------------
-- numData
--
-- Given a table with keys like {"foo1", "bar1", "foo2", "baz2"}, returns a table
-- of subtables in the format
-- {[1] = {foo = 'text', bar = 'text'}, [2] = {foo = 'text', baz = 'text'}}.
-- Keys that don't end with an integer are stored in a subtable named "other". The
-- compress option compresses the table so that it can be iterated over with
-- ipairs.
------------------------------------------------------------------------------------
function p.numData(t, compress)
checkType('numData', 1, t, 'table')
checkType('numData', 2, compress, 'boolean', true)
local ret = {}
for k, v in pairs(t) do
local prefix, num = mw.ustring.match(tostring(k), '^([^0-9]*)([1-9][0-9]*)$')
if num then
num = tonumber(num)
local subtable = ret[num] or {}
if prefix == '' then
-- Positional parameters match the blank string; put them at the start of the subtable instead.
prefix = 1
end
subtable[prefix] = v
ret[num] = subtable
else
local subtable = ret.other or {}
subtable[k] = v
ret.other = subtable
end
end
if compress then
local other = ret.other
ret = p.compressSparseArray(ret)
ret.other = other
end
return ret
end
------------------------------------------------------------------------------------
-- compressSparseArray
--
-- This takes an array with one or more nil values, and removes the nil values
-- while preserving the order, so that the array can be safely traversed with
-- ipairs.
------------------------------------------------------------------------------------
function p.compressSparseArray(t)
checkType('compressSparseArray', 1, t, 'table')
local ret = {}
local nums = p.numKeys(t)
for _, num in ipairs(nums) do
ret[#ret + 1] = t[num]
end
return ret
end
------------------------------------------------------------------------------------
-- sparseIpairs
--
-- This is an iterator for sparse arrays. It can be used like ipairs, but can
-- handle nil values.
------------------------------------------------------------------------------------
function p.sparseIpairs(t)
checkType('sparseIpairs', 1, t, 'table')
local nums = p.numKeys(t)
local i = 0
local lim = #nums
return function ()
i = i + 1
if i <= lim then
local key = nums[i]
return key, t[key]
else
return nil, nil
end
end
end
------------------------------------------------------------------------------------
-- size
--
-- This returns the size of a key/value pair table. It will also work on arrays,
-- but for arrays it is more efficient to use the # operator.
------------------------------------------------------------------------------------
function p.size(t)
checkType('size', 1, t, 'table')
local i = 0
for _ in pairs(t) do
i = i + 1
end
return i
end
local function defaultKeySort(item1, item2)
-- "number" < "string", so numbers will be sorted before strings.
local type1, type2 = type(item1), type(item2)
if type1 ~= type2 then
return type1 < type2
elseif type1 == 'table' or type1 == 'boolean' or type1 == 'function' then
return tostring(item1) < tostring(item2)
else
return item1 < item2
end
end
------------------------------------------------------------------------------------
-- keysToList
--
-- Returns an array of the keys in a table, sorted using either a default
-- comparison function or a custom keySort function.
------------------------------------------------------------------------------------
function p.keysToList(t, keySort, checked)
if not checked then
checkType('keysToList', 1, t, 'table')
checkTypeMulti('keysToList', 2, keySort, {'function', 'boolean', 'nil'})
end
local arr = {}
local index = 1
for k in pairs(t) do
arr[index] = k
index = index + 1
end
if keySort ~= false then
keySort = type(keySort) == 'function' and keySort or defaultKeySort
table.sort(arr, keySort)
end
return arr
end
------------------------------------------------------------------------------------
-- sortedPairs
--
-- Iterates through a table, with the keys sorted using the keysToList function.
-- If there are only numerical keys, sparseIpairs is probably more efficient.
------------------------------------------------------------------------------------
function p.sortedPairs(t, keySort)
checkType('sortedPairs', 1, t, 'table')
checkType('sortedPairs', 2, keySort, 'function', true)
local arr = p.keysToList(t, keySort, true)
local i = 0
return function ()
i = i + 1
local key = arr[i]
if key ~= nil then
return key, t[key]
else
return nil, nil
end
end
end
------------------------------------------------------------------------------------
-- isArray
--
-- Returns true if the given value is a table and all keys are consecutive
-- integers starting at 1.
------------------------------------------------------------------------------------
function p.isArray(v)
if type(v) ~= 'table' then
return false
end
local i = 0
for _ in pairs(v) do
i = i + 1
if v[i] == nil then
return false
end
end
return true
end
------------------------------------------------------------------------------------
-- isArrayLike
--
-- Returns true if the given value is iterable and all keys are consecutive
-- integers starting at 1.
------------------------------------------------------------------------------------
function p.isArrayLike(v)
if not pcall(pairs, v) then
return false
end
local i = 0
for _ in pairs(v) do
i = i + 1
if v[i] == nil then
return false
end
end
return true
end
------------------------------------------------------------------------------------
-- invert
--
-- Transposes the keys and values in an array. For example, {"a", "b", "c"} ->
-- {a = 1, b = 2, c = 3}. Duplicates are not supported (result values refer to
-- the index of the last duplicate) and NaN values are ignored.
------------------------------------------------------------------------------------
function p.invert(arr)
checkType("invert", 1, arr, "table")
local isNan = p.isNan
local map = {}
for i, v in ipairs(arr) do
if not isNan(v) then
map[v] = i
end
end
return map
end
------------------------------------------------------------------------------------
-- listToSet
--
-- Creates a set from the array part of the table. Indexing the set by any of the
-- values of the array returns true. For example, {"a", "b", "c"} ->
-- {a = true, b = true, c = true}. NaN values are ignored as Lua considers them
-- never equal to any value (including other NaNs or even themselves).
------------------------------------------------------------------------------------
function p.listToSet(arr)
checkType("listToSet", 1, arr, "table")
local isNan = p.isNan
local set = {}
for _, v in ipairs(arr) do
if not isNan(v) then
set[v] = true
end
end
return set
end
------------------------------------------------------------------------------------
-- deepCopy
--
-- Recursive deep copy function. Preserves identities of subtables.
------------------------------------------------------------------------------------
local function _deepCopy(orig, includeMetatable, already_seen)
if type(orig) ~= "table" then
return orig
end
-- already_seen stores copies of tables indexed by the original table.
local copy = already_seen[orig]
if copy ~= nil then
return copy
end
copy = {}
already_seen[orig] = copy -- memoize before any recursion, to avoid infinite loops
for orig_key, orig_value in pairs(orig) do
copy[_deepCopy(orig_key, includeMetatable, already_seen)] = _deepCopy(orig_value, includeMetatable, already_seen)
end
if includeMetatable then
local mt = getmetatable(orig)
if mt ~= nil then
setmetatable(copy, _deepCopy(mt, true, already_seen))
end
end
return copy
end
function p.deepCopy(orig, noMetatable, already_seen)
checkType("deepCopy", 3, already_seen, "table", true)
return _deepCopy(orig, not noMetatable, already_seen or {})
end
------------------------------------------------------------------------------------
-- sparseConcat
--
-- Concatenates all values in the table that are indexed by a number, in order.
-- sparseConcat{a, nil, c, d} => "acd"
-- sparseConcat{nil, b, c, d} => "bcd"
------------------------------------------------------------------------------------
function p.sparseConcat(t, sep, i, j)
local arr = {}
local arr_i = 0
for _, v in p.sparseIpairs(t) do
arr_i = arr_i + 1
arr[arr_i] = v
end
return table.concat(arr, sep, i, j)
end
------------------------------------------------------------------------------------
-- length
--
-- Finds the length of an array, or of a quasi-array with keys such as "data1",
-- "data2", etc., using an exponential search algorithm. It is similar to the
-- operator #, but may return a different value when there are gaps in the array
-- portion of the table. Intended to be used on data loaded with mw.loadData. For
-- other tables, use #.
-- Note: #frame.args in frame object always be set to 0, regardless of the number
-- of unnamed template parameters, so use this function for frame.args.
------------------------------------------------------------------------------------
function p.length(t, prefix)
-- requiring module inline so that [[Module:Exponential search]] which is
-- only needed by this one function doesn't get millions of transclusions
local expSearch = require("Module:Exponential search")
checkType('length', 1, t, 'table')
checkType('length', 2, prefix, 'string', true)
return expSearch(function (i)
local key
if prefix then
key = prefix .. tostring(i)
else
key = i
end
return t[key] ~= nil
end) or 0
end
------------------------------------------------------------------------------------
-- inArray
--
-- Returns true if searchElement is a member of the array, and false otherwise.
-- Equivalent to JavaScript array.includes(searchElement) or
-- array.includes(searchElement, fromIndex), except fromIndex is 1 indexed
------------------------------------------------------------------------------------
function p.inArray(array, searchElement, fromIndex)
checkType("inArray", 1, array, "table")
-- if searchElement is nil, error?
fromIndex = tonumber(fromIndex)
if fromIndex then
if (fromIndex < 0) then
fromIndex = #array + fromIndex + 1
end
if fromIndex < 1 then fromIndex = 1 end
for _, v in ipairs({unpack(array, fromIndex)}) do
if v == searchElement then
return true
end
end
else
for _, v in pairs(array) do
if v == searchElement then
return true
end
end
end
return false
end
------------------------------------------------------------------------------------
-- merge
--
-- Given the arrays, returns an array containing the elements of each input array
-- in sequence.
------------------------------------------------------------------------------------
function p.merge(...)
local arrays = {...}
local ret = {}
for i, arr in ipairs(arrays) do
checkType('merge', i, arr, 'table')
for _, v in ipairs(arr) do
ret[#ret + 1] = v
end
end
return ret
end
------------------------------------------------------------------------------------
-- extend
--
-- Extends the first array in place by appending all elements from the second
-- array.
------------------------------------------------------------------------------------
function p.extend(arr1, arr2)
checkType('extend', 1, arr1, 'table')
checkType('extend', 2, arr2, 'table')
for _, v in ipairs(arr2) do
arr1[#arr1 + 1] = v
end
end
return p
4n03zk6kcoeg4gz82mieeh94c1szcjy
વિભાગ:Hatnote
828
65786
886378
810414
2025-03-18T15:47:33Z
en>Ahecht
0
Revert
886378
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{hatnote}} and {{format link}} meta-templates and includes --
-- helper functions for other Lua hatnote modules. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local formatLink -- lazily initialise [[Module:Format link]] ._formatLink
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.defaultClasses(inline)
-- Provides the default hatnote classes as a space-separated string; useful
-- for hatnote-manipulation modules like [[Module:Hatnote group]].
return
(inline == 1 and 'hatnote-inline' or 'hatnote') .. ' ' ..
'navigation-not-searchable'
end
function p.disambiguate(page, disambiguator)
-- Formats a page title with a disambiguation parenthetical,
-- i.e. "Example" → "Example (disambiguation)".
checkType('disambiguate', 1, page, 'string')
checkType('disambiguate', 2, disambiguator, 'string', true)
disambiguator = disambiguator or 'disambiguation'
return mw.ustring.format('%s (%s)', page, disambiguator)
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to false.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.makeWikitextError(msg, helpLink, addTrackingCategory, title)
-- Formats an error message to be returned to wikitext. If
-- addTrackingCategory is not false after being returned from
-- [[Module:Yesno]], and if we are not on a talk page, a tracking category
-- is added.
checkType('makeWikitextError', 1, msg, 'string')
checkType('makeWikitextError', 2, helpLink, 'string', true)
yesno = require('Module:Yesno')
title = title or mw.title.getCurrentTitle()
-- Make the help link text.
local helpText
if helpLink then
helpText = ' ([[' .. helpLink .. '|help]])'
else
helpText = ''
end
-- Make the category text.
local category
if not title.isTalkPage -- Don't categorise talk pages
and title.namespace ~= 2 -- Don't categorise userspace
and yesno(addTrackingCategory) ~= false -- Allow opting out
then
category = 'Hatnote templates with errors'
category = mw.ustring.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
category
)
else
category = ''
end
return mw.ustring.format(
'<strong class="error">Error: %s%s.</strong>%s',
msg,
helpText,
category
)
end
local curNs = mw.title.getCurrentTitle().namespace
p.missingTargetCat =
--Default missing target category, exported for use in related modules
((curNs == 0) or (curNs == 14)) and
'Articles with hatnote templates targeting a nonexistent page' or nil
function p.quote(title)
--Wraps titles in quotation marks. If the title starts/ends with a quotation
--mark, kerns that side as with {{-'}}
local quotationMarks = {
["'"]=true, ['"']=true, ['“']=true, ["‘"]=true, ['”']=true, ["’"]=true
}
local quoteLeft, quoteRight = -- Test if start/end are quotation marks
quotationMarks[string.sub(title, 1, 1)],
quotationMarks[string.sub(title, -1, -1)]
if quoteLeft or quoteRight then
title = mw.html.create("span"):wikitext(title)
end
if quoteLeft then title:css("padding-left", "0.15em") end
if quoteRight then title:css("padding-right", "0.15em") end
return '"' .. tostring(title) .. '"'
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
p[''] = function (frame) return p.hatnote(frame:newChild{ title = "Template:Hatnote" }) end
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
if not s then
return p.makeWikitextError(
'no text specified',
'Template:Hatnote#Errors',
args.category
)
end
return p._hatnote(s, {
extraclasses = args.extraclasses,
selfref = args.selfref
})
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
options = options or {}
local inline = options.inline
local hatnote = mw.html.create(inline == 1 and 'span' or 'div')
local extraclasses
if type(options.extraclasses) == 'string' then
extraclasses = options.extraclasses
end
hatnote
:attr('role', 'note')
:addClass(p.defaultClasses(inline))
:addClass(extraclasses)
:addClass(options.selfref and 'selfref' or nil)
:wikitext(s)
return mw.getCurrentFrame():extensionTag{
name = 'templatestyles', args = { src = 'Module:Hatnote/styles.css' }
} .. tostring(hatnote)
end
return p
qps4k9ys6ba1dq9msz6m6lq3kfc4w6t
886379
886378
2025-06-13T17:03:26Z
KartikMistry
10383
[[:en:Module:Hatnote]] માંથી આયાત કરેલ ૧ પુનરાવર્તન
886378
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{hatnote}} and {{format link}} meta-templates and includes --
-- helper functions for other Lua hatnote modules. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local formatLink -- lazily initialise [[Module:Format link]] ._formatLink
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.defaultClasses(inline)
-- Provides the default hatnote classes as a space-separated string; useful
-- for hatnote-manipulation modules like [[Module:Hatnote group]].
return
(inline == 1 and 'hatnote-inline' or 'hatnote') .. ' ' ..
'navigation-not-searchable'
end
function p.disambiguate(page, disambiguator)
-- Formats a page title with a disambiguation parenthetical,
-- i.e. "Example" → "Example (disambiguation)".
checkType('disambiguate', 1, page, 'string')
checkType('disambiguate', 2, disambiguator, 'string', true)
disambiguator = disambiguator or 'disambiguation'
return mw.ustring.format('%s (%s)', page, disambiguator)
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to false.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.makeWikitextError(msg, helpLink, addTrackingCategory, title)
-- Formats an error message to be returned to wikitext. If
-- addTrackingCategory is not false after being returned from
-- [[Module:Yesno]], and if we are not on a talk page, a tracking category
-- is added.
checkType('makeWikitextError', 1, msg, 'string')
checkType('makeWikitextError', 2, helpLink, 'string', true)
yesno = require('Module:Yesno')
title = title or mw.title.getCurrentTitle()
-- Make the help link text.
local helpText
if helpLink then
helpText = ' ([[' .. helpLink .. '|help]])'
else
helpText = ''
end
-- Make the category text.
local category
if not title.isTalkPage -- Don't categorise talk pages
and title.namespace ~= 2 -- Don't categorise userspace
and yesno(addTrackingCategory) ~= false -- Allow opting out
then
category = 'Hatnote templates with errors'
category = mw.ustring.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
category
)
else
category = ''
end
return mw.ustring.format(
'<strong class="error">Error: %s%s.</strong>%s',
msg,
helpText,
category
)
end
local curNs = mw.title.getCurrentTitle().namespace
p.missingTargetCat =
--Default missing target category, exported for use in related modules
((curNs == 0) or (curNs == 14)) and
'Articles with hatnote templates targeting a nonexistent page' or nil
function p.quote(title)
--Wraps titles in quotation marks. If the title starts/ends with a quotation
--mark, kerns that side as with {{-'}}
local quotationMarks = {
["'"]=true, ['"']=true, ['“']=true, ["‘"]=true, ['”']=true, ["’"]=true
}
local quoteLeft, quoteRight = -- Test if start/end are quotation marks
quotationMarks[string.sub(title, 1, 1)],
quotationMarks[string.sub(title, -1, -1)]
if quoteLeft or quoteRight then
title = mw.html.create("span"):wikitext(title)
end
if quoteLeft then title:css("padding-left", "0.15em") end
if quoteRight then title:css("padding-right", "0.15em") end
return '"' .. tostring(title) .. '"'
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
p[''] = function (frame) return p.hatnote(frame:newChild{ title = "Template:Hatnote" }) end
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
if not s then
return p.makeWikitextError(
'no text specified',
'Template:Hatnote#Errors',
args.category
)
end
return p._hatnote(s, {
extraclasses = args.extraclasses,
selfref = args.selfref
})
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
options = options or {}
local inline = options.inline
local hatnote = mw.html.create(inline == 1 and 'span' or 'div')
local extraclasses
if type(options.extraclasses) == 'string' then
extraclasses = options.extraclasses
end
hatnote
:attr('role', 'note')
:addClass(p.defaultClasses(inline))
:addClass(extraclasses)
:addClass(options.selfref and 'selfref' or nil)
:wikitext(s)
return mw.getCurrentFrame():extensionTag{
name = 'templatestyles', args = { src = 'Module:Hatnote/styles.css' }
} .. tostring(hatnote)
end
return p
qps4k9ys6ba1dq9msz6m6lq3kfc4w6t
વિભાગ:Check for unknown parameters
828
67350
886358
818259
2024-08-29T18:05:43Z
en>Trappist the monk
0
per [[Special:permalink/1242956570#Protected edit request on 29 August 2024|edit request (permalink)]]; + require ('strict');
886358
Scribunto
text/plain
-- This module may be used to compare the arguments passed to the parent
-- with a list of arguments, returning a specified result if an argument is
-- not on the list
require ('strict');
local p = {}
local function trim(s)
return s:match('^%s*(.-)%s*$')
end
local function isnotempty(s)
return s and s:match('%S')
end
local function clean(text)
-- Return text cleaned for display and truncated if too long.
-- Strip markers are replaced with dummy text representing the original wikitext.
local pos, truncated
local function truncate(text)
if truncated then
return ''
end
if mw.ustring.len(text) > 25 then
truncated = true
text = mw.ustring.sub(text, 1, 25) .. '...'
end
return mw.text.nowiki(text)
end
local parts = {}
for before, tag, remainder in text:gmatch('([^\127]*)\127[^\127]*%-(%l+)%-[^\127]*\127()') do
pos = remainder
table.insert(parts, truncate(before) .. '<' .. tag .. '>...</' .. tag .. '>')
end
table.insert(parts, truncate(text:sub(pos or 1)))
return table.concat(parts)
end
function p._check(args, pargs)
if type(args) ~= "table" or type(pargs) ~= "table" then
-- TODO: error handling
return
end
-- create the list of known args, regular expressions, and the return string
local knownargs = {}
local regexps = {}
for k, v in pairs(args) do
if type(k) == 'number' then
v = trim(v)
knownargs[v] = 1
elseif k:find('^regexp[1-9][0-9]*$') then
table.insert(regexps, '^' .. v .. '$')
end
end
-- loop over the parent args, and make sure they are on the list
local ignoreblank = isnotempty(args['ignoreblank'])
local showblankpos = isnotempty(args['showblankpositional'])
local values = {}
for k, v in pairs(pargs) do
if type(k) == 'string' and knownargs[k] == nil then
local knownflag = false
for _, regexp in ipairs(regexps) do
if mw.ustring.match(k, regexp) then
knownflag = true
break
end
end
if not knownflag and ( not ignoreblank or isnotempty(v) ) then
table.insert(values, clean(k))
end
elseif type(k) == 'number' and knownargs[tostring(k)] == nil then
local knownflag = false
for _, regexp in ipairs(regexps) do
if mw.ustring.match(tostring(k), regexp) then
knownflag = true
break
end
end
if not knownflag and ( showblankpos or isnotempty(v) ) then
table.insert(values, k .. ' = ' .. clean(v))
end
end
end
-- add results to the output tables
local res = {}
if #values > 0 then
local unknown_text = args['unknown'] or 'Found _VALUE_, '
if mw.getCurrentFrame():preprocess( "{{REVISIONID}}" ) == "" then
local preview_text = args['preview']
if isnotempty(preview_text) then
preview_text = require('Module:If preview')._warning({preview_text})
elseif preview_text == nil then
preview_text = unknown_text
end
unknown_text = preview_text
end
for _, v in pairs(values) do
-- Fix odd bug for | = which gets stripped to the empty string and
-- breaks category links
if v == '' then v = ' ' end
-- avoid error with v = 'example%2' ("invalid capture index")
local r = unknown_text:gsub('_VALUE_', {_VALUE_ = v})
table.insert(res, r)
end
end
return table.concat(res)
end
function p.check(frame)
local args = frame.args
local pargs = frame:getParent().args
return p._check(args, pargs)
end
return p
duq9iab2i1yitd2f8nw1por2veybp89
886359
886358
2025-06-13T17:03:25Z
KartikMistry
10383
[[:en:Module:Check_for_unknown_parameters]] માંથી આયાત કરેલ ૧ પુનરાવર્તન
886358
Scribunto
text/plain
-- This module may be used to compare the arguments passed to the parent
-- with a list of arguments, returning a specified result if an argument is
-- not on the list
require ('strict');
local p = {}
local function trim(s)
return s:match('^%s*(.-)%s*$')
end
local function isnotempty(s)
return s and s:match('%S')
end
local function clean(text)
-- Return text cleaned for display and truncated if too long.
-- Strip markers are replaced with dummy text representing the original wikitext.
local pos, truncated
local function truncate(text)
if truncated then
return ''
end
if mw.ustring.len(text) > 25 then
truncated = true
text = mw.ustring.sub(text, 1, 25) .. '...'
end
return mw.text.nowiki(text)
end
local parts = {}
for before, tag, remainder in text:gmatch('([^\127]*)\127[^\127]*%-(%l+)%-[^\127]*\127()') do
pos = remainder
table.insert(parts, truncate(before) .. '<' .. tag .. '>...</' .. tag .. '>')
end
table.insert(parts, truncate(text:sub(pos or 1)))
return table.concat(parts)
end
function p._check(args, pargs)
if type(args) ~= "table" or type(pargs) ~= "table" then
-- TODO: error handling
return
end
-- create the list of known args, regular expressions, and the return string
local knownargs = {}
local regexps = {}
for k, v in pairs(args) do
if type(k) == 'number' then
v = trim(v)
knownargs[v] = 1
elseif k:find('^regexp[1-9][0-9]*$') then
table.insert(regexps, '^' .. v .. '$')
end
end
-- loop over the parent args, and make sure they are on the list
local ignoreblank = isnotempty(args['ignoreblank'])
local showblankpos = isnotempty(args['showblankpositional'])
local values = {}
for k, v in pairs(pargs) do
if type(k) == 'string' and knownargs[k] == nil then
local knownflag = false
for _, regexp in ipairs(regexps) do
if mw.ustring.match(k, regexp) then
knownflag = true
break
end
end
if not knownflag and ( not ignoreblank or isnotempty(v) ) then
table.insert(values, clean(k))
end
elseif type(k) == 'number' and knownargs[tostring(k)] == nil then
local knownflag = false
for _, regexp in ipairs(regexps) do
if mw.ustring.match(tostring(k), regexp) then
knownflag = true
break
end
end
if not knownflag and ( showblankpos or isnotempty(v) ) then
table.insert(values, k .. ' = ' .. clean(v))
end
end
end
-- add results to the output tables
local res = {}
if #values > 0 then
local unknown_text = args['unknown'] or 'Found _VALUE_, '
if mw.getCurrentFrame():preprocess( "{{REVISIONID}}" ) == "" then
local preview_text = args['preview']
if isnotempty(preview_text) then
preview_text = require('Module:If preview')._warning({preview_text})
elseif preview_text == nil then
preview_text = unknown_text
end
unknown_text = preview_text
end
for _, v in pairs(values) do
-- Fix odd bug for | = which gets stripped to the empty string and
-- breaks category links
if v == '' then v = ' ' end
-- avoid error with v = 'example%2' ("invalid capture index")
local r = unknown_text:gsub('_VALUE_', {_VALUE_ = v})
table.insert(res, r)
end
end
return table.concat(res)
end
function p.check(frame)
local args = frame.args
local pargs = frame:getParent().args
return p._check(args, pargs)
end
return p
duq9iab2i1yitd2f8nw1por2veybp89
ઢાંચો:Font color
10
67377
886394
705849
2022-12-03T14:31:07Z
en>GKFX
0
Use #if:trim rather than trim for [[WP:PEIS]], and use <includeonly> rather than the {{{|safesubst:}}} trick for readability.
886394
wikitext
text/x-wiki
<includeonly>{{ safesubst:#if: {{{text|{{{3|}}}}}}
| {{ safesubst:#if: {{{link|}}}
| {{ safesubst:#ifeq: {{{link|}}} | yes
| [[ {{ safesubst:#if:trim | {{{text|{{{3|}}}}}} }}|<span style="background-color:{{ safesubst:#if:trim | {{{bg|{{{2|inherit}}}}}} }}; color:{{ safesubst:#if:trim | {{{fg|{{{1|inherit}}}}}} }};">{{ safesubst:#if:trim | {{{text|{{{3|}}}}}} }}</span>]]
| [[{{{link|}}}|<span style="background-color:{{ safesubst:#if:trim | {{{bg|{{{2|inherit}}}}}} }}; color:{{ safesubst:#if:trim | {{{fg|{{{1|inherit}}}}}} }};">{{ safesubst:#if:trim | {{{text|{{{3|}}}}}} }}</span>]]
}}
| <span style="background-color:{{ safesubst:#if:trim | {{{bg|{{{2|inherit}}}}}} }}; color:{{ safesubst:#if:trim | {{{fg|{{{1|inherit}}}}}} }};">{{ safesubst:#if:trim | {{{text|{{{3|}}}}}} }}</span>
}}
| {{ safesubst:#if: {{{link|}}}
| {{ safesubst:#ifeq: {{{link|}}} | yes
| [[ {{ safesubst:#if:trim | {{{bg|{{{2|}}}}}} }} |<span style="color:{{ safesubst:#if:trim | {{{fg|{{{1|inherit}}}}}} }};">{{ safesubst:#if:trim | {{{bg|{{{2|}}}}}} }}</span>]]
| [[ {{ safesubst:#if:trim | {{{link|}}} }} |<span style="color:{{ safesubst:#if:trim | {{{fg|{{{1|inherit}}}}}} }};">{{ safesubst:#if:trim | {{{bg|{{{2|}}}}}} }}</span>]]
}}
| <span style="color:{{ safesubst:#if:trim | {{{fg|{{{1|inherit}}}}}} }};">{{ safesubst:#if:trim | {{{bg|{{{2|}}}}}} }}</span>
}}
}}</includeonly><noinclude>
{{documentation}}
</noinclude>
ie83cnoicyekcaw9x4vxa0nhbwv8ull
886395
886394
2025-06-13T17:03:29Z
KartikMistry
10383
[[:en:Template:Font_color]] માંથી આયાત કરેલ ૧ પુનરાવર્તન
886394
wikitext
text/x-wiki
<includeonly>{{ safesubst:#if: {{{text|{{{3|}}}}}}
| {{ safesubst:#if: {{{link|}}}
| {{ safesubst:#ifeq: {{{link|}}} | yes
| [[ {{ safesubst:#if:trim | {{{text|{{{3|}}}}}} }}|<span style="background-color:{{ safesubst:#if:trim | {{{bg|{{{2|inherit}}}}}} }}; color:{{ safesubst:#if:trim | {{{fg|{{{1|inherit}}}}}} }};">{{ safesubst:#if:trim | {{{text|{{{3|}}}}}} }}</span>]]
| [[{{{link|}}}|<span style="background-color:{{ safesubst:#if:trim | {{{bg|{{{2|inherit}}}}}} }}; color:{{ safesubst:#if:trim | {{{fg|{{{1|inherit}}}}}} }};">{{ safesubst:#if:trim | {{{text|{{{3|}}}}}} }}</span>]]
}}
| <span style="background-color:{{ safesubst:#if:trim | {{{bg|{{{2|inherit}}}}}} }}; color:{{ safesubst:#if:trim | {{{fg|{{{1|inherit}}}}}} }};">{{ safesubst:#if:trim | {{{text|{{{3|}}}}}} }}</span>
}}
| {{ safesubst:#if: {{{link|}}}
| {{ safesubst:#ifeq: {{{link|}}} | yes
| [[ {{ safesubst:#if:trim | {{{bg|{{{2|}}}}}} }} |<span style="color:{{ safesubst:#if:trim | {{{fg|{{{1|inherit}}}}}} }};">{{ safesubst:#if:trim | {{{bg|{{{2|}}}}}} }}</span>]]
| [[ {{ safesubst:#if:trim | {{{link|}}} }} |<span style="color:{{ safesubst:#if:trim | {{{fg|{{{1|inherit}}}}}} }};">{{ safesubst:#if:trim | {{{bg|{{{2|}}}}}} }}</span>]]
}}
| <span style="color:{{ safesubst:#if:trim | {{{fg|{{{1|inherit}}}}}} }};">{{ safesubst:#if:trim | {{{bg|{{{2|}}}}}} }}</span>
}}
}}</includeonly><noinclude>
{{documentation}}
</noinclude>
ie83cnoicyekcaw9x4vxa0nhbwv8ull
વિભાગ:Citation/CS1/Date validation
828
70733
886350
821285
2024-08-17T18:39:07Z
en>Trappist the monk
0
sync from sandbox;
886350
Scribunto
text/plain
--[[--------------------------< F O R W A R D D E C L A R A T I O N S >--------------------------------------
]]
local add_prop_cat, is_set, in_array, set_message, substitute, wrap_style; -- imported functions from selected Module:Citation/CS1/Utilities
local cfg; -- table of tables imported from selected Module:Citation/CS1/Configuration
--[[--------------------------< F I L E - S C O P E D E C L A R A T I O N S >--------------------------------
File-scope variables are declared here
]]
local lang_object = mw.getContentLanguage(); -- used by is_valid_accessdate(), is_valid_year(), date_name_xlate(); TODO: move to ~/Configuration?
local year_limit; -- used by is_valid_year()
--[=[-------------------------< I S _ V A L I D _ A C C E S S D A T E >----------------------------------------
returns true if:
Wikipedia start date <= accessdate < today + 2 days
Wikipedia start date is 2001-01-15T00:00:00 UTC which is 979516800 seconds after 1970-01-01T00:00:00 UTC (the start of Unix time)
accessdate is the date provided in |access-date= at time 00:00:00 UTC
today is the current date at time 00:00:00 UTC plus 48 hours
if today is 2015-01-01T00:00:00 then
adding 24 hours gives 2015-01-02T00:00:00 – one second more than today
adding 24 hours gives 2015-01-03T00:00:00 – one second more than tomorrow
This function does not work if it is fed month names for languages other than English. Wikimedia #time: parser
apparently doesn't understand non-English date month names. This function will always return false when the date
contains a non-English month name because good1 is false after the call to lang.formatDate(). To get around that
call this function with YYYY-MM-DD format dates.
]=]
local function is_valid_accessdate (accessdate)
local good1, good2;
local access_ts, tomorrow_ts; -- to hold Unix time stamps representing the dates
good1, access_ts = pcall (lang_object.formatDate, lang_object, 'U', accessdate ); -- convert accessdate value to Unix timestamp
good2, tomorrow_ts = pcall (lang_object.formatDate, lang_object, 'U', 'today + 2 days' ); -- today midnight + 2 days is one second more than all day tomorrow
if good1 and good2 then -- lang.formatDate() returns a timestamp in the local script which which tonumber() may not understand
access_ts = tonumber (access_ts) or lang_object:parseFormattedNumber (access_ts); -- convert to numbers for the comparison;
tomorrow_ts = tonumber (tomorrow_ts) or lang_object:parseFormattedNumber (tomorrow_ts);
else
return false; -- one or both failed to convert to Unix time stamp
end
if 979516800 <= access_ts and access_ts < tomorrow_ts then -- Wikipedia start date <= accessdate < tomorrow's date
return true;
else
return false; -- accessdate out of range
end
end
--[[--------------------------< G E T _ M O N T H _ N U M B E R >----------------------------------------------
returns a number according to the month in a date: 1 for January, etc. Capitalization and spelling must be correct.
If not a valid month, returns 0
]]
local function get_month_number (month)
return cfg.date_names['local'].long[month] or cfg.date_names['local'].short[month] or -- look for local names first
cfg.date_names['en'].long[month] or cfg.date_names['en'].short[month] or -- failing that, look for English names
0; -- not a recognized month name
end
--[[--------------------------< G E T _ S E A S O N _ N U M B E R >--------------------------------------------
returns a number according to the sequence of seasons in a year: 21 for Spring, etc. Capitalization and spelling
must be correct. If not a valid season, returns 0.
21-24 = Spring, Summer, Autumn, Winter, independent of “Hemisphere”
returns 0 when <param> is not |date=
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.
These additional divisions not currently supported:
25-28 = Spring - Northern Hemisphere, Summer- Northern Hemisphere, Autumn - Northern Hemisphere, Winter - Northern Hemisphere
29-32 = Spring – Southern Hemisphere, Summer– Southern Hemisphere, Autumn – Southern Hemisphere, Winter - Southern Hemisphere
33-36 = Quarter 1, Quarter 2, Quarter 3, Quarter 4 (3 months each)
37-39 = Quadrimester 1, Quadrimester 2, Quadrimester 3 (4 months each)
40-41 = Semestral 1, Semestral-2 (6 months each)
]]
local function get_season_number (season, param)
if 'date' ~= param then
return 0; -- season dates only supported by |date=
end
return cfg.date_names['local'].season[season] or -- look for local names first
cfg.date_names['en'].season[season] or -- failing that, look for English names
0; -- not a recognized season name
end
--[[--------------------------< G E T _ Q U A R T E R _ N U M B E R >------------------------------------------
returns a number according to the sequence of quarters in a year: 33 for first quarter, etc. Capitalization and spelling
must be correct. If not a valid quarter, returns 0.
33-36 = Quarter 1, Quarter 2, Quarter 3, Quarter 4 (3 months each)
returns 0 when <param> is not |date=
Quarter 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 and quarters.
These additional divisions not currently supported:
37-39 = Quadrimester 1, Quadrimester 2, Quadrimester 3 (4 months each)
40-41 = Semestral 1, Semestral-2 (6 months each)
]]
local function get_quarter_number (quarter, param)
if 'date' ~= param then
return 0; -- quarter dates only supported by |date=
end
quarter = mw.ustring.gsub (quarter, ' +', ' '); -- special case replace multiple space chars with a single space char
return cfg.date_names['local'].quarter[quarter] or -- look for local names first
cfg.date_names['en'].quarter[quarter] or -- failing that, look for English names
0; -- not a recognized quarter name
end
--[[--------------------------< G E T _ P R O P E R _ N A M E _ N U M B E R >----------------------------------
returns a non-zero number if date contains a recognized proper-name. Capitalization and spelling must be correct.
returns 0 when <param> is not |date=
]]
local function get_proper_name_number (name, param)
if 'date' ~= param then
return 0; -- proper-name dates only supported by |date=
end
return cfg.date_names['local'].named[name] or -- look for local names dates first
cfg.date_names['en'].named[name] or -- failing that, look for English names
0; -- not a recognized named date
end
--[[--------------------------< G E T _ E L E M E N T _ N U M B E R <------------------------------------------
returns true if month or season or quarter or proper name is valid (properly spelled, capitalized, abbreviated)
]]
local function get_element_number (element, param)
local num;
local funcs = {get_month_number, get_season_number, get_quarter_number, get_proper_name_number}; -- list of functions to execute in order
for _, func in ipairs (funcs) do -- spin through the function list
num = func (element, param); -- call the function and get the returned number
if 0 ~= num then -- non-zero when valid month season quarter
return num; -- return that number
end
end
return nil; -- not valid
end
--[[--------------------------< I S _ V A L I D _ Y E A R >----------------------------------------------------
Function gets current year from the server and compares it to year from a citation parameter. Years more than one
year in the future are not acceptable.
Special case for |pmc-embargo-date=: years more than two years in the future are not acceptable
]]
local function is_valid_year (year, param)
if not is_set (year_limit) then
year_limit = tonumber(os.date("%Y"))+1; -- global variable so we only have to fetch it once
end
year = tonumber (year) or lang_object:parseFormattedNumber (year); -- convert to number for the comparison
if year and (100 > year) then -- years less than 100 not supported
return false;
end
if 'pmc-embargo-date' == param then -- special case for |pmc-embargo-date=
return year and (year <= tonumber(os.date("%Y"))+2) or false; -- years more than two years in the future are not accepted
end
return year and (year <= year_limit) or false;
end
--[[--------------------------< I S _ V A L I D _ D A T E >----------------------------------------------------
Returns true if day is less than or equal to the number of days in month and year is no farther into the future
than next year; else returns false.
Assumes Julian calendar prior to year 1582 and Gregorian calendar thereafter. Accounts for Julian calendar leap
years before 1582 and Gregorian leap years after 1582. Where the two calendars overlap (1582 to approximately
1923) dates are assumed to be Gregorian.
]]
local function is_valid_date (year, month, day, param)
local days_in_month = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
local month_length;
if not is_valid_year (year, param) then -- no farther into the future than next year except |pmc-embargo-date= no more than two years in the future
return false;
end
month = tonumber (month); -- required for YYYY-MM-DD dates
if (2 == month) then -- if February
month_length = 28; -- then 28 days unless
if 1582 > tonumber(year) then -- Julian calendar
if 0 == (year%4) then -- is a leap year?
month_length = 29; -- if leap year then 29 days in February
end
else -- Gregorian calendar
if (0 == (year%4) and (0 ~= (year%100) or 0 == (year%400))) then -- is a leap year?
month_length = 29; -- if leap year then 29 days in February
end
end
else
month_length = days_in_month[month];
end
if tonumber (day) > month_length then
return false;
end
return true;
end
--[[--------------------------< I S _ V A L I D _ M O N T H _ R A N G E _ S T Y L E >--------------------------
Months in a range are expected to have the same style: Jan–Mar or October–December but not February–Mar or Jul–August.
This function looks in cfg.date_names{} to see if both month names are listed in the long subtable or both are
listed in the short subtable. When both have the same style (both are listed in the same table), returns true; false else
]]
local function is_valid_month_range_style (month1, month2)
if (cfg.date_names.en.long[month1] and cfg.date_names.en.long[month2]) or -- are both English names listed in the long subtable?
(cfg.date_names.en.short[month1] and cfg.date_names.en.short[month2]) or -- are both English names listed in the short subtable?
(cfg.date_names['local'].long[month1] and cfg.date_names['local'].long[month2]) or -- are both local names listed in the long subtable?
(cfg.date_names['local'].short[month1] and cfg.date_names['local'].short[month2]) then -- are both local names listed in the short subtable?
return true;
end
return false; -- names are mixed
end
--[[--------------------------< I S _ V A L I D _ M O N T H _ S E A S O N _ R A N G E >------------------------
Check a pair of months or seasons to see if both are valid members of a month or season pair.
Month pairs are expected to be left to right, earliest to latest in time.
All season ranges are accepted as valid because there are publishers out there who have published a Summer–Spring YYYY issue, hence treat as ok
]]
local function is_valid_month_season_range(range_start, range_end, param)
local range_start_number = get_month_number (range_start);
local range_end_number;
if 0 == range_start_number then -- is this a month range?
range_start_number = get_season_number (range_start, param); -- not a month; is it a season? get start season number
range_end_number = get_season_number (range_end, param); -- get end season number
if (0 ~= range_start_number) and (0 ~= range_end_number) and (range_start_number ~= range_end_number) then
return true; -- any season pairing is accepted except when both are the same
end
return false; -- range_start and/or range_end is not a season
end
-- here when range_start is a month
range_end_number = get_month_number (range_end); -- get end month number
if range_start_number < range_end_number and -- range_start is a month; does range_start precede range_end?
is_valid_month_range_style (range_start, range_end) then -- do months have the same style?
return true; -- proper order and same style
end
return false; -- range_start month number is greater than or equal to range end number; or range end isn't a month
end
--[[--------------------------< M A K E _ C O I N S _ D A T E >------------------------------------------------
This function receives a table of date parts for one or two dates and an empty table reference declared in
Module:Citation/CS1. The function is called only for |date= parameters and only if the |date=<value> is
determined to be a valid date format. The question of what to do with invalid date formats is not answered here.
The date parts in the input table are converted to an ISO 8601 conforming date string:
single whole dates: yyyy-mm-dd
month and year dates: yyyy-mm
year dates: yyyy
ranges: yyyy-mm-dd/yyyy-mm-dd
yyyy-mm/yyyy-mm
yyyy/yyyy
Dates in the Julian calendar are reduced to year or year/year so that we don't have to do calendar conversion from
Julian to Proleptic Gregorian.
The input table has:
year, year2 – always present; if before 1582, ignore months and days if present
month, month2 – 0 if not provided, 1-12 for months, 21-24 for seasons; 99 Christmas
day, day2 – 0 if not provided, 1-31 for days
the output table receives:
rftdate: an ISO 8601 formatted date
rftchron: a free-form version of the date, usually without year which is in rftdate (season ranges and proper-name dates)
rftssn: one of four season keywords: winter, spring, summer, fall (lowercase)
rftquarter: one of four values: 1, 2, 3, 4
]]
local function make_COinS_date (input, tCOinS_date)
local date; -- one date or first date in a range
local date2 = ''; -- end of range date
input.year = tonumber (input.year) or lang_object:parseFormattedNumber (input.year); -- language-aware tonumber()
input.year2 = tonumber (input.year2) or lang_object:parseFormattedNumber (input.year2); -- COinS dates are pseudo-ISO 8601 so convert to Arabic numerals
if ((1582 == input.year) and (10 > tonumber(input.month))) or (1582 > input.year) then -- if a Julian calendar date
tCOinS_date.rftdate = tostring (input.year); -- &rft.date gets year only
return; -- done
end
-- here for all forms of Gregorian dates
if 20 < tonumber (input.month) then -- if season, quarter, or proper-name date
date = input.year; -- &rft.date gets year only
if 0 ~= input.year2 and input.year ~= input.year2 then -- if a range, only the second year portion when not the same as range start year
date = string.format ('%.4d/%.4d', input.year, input.year2) -- assemble the date range
end
local season = {[24] = 'winter', [21] = 'spring', [22] = 'summer', [23] = 'fall', [33] = '1', [34] = '2', [35] = '3', [36] = '4', [98] = 'Easter', [99] = 'Christmas'}; -- seasons lowercase, no autumn; proper-names use title case
if 0 == input.month2 then -- single season, quarter, or proper-name date
if 40 < tonumber(input.month) then
tCOinS_date.rftchron = season[input.month]; -- proper-name date; used in journal metadata only
elseif 30 < tonumber(input.month) then
tCOinS_date.rftquarter = season[input.month]; -- quarter date; used in journal metadata only
else
tCOinS_date.rftssn = season[input.month]; -- season date; used in journal metadata only
end
else -- season ranges are lumped into &rft.chron; &rft.ssn and &rft.quarter are left blank
if input.year ~= input.year2 then -- season year – season year range or season year–year
if 0 ~= input.month2 then
tCOinS_date.rftchron = string.format ('%s %s – %s %s', season[input.month], input.year, season[input.month2], input.year2); -- used in journal metadata only
end
else -- season–season year range
tCOinS_date.rftchron = season[input.month] .. '–' .. season[input.month2]; -- season–season year range; used in journal metadata only
end
end
tCOinS_date.rftdate = tostring (date);
return; -- done
end
-- here for gregorian calendar dates
if 0 ~= input.day then
date = string.format ('%s-%.2d-%.2d', input.year, tonumber(input.month), tonumber(input.day)); -- whole date
elseif 0 ~= input.month then
date = string.format ('%s-%.2d', input.year, tonumber(input.month)); -- year and month
else
date = string.format ('%s', input.year); -- just year
end
if 0 ~= input.year2 then
if 0 ~= input.day2 then
date2 = string.format ('/%s-%.2d-%.2d', input.year2, tonumber(input.month2), tonumber(input.day2)); -- whole date
elseif 0 ~= input.month2 then
date2 = string.format ('/%s-%.2d', input.year2, tonumber(input.month2)); -- year and month
else
date2 = string.format ('/%s', input.year2); -- just year
end
end
tCOinS_date.rftdate = date .. date2; -- date2 has the '/' separator
return;
end
--[[--------------------------< P A T T E R N S _ T >----------------------------------------------------------
this is the list of patterns for date formats that this module recognizes. Approximately the first half of these
patterns represent formats that might be reformatted into another format. Those that might be reformatted have
'indicator' letters that identify the content of the matching capture: 'd' (day), 'm' (month), 'a' (anchor year),
'y' (year); second day, month, year have a '2' suffix.
These patterns are used for both date validation and for reformatting. This table should not be moved to ~/Configuration
because changes to this table require changes to check_date() and to reformatter() and reformat_date()
]]
local patterns_t = {
-- year-initial numerical year-month-day
['ymd'] = {'^(%d%d%d%d)%-(%d%d)%-(%d%d)$', 'y', 'm', 'd'},
-- month-initial: month day, year
['Mdy'] = {'^(%D-) +([1-9]%d?), +((%d%d%d%d?)%a?)$', 'm', 'd', 'a', 'y'},
-- month-initial day range: month day–day, year; days are separated by endash
['Md-dy'] = {'^(%D-) +([1-9]%d?)[%-–]([1-9]%d?), +((%d%d%d%d)%a?)$', 'm', 'd', 'd2', 'a', 'y'},
-- day-initial: day month year
['dMy'] = {'^([1-9]%d?) +(%D-) +((%d%d%d%d?)%a?)$', 'd', 'm', 'a', 'y'},
-- year-initial: year month day; day: 1 or 2 two digits, leading zero allowed; not supported at en.wiki
-- ['yMd'] = {'^((%d%d%d%d?)%a?) +(%D-) +(%d%d?)$', 'a', 'y', 'm', 'd'},
-- day-range-initial: day–day month year; days are separated by endash
['d-dMy'] = {'^([1-9]%d?)[%-–]([1-9]%d?) +(%D-) +((%d%d%d%d)%a?)$', 'd', 'd2', 'm', 'a', 'y'},
-- day initial month-day-range: day month - day month year; uses spaced endash
['dM-dMy'] = {'^([1-9]%d?) +(%D-) +[%-–] +([1-9]%d?) +(%D-) +((%d%d%d%d)%a?)$', 'd', 'm', 'd2', 'm2', 'a', 'y'},
-- month initial month-day-range: month day – month day, year; uses spaced endash
['Md-Mdy'] = {'^(%D-) +([1-9]%d?) +[%-–] +(%D-) +([1-9]%d?), +((%d%d%d%d)%a?)$','m', 'd', 'm2', 'd2', 'a', 'y'},
-- day initial month-day-year-range: day month year - day month year; uses spaced endash
['dMy-dMy'] = {'^([1-9]%d?) +(%D-) +(%d%d%d%d) +[%-–] +([1-9]%d?) +(%D-) +((%d%d%d%d)%a?)$', 'd', 'm', 'y', 'd2', 'm2', 'a', 'y2'},
-- month initial month-day-year-range: month day, year – month day, year; uses spaced endash
['Mdy-Mdy'] = {'^(%D-) +([1-9]%d?), +(%d%d%d%d) +[%-–] +(%D-) +([1-9]%d?), +((%d%d%d%d)%a?)$', 'm', 'd', 'y', 'm2', 'd2', 'a', 'y2'},
-- these date formats cannot be converted, per se, but month name can be rendered short or long
-- month/season year - month/season year; separated by spaced endash
['My-My'] = {'^(%D-) +(%d%d%d%d) +[%-–] +(%D-) +((%d%d%d%d)%a?)$', 'm', 'y', 'm2', 'a', 'y2'},
-- month/season range year; months separated by endash
['M-My'] = {'^(%D-)[%-–](%D-) +((%d%d%d%d)%a?)$', 'm', 'm2', 'a', 'y'},
-- month/season year or proper-name year; quarter year when First Quarter YYYY etc.
['My'] = {'^([^%d–]-) +((%d%d%d%d)%a?)$', 'm', 'a', 'y'}, -- this way because endash is a member of %D; %D- will match January–March 2019 when it shouldn't
-- these date formats cannot be converted
['Sy4-y2'] = {'^(%D-) +((%d%d)%d%d)[%-–]((%d%d)%a?)$'}, -- special case Winter/Summer year-year (YYYY-YY); year separated with unspaced endash
['Sy-y'] = {'^(%D-) +(%d%d%d%d)[%-–]((%d%d%d%d)%a?)$'}, -- special case Winter/Summer year-year; year separated with unspaced endash
['y-y'] = {'^(%d%d%d%d?)[%-–]((%d%d%d%d?)%a?)$'}, -- year range: YYY-YYY or YYY-YYYY or YYYY–YYYY; separated by unspaced endash; 100-9999
['y4-y2'] = {'^((%d%d)%d%d)[%-–]((%d%d)%a?)$'}, -- year range: YYYY–YY; separated by unspaced endash
['y'] = {'^((%d%d%d%d?)%a?)$'}, -- year; here accept either YYY or YYYY
}
--[[--------------------------< I S _ V A L I D _ E M B A R G O _ D A T E >------------------------------------
returns true and date value if that value has proper dmy, mdy, ymd format.
returns false and 9999 (embargoed forever) when date value is not proper format; assumes that when |pmc-embargo-date= is
set, the editor intended to embargo a PMC but |pmc-embargo-date= does not hold a single date.
]]
local function is_valid_embargo_date (v)
if v:match (patterns_t['ymd'][1]) or -- ymd
v:match (patterns_t['Mdy'][1]) or -- dmy
v:match (patterns_t['dMy'][1]) then -- mdy
return true, v;
end
return false, '9999'; -- if here not good date so return false and set embargo date to long time in future
end
--[[--------------------------< C H E C K _ D A T E >----------------------------------------------------------
Check date format to see that it is one of the formats approved by WP:DATESNO or WP:DATERANGE. Exception: only
allowed range separator is endash. Additionally, check the date to see that it is a real date: no 31 in 30-day
months; no 29 February when not a leap year. Months, both long-form and three character abbreviations, and seasons
must be spelled correctly. Future years beyond next year are not allowed.
If the date fails the format tests, this function returns false and does not return values for anchor_year and
COinS_date. When this happens, the date parameter is (DEBUG: not?) used in the COinS metadata and the CITEREF identifier gets
its year from the year parameter if present otherwise CITEREF does not get a date value.
Inputs:
date_string - date string from date-holding parameters (date, year, publication-date, access-date, pmc-embargo-date, archive-date, lay-date)
Returns:
false if date string is not a real date; else
true, anchor_year, COinS_date
anchor_year can be used in CITEREF anchors
COinS_date is ISO 8601 format date; see make_COInS_date()
]]
local function check_date (date_string, param, tCOinS_date)
local year; -- assume that year2, months, and days are not used;
local year2 = 0; -- second year in a year range
local month = 0;
local month2 = 0; -- second month in a month range
local day = 0;
local day2 = 0; -- second day in a day range
local anchor_year;
local coins_date;
if date_string:match (patterns_t['ymd'][1]) then -- year-initial numerical year month day format
year, month, day = date_string:match (patterns_t['ymd'][1]);
if 12 < tonumber(month) or 1 > tonumber(month) or 1582 > tonumber(year) or 0 == tonumber(day) then return false; end -- month or day number not valid or not Gregorian calendar
anchor_year = year;
elseif mw.ustring.match(date_string, patterns_t['Mdy'][1]) then -- month-initial: month day, year
month, day, anchor_year, year = mw.ustring.match(date_string, patterns_t['Mdy'][1]);
month = get_month_number (month);
if 0 == month then return false; end -- return false if month text isn't one of the twelve months
elseif mw.ustring.match(date_string, patterns_t['Md-dy'][1]) then -- month-initial day range: month day–day, year; days are separated by endash
month, day, day2, anchor_year, year = mw.ustring.match(date_string, patterns_t['Md-dy'][1]);
if tonumber(day) >= tonumber(day2) then return false; end -- date range order is left to right: earlier to later; dates may not be the same;
month = get_month_number (month);
if 0 == month then return false; end -- return false if month text isn't one of the twelve months
month2=month; -- for metadata
year2 = year;
elseif mw.ustring.match(date_string, patterns_t['dMy'][1]) then -- day-initial: day month year
day, month, anchor_year, year = mw.ustring.match(date_string, patterns_t['dMy'][1]);
month = get_month_number (month);
if 0 == month then return false; end -- return false if month text isn't one of the twelve months
--[[ NOT supported at en.wiki
elseif mw.ustring.match(date_string, patterns_t['yMd'][1]) then -- year-initial: year month day; day: 1 or 2 two digits, leading zero allowed
anchor_year, year, month, day = mw.ustring.match(date_string, patterns_t['yMd'][1]);
month = get_month_number (month);
if 0 == month then return false; end -- return false if month text isn't one of the twelve months
-- end NOT supported at en.wiki ]]
elseif mw.ustring.match(date_string, patterns_t['d-dMy'][1]) then -- day-range-initial: day–day month year; days are separated by endash
day, day2, month, anchor_year, year = mw.ustring.match(date_string, patterns_t['d-dMy'][1]);
if tonumber(day) >= tonumber(day2) then return false; end -- date range order is left to right: earlier to later; dates may not be the same;
month = get_month_number (month);
if 0 == month then return false; end -- return false if month text isn't one of the twelve months
month2 = month; -- for metadata
year2 = year;
elseif mw.ustring.match(date_string, patterns_t['dM-dMy'][1]) then -- day initial month-day-range: day month - day month year; uses spaced endash
day, month, day2, month2, anchor_year, year = mw.ustring.match(date_string, patterns_t['dM-dMy'][1]);
if (not is_valid_month_season_range(month, month2)) or not is_valid_year(year) then return false; end -- date range order is left to right: earlier to later;
month = get_month_number (month); -- for metadata
month2 = get_month_number (month2);
year2 = year;
elseif mw.ustring.match(date_string, patterns_t['Md-Mdy'][1]) then -- month initial month-day-range: month day – month day, year; uses spaced endash
month, day, month2, day2, anchor_year, year = mw.ustring.match(date_string, patterns_t['Md-Mdy'][1]);
if (not is_valid_month_season_range(month, month2, param)) or not is_valid_year(year) then return false; end
month = get_month_number (month); -- for metadata
month2 = get_month_number (month2);
year2 = year;
elseif mw.ustring.match(date_string, patterns_t['dMy-dMy'][1]) then -- day initial month-day-year-range: day month year - day month year; uses spaced endash
day, month, year, day2, month2, anchor_year, year2 = mw.ustring.match(date_string, patterns_t['dMy-dMy'][1]);
if tonumber(year2) <= tonumber(year) then return false; end -- must be sequential years, left to right, earlier to later
if not is_valid_year(year2) or not is_valid_month_range_style(month, month2) then return false; end -- year2 no more than one year in the future; months same style
month = get_month_number (month); -- for metadata
month2 = get_month_number (month2);
if 0 == month or 0 == month2 then return false; end -- both must be valid
elseif mw.ustring.match(date_string, patterns_t['Mdy-Mdy'][1]) then -- month initial month-day-year-range: month day, year – month day, year; uses spaced endash
month, day, year, month2, day2, anchor_year, year2 = mw.ustring.match(date_string, patterns_t['Mdy-Mdy'][1]);
if tonumber(year2) <= tonumber(year) then return false; end -- must be sequential years, left to right, earlier to later
if not is_valid_year(year2) or not is_valid_month_range_style(month, month2) then return false; end -- year2 no more than one year in the future; months same style
month = get_month_number (month); -- for metadata
month2 = get_month_number(month2);
if 0 == month or 0 == month2 then return false; end -- both must be valid
elseif mw.ustring.match(date_string, patterns_t['Sy4-y2'][1]) then -- special case Winter/Summer year-year (YYYY-YY); year separated with unspaced endash
local century;
month, year, century, anchor_year, year2 = mw.ustring.match(date_string, patterns_t['Sy4-y2'][1]);
if 'Winter' ~= month and 'Summer' ~= month then return false end; -- 'month' can only be Winter or Summer
anchor_year = year .. '–' .. anchor_year; -- assemble anchor_year from both years
year2 = century..year2; -- add the century to year2 for comparisons
if 1 ~= tonumber(year2) - tonumber(year) then return false; end -- must be sequential years, left to right, earlier to later
if not is_valid_year(year2) then return false; end -- no year farther in the future than next year
month = get_season_number(month, param);
elseif mw.ustring.match(date_string, patterns_t['Sy-y'][1]) then -- special case Winter/Summer year-year; year separated with unspaced endash
month, year, anchor_year, year2 = mw.ustring.match(date_string, patterns_t['Sy-y'][1]);
month = get_season_number (month, param); -- <month> can only be winter or summer; also for metadata
if (month ~= cfg.date_names['en'].season['Winter']) and (month ~= cfg.date_names['en'].season['Summer']) then
return false; -- not Summer or Winter; abandon
end
anchor_year = year .. '–' .. anchor_year; -- assemble anchor_year from both years
if 1 ~= tonumber(year2) - tonumber(year) then return false; end -- must be sequential years, left to right, earlier to later
if not is_valid_year(year2) then return false; end -- no year farther in the future than next year
elseif mw.ustring.match(date_string, patterns_t['My-My'][1]) then -- month/season year - month/season year; separated by spaced endash
month, year, month2, anchor_year, year2 = mw.ustring.match(date_string, patterns_t['My-My'][1]);
anchor_year = year .. '–' .. anchor_year; -- assemble anchor_year from both years
if tonumber(year) >= tonumber(year2) then return false; end -- left to right, earlier to later, not the same
if not is_valid_year(year2) then return false; end -- no year farther in the future than next year
if 0 ~= get_month_number(month) and 0 ~= get_month_number(month2) and is_valid_month_range_style(month, month2) then -- both must be month year, same month style
month = get_month_number(month);
month2 = get_month_number(month2);
elseif 0 ~= get_season_number(month, param) and 0 ~= get_season_number(month2, param) then -- both must be season year, not mixed
month = get_season_number(month, param);
month2 = get_season_number(month2, param);
else
return false;
end
elseif mw.ustring.match(date_string, patterns_t['M-My'][1]) then -- month/season range year; months separated by endash
month, month2, anchor_year, year = mw.ustring.match(date_string, patterns_t['M-My'][1]);
if (not is_valid_month_season_range(month, month2, param)) or (not is_valid_year(year)) then return false; end
if 0 ~= get_month_number(month) then -- determined to be a valid range so just check this one to know if month or season
month = get_month_number(month);
month2 = get_month_number(month2);
if 0 == month or 0 == month2 then return false; end
else
month = get_season_number(month, param);
month2 = get_season_number(month2, param);
end
year2 = year;
elseif mw.ustring.match(date_string, patterns_t['My'][1]) then -- month/season/quarter/proper-name year
month, anchor_year, year = mw.ustring.match(date_string, patterns_t['My'][1]);
if not is_valid_year(year) then return false; end
month = get_element_number(month, param); -- get month season quarter proper-name number or nil
if not month then return false; end -- not valid whatever it is
elseif mw.ustring.match(date_string, patterns_t['y-y'][1]) then -- Year range: YYY-YYY or YYY-YYYY or YYYY–YYYY; separated by unspaced endash; 100-9999
year, anchor_year, year2 = mw.ustring.match(date_string, patterns_t['y-y'][1]);
anchor_year = year .. '–' .. anchor_year; -- assemble anchor year from both years
if tonumber(year) >= tonumber(year2) then return false; end -- left to right, earlier to later, not the same
if not is_valid_year(year2) then return false; end -- no year farther in the future than next year
elseif mw.ustring.match(date_string, patterns_t['y4-y2'][1]) then -- Year range: YYYY–YY; separated by unspaced endash
local century;
year, century, anchor_year, year2 = mw.ustring.match(date_string, patterns_t['y4-y2'][1]);
anchor_year = year .. '–' .. anchor_year; -- assemble anchor year from both years
if 13 > tonumber(year2) then return false; end -- don't allow 2003-05 which might be May 2003
year2 = century .. year2; -- add the century to year2 for comparisons
if tonumber(year) >= tonumber(year2) then return false; end -- left to right, earlier to later, not the same
if not is_valid_year(year2) then return false; end -- no year farther in the future than next year
if in_array (param, {'date', 'publication-date', 'year'}) then -- here when 'valid' abbreviated year range; if one of these parameters
add_prop_cat ('year-range-abbreviated'); -- add properties cat
end
elseif mw.ustring.match(date_string, patterns_t['y'][1]) then -- year; here accept either YYY or YYYY
anchor_year, year = mw.ustring.match(date_string, patterns_t['y'][1]);
if false == is_valid_year(year) then
return false;
end
else
return false; -- date format not one of the MOS:DATE approved formats
end
if param ~= 'date' then -- CITEREF disambiguation only allowed in |date=; |year= & |publication-date= promote to date
if anchor_year:match ('%l$') then
return false;
end
end
if 'access-date' == param then -- test access-date here because we have numerical date parts
if 0 ~= year and 0 ~= month and 0 ~= day and -- all parts of a single date required
0 == year2 and 0 == month2 and 0 == day2 then -- none of these; access-date must not be a range
if not is_valid_accessdate(year .. '-' .. month .. '-' .. day) then
return false; -- return false when access-date out of bounds
end
else
return false; -- return false when access-date is a range of two dates
end
end
if 'archive-date' == param then -- test archive-date here because we have numerical date parts
if not (0 ~= year and 0 ~= month and 0 ~= day and -- all parts of a single date required
0 == year2 and 0 == month2 and 0 == day2) then -- none of these; archive-date must not be a range
return false; -- return false when archive-date is a range of two dates
end
end
local result=true; -- check whole dates for validity; assume true because not all dates will go through this test
if 0 ~= year and 0 ~= month and 0 ~= day and 0 == year2 and 0 == month2 and 0 == day2 then -- YMD (simple whole date)
result = is_valid_date (year, month, day, param); -- <param> for |pmc-embargo-date=
elseif 0 ~= year and 0 ~= month and 0 ~= day and 0 == year2 and 0 == month2 and 0 ~= day2 then -- YMD-d (day range)
result = is_valid_date (year, month, day);
result = result and is_valid_date (year, month, day2);
elseif 0 ~= year and 0 ~= month and 0 ~= day and 0 == year2 and 0 ~= month2 and 0 ~= day2 then -- YMD-md (day month range)
result = is_valid_date (year, month, day);
result = result and is_valid_date (year, month2, day2);
elseif 0 ~= year and 0 ~= month and 0 ~= day and 0 ~= year2 and 0 ~= month2 and 0 ~= day2 then -- YMD-ymd (day month year range)
result = is_valid_date(year, month, day);
result = result and is_valid_date(year2, month2, day2);
end
if false == result then return false; end
if nil ~= tCOinS_date then -- this table only passed into this function when testing |date= parameter values
make_COinS_date ({year = year, month = month, day = day, year2 = year2, month2 = month2, day2 = day2}, tCOinS_date); -- make an ISO 8601 date string for COinS
end
return true, anchor_year; -- format is good and date string represents a real date
end
--[[--------------------------< D A T E S >--------------------------------------------------------------------
Cycle the date-holding parameters in passed table date_parameters_list through check_date() to check compliance with MOS:DATE. For all valid dates, check_date() returns
true. The |date= parameter test is unique, it is the only date holding parameter from which values for anchor_year (used in CITEREF identifiers) and COinS_date (used in
the COinS metadata) are derived. The |date= parameter is the only date-holding parameter that is allowed to contain the no-date keywords "n.d." or "nd" (without quotes).
Unlike most error messages created in this module, only one error message is created by this function. Because all of the date holding parameters are processed serially,
parameters with errors are added to the <error_list> sequence table as the dates are tested.
]]
local function dates(date_parameters_list, tCOinS_date, error_list)
local anchor_year; -- will return as nil if the date being tested is not |date=
local COinS_date; -- will return as nil if the date being tested is not |date=
local embargo_date; -- if embargo date is a good dmy, mdy, ymd date then holds original value else reset to 9999
local good_date = false;
for k, v in pairs(date_parameters_list) do -- for each date-holding parameter in the list
if is_set(v.val) then -- if the parameter has a value
v.val = mw.ustring.gsub(v.val, '%d', cfg.date_names.local_digits); -- translate 'local' digits to Western 0-9
if v.val:match("^c%. [1-9]%d%d%d?%a?$") then -- special case for c. year or with or without CITEREF disambiguator - only |date= and |year=
local year = v.val:match("c%. ([1-9]%d%d%d?)%a?"); -- get the year portion so it can be tested
if 'date' == k then
anchor_year, COinS_date = v.val:match("((c%. [1-9]%d%d%d?)%a?)"); -- anchor year and COinS_date only from |date= parameter
good_date = is_valid_year(year);
elseif 'year' == k then
good_date = is_valid_year(year);
end
elseif 'date' == k then -- if the parameter is |date=
if v.val:match("^n%.d%.%a?$") then -- ToDo: I18N -- if |date=n.d. with or without a CITEREF disambiguator
good_date, anchor_year, COinS_date = true, v.val:match("((n%.d%.)%a?)"); -- ToDo: I18N -- "n.d."; no error when date parameter is set to no date
elseif v.val:match("^nd%a?$") then -- ToDo: I18N -- if |date=nd with or without a CITEREF disambiguator
good_date, anchor_year, COinS_date = true, v.val:match("((nd)%a?)"); -- ToDo: I18N -- "nd"; no error when date parameter is set to no date
else
good_date, anchor_year, COinS_date = check_date (v.val, k, tCOinS_date); -- go test the date
end
elseif 'year' == k then -- if the parameter is |year= it should hold only a year value
if v.val:match("^[1-9]%d%d%d?%a?$") then -- if |year = 3 or 4 digits only with or without a CITEREF disambiguator
good_date, anchor_year, COinS_date = true, v.val:match("((%d+)%a?)");
end
elseif 'pmc-embargo-date' == k then -- if the parameter is |pmc-embargo-date=
good_date = check_date (v.val, k); -- go test the date
if true == good_date then -- if the date is a valid date
good_date, embargo_date = is_valid_embargo_date (v.val); -- is |pmc-embargo-date= date a single dmy, mdy, or ymd formatted date? yes: returns embargo date; no: returns 9999
end
else -- any other date-holding parameter
good_date = check_date (v.val, k); -- go test the date
end
if false == good_date then -- assemble one error message so we don't add the tracking category multiple times
table.insert (error_list, wrap_style ('parameter', v.name)); -- make parameter name suitable for error message list
end
end
end
return anchor_year, embargo_date; -- and done
end
--[[--------------------------< Y E A R _ C H E C K >----------------------------------------------------------
Temporary function to test |year= for acceptable values:
YYY, YYYY, year-only ranges, their circa forms, with or without CITEREF disambiguators.
When |year= holds some form of date that is not one of these year-only dates, emit a maintenance message.
This function necessary because many non-cs1|2 templates have a |year= parameter so cirrus searches are more-or-
less useless
]]
local function year_check (year)
year = year:gsub ('c%. *', ''); -- remove circa annotation (if present) before testing <year>
for _, index in ipairs ({'y-y', 'y4-y2', 'y'}) do -- spin through these indexes into patterns_t
if mw.ustring.match (year, patterns_t[index][1]) then
return; -- if a match then |year= holds a valid 'year'
end
end
set_message ('maint_year'); -- if here, |year= value is not an accepted value; add a maint cat
end
--[[--------------------------< Y E A R _ D A T E _ C H E C K >------------------------------------------------
Compare the value provided in |year= with the year value(s) provided in |date=. This function sets a local numeric value:
0 - year value does not match the year value in date
1 - (default) year value matches the year value in date or one of the year values when date contains two years
2 - year value matches the year value in date when date is in the form YYYY-MM-DD and year is disambiguated (|year=YYYYx)
the numeric value in <result> determines the 'output' if any from this function:
0 – adds error message to error_list sequence table
1 – adds maint cat
2 – does nothing
]]
local function year_date_check (year_string, year_origin, date_string, date_origin, error_list)
local year;
local date1;
local date2;
local result = 1; -- result of the test; assume that the test passes
year = year_string:match ('(%d%d%d%d?)');
if date_string:match ('%d%d%d%d%-%d%d%-%d%d') and year_string:match ('%d%d%d%d%a') then --special case where both date and year are required YYYY-MM-DD and YYYYx
date1 = date_string:match ('(%d%d%d%d)');
year = year_string:match ('(%d%d%d%d)');
if year ~= date1 then
result = 0; -- years don't match
else
result = 2; -- years match; but because disambiguated, don't add to maint cat
end
elseif date_string:match ("%d%d%d%d?.-%d%d%d%d?") then -- any of the standard range formats of date with two three- or four-digit years
date1, date2 = date_string:match ("(%d%d%d%d?).-(%d%d%d%d?)");
if year ~= date1 and year ~= date2 then
result = 0;
end
elseif mw.ustring.match(date_string, "%d%d%d%d[%-–]%d%d") then -- YYYY-YY date ranges
local century;
date1, century, date2 = mw.ustring.match(date_string, "((%d%d)%d%d)[%-–]+(%d%d)");
date2 = century..date2; -- convert YY to YYYY
if year ~= date1 and year ~= date2 then
result = 0;
end
elseif date_string:match ("%d%d%d%d?") then -- any of the standard formats of date with one year
date1 = date_string:match ("(%d%d%d%d?)");
if year ~= date1 then
result = 0;
end
else -- should never get here; this function called only when no other date errors
result = 0; -- no recognizable year in date
end
if 0 == result then -- year / date mismatch
table.insert (error_list, substitute (cfg.messages['mismatch'], {year_origin, date_origin})); -- add error message to error_list sequence table
elseif 1 == result then -- redundant year / date
set_message ('maint_date_year'); -- add a maint cat
end
end
--[[--------------------------< R E F O R M A T T E R >--------------------------------------------------------
reformat 'date' into new format specified by format_param if pattern_idx (the current format of 'date') can be
reformatted. Does the grunt work for reformat_dates().
The table re_formats maps pattern_idx (current format) and format_param (desired format) to a table that holds:
format string used by string.format()
identifier letters ('d', 'm', 'y', 'd2', 'm2', 'y2') that serve as indexes into a table t{} that holds captures
from mw.ustring.match() for the various date parts specified by patterns_t[pattern_idx][1]
Items in patterns_t{} have the general form:
['ymd'] = {'^(%d%d%d%d)%-(%d%d)%-(%d%d)$', 'y', 'm', 'd'}, where:
['ymd'] is pattern_idx
patterns_t['ymd'][1] is the match pattern with captures for mw.ustring.match()
patterns_t['ymd'][2] is an indicator letter identifying the content of the first capture
patterns_t['ymd'][3] ... the second capture etc.
when a pattern matches a date, the captures are loaded into table t{} in capture order using the idemtifier
characters as indexes into t{} For the above, a ymd date is in t{} as:
t.y = first capture (year), t.m = second capture (month), t.d = third capture (day)
To reformat, this function is called with the pattern_idx that matches the current format of the date and with
format_param set to the desired format. This function loads table t{} as described and then calls string.format()
with the format string specified by re_format[pattern_idx][format_param][1] using values taken from t{} according
to the capture identifier letters specified by patterns_t[pattern_idx][format_param][n] where n is 2..
]]
local re_formats = {
['ymd'] = { -- date format is ymd; reformat to:
['mdy'] = {'%s %s, %s', 'm', 'd', 'y'}, -- |df=mdy
['dmy'] = {'%s %s %s', 'd', 'm', 'y'}, -- |df=dmy
-- ['yMd'] = {'%s %s %s', 'y', 'm', 'd'}, -- |df=yMd; not supported at en.wiki
},
['Mdy'] = { -- date format is Mdy; reformat to:
['mdy'] = {'%s %s, %s', 'm', 'd', 'y'}, -- for long/short reformatting
['dmy'] = {'%s %s %s', 'd', 'm', 'y'}, -- |df=dmy
['ymd'] = {'%s-%s-%s', 'y', 'm', 'd'}, -- |df=ymd
-- ['yMd'] = {'%s %s %s', 'y', 'm', 'd'}, -- |df=yMd; not supported at en.wiki
},
['dMy'] = { -- date format is dMy; reformat to:
['dmy'] = {'%s %s %s', 'd', 'm', 'y'}, -- for long/short reformatting
['mdy'] = {'%s %s, %s', 'm', 'd', 'y'}, -- |df=mdy
['ymd'] = {'%s-%s-%s', 'y', 'm', 'd'}, -- |df=ymd
-- ['yMd'] = {'%s %s %s', 'y', 'm', 'd'}, -- |df=yMd; not supported at en.wiki
},
['Md-dy'] = { -- date format is Md-dy; reformat to:
['mdy'] = {'%s %s–%s, %s', 'm', 'd', 'd2', 'y'}, -- for long/short reformatting
['dmy'] = {'%s–%s %s %s', 'd', 'd2', 'm', 'y'}, -- |df=dmy -> d-dMy
},
['d-dMy'] = { -- date format is d-d>y; reformat to:
['dmy'] = {'%s–%s %s %s', 'd', 'd2', 'm', 'y'}, -- for long/short reformatting
['mdy'] = {'%s %s–%s, %s', 'm', 'd', 'd2', 'y'}, -- |df=mdy -> Md-dy
},
['dM-dMy'] = { -- date format is dM-dMy; reformat to:
['dmy'] = {'%s %s – %s %s %s', 'd', 'm', 'd2', 'm2', 'y'}, -- for long/short reformatting
['mdy'] = {'%s %s – %s %s, %s', 'm', 'd', 'm2', 'd2', 'y'}, -- |df=mdy -> Md-Mdy
},
['Md-Mdy'] = { -- date format is Md-Mdy; reformat to:
['mdy'] = {'%s %s – %s %s, %s', 'm', 'd', 'm2', 'd2', 'y'}, -- for long/short reformatting
['dmy'] = {'%s %s – %s %s %s', 'd', 'm', 'd2', 'm2', 'y'}, -- |df=dmy -> dM-dMy
},
['dMy-dMy'] = { -- date format is dMy-dMy; reformat to:
['dmy'] = {'%s %s %s – %s %s %s', 'd', 'm', 'y', 'd2', 'm2', 'y2'}, -- for long/short reformatting
['mdy'] = {'%s %s, %s – %s %s, %s', 'm', 'd', 'y', 'm2', 'd2', 'y2'}, -- |df=mdy -> Mdy-Mdy
},
['Mdy-Mdy'] = { -- date format is Mdy-Mdy; reformat to:
['mdy'] = {'%s %s, %s – %s %s, %s', 'm', 'd', 'y', 'm2', 'd2', 'y2'}, -- for long/short reformatting
['dmy'] = {'%s %s %s – %s %s %s', 'd', 'm', 'y', 'd2', 'm2', 'y2'}, -- |df=dmy -> dMy-dMy
},
['My-My'] = { -- these for long/short reformatting
['any'] = {'%s %s – %s %s', 'm', 'y', 'm2', 'y2'}, -- dmy/mdy agnostic
},
['M-My'] = { -- these for long/short reformatting
['any'] = {'%s–%s %s', 'm', 'm2', 'y'}, -- dmy/mdy agnostic
},
['My'] = { -- these for long/short reformatting
['any'] = {'%s %s', 'm', 'y'}, -- dmy/mdy agnostic
},
-- ['yMd'] = { -- not supported at en.wiki
-- ['mdy'] = {'%s %s, %s', 'm', 'd', 'y'}, -- |df=mdy
-- ['dmy'] = {'%s %s %s', 'd', 'm', 'y'}, -- |df=dmy
-- ['ymd'] = {'%s-%s-%s', 'y', 'm', 'd'}, -- |df=ymd
-- },
}
local function reformatter (date, pattern_idx, format_param, mon_len)
if not in_array (pattern_idx, {'ymd', 'Mdy', 'Md-dy', 'dMy', 'yMd', 'd-dMy', 'dM-dMy', 'Md-Mdy', 'dMy-dMy', 'Mdy-Mdy', 'My-My', 'M-My', 'My'}) then
return; -- not in this set of date format patterns_t then not a reformattable date
end
if 'ymd' == format_param and in_array (pattern_idx, {'ymd', 'Md-dy', 'd-dMy', 'dM-dMy', 'Md-Mdy', 'dMy-dMy', 'Mdy-Mdy', 'My-My', 'M-My', 'My'}) then
return; -- ymd date ranges not supported at en.wiki; no point in reformatting ymd to ymd
end
if in_array (pattern_idx, {'My', 'M-My', 'My-My'}) then -- these are not dmy/mdy so can't be 'reformatted' into either
format_param = 'any'; -- so format-agnostic
end
-- yMd is not supported at en.wiki; when yMd is supported at your wiki, uncomment the next line
-- if 'yMd' == format_param and in_array (pattern_idx, {'yMd', 'Md-dy', 'd-dMy', 'dM-dMy', 'Md-Mdy', 'dMy-dMy', 'Mdy-Mdy'}) then -- these formats not convertable; yMd not supported at en.wiki
if 'yMd' == format_param then -- yMd not supported at en.wiki; when yMd is supported at your wiki, remove or comment-out this line
return; -- not a reformattable date
end
local c1, c2, c3, c4, c5, c6, c7; -- these hold the captures specified in patterns_t[pattern_idx][1]
c1, c2, c3, c4, c5, c6, c7 = mw.ustring.match (date, patterns_t[pattern_idx][1]); -- get the captures
local t = { -- table that holds k/v pairs of date parts from the captures and patterns_t[pattern_idx][2..]
[patterns_t[pattern_idx][2]] = c1; -- at minimum there is always one capture with a matching indicator letter
[patterns_t[pattern_idx][3] or 'x'] = c2; -- patterns_t can have a variable number of captures; each capture requires an indicator letter;
[patterns_t[pattern_idx][4] or 'x'] = c3; -- where there is no capture, there is no indicator letter so n in patterns_t[pattern_idx][n] will be nil;
[patterns_t[pattern_idx][5] or 'x'] = c4; -- the 'x' here spoofs an indicator letter to prevent 'table index is nil' error
[patterns_t[pattern_idx][6] or 'x'] = c5;
[patterns_t[pattern_idx][7] or 'x'] = c6;
[patterns_t[pattern_idx][8] or 'x'] = c7;
};
if t.a then -- if this date has an anchor year capture (all convertable date formats except ymd)
if t.y2 then -- for year range date formats
t.y2 = t.a; -- use the anchor year capture when reassembling the date
else -- here for single date formats (except ymd)
t.y = t.a; -- use the anchor year capture when reassembling the date
end
end
if tonumber(t.m) then -- if raw month is a number (converting from ymd)
if 's' == mon_len then -- if we are to use abbreviated month names
t.m = cfg.date_names['inv_local_short'][tonumber(t.m)]; -- convert it to a month name
else
t.m = cfg.date_names['inv_local_long'][tonumber(t.m)]; -- convert it to a month name
end
t.d = t.d:gsub ('0(%d)', '%1'); -- strip leading '0' from day if present
elseif 'ymd' == format_param then -- when converting to ymd
t.y = t.y:gsub ('%a', ''); -- strip CITREF disambiguator if present; anchor year already known so process can proceed; TODO: maint message?
if 1582 > tonumber (t.y) then -- ymd format dates not allowed before 1582
return;
end
t.m = string.format ('%02d', get_month_number (t.m)); -- make sure that month and day are two digits
t.d = string.format ('%02d', t.d);
elseif mon_len then -- if mon_len is set to either 'short' or 'long'
for _, mon in ipairs ({'m', 'm2'}) do -- because there can be two month names, check both
if t[mon] then
t[mon] = get_month_number (t[mon]); -- get the month number for this month (is length agnostic)
if 0 == t[mon] then return; end -- seasons and named dates can't be converted
t[mon] = (('s' == mon_len) and cfg.date_names['inv_local_short'][t[mon]]) or cfg.date_names['inv_local_long'][t[mon]]; -- fetch month name according to length
end
end
end
local new_date = string.format (re_formats[pattern_idx][format_param][1], -- format string
t[re_formats[pattern_idx][format_param][2]], -- named captures from t{}
t[re_formats[pattern_idx][format_param][3]],
t[re_formats[pattern_idx][format_param][4]],
t[re_formats[pattern_idx][format_param][5]],
t[re_formats[pattern_idx][format_param][6]],
t[re_formats[pattern_idx][format_param][7]],
t[re_formats[pattern_idx][format_param][8]]
);
return new_date;
end
--[[-------------------------< R E F O R M A T _ D A T E S >--------------------------------------------------
Reformats existing dates into the format specified by format.
format is one of several manual keywords: dmy, dmy-all, mdy, mdy-all, ymd, ymd-all. The -all version includes
access- and archive-dates; otherwise these dates are not reformatted.
This function allows automatic date formatting. In ~/Configuration, the article source is searched for one of
the {{use xxx dates}} templates. If found, xxx becomes the global date format as xxx-all. If |cs1-dates= in
{{use xxx dates}} has legitimate value then that value determines how cs1|2 dates will be rendered. Legitimate
values for |cs1-dates= are:
l - all dates are rendered with long month names
ls - publication dates use long month names; access-/archive-dates use abbreviated month names
ly - publication dates use long month names; access-/archive-dates rendered in ymd format
s - all dates are rendered with abbreviated (short) month names
sy - publication dates use abbreviated month names; access-/archive-dates rendered in ymd format
y - all dates are rendered in ymd format
the format argument for automatic date formatting will be the format specified by {{use xxx dates}} with the
value supplied by |cs1-dates so one of: xxx-l, xxx-ls, xxx-ly, xxx-s, xxx-sy, xxx-y, or simply xxx (|cs1-dates=
empty, omitted, or invalid) where xxx shall be either of dmy or mdy.
dates are extracted from date_parameters_list, reformatted (if appropriate), and then written back into the
list in the new format. Dates in date_parameters_list are presumed here to be valid (no errors). This function
returns true when a date has been reformatted, false else. Actual reformatting is done by reformatter().
]]
local function reformat_dates (date_parameters_list, format)
local all = false; -- set to false to skip access- and archive-dates
local len_p = 'l'; -- default publication date length shall be long
local len_a = 'l'; -- default access-/archive-date length shall be long
local result = false;
local new_date;
if format:match('%a+%-all') then -- manual df keyword; auto df keyword when length not specified in {{use xxx dates}};
format = format:match('(%a+)%-all'); -- extract the format
all = true; -- all dates are long format dates because this keyword doesn't specify length
elseif format:match('%a+%-[lsy][sy]?') then -- auto df keywords; internal only
all = true; -- auto df applies to all dates; use length specified by capture len_p for all dates
format, len_p, len_a = format:match('(%a+)%-([lsy])([sy]?)'); -- extract the format and length keywords
if 'y' == len_p then -- because allowed by MOS:DATEUNIFY (sort of) range dates and My dates not reformatted
format = 'ymd'; -- override {{use xxx dates}}
elseif (not is_set(len_a)) or (len_p == len_a) then -- no access-/archive-date length specified or same length as publication dates then
len_a = len_p; -- in case len_a not set
end
end -- else only publication dates and they are long
for param_name, param_val in pairs (date_parameters_list) do -- for each date-holding parameter in the list
if is_set (param_val.val) then -- if the parameter has a value
if not (not all and in_array (param_name, {'access-date', 'archive-date'})) then -- skip access- or archive-date unless format is xxx-all; yeah, ugly; TODO: find a better way
for pattern_idx, pattern in pairs (patterns_t) do
if mw.ustring.match (param_val.val, pattern[1]) then
if all and in_array (param_name, {'access-date', 'archive-date'}) then -- if this date is an access- or archive-date
new_date = reformatter (param_val.val, pattern_idx, (('y' == len_a) and 'ymd') or format, len_a); -- choose ymd or dmy/mdy according to len_a setting
else -- all other dates
new_date = reformatter (param_val.val, pattern_idx, format, len_p);
end
if new_date then -- set when date was reformatted
date_parameters_list[param_name].val = new_date; -- update date in date list
result = true; -- and announce that changes have been made
break;
end
end -- if
end -- for
end -- if
end -- if
end -- for
return result; -- declare boolean result and done
end
--[[--------------------------< D A T E _ H Y P H E N _ T O _ D A S H >----------------------------------------
Loops through the list of date-holding parameters and converts any hyphen to an ndash. Not called if the cs1|2
template has any date errors.
Modifies the date_parameters_list and returns true if hyphens are replaced, else returns false.
]]
local function date_hyphen_to_dash (date_parameters_list)
local result = false;
local n;
for param_name, param_val in pairs(date_parameters_list) do -- for each date-holding parameter in the list
if is_set (param_val.val) and
not mw.ustring.match (param_val.val, patterns_t.ymd[1]) then -- for those that are not ymd dates (ustring because here digits may not be Western)
param_val.val, n = param_val.val:gsub ('%-', '–'); -- replace any hyphen with ndash
if 0 ~= n then
date_parameters_list[param_name].val = param_val.val; -- update the list
result = true;
end
end
end
return result; -- so we know if any hyphens were replaced
end
--[[-------------------------< D A T E _ N A M E _ X L A T E >------------------------------------------------
Attempts to translate English date names to local-language date names using names supplied by MediaWiki's
date parser function. This is simple name-for-name replacement and may not work for all languages.
if xlat_dig is true, this function will also translate Western (English) digits to the local language's digits.
This will also translate ymd dates.
]]
local function date_name_xlate (date_parameters_list, xlt_dig)
local xlate;
local mode; -- long or short month names
local modified = false;
local date;
local sources_t = {
{cfg.date_names.en.long, cfg.date_names.inv_local_long}, -- for translating long English month names to long local month names
{cfg.date_names.en.short, cfg.date_names.inv_local_short}, -- short month names
{cfg.date_names.en.quarter, cfg.date_names.inv_local_quarter}, -- quarter date names
{cfg.date_names.en.season, cfg.date_names.inv_local_season}, -- season date nam
{cfg.date_names.en.named, cfg.date_names.inv_local_named}, -- named dates
}
local function is_xlateable (month) -- local function to get local date name that replaces existing English-language date name
for _, date_names_t in ipairs (sources_t) do -- for each sequence table in date_names_t
if date_names_t[1][month] then -- if date name is English month (long or short), quarter, season or named and
if date_names_t[2][date_names_t[1][month]] then -- if there is a matching local date name
return date_names_t[2][date_names_t[1][month]]; -- return the local date name
end
end
end
end
for param_name, param_val in pairs(date_parameters_list) do -- for each date-holding parameter in the list
if is_set(param_val.val) then -- if the parameter has a value
date = param_val.val;
for month in mw.ustring.gmatch (date, '[%a ]+') do -- iterate through all date names in the date (single date or date range)
month = mw.text.trim (month); -- this because quarterly dates contain whitespace
xlate = is_xlateable (month); -- get translate <month>; returns translation or nil
if xlate then
date = mw.ustring.gsub (date, month, xlate); -- replace the English with the translation
date_parameters_list[param_name].val = date; -- save the translated date
modified = true;
end
end
if xlt_dig then -- shall we also translate digits?
date = date:gsub ('%d', cfg.date_names.xlate_digits); -- translate digits from Western to 'local digits'
date_parameters_list[param_name].val = date; -- save the translated date
modified = true;
end
end
end
return modified;
end
--[[--------------------------< S E T _ S E L E C T E D _ M O D U L E S >--------------------------------------
Sets local imported functions table to same (live or sandbox) as that used by the other modules.
]]
local function set_selected_modules (cfg_table_ptr, utilities_page_ptr)
add_prop_cat = utilities_page_ptr.add_prop_cat ; -- import functions from selected Module:Citation/CS1/Utilities module
is_set = utilities_page_ptr.is_set;
in_array = utilities_page_ptr.in_array;
set_message = utilities_page_ptr.set_message;
substitute = utilities_page_ptr.substitute;
wrap_style = utilities_page_ptr.wrap_style;
cfg = cfg_table_ptr; -- import tables from selected Module:Citation/CS1/Configuration
end
--[[--------------------------< A R C H I V E _ D A T E _ C H E C K >------------------------------------------
Compare value in |archive-date= with the timestamp in Wayback machine urls. Emits an error message with suggested
date from the |archive-url= timestamp in an appropriate format when the value in |archive-date= does not match
the timestamp.
this function never called when any date in a cs1|2 template has errors
error message suggests new |archive-date= value in an appropriate format specified by <df>. <df> is either
|df= or cfg.global_df in that order. If <df> is nil, suggested date has format from |archive-date=. There is
a caveat: when |df=dmy or |df=mdy, the reformatter leaves |access-date= and |archive-date= formats as they are.
The error message suggested date is passed to the formatter as YYYY-MM-DD so when |df=dmy or |df=mdy, the format
is not changed.
]]
local function archive_date_check (archive_date, archive_url_timestamp, df)
local archive_date_format = 'dmy-y'; -- holds the date format of date in |archive-date; default to ymd; 'dmy' used here to spoof reformat_dates()
for _, v_t in ipairs ({{'dMy', 'dmy-all'}, {'Mdy', 'mdy-all'}}) do -- is |archive-date= format dmy or mdy?
if archive_date:match (patterns_t[v_t[1]][1]) then -- does the pattern match?
archive_date_format = cfg.keywords_xlate[v_t[2]]; -- get appropriate |df= supported keyword from the i18n translator table
break;
end
end
local dates_t = {};
dates_t['archive-date'] = {val=archive_date, name=''}; -- setup to call reformat_dates(); never called when errors so <name> unset as not needed
reformat_dates (dates_t, 'dmy-y'); -- reformat |archive-date= to ymd; 'dmy' used here to spoof reformat_dates()
local archive_url_date = archive_url_timestamp:gsub ('(%d%d%d%d)(%d%d)(%d%d)%d*', '%1-%2-%3'); -- make ymd format date from timestamp
if dates_t['archive-date'].val == archive_url_date then -- are the two dates the same
return; -- yes, done
else
dates_t['archive-date'] = {val=archive_url_date, name=''}; -- setup to call reformat_dates() with the timestamp date
reformat_dates (dates_t, df or archive_date_format); -- reformat timestamp to format specified by <df> or format used in |archive-date=
archive_url_date = dates_t['archive-date'].val;
set_message ('err_archive_date_url_ts_mismatch', archive_url_date); -- emit an error message
end
end
--[[--------------------------< E X P O R T E D F U N C T I O N S >------------------------------------------
]]
return { -- return exported functions
archive_date_check = archive_date_check,
date_hyphen_to_dash = date_hyphen_to_dash,
date_name_xlate = date_name_xlate,
dates = dates,
reformat_dates = reformat_dates,
set_selected_modules = set_selected_modules,
year_check = year_check,
year_date_check = year_date_check,
}
h37xf2hxsrjgq13ms7lozl3uiigkklr
886351
886350
2025-06-13T17:03:24Z
KartikMistry
10383
[[:en:Module:Citation/CS1/Date_validation]] માંથી આયાત કરેલ ૧ પુનરાવર્તન
886350
Scribunto
text/plain
--[[--------------------------< F O R W A R D D E C L A R A T I O N S >--------------------------------------
]]
local add_prop_cat, is_set, in_array, set_message, substitute, wrap_style; -- imported functions from selected Module:Citation/CS1/Utilities
local cfg; -- table of tables imported from selected Module:Citation/CS1/Configuration
--[[--------------------------< F I L E - S C O P E D E C L A R A T I O N S >--------------------------------
File-scope variables are declared here
]]
local lang_object = mw.getContentLanguage(); -- used by is_valid_accessdate(), is_valid_year(), date_name_xlate(); TODO: move to ~/Configuration?
local year_limit; -- used by is_valid_year()
--[=[-------------------------< I S _ V A L I D _ A C C E S S D A T E >----------------------------------------
returns true if:
Wikipedia start date <= accessdate < today + 2 days
Wikipedia start date is 2001-01-15T00:00:00 UTC which is 979516800 seconds after 1970-01-01T00:00:00 UTC (the start of Unix time)
accessdate is the date provided in |access-date= at time 00:00:00 UTC
today is the current date at time 00:00:00 UTC plus 48 hours
if today is 2015-01-01T00:00:00 then
adding 24 hours gives 2015-01-02T00:00:00 – one second more than today
adding 24 hours gives 2015-01-03T00:00:00 – one second more than tomorrow
This function does not work if it is fed month names for languages other than English. Wikimedia #time: parser
apparently doesn't understand non-English date month names. This function will always return false when the date
contains a non-English month name because good1 is false after the call to lang.formatDate(). To get around that
call this function with YYYY-MM-DD format dates.
]=]
local function is_valid_accessdate (accessdate)
local good1, good2;
local access_ts, tomorrow_ts; -- to hold Unix time stamps representing the dates
good1, access_ts = pcall (lang_object.formatDate, lang_object, 'U', accessdate ); -- convert accessdate value to Unix timestamp
good2, tomorrow_ts = pcall (lang_object.formatDate, lang_object, 'U', 'today + 2 days' ); -- today midnight + 2 days is one second more than all day tomorrow
if good1 and good2 then -- lang.formatDate() returns a timestamp in the local script which which tonumber() may not understand
access_ts = tonumber (access_ts) or lang_object:parseFormattedNumber (access_ts); -- convert to numbers for the comparison;
tomorrow_ts = tonumber (tomorrow_ts) or lang_object:parseFormattedNumber (tomorrow_ts);
else
return false; -- one or both failed to convert to Unix time stamp
end
if 979516800 <= access_ts and access_ts < tomorrow_ts then -- Wikipedia start date <= accessdate < tomorrow's date
return true;
else
return false; -- accessdate out of range
end
end
--[[--------------------------< G E T _ M O N T H _ N U M B E R >----------------------------------------------
returns a number according to the month in a date: 1 for January, etc. Capitalization and spelling must be correct.
If not a valid month, returns 0
]]
local function get_month_number (month)
return cfg.date_names['local'].long[month] or cfg.date_names['local'].short[month] or -- look for local names first
cfg.date_names['en'].long[month] or cfg.date_names['en'].short[month] or -- failing that, look for English names
0; -- not a recognized month name
end
--[[--------------------------< G E T _ S E A S O N _ N U M B E R >--------------------------------------------
returns a number according to the sequence of seasons in a year: 21 for Spring, etc. Capitalization and spelling
must be correct. If not a valid season, returns 0.
21-24 = Spring, Summer, Autumn, Winter, independent of “Hemisphere”
returns 0 when <param> is not |date=
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.
These additional divisions not currently supported:
25-28 = Spring - Northern Hemisphere, Summer- Northern Hemisphere, Autumn - Northern Hemisphere, Winter - Northern Hemisphere
29-32 = Spring – Southern Hemisphere, Summer– Southern Hemisphere, Autumn – Southern Hemisphere, Winter - Southern Hemisphere
33-36 = Quarter 1, Quarter 2, Quarter 3, Quarter 4 (3 months each)
37-39 = Quadrimester 1, Quadrimester 2, Quadrimester 3 (4 months each)
40-41 = Semestral 1, Semestral-2 (6 months each)
]]
local function get_season_number (season, param)
if 'date' ~= param then
return 0; -- season dates only supported by |date=
end
return cfg.date_names['local'].season[season] or -- look for local names first
cfg.date_names['en'].season[season] or -- failing that, look for English names
0; -- not a recognized season name
end
--[[--------------------------< G E T _ Q U A R T E R _ N U M B E R >------------------------------------------
returns a number according to the sequence of quarters in a year: 33 for first quarter, etc. Capitalization and spelling
must be correct. If not a valid quarter, returns 0.
33-36 = Quarter 1, Quarter 2, Quarter 3, Quarter 4 (3 months each)
returns 0 when <param> is not |date=
Quarter 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 and quarters.
These additional divisions not currently supported:
37-39 = Quadrimester 1, Quadrimester 2, Quadrimester 3 (4 months each)
40-41 = Semestral 1, Semestral-2 (6 months each)
]]
local function get_quarter_number (quarter, param)
if 'date' ~= param then
return 0; -- quarter dates only supported by |date=
end
quarter = mw.ustring.gsub (quarter, ' +', ' '); -- special case replace multiple space chars with a single space char
return cfg.date_names['local'].quarter[quarter] or -- look for local names first
cfg.date_names['en'].quarter[quarter] or -- failing that, look for English names
0; -- not a recognized quarter name
end
--[[--------------------------< G E T _ P R O P E R _ N A M E _ N U M B E R >----------------------------------
returns a non-zero number if date contains a recognized proper-name. Capitalization and spelling must be correct.
returns 0 when <param> is not |date=
]]
local function get_proper_name_number (name, param)
if 'date' ~= param then
return 0; -- proper-name dates only supported by |date=
end
return cfg.date_names['local'].named[name] or -- look for local names dates first
cfg.date_names['en'].named[name] or -- failing that, look for English names
0; -- not a recognized named date
end
--[[--------------------------< G E T _ E L E M E N T _ N U M B E R <------------------------------------------
returns true if month or season or quarter or proper name is valid (properly spelled, capitalized, abbreviated)
]]
local function get_element_number (element, param)
local num;
local funcs = {get_month_number, get_season_number, get_quarter_number, get_proper_name_number}; -- list of functions to execute in order
for _, func in ipairs (funcs) do -- spin through the function list
num = func (element, param); -- call the function and get the returned number
if 0 ~= num then -- non-zero when valid month season quarter
return num; -- return that number
end
end
return nil; -- not valid
end
--[[--------------------------< I S _ V A L I D _ Y E A R >----------------------------------------------------
Function gets current year from the server and compares it to year from a citation parameter. Years more than one
year in the future are not acceptable.
Special case for |pmc-embargo-date=: years more than two years in the future are not acceptable
]]
local function is_valid_year (year, param)
if not is_set (year_limit) then
year_limit = tonumber(os.date("%Y"))+1; -- global variable so we only have to fetch it once
end
year = tonumber (year) or lang_object:parseFormattedNumber (year); -- convert to number for the comparison
if year and (100 > year) then -- years less than 100 not supported
return false;
end
if 'pmc-embargo-date' == param then -- special case for |pmc-embargo-date=
return year and (year <= tonumber(os.date("%Y"))+2) or false; -- years more than two years in the future are not accepted
end
return year and (year <= year_limit) or false;
end
--[[--------------------------< I S _ V A L I D _ D A T E >----------------------------------------------------
Returns true if day is less than or equal to the number of days in month and year is no farther into the future
than next year; else returns false.
Assumes Julian calendar prior to year 1582 and Gregorian calendar thereafter. Accounts for Julian calendar leap
years before 1582 and Gregorian leap years after 1582. Where the two calendars overlap (1582 to approximately
1923) dates are assumed to be Gregorian.
]]
local function is_valid_date (year, month, day, param)
local days_in_month = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
local month_length;
if not is_valid_year (year, param) then -- no farther into the future than next year except |pmc-embargo-date= no more than two years in the future
return false;
end
month = tonumber (month); -- required for YYYY-MM-DD dates
if (2 == month) then -- if February
month_length = 28; -- then 28 days unless
if 1582 > tonumber(year) then -- Julian calendar
if 0 == (year%4) then -- is a leap year?
month_length = 29; -- if leap year then 29 days in February
end
else -- Gregorian calendar
if (0 == (year%4) and (0 ~= (year%100) or 0 == (year%400))) then -- is a leap year?
month_length = 29; -- if leap year then 29 days in February
end
end
else
month_length = days_in_month[month];
end
if tonumber (day) > month_length then
return false;
end
return true;
end
--[[--------------------------< I S _ V A L I D _ M O N T H _ R A N G E _ S T Y L E >--------------------------
Months in a range are expected to have the same style: Jan–Mar or October–December but not February–Mar or Jul–August.
This function looks in cfg.date_names{} to see if both month names are listed in the long subtable or both are
listed in the short subtable. When both have the same style (both are listed in the same table), returns true; false else
]]
local function is_valid_month_range_style (month1, month2)
if (cfg.date_names.en.long[month1] and cfg.date_names.en.long[month2]) or -- are both English names listed in the long subtable?
(cfg.date_names.en.short[month1] and cfg.date_names.en.short[month2]) or -- are both English names listed in the short subtable?
(cfg.date_names['local'].long[month1] and cfg.date_names['local'].long[month2]) or -- are both local names listed in the long subtable?
(cfg.date_names['local'].short[month1] and cfg.date_names['local'].short[month2]) then -- are both local names listed in the short subtable?
return true;
end
return false; -- names are mixed
end
--[[--------------------------< I S _ V A L I D _ M O N T H _ S E A S O N _ R A N G E >------------------------
Check a pair of months or seasons to see if both are valid members of a month or season pair.
Month pairs are expected to be left to right, earliest to latest in time.
All season ranges are accepted as valid because there are publishers out there who have published a Summer–Spring YYYY issue, hence treat as ok
]]
local function is_valid_month_season_range(range_start, range_end, param)
local range_start_number = get_month_number (range_start);
local range_end_number;
if 0 == range_start_number then -- is this a month range?
range_start_number = get_season_number (range_start, param); -- not a month; is it a season? get start season number
range_end_number = get_season_number (range_end, param); -- get end season number
if (0 ~= range_start_number) and (0 ~= range_end_number) and (range_start_number ~= range_end_number) then
return true; -- any season pairing is accepted except when both are the same
end
return false; -- range_start and/or range_end is not a season
end
-- here when range_start is a month
range_end_number = get_month_number (range_end); -- get end month number
if range_start_number < range_end_number and -- range_start is a month; does range_start precede range_end?
is_valid_month_range_style (range_start, range_end) then -- do months have the same style?
return true; -- proper order and same style
end
return false; -- range_start month number is greater than or equal to range end number; or range end isn't a month
end
--[[--------------------------< M A K E _ C O I N S _ D A T E >------------------------------------------------
This function receives a table of date parts for one or two dates and an empty table reference declared in
Module:Citation/CS1. The function is called only for |date= parameters and only if the |date=<value> is
determined to be a valid date format. The question of what to do with invalid date formats is not answered here.
The date parts in the input table are converted to an ISO 8601 conforming date string:
single whole dates: yyyy-mm-dd
month and year dates: yyyy-mm
year dates: yyyy
ranges: yyyy-mm-dd/yyyy-mm-dd
yyyy-mm/yyyy-mm
yyyy/yyyy
Dates in the Julian calendar are reduced to year or year/year so that we don't have to do calendar conversion from
Julian to Proleptic Gregorian.
The input table has:
year, year2 – always present; if before 1582, ignore months and days if present
month, month2 – 0 if not provided, 1-12 for months, 21-24 for seasons; 99 Christmas
day, day2 – 0 if not provided, 1-31 for days
the output table receives:
rftdate: an ISO 8601 formatted date
rftchron: a free-form version of the date, usually without year which is in rftdate (season ranges and proper-name dates)
rftssn: one of four season keywords: winter, spring, summer, fall (lowercase)
rftquarter: one of four values: 1, 2, 3, 4
]]
local function make_COinS_date (input, tCOinS_date)
local date; -- one date or first date in a range
local date2 = ''; -- end of range date
input.year = tonumber (input.year) or lang_object:parseFormattedNumber (input.year); -- language-aware tonumber()
input.year2 = tonumber (input.year2) or lang_object:parseFormattedNumber (input.year2); -- COinS dates are pseudo-ISO 8601 so convert to Arabic numerals
if ((1582 == input.year) and (10 > tonumber(input.month))) or (1582 > input.year) then -- if a Julian calendar date
tCOinS_date.rftdate = tostring (input.year); -- &rft.date gets year only
return; -- done
end
-- here for all forms of Gregorian dates
if 20 < tonumber (input.month) then -- if season, quarter, or proper-name date
date = input.year; -- &rft.date gets year only
if 0 ~= input.year2 and input.year ~= input.year2 then -- if a range, only the second year portion when not the same as range start year
date = string.format ('%.4d/%.4d', input.year, input.year2) -- assemble the date range
end
local season = {[24] = 'winter', [21] = 'spring', [22] = 'summer', [23] = 'fall', [33] = '1', [34] = '2', [35] = '3', [36] = '4', [98] = 'Easter', [99] = 'Christmas'}; -- seasons lowercase, no autumn; proper-names use title case
if 0 == input.month2 then -- single season, quarter, or proper-name date
if 40 < tonumber(input.month) then
tCOinS_date.rftchron = season[input.month]; -- proper-name date; used in journal metadata only
elseif 30 < tonumber(input.month) then
tCOinS_date.rftquarter = season[input.month]; -- quarter date; used in journal metadata only
else
tCOinS_date.rftssn = season[input.month]; -- season date; used in journal metadata only
end
else -- season ranges are lumped into &rft.chron; &rft.ssn and &rft.quarter are left blank
if input.year ~= input.year2 then -- season year – season year range or season year–year
if 0 ~= input.month2 then
tCOinS_date.rftchron = string.format ('%s %s – %s %s', season[input.month], input.year, season[input.month2], input.year2); -- used in journal metadata only
end
else -- season–season year range
tCOinS_date.rftchron = season[input.month] .. '–' .. season[input.month2]; -- season–season year range; used in journal metadata only
end
end
tCOinS_date.rftdate = tostring (date);
return; -- done
end
-- here for gregorian calendar dates
if 0 ~= input.day then
date = string.format ('%s-%.2d-%.2d', input.year, tonumber(input.month), tonumber(input.day)); -- whole date
elseif 0 ~= input.month then
date = string.format ('%s-%.2d', input.year, tonumber(input.month)); -- year and month
else
date = string.format ('%s', input.year); -- just year
end
if 0 ~= input.year2 then
if 0 ~= input.day2 then
date2 = string.format ('/%s-%.2d-%.2d', input.year2, tonumber(input.month2), tonumber(input.day2)); -- whole date
elseif 0 ~= input.month2 then
date2 = string.format ('/%s-%.2d', input.year2, tonumber(input.month2)); -- year and month
else
date2 = string.format ('/%s', input.year2); -- just year
end
end
tCOinS_date.rftdate = date .. date2; -- date2 has the '/' separator
return;
end
--[[--------------------------< P A T T E R N S _ T >----------------------------------------------------------
this is the list of patterns for date formats that this module recognizes. Approximately the first half of these
patterns represent formats that might be reformatted into another format. Those that might be reformatted have
'indicator' letters that identify the content of the matching capture: 'd' (day), 'm' (month), 'a' (anchor year),
'y' (year); second day, month, year have a '2' suffix.
These patterns are used for both date validation and for reformatting. This table should not be moved to ~/Configuration
because changes to this table require changes to check_date() and to reformatter() and reformat_date()
]]
local patterns_t = {
-- year-initial numerical year-month-day
['ymd'] = {'^(%d%d%d%d)%-(%d%d)%-(%d%d)$', 'y', 'm', 'd'},
-- month-initial: month day, year
['Mdy'] = {'^(%D-) +([1-9]%d?), +((%d%d%d%d?)%a?)$', 'm', 'd', 'a', 'y'},
-- month-initial day range: month day–day, year; days are separated by endash
['Md-dy'] = {'^(%D-) +([1-9]%d?)[%-–]([1-9]%d?), +((%d%d%d%d)%a?)$', 'm', 'd', 'd2', 'a', 'y'},
-- day-initial: day month year
['dMy'] = {'^([1-9]%d?) +(%D-) +((%d%d%d%d?)%a?)$', 'd', 'm', 'a', 'y'},
-- year-initial: year month day; day: 1 or 2 two digits, leading zero allowed; not supported at en.wiki
-- ['yMd'] = {'^((%d%d%d%d?)%a?) +(%D-) +(%d%d?)$', 'a', 'y', 'm', 'd'},
-- day-range-initial: day–day month year; days are separated by endash
['d-dMy'] = {'^([1-9]%d?)[%-–]([1-9]%d?) +(%D-) +((%d%d%d%d)%a?)$', 'd', 'd2', 'm', 'a', 'y'},
-- day initial month-day-range: day month - day month year; uses spaced endash
['dM-dMy'] = {'^([1-9]%d?) +(%D-) +[%-–] +([1-9]%d?) +(%D-) +((%d%d%d%d)%a?)$', 'd', 'm', 'd2', 'm2', 'a', 'y'},
-- month initial month-day-range: month day – month day, year; uses spaced endash
['Md-Mdy'] = {'^(%D-) +([1-9]%d?) +[%-–] +(%D-) +([1-9]%d?), +((%d%d%d%d)%a?)$','m', 'd', 'm2', 'd2', 'a', 'y'},
-- day initial month-day-year-range: day month year - day month year; uses spaced endash
['dMy-dMy'] = {'^([1-9]%d?) +(%D-) +(%d%d%d%d) +[%-–] +([1-9]%d?) +(%D-) +((%d%d%d%d)%a?)$', 'd', 'm', 'y', 'd2', 'm2', 'a', 'y2'},
-- month initial month-day-year-range: month day, year – month day, year; uses spaced endash
['Mdy-Mdy'] = {'^(%D-) +([1-9]%d?), +(%d%d%d%d) +[%-–] +(%D-) +([1-9]%d?), +((%d%d%d%d)%a?)$', 'm', 'd', 'y', 'm2', 'd2', 'a', 'y2'},
-- these date formats cannot be converted, per se, but month name can be rendered short or long
-- month/season year - month/season year; separated by spaced endash
['My-My'] = {'^(%D-) +(%d%d%d%d) +[%-–] +(%D-) +((%d%d%d%d)%a?)$', 'm', 'y', 'm2', 'a', 'y2'},
-- month/season range year; months separated by endash
['M-My'] = {'^(%D-)[%-–](%D-) +((%d%d%d%d)%a?)$', 'm', 'm2', 'a', 'y'},
-- month/season year or proper-name year; quarter year when First Quarter YYYY etc.
['My'] = {'^([^%d–]-) +((%d%d%d%d)%a?)$', 'm', 'a', 'y'}, -- this way because endash is a member of %D; %D- will match January–March 2019 when it shouldn't
-- these date formats cannot be converted
['Sy4-y2'] = {'^(%D-) +((%d%d)%d%d)[%-–]((%d%d)%a?)$'}, -- special case Winter/Summer year-year (YYYY-YY); year separated with unspaced endash
['Sy-y'] = {'^(%D-) +(%d%d%d%d)[%-–]((%d%d%d%d)%a?)$'}, -- special case Winter/Summer year-year; year separated with unspaced endash
['y-y'] = {'^(%d%d%d%d?)[%-–]((%d%d%d%d?)%a?)$'}, -- year range: YYY-YYY or YYY-YYYY or YYYY–YYYY; separated by unspaced endash; 100-9999
['y4-y2'] = {'^((%d%d)%d%d)[%-–]((%d%d)%a?)$'}, -- year range: YYYY–YY; separated by unspaced endash
['y'] = {'^((%d%d%d%d?)%a?)$'}, -- year; here accept either YYY or YYYY
}
--[[--------------------------< I S _ V A L I D _ E M B A R G O _ D A T E >------------------------------------
returns true and date value if that value has proper dmy, mdy, ymd format.
returns false and 9999 (embargoed forever) when date value is not proper format; assumes that when |pmc-embargo-date= is
set, the editor intended to embargo a PMC but |pmc-embargo-date= does not hold a single date.
]]
local function is_valid_embargo_date (v)
if v:match (patterns_t['ymd'][1]) or -- ymd
v:match (patterns_t['Mdy'][1]) or -- dmy
v:match (patterns_t['dMy'][1]) then -- mdy
return true, v;
end
return false, '9999'; -- if here not good date so return false and set embargo date to long time in future
end
--[[--------------------------< C H E C K _ D A T E >----------------------------------------------------------
Check date format to see that it is one of the formats approved by WP:DATESNO or WP:DATERANGE. Exception: only
allowed range separator is endash. Additionally, check the date to see that it is a real date: no 31 in 30-day
months; no 29 February when not a leap year. Months, both long-form and three character abbreviations, and seasons
must be spelled correctly. Future years beyond next year are not allowed.
If the date fails the format tests, this function returns false and does not return values for anchor_year and
COinS_date. When this happens, the date parameter is (DEBUG: not?) used in the COinS metadata and the CITEREF identifier gets
its year from the year parameter if present otherwise CITEREF does not get a date value.
Inputs:
date_string - date string from date-holding parameters (date, year, publication-date, access-date, pmc-embargo-date, archive-date, lay-date)
Returns:
false if date string is not a real date; else
true, anchor_year, COinS_date
anchor_year can be used in CITEREF anchors
COinS_date is ISO 8601 format date; see make_COInS_date()
]]
local function check_date (date_string, param, tCOinS_date)
local year; -- assume that year2, months, and days are not used;
local year2 = 0; -- second year in a year range
local month = 0;
local month2 = 0; -- second month in a month range
local day = 0;
local day2 = 0; -- second day in a day range
local anchor_year;
local coins_date;
if date_string:match (patterns_t['ymd'][1]) then -- year-initial numerical year month day format
year, month, day = date_string:match (patterns_t['ymd'][1]);
if 12 < tonumber(month) or 1 > tonumber(month) or 1582 > tonumber(year) or 0 == tonumber(day) then return false; end -- month or day number not valid or not Gregorian calendar
anchor_year = year;
elseif mw.ustring.match(date_string, patterns_t['Mdy'][1]) then -- month-initial: month day, year
month, day, anchor_year, year = mw.ustring.match(date_string, patterns_t['Mdy'][1]);
month = get_month_number (month);
if 0 == month then return false; end -- return false if month text isn't one of the twelve months
elseif mw.ustring.match(date_string, patterns_t['Md-dy'][1]) then -- month-initial day range: month day–day, year; days are separated by endash
month, day, day2, anchor_year, year = mw.ustring.match(date_string, patterns_t['Md-dy'][1]);
if tonumber(day) >= tonumber(day2) then return false; end -- date range order is left to right: earlier to later; dates may not be the same;
month = get_month_number (month);
if 0 == month then return false; end -- return false if month text isn't one of the twelve months
month2=month; -- for metadata
year2 = year;
elseif mw.ustring.match(date_string, patterns_t['dMy'][1]) then -- day-initial: day month year
day, month, anchor_year, year = mw.ustring.match(date_string, patterns_t['dMy'][1]);
month = get_month_number (month);
if 0 == month then return false; end -- return false if month text isn't one of the twelve months
--[[ NOT supported at en.wiki
elseif mw.ustring.match(date_string, patterns_t['yMd'][1]) then -- year-initial: year month day; day: 1 or 2 two digits, leading zero allowed
anchor_year, year, month, day = mw.ustring.match(date_string, patterns_t['yMd'][1]);
month = get_month_number (month);
if 0 == month then return false; end -- return false if month text isn't one of the twelve months
-- end NOT supported at en.wiki ]]
elseif mw.ustring.match(date_string, patterns_t['d-dMy'][1]) then -- day-range-initial: day–day month year; days are separated by endash
day, day2, month, anchor_year, year = mw.ustring.match(date_string, patterns_t['d-dMy'][1]);
if tonumber(day) >= tonumber(day2) then return false; end -- date range order is left to right: earlier to later; dates may not be the same;
month = get_month_number (month);
if 0 == month then return false; end -- return false if month text isn't one of the twelve months
month2 = month; -- for metadata
year2 = year;
elseif mw.ustring.match(date_string, patterns_t['dM-dMy'][1]) then -- day initial month-day-range: day month - day month year; uses spaced endash
day, month, day2, month2, anchor_year, year = mw.ustring.match(date_string, patterns_t['dM-dMy'][1]);
if (not is_valid_month_season_range(month, month2)) or not is_valid_year(year) then return false; end -- date range order is left to right: earlier to later;
month = get_month_number (month); -- for metadata
month2 = get_month_number (month2);
year2 = year;
elseif mw.ustring.match(date_string, patterns_t['Md-Mdy'][1]) then -- month initial month-day-range: month day – month day, year; uses spaced endash
month, day, month2, day2, anchor_year, year = mw.ustring.match(date_string, patterns_t['Md-Mdy'][1]);
if (not is_valid_month_season_range(month, month2, param)) or not is_valid_year(year) then return false; end
month = get_month_number (month); -- for metadata
month2 = get_month_number (month2);
year2 = year;
elseif mw.ustring.match(date_string, patterns_t['dMy-dMy'][1]) then -- day initial month-day-year-range: day month year - day month year; uses spaced endash
day, month, year, day2, month2, anchor_year, year2 = mw.ustring.match(date_string, patterns_t['dMy-dMy'][1]);
if tonumber(year2) <= tonumber(year) then return false; end -- must be sequential years, left to right, earlier to later
if not is_valid_year(year2) or not is_valid_month_range_style(month, month2) then return false; end -- year2 no more than one year in the future; months same style
month = get_month_number (month); -- for metadata
month2 = get_month_number (month2);
if 0 == month or 0 == month2 then return false; end -- both must be valid
elseif mw.ustring.match(date_string, patterns_t['Mdy-Mdy'][1]) then -- month initial month-day-year-range: month day, year – month day, year; uses spaced endash
month, day, year, month2, day2, anchor_year, year2 = mw.ustring.match(date_string, patterns_t['Mdy-Mdy'][1]);
if tonumber(year2) <= tonumber(year) then return false; end -- must be sequential years, left to right, earlier to later
if not is_valid_year(year2) or not is_valid_month_range_style(month, month2) then return false; end -- year2 no more than one year in the future; months same style
month = get_month_number (month); -- for metadata
month2 = get_month_number(month2);
if 0 == month or 0 == month2 then return false; end -- both must be valid
elseif mw.ustring.match(date_string, patterns_t['Sy4-y2'][1]) then -- special case Winter/Summer year-year (YYYY-YY); year separated with unspaced endash
local century;
month, year, century, anchor_year, year2 = mw.ustring.match(date_string, patterns_t['Sy4-y2'][1]);
if 'Winter' ~= month and 'Summer' ~= month then return false end; -- 'month' can only be Winter or Summer
anchor_year = year .. '–' .. anchor_year; -- assemble anchor_year from both years
year2 = century..year2; -- add the century to year2 for comparisons
if 1 ~= tonumber(year2) - tonumber(year) then return false; end -- must be sequential years, left to right, earlier to later
if not is_valid_year(year2) then return false; end -- no year farther in the future than next year
month = get_season_number(month, param);
elseif mw.ustring.match(date_string, patterns_t['Sy-y'][1]) then -- special case Winter/Summer year-year; year separated with unspaced endash
month, year, anchor_year, year2 = mw.ustring.match(date_string, patterns_t['Sy-y'][1]);
month = get_season_number (month, param); -- <month> can only be winter or summer; also for metadata
if (month ~= cfg.date_names['en'].season['Winter']) and (month ~= cfg.date_names['en'].season['Summer']) then
return false; -- not Summer or Winter; abandon
end
anchor_year = year .. '–' .. anchor_year; -- assemble anchor_year from both years
if 1 ~= tonumber(year2) - tonumber(year) then return false; end -- must be sequential years, left to right, earlier to later
if not is_valid_year(year2) then return false; end -- no year farther in the future than next year
elseif mw.ustring.match(date_string, patterns_t['My-My'][1]) then -- month/season year - month/season year; separated by spaced endash
month, year, month2, anchor_year, year2 = mw.ustring.match(date_string, patterns_t['My-My'][1]);
anchor_year = year .. '–' .. anchor_year; -- assemble anchor_year from both years
if tonumber(year) >= tonumber(year2) then return false; end -- left to right, earlier to later, not the same
if not is_valid_year(year2) then return false; end -- no year farther in the future than next year
if 0 ~= get_month_number(month) and 0 ~= get_month_number(month2) and is_valid_month_range_style(month, month2) then -- both must be month year, same month style
month = get_month_number(month);
month2 = get_month_number(month2);
elseif 0 ~= get_season_number(month, param) and 0 ~= get_season_number(month2, param) then -- both must be season year, not mixed
month = get_season_number(month, param);
month2 = get_season_number(month2, param);
else
return false;
end
elseif mw.ustring.match(date_string, patterns_t['M-My'][1]) then -- month/season range year; months separated by endash
month, month2, anchor_year, year = mw.ustring.match(date_string, patterns_t['M-My'][1]);
if (not is_valid_month_season_range(month, month2, param)) or (not is_valid_year(year)) then return false; end
if 0 ~= get_month_number(month) then -- determined to be a valid range so just check this one to know if month or season
month = get_month_number(month);
month2 = get_month_number(month2);
if 0 == month or 0 == month2 then return false; end
else
month = get_season_number(month, param);
month2 = get_season_number(month2, param);
end
year2 = year;
elseif mw.ustring.match(date_string, patterns_t['My'][1]) then -- month/season/quarter/proper-name year
month, anchor_year, year = mw.ustring.match(date_string, patterns_t['My'][1]);
if not is_valid_year(year) then return false; end
month = get_element_number(month, param); -- get month season quarter proper-name number or nil
if not month then return false; end -- not valid whatever it is
elseif mw.ustring.match(date_string, patterns_t['y-y'][1]) then -- Year range: YYY-YYY or YYY-YYYY or YYYY–YYYY; separated by unspaced endash; 100-9999
year, anchor_year, year2 = mw.ustring.match(date_string, patterns_t['y-y'][1]);
anchor_year = year .. '–' .. anchor_year; -- assemble anchor year from both years
if tonumber(year) >= tonumber(year2) then return false; end -- left to right, earlier to later, not the same
if not is_valid_year(year2) then return false; end -- no year farther in the future than next year
elseif mw.ustring.match(date_string, patterns_t['y4-y2'][1]) then -- Year range: YYYY–YY; separated by unspaced endash
local century;
year, century, anchor_year, year2 = mw.ustring.match(date_string, patterns_t['y4-y2'][1]);
anchor_year = year .. '–' .. anchor_year; -- assemble anchor year from both years
if 13 > tonumber(year2) then return false; end -- don't allow 2003-05 which might be May 2003
year2 = century .. year2; -- add the century to year2 for comparisons
if tonumber(year) >= tonumber(year2) then return false; end -- left to right, earlier to later, not the same
if not is_valid_year(year2) then return false; end -- no year farther in the future than next year
if in_array (param, {'date', 'publication-date', 'year'}) then -- here when 'valid' abbreviated year range; if one of these parameters
add_prop_cat ('year-range-abbreviated'); -- add properties cat
end
elseif mw.ustring.match(date_string, patterns_t['y'][1]) then -- year; here accept either YYY or YYYY
anchor_year, year = mw.ustring.match(date_string, patterns_t['y'][1]);
if false == is_valid_year(year) then
return false;
end
else
return false; -- date format not one of the MOS:DATE approved formats
end
if param ~= 'date' then -- CITEREF disambiguation only allowed in |date=; |year= & |publication-date= promote to date
if anchor_year:match ('%l$') then
return false;
end
end
if 'access-date' == param then -- test access-date here because we have numerical date parts
if 0 ~= year and 0 ~= month and 0 ~= day and -- all parts of a single date required
0 == year2 and 0 == month2 and 0 == day2 then -- none of these; access-date must not be a range
if not is_valid_accessdate(year .. '-' .. month .. '-' .. day) then
return false; -- return false when access-date out of bounds
end
else
return false; -- return false when access-date is a range of two dates
end
end
if 'archive-date' == param then -- test archive-date here because we have numerical date parts
if not (0 ~= year and 0 ~= month and 0 ~= day and -- all parts of a single date required
0 == year2 and 0 == month2 and 0 == day2) then -- none of these; archive-date must not be a range
return false; -- return false when archive-date is a range of two dates
end
end
local result=true; -- check whole dates for validity; assume true because not all dates will go through this test
if 0 ~= year and 0 ~= month and 0 ~= day and 0 == year2 and 0 == month2 and 0 == day2 then -- YMD (simple whole date)
result = is_valid_date (year, month, day, param); -- <param> for |pmc-embargo-date=
elseif 0 ~= year and 0 ~= month and 0 ~= day and 0 == year2 and 0 == month2 and 0 ~= day2 then -- YMD-d (day range)
result = is_valid_date (year, month, day);
result = result and is_valid_date (year, month, day2);
elseif 0 ~= year and 0 ~= month and 0 ~= day and 0 == year2 and 0 ~= month2 and 0 ~= day2 then -- YMD-md (day month range)
result = is_valid_date (year, month, day);
result = result and is_valid_date (year, month2, day2);
elseif 0 ~= year and 0 ~= month and 0 ~= day and 0 ~= year2 and 0 ~= month2 and 0 ~= day2 then -- YMD-ymd (day month year range)
result = is_valid_date(year, month, day);
result = result and is_valid_date(year2, month2, day2);
end
if false == result then return false; end
if nil ~= tCOinS_date then -- this table only passed into this function when testing |date= parameter values
make_COinS_date ({year = year, month = month, day = day, year2 = year2, month2 = month2, day2 = day2}, tCOinS_date); -- make an ISO 8601 date string for COinS
end
return true, anchor_year; -- format is good and date string represents a real date
end
--[[--------------------------< D A T E S >--------------------------------------------------------------------
Cycle the date-holding parameters in passed table date_parameters_list through check_date() to check compliance with MOS:DATE. For all valid dates, check_date() returns
true. The |date= parameter test is unique, it is the only date holding parameter from which values for anchor_year (used in CITEREF identifiers) and COinS_date (used in
the COinS metadata) are derived. The |date= parameter is the only date-holding parameter that is allowed to contain the no-date keywords "n.d." or "nd" (without quotes).
Unlike most error messages created in this module, only one error message is created by this function. Because all of the date holding parameters are processed serially,
parameters with errors are added to the <error_list> sequence table as the dates are tested.
]]
local function dates(date_parameters_list, tCOinS_date, error_list)
local anchor_year; -- will return as nil if the date being tested is not |date=
local COinS_date; -- will return as nil if the date being tested is not |date=
local embargo_date; -- if embargo date is a good dmy, mdy, ymd date then holds original value else reset to 9999
local good_date = false;
for k, v in pairs(date_parameters_list) do -- for each date-holding parameter in the list
if is_set(v.val) then -- if the parameter has a value
v.val = mw.ustring.gsub(v.val, '%d', cfg.date_names.local_digits); -- translate 'local' digits to Western 0-9
if v.val:match("^c%. [1-9]%d%d%d?%a?$") then -- special case for c. year or with or without CITEREF disambiguator - only |date= and |year=
local year = v.val:match("c%. ([1-9]%d%d%d?)%a?"); -- get the year portion so it can be tested
if 'date' == k then
anchor_year, COinS_date = v.val:match("((c%. [1-9]%d%d%d?)%a?)"); -- anchor year and COinS_date only from |date= parameter
good_date = is_valid_year(year);
elseif 'year' == k then
good_date = is_valid_year(year);
end
elseif 'date' == k then -- if the parameter is |date=
if v.val:match("^n%.d%.%a?$") then -- ToDo: I18N -- if |date=n.d. with or without a CITEREF disambiguator
good_date, anchor_year, COinS_date = true, v.val:match("((n%.d%.)%a?)"); -- ToDo: I18N -- "n.d."; no error when date parameter is set to no date
elseif v.val:match("^nd%a?$") then -- ToDo: I18N -- if |date=nd with or without a CITEREF disambiguator
good_date, anchor_year, COinS_date = true, v.val:match("((nd)%a?)"); -- ToDo: I18N -- "nd"; no error when date parameter is set to no date
else
good_date, anchor_year, COinS_date = check_date (v.val, k, tCOinS_date); -- go test the date
end
elseif 'year' == k then -- if the parameter is |year= it should hold only a year value
if v.val:match("^[1-9]%d%d%d?%a?$") then -- if |year = 3 or 4 digits only with or without a CITEREF disambiguator
good_date, anchor_year, COinS_date = true, v.val:match("((%d+)%a?)");
end
elseif 'pmc-embargo-date' == k then -- if the parameter is |pmc-embargo-date=
good_date = check_date (v.val, k); -- go test the date
if true == good_date then -- if the date is a valid date
good_date, embargo_date = is_valid_embargo_date (v.val); -- is |pmc-embargo-date= date a single dmy, mdy, or ymd formatted date? yes: returns embargo date; no: returns 9999
end
else -- any other date-holding parameter
good_date = check_date (v.val, k); -- go test the date
end
if false == good_date then -- assemble one error message so we don't add the tracking category multiple times
table.insert (error_list, wrap_style ('parameter', v.name)); -- make parameter name suitable for error message list
end
end
end
return anchor_year, embargo_date; -- and done
end
--[[--------------------------< Y E A R _ C H E C K >----------------------------------------------------------
Temporary function to test |year= for acceptable values:
YYY, YYYY, year-only ranges, their circa forms, with or without CITEREF disambiguators.
When |year= holds some form of date that is not one of these year-only dates, emit a maintenance message.
This function necessary because many non-cs1|2 templates have a |year= parameter so cirrus searches are more-or-
less useless
]]
local function year_check (year)
year = year:gsub ('c%. *', ''); -- remove circa annotation (if present) before testing <year>
for _, index in ipairs ({'y-y', 'y4-y2', 'y'}) do -- spin through these indexes into patterns_t
if mw.ustring.match (year, patterns_t[index][1]) then
return; -- if a match then |year= holds a valid 'year'
end
end
set_message ('maint_year'); -- if here, |year= value is not an accepted value; add a maint cat
end
--[[--------------------------< Y E A R _ D A T E _ C H E C K >------------------------------------------------
Compare the value provided in |year= with the year value(s) provided in |date=. This function sets a local numeric value:
0 - year value does not match the year value in date
1 - (default) year value matches the year value in date or one of the year values when date contains two years
2 - year value matches the year value in date when date is in the form YYYY-MM-DD and year is disambiguated (|year=YYYYx)
the numeric value in <result> determines the 'output' if any from this function:
0 – adds error message to error_list sequence table
1 – adds maint cat
2 – does nothing
]]
local function year_date_check (year_string, year_origin, date_string, date_origin, error_list)
local year;
local date1;
local date2;
local result = 1; -- result of the test; assume that the test passes
year = year_string:match ('(%d%d%d%d?)');
if date_string:match ('%d%d%d%d%-%d%d%-%d%d') and year_string:match ('%d%d%d%d%a') then --special case where both date and year are required YYYY-MM-DD and YYYYx
date1 = date_string:match ('(%d%d%d%d)');
year = year_string:match ('(%d%d%d%d)');
if year ~= date1 then
result = 0; -- years don't match
else
result = 2; -- years match; but because disambiguated, don't add to maint cat
end
elseif date_string:match ("%d%d%d%d?.-%d%d%d%d?") then -- any of the standard range formats of date with two three- or four-digit years
date1, date2 = date_string:match ("(%d%d%d%d?).-(%d%d%d%d?)");
if year ~= date1 and year ~= date2 then
result = 0;
end
elseif mw.ustring.match(date_string, "%d%d%d%d[%-–]%d%d") then -- YYYY-YY date ranges
local century;
date1, century, date2 = mw.ustring.match(date_string, "((%d%d)%d%d)[%-–]+(%d%d)");
date2 = century..date2; -- convert YY to YYYY
if year ~= date1 and year ~= date2 then
result = 0;
end
elseif date_string:match ("%d%d%d%d?") then -- any of the standard formats of date with one year
date1 = date_string:match ("(%d%d%d%d?)");
if year ~= date1 then
result = 0;
end
else -- should never get here; this function called only when no other date errors
result = 0; -- no recognizable year in date
end
if 0 == result then -- year / date mismatch
table.insert (error_list, substitute (cfg.messages['mismatch'], {year_origin, date_origin})); -- add error message to error_list sequence table
elseif 1 == result then -- redundant year / date
set_message ('maint_date_year'); -- add a maint cat
end
end
--[[--------------------------< R E F O R M A T T E R >--------------------------------------------------------
reformat 'date' into new format specified by format_param if pattern_idx (the current format of 'date') can be
reformatted. Does the grunt work for reformat_dates().
The table re_formats maps pattern_idx (current format) and format_param (desired format) to a table that holds:
format string used by string.format()
identifier letters ('d', 'm', 'y', 'd2', 'm2', 'y2') that serve as indexes into a table t{} that holds captures
from mw.ustring.match() for the various date parts specified by patterns_t[pattern_idx][1]
Items in patterns_t{} have the general form:
['ymd'] = {'^(%d%d%d%d)%-(%d%d)%-(%d%d)$', 'y', 'm', 'd'}, where:
['ymd'] is pattern_idx
patterns_t['ymd'][1] is the match pattern with captures for mw.ustring.match()
patterns_t['ymd'][2] is an indicator letter identifying the content of the first capture
patterns_t['ymd'][3] ... the second capture etc.
when a pattern matches a date, the captures are loaded into table t{} in capture order using the idemtifier
characters as indexes into t{} For the above, a ymd date is in t{} as:
t.y = first capture (year), t.m = second capture (month), t.d = third capture (day)
To reformat, this function is called with the pattern_idx that matches the current format of the date and with
format_param set to the desired format. This function loads table t{} as described and then calls string.format()
with the format string specified by re_format[pattern_idx][format_param][1] using values taken from t{} according
to the capture identifier letters specified by patterns_t[pattern_idx][format_param][n] where n is 2..
]]
local re_formats = {
['ymd'] = { -- date format is ymd; reformat to:
['mdy'] = {'%s %s, %s', 'm', 'd', 'y'}, -- |df=mdy
['dmy'] = {'%s %s %s', 'd', 'm', 'y'}, -- |df=dmy
-- ['yMd'] = {'%s %s %s', 'y', 'm', 'd'}, -- |df=yMd; not supported at en.wiki
},
['Mdy'] = { -- date format is Mdy; reformat to:
['mdy'] = {'%s %s, %s', 'm', 'd', 'y'}, -- for long/short reformatting
['dmy'] = {'%s %s %s', 'd', 'm', 'y'}, -- |df=dmy
['ymd'] = {'%s-%s-%s', 'y', 'm', 'd'}, -- |df=ymd
-- ['yMd'] = {'%s %s %s', 'y', 'm', 'd'}, -- |df=yMd; not supported at en.wiki
},
['dMy'] = { -- date format is dMy; reformat to:
['dmy'] = {'%s %s %s', 'd', 'm', 'y'}, -- for long/short reformatting
['mdy'] = {'%s %s, %s', 'm', 'd', 'y'}, -- |df=mdy
['ymd'] = {'%s-%s-%s', 'y', 'm', 'd'}, -- |df=ymd
-- ['yMd'] = {'%s %s %s', 'y', 'm', 'd'}, -- |df=yMd; not supported at en.wiki
},
['Md-dy'] = { -- date format is Md-dy; reformat to:
['mdy'] = {'%s %s–%s, %s', 'm', 'd', 'd2', 'y'}, -- for long/short reformatting
['dmy'] = {'%s–%s %s %s', 'd', 'd2', 'm', 'y'}, -- |df=dmy -> d-dMy
},
['d-dMy'] = { -- date format is d-d>y; reformat to:
['dmy'] = {'%s–%s %s %s', 'd', 'd2', 'm', 'y'}, -- for long/short reformatting
['mdy'] = {'%s %s–%s, %s', 'm', 'd', 'd2', 'y'}, -- |df=mdy -> Md-dy
},
['dM-dMy'] = { -- date format is dM-dMy; reformat to:
['dmy'] = {'%s %s – %s %s %s', 'd', 'm', 'd2', 'm2', 'y'}, -- for long/short reformatting
['mdy'] = {'%s %s – %s %s, %s', 'm', 'd', 'm2', 'd2', 'y'}, -- |df=mdy -> Md-Mdy
},
['Md-Mdy'] = { -- date format is Md-Mdy; reformat to:
['mdy'] = {'%s %s – %s %s, %s', 'm', 'd', 'm2', 'd2', 'y'}, -- for long/short reformatting
['dmy'] = {'%s %s – %s %s %s', 'd', 'm', 'd2', 'm2', 'y'}, -- |df=dmy -> dM-dMy
},
['dMy-dMy'] = { -- date format is dMy-dMy; reformat to:
['dmy'] = {'%s %s %s – %s %s %s', 'd', 'm', 'y', 'd2', 'm2', 'y2'}, -- for long/short reformatting
['mdy'] = {'%s %s, %s – %s %s, %s', 'm', 'd', 'y', 'm2', 'd2', 'y2'}, -- |df=mdy -> Mdy-Mdy
},
['Mdy-Mdy'] = { -- date format is Mdy-Mdy; reformat to:
['mdy'] = {'%s %s, %s – %s %s, %s', 'm', 'd', 'y', 'm2', 'd2', 'y2'}, -- for long/short reformatting
['dmy'] = {'%s %s %s – %s %s %s', 'd', 'm', 'y', 'd2', 'm2', 'y2'}, -- |df=dmy -> dMy-dMy
},
['My-My'] = { -- these for long/short reformatting
['any'] = {'%s %s – %s %s', 'm', 'y', 'm2', 'y2'}, -- dmy/mdy agnostic
},
['M-My'] = { -- these for long/short reformatting
['any'] = {'%s–%s %s', 'm', 'm2', 'y'}, -- dmy/mdy agnostic
},
['My'] = { -- these for long/short reformatting
['any'] = {'%s %s', 'm', 'y'}, -- dmy/mdy agnostic
},
-- ['yMd'] = { -- not supported at en.wiki
-- ['mdy'] = {'%s %s, %s', 'm', 'd', 'y'}, -- |df=mdy
-- ['dmy'] = {'%s %s %s', 'd', 'm', 'y'}, -- |df=dmy
-- ['ymd'] = {'%s-%s-%s', 'y', 'm', 'd'}, -- |df=ymd
-- },
}
local function reformatter (date, pattern_idx, format_param, mon_len)
if not in_array (pattern_idx, {'ymd', 'Mdy', 'Md-dy', 'dMy', 'yMd', 'd-dMy', 'dM-dMy', 'Md-Mdy', 'dMy-dMy', 'Mdy-Mdy', 'My-My', 'M-My', 'My'}) then
return; -- not in this set of date format patterns_t then not a reformattable date
end
if 'ymd' == format_param and in_array (pattern_idx, {'ymd', 'Md-dy', 'd-dMy', 'dM-dMy', 'Md-Mdy', 'dMy-dMy', 'Mdy-Mdy', 'My-My', 'M-My', 'My'}) then
return; -- ymd date ranges not supported at en.wiki; no point in reformatting ymd to ymd
end
if in_array (pattern_idx, {'My', 'M-My', 'My-My'}) then -- these are not dmy/mdy so can't be 'reformatted' into either
format_param = 'any'; -- so format-agnostic
end
-- yMd is not supported at en.wiki; when yMd is supported at your wiki, uncomment the next line
-- if 'yMd' == format_param and in_array (pattern_idx, {'yMd', 'Md-dy', 'd-dMy', 'dM-dMy', 'Md-Mdy', 'dMy-dMy', 'Mdy-Mdy'}) then -- these formats not convertable; yMd not supported at en.wiki
if 'yMd' == format_param then -- yMd not supported at en.wiki; when yMd is supported at your wiki, remove or comment-out this line
return; -- not a reformattable date
end
local c1, c2, c3, c4, c5, c6, c7; -- these hold the captures specified in patterns_t[pattern_idx][1]
c1, c2, c3, c4, c5, c6, c7 = mw.ustring.match (date, patterns_t[pattern_idx][1]); -- get the captures
local t = { -- table that holds k/v pairs of date parts from the captures and patterns_t[pattern_idx][2..]
[patterns_t[pattern_idx][2]] = c1; -- at minimum there is always one capture with a matching indicator letter
[patterns_t[pattern_idx][3] or 'x'] = c2; -- patterns_t can have a variable number of captures; each capture requires an indicator letter;
[patterns_t[pattern_idx][4] or 'x'] = c3; -- where there is no capture, there is no indicator letter so n in patterns_t[pattern_idx][n] will be nil;
[patterns_t[pattern_idx][5] or 'x'] = c4; -- the 'x' here spoofs an indicator letter to prevent 'table index is nil' error
[patterns_t[pattern_idx][6] or 'x'] = c5;
[patterns_t[pattern_idx][7] or 'x'] = c6;
[patterns_t[pattern_idx][8] or 'x'] = c7;
};
if t.a then -- if this date has an anchor year capture (all convertable date formats except ymd)
if t.y2 then -- for year range date formats
t.y2 = t.a; -- use the anchor year capture when reassembling the date
else -- here for single date formats (except ymd)
t.y = t.a; -- use the anchor year capture when reassembling the date
end
end
if tonumber(t.m) then -- if raw month is a number (converting from ymd)
if 's' == mon_len then -- if we are to use abbreviated month names
t.m = cfg.date_names['inv_local_short'][tonumber(t.m)]; -- convert it to a month name
else
t.m = cfg.date_names['inv_local_long'][tonumber(t.m)]; -- convert it to a month name
end
t.d = t.d:gsub ('0(%d)', '%1'); -- strip leading '0' from day if present
elseif 'ymd' == format_param then -- when converting to ymd
t.y = t.y:gsub ('%a', ''); -- strip CITREF disambiguator if present; anchor year already known so process can proceed; TODO: maint message?
if 1582 > tonumber (t.y) then -- ymd format dates not allowed before 1582
return;
end
t.m = string.format ('%02d', get_month_number (t.m)); -- make sure that month and day are two digits
t.d = string.format ('%02d', t.d);
elseif mon_len then -- if mon_len is set to either 'short' or 'long'
for _, mon in ipairs ({'m', 'm2'}) do -- because there can be two month names, check both
if t[mon] then
t[mon] = get_month_number (t[mon]); -- get the month number for this month (is length agnostic)
if 0 == t[mon] then return; end -- seasons and named dates can't be converted
t[mon] = (('s' == mon_len) and cfg.date_names['inv_local_short'][t[mon]]) or cfg.date_names['inv_local_long'][t[mon]]; -- fetch month name according to length
end
end
end
local new_date = string.format (re_formats[pattern_idx][format_param][1], -- format string
t[re_formats[pattern_idx][format_param][2]], -- named captures from t{}
t[re_formats[pattern_idx][format_param][3]],
t[re_formats[pattern_idx][format_param][4]],
t[re_formats[pattern_idx][format_param][5]],
t[re_formats[pattern_idx][format_param][6]],
t[re_formats[pattern_idx][format_param][7]],
t[re_formats[pattern_idx][format_param][8]]
);
return new_date;
end
--[[-------------------------< R E F O R M A T _ D A T E S >--------------------------------------------------
Reformats existing dates into the format specified by format.
format is one of several manual keywords: dmy, dmy-all, mdy, mdy-all, ymd, ymd-all. The -all version includes
access- and archive-dates; otherwise these dates are not reformatted.
This function allows automatic date formatting. In ~/Configuration, the article source is searched for one of
the {{use xxx dates}} templates. If found, xxx becomes the global date format as xxx-all. If |cs1-dates= in
{{use xxx dates}} has legitimate value then that value determines how cs1|2 dates will be rendered. Legitimate
values for |cs1-dates= are:
l - all dates are rendered with long month names
ls - publication dates use long month names; access-/archive-dates use abbreviated month names
ly - publication dates use long month names; access-/archive-dates rendered in ymd format
s - all dates are rendered with abbreviated (short) month names
sy - publication dates use abbreviated month names; access-/archive-dates rendered in ymd format
y - all dates are rendered in ymd format
the format argument for automatic date formatting will be the format specified by {{use xxx dates}} with the
value supplied by |cs1-dates so one of: xxx-l, xxx-ls, xxx-ly, xxx-s, xxx-sy, xxx-y, or simply xxx (|cs1-dates=
empty, omitted, or invalid) where xxx shall be either of dmy or mdy.
dates are extracted from date_parameters_list, reformatted (if appropriate), and then written back into the
list in the new format. Dates in date_parameters_list are presumed here to be valid (no errors). This function
returns true when a date has been reformatted, false else. Actual reformatting is done by reformatter().
]]
local function reformat_dates (date_parameters_list, format)
local all = false; -- set to false to skip access- and archive-dates
local len_p = 'l'; -- default publication date length shall be long
local len_a = 'l'; -- default access-/archive-date length shall be long
local result = false;
local new_date;
if format:match('%a+%-all') then -- manual df keyword; auto df keyword when length not specified in {{use xxx dates}};
format = format:match('(%a+)%-all'); -- extract the format
all = true; -- all dates are long format dates because this keyword doesn't specify length
elseif format:match('%a+%-[lsy][sy]?') then -- auto df keywords; internal only
all = true; -- auto df applies to all dates; use length specified by capture len_p for all dates
format, len_p, len_a = format:match('(%a+)%-([lsy])([sy]?)'); -- extract the format and length keywords
if 'y' == len_p then -- because allowed by MOS:DATEUNIFY (sort of) range dates and My dates not reformatted
format = 'ymd'; -- override {{use xxx dates}}
elseif (not is_set(len_a)) or (len_p == len_a) then -- no access-/archive-date length specified or same length as publication dates then
len_a = len_p; -- in case len_a not set
end
end -- else only publication dates and they are long
for param_name, param_val in pairs (date_parameters_list) do -- for each date-holding parameter in the list
if is_set (param_val.val) then -- if the parameter has a value
if not (not all and in_array (param_name, {'access-date', 'archive-date'})) then -- skip access- or archive-date unless format is xxx-all; yeah, ugly; TODO: find a better way
for pattern_idx, pattern in pairs (patterns_t) do
if mw.ustring.match (param_val.val, pattern[1]) then
if all and in_array (param_name, {'access-date', 'archive-date'}) then -- if this date is an access- or archive-date
new_date = reformatter (param_val.val, pattern_idx, (('y' == len_a) and 'ymd') or format, len_a); -- choose ymd or dmy/mdy according to len_a setting
else -- all other dates
new_date = reformatter (param_val.val, pattern_idx, format, len_p);
end
if new_date then -- set when date was reformatted
date_parameters_list[param_name].val = new_date; -- update date in date list
result = true; -- and announce that changes have been made
break;
end
end -- if
end -- for
end -- if
end -- if
end -- for
return result; -- declare boolean result and done
end
--[[--------------------------< D A T E _ H Y P H E N _ T O _ D A S H >----------------------------------------
Loops through the list of date-holding parameters and converts any hyphen to an ndash. Not called if the cs1|2
template has any date errors.
Modifies the date_parameters_list and returns true if hyphens are replaced, else returns false.
]]
local function date_hyphen_to_dash (date_parameters_list)
local result = false;
local n;
for param_name, param_val in pairs(date_parameters_list) do -- for each date-holding parameter in the list
if is_set (param_val.val) and
not mw.ustring.match (param_val.val, patterns_t.ymd[1]) then -- for those that are not ymd dates (ustring because here digits may not be Western)
param_val.val, n = param_val.val:gsub ('%-', '–'); -- replace any hyphen with ndash
if 0 ~= n then
date_parameters_list[param_name].val = param_val.val; -- update the list
result = true;
end
end
end
return result; -- so we know if any hyphens were replaced
end
--[[-------------------------< D A T E _ N A M E _ X L A T E >------------------------------------------------
Attempts to translate English date names to local-language date names using names supplied by MediaWiki's
date parser function. This is simple name-for-name replacement and may not work for all languages.
if xlat_dig is true, this function will also translate Western (English) digits to the local language's digits.
This will also translate ymd dates.
]]
local function date_name_xlate (date_parameters_list, xlt_dig)
local xlate;
local mode; -- long or short month names
local modified = false;
local date;
local sources_t = {
{cfg.date_names.en.long, cfg.date_names.inv_local_long}, -- for translating long English month names to long local month names
{cfg.date_names.en.short, cfg.date_names.inv_local_short}, -- short month names
{cfg.date_names.en.quarter, cfg.date_names.inv_local_quarter}, -- quarter date names
{cfg.date_names.en.season, cfg.date_names.inv_local_season}, -- season date nam
{cfg.date_names.en.named, cfg.date_names.inv_local_named}, -- named dates
}
local function is_xlateable (month) -- local function to get local date name that replaces existing English-language date name
for _, date_names_t in ipairs (sources_t) do -- for each sequence table in date_names_t
if date_names_t[1][month] then -- if date name is English month (long or short), quarter, season or named and
if date_names_t[2][date_names_t[1][month]] then -- if there is a matching local date name
return date_names_t[2][date_names_t[1][month]]; -- return the local date name
end
end
end
end
for param_name, param_val in pairs(date_parameters_list) do -- for each date-holding parameter in the list
if is_set(param_val.val) then -- if the parameter has a value
date = param_val.val;
for month in mw.ustring.gmatch (date, '[%a ]+') do -- iterate through all date names in the date (single date or date range)
month = mw.text.trim (month); -- this because quarterly dates contain whitespace
xlate = is_xlateable (month); -- get translate <month>; returns translation or nil
if xlate then
date = mw.ustring.gsub (date, month, xlate); -- replace the English with the translation
date_parameters_list[param_name].val = date; -- save the translated date
modified = true;
end
end
if xlt_dig then -- shall we also translate digits?
date = date:gsub ('%d', cfg.date_names.xlate_digits); -- translate digits from Western to 'local digits'
date_parameters_list[param_name].val = date; -- save the translated date
modified = true;
end
end
end
return modified;
end
--[[--------------------------< S E T _ S E L E C T E D _ M O D U L E S >--------------------------------------
Sets local imported functions table to same (live or sandbox) as that used by the other modules.
]]
local function set_selected_modules (cfg_table_ptr, utilities_page_ptr)
add_prop_cat = utilities_page_ptr.add_prop_cat ; -- import functions from selected Module:Citation/CS1/Utilities module
is_set = utilities_page_ptr.is_set;
in_array = utilities_page_ptr.in_array;
set_message = utilities_page_ptr.set_message;
substitute = utilities_page_ptr.substitute;
wrap_style = utilities_page_ptr.wrap_style;
cfg = cfg_table_ptr; -- import tables from selected Module:Citation/CS1/Configuration
end
--[[--------------------------< A R C H I V E _ D A T E _ C H E C K >------------------------------------------
Compare value in |archive-date= with the timestamp in Wayback machine urls. Emits an error message with suggested
date from the |archive-url= timestamp in an appropriate format when the value in |archive-date= does not match
the timestamp.
this function never called when any date in a cs1|2 template has errors
error message suggests new |archive-date= value in an appropriate format specified by <df>. <df> is either
|df= or cfg.global_df in that order. If <df> is nil, suggested date has format from |archive-date=. There is
a caveat: when |df=dmy or |df=mdy, the reformatter leaves |access-date= and |archive-date= formats as they are.
The error message suggested date is passed to the formatter as YYYY-MM-DD so when |df=dmy or |df=mdy, the format
is not changed.
]]
local function archive_date_check (archive_date, archive_url_timestamp, df)
local archive_date_format = 'dmy-y'; -- holds the date format of date in |archive-date; default to ymd; 'dmy' used here to spoof reformat_dates()
for _, v_t in ipairs ({{'dMy', 'dmy-all'}, {'Mdy', 'mdy-all'}}) do -- is |archive-date= format dmy or mdy?
if archive_date:match (patterns_t[v_t[1]][1]) then -- does the pattern match?
archive_date_format = cfg.keywords_xlate[v_t[2]]; -- get appropriate |df= supported keyword from the i18n translator table
break;
end
end
local dates_t = {};
dates_t['archive-date'] = {val=archive_date, name=''}; -- setup to call reformat_dates(); never called when errors so <name> unset as not needed
reformat_dates (dates_t, 'dmy-y'); -- reformat |archive-date= to ymd; 'dmy' used here to spoof reformat_dates()
local archive_url_date = archive_url_timestamp:gsub ('(%d%d%d%d)(%d%d)(%d%d)%d*', '%1-%2-%3'); -- make ymd format date from timestamp
if dates_t['archive-date'].val == archive_url_date then -- are the two dates the same
return; -- yes, done
else
dates_t['archive-date'] = {val=archive_url_date, name=''}; -- setup to call reformat_dates() with the timestamp date
reformat_dates (dates_t, df or archive_date_format); -- reformat timestamp to format specified by <df> or format used in |archive-date=
archive_url_date = dates_t['archive-date'].val;
set_message ('err_archive_date_url_ts_mismatch', archive_url_date); -- emit an error message
end
end
--[[--------------------------< E X P O R T E D F U N C T I O N S >------------------------------------------
]]
return { -- return exported functions
archive_date_check = archive_date_check,
date_hyphen_to_dash = date_hyphen_to_dash,
date_name_xlate = date_name_xlate,
dates = dates,
reformat_dates = reformat_dates,
set_selected_modules = set_selected_modules,
year_check = year_check,
year_date_check = year_date_check,
}
h37xf2hxsrjgq13ms7lozl3uiigkklr
વિભાગ:Citation/CS1/Utilities
828
72168
886348
821288
2024-12-28T15:31:46Z
en>Trappist the monk
0
sync from sandbox;
886348
Scribunto
text/plain
local z = {
error_cats_t = {}; -- for categorizing citations that contain errors
error_ids_t = {}; -- list of error identifiers; used to prevent duplication of certain errors; local to this module
error_msgs_t = {}; -- sequence table of error messages
maint_cats_t = {}; -- for categorizing citations that aren't erroneous per se, but could use a little work
prop_cats_t = {}; -- for categorizing citations based on certain properties, language of source for instance
prop_keys_t = {}; -- for adding classes to the citation's <cite> tag
};
--[[--------------------------< F O R W A R D D E C L A R A T I O N S >--------------------------------------
]]
local cfg; -- table of tables imported from selected Module:Citation/CS1/Configuration
--[[--------------------------< I S _ S E T >------------------------------------------------------------------
Returns true if argument is set; false otherwise. Argument is 'set' when it exists (not nil) or when it is not an empty string.
]]
local function is_set (var)
return not (var == nil or var == '');
end
--[[--------------------------< I N _ A R R A Y >--------------------------------------------------------------
Whether needle is in haystack
]]
local function in_array (needle, haystack)
if needle == nil then
return false;
end
for n, v in ipairs (haystack) do
if v == needle then
return n;
end
end
return false;
end
--[[--------------------------< H A S _ A C C E P T _ A S _ W R I T T E N >------------------------------------
When <str> is wholly wrapped in accept-as-written markup, return <str> without markup and true; return <str> and false else
with allow_empty = false, <str> must have at least one character inside the markup
with allow_empty = true, <str> the markup frame can be empty like (()) to distinguish an empty template parameter from the specific condition "has no applicable value" in citation-context.
After further evaluation the two cases might be merged at a later stage, but should be kept separated for now.
]]
local function has_accept_as_written (str, allow_empty)
if not is_set (str) then
return str, false;
end
local count;
if true == allow_empty then
str, count = str:gsub ('^%(%((.*)%)%)$', '%1'); -- allows (()) to be an empty set
else
str, count = str:gsub ('^%(%((.+)%)%)$', '%1');
end
return str, 0 ~= count;
end
--[[--------------------------< S U B S T I T U T E >----------------------------------------------------------
Populates numbered arguments in a message string using an argument table. <args> may be a single string or a
sequence table of multiple strings.
]]
local function substitute (msg, args)
return args and mw.message.newRawMessage (msg, args):plain() or msg;
end
--[[--------------------------< E R R O R _ C O M M E N T >----------------------------------------------------
Wraps error messages with CSS markup according to the state of hidden. <content> may be a single string or a
sequence table of multiple strings.
]]
local function error_comment (content, hidden)
return substitute (hidden and cfg.presentation['hidden-error'] or cfg.presentation['visible-error'], content);
end
--[[--------------------------< H Y P H E N _ T O _ D A S H >--------------------------------------------------
Converts a hyphen, endash, emdash to endash under certain conditions. The hyphen/en/em must separate
like items; unlike items are returned unmodified. These forms are modified:
letter - letter (A-B)
digit - digit (4-5)
digit separator digit - digit separator digit (4.1-4.5 or 4-1-4-5)
letterdigit - letterdigit (A1-A5) (an optional separator between letter and
digit is supported – a.1-a.5 or a-1-a-5)
digitletter - digitletter (5a-5d) (an optional separator between letter and
digit is supported – 5.a-5.d or 5-a-5-d)
any other forms are returned unmodified.
str may be a comma- or semicolon-separated list of page ranges with/without single pages
]]
local function hyphen_to_dash (str)
if not is_set (str) then
return str;
end
str = str:gsub ("(%(%(.-%)%))", function(m) return m:gsub(",", ","):gsub(";", ";") end) -- replace commas and semicolons in accept-as-written markup with similar unicode characters so they'll be ignored during the split
str = str:gsub ('&[nm]dash;', {['–'] = '–', ['—'] = '—'}); -- replace — and – entities with their characters; semicolon mucks up the text.split
str = str:gsub ('-', '-'); -- replace HTML numeric entity with hyphen character
str = str:gsub (' ', ' '); -- replace entity with generic keyboard space character
local out = {};
local list = mw.text.split (str, '%s*[,;]%s*'); -- split str at comma or semicolon separators if there are any
local accept; -- boolean
for _, item in ipairs (list) do -- for each item in the list
item, accept = has_accept_as_written (item); -- remove accept-this-as-written markup when it wraps all of item
if not accept and mw.ustring.match (item, '^%w*[%.%-]?%w+%s*[—–-]%s*%w*[%.%-]?%w+$') then -- if a hyphenated range or has endash or emdash separators
if mw.ustring.match (item, '^%a+[%.%-]?%d+%s*[—–-]%s*%a+[%.%-]?%d+$') or -- letterdigit hyphen letterdigit (optional separator between letter and digit)
mw.ustring.match (item, '^%d+[%.%-]?%a+%s*[—–-]%s*%d+[%.%-]?%a+$') or -- digitletter hyphen digitletter (optional separator between digit and letter)
mw.ustring.match (item, '^%d+[%.%-]%d+%s*[—–-]%s*%d+[%.%-]%d+$') then -- digit separator digit hyphen digit separator digit
item = mw.ustring.gsub (item, '(%w*[%.%-]?%w+)%s*[—–-]%s*(%w*[%.%-]?%w+)', '<span class="nowrap">%1 –</span> <span class="nowrap">%2</span>'); -- replace hyphen/dash, with spaced endash
elseif mw.ustring.match (item, '^%d+%s*[—–-]%s*%d+$') or -- digit hyphen digit
mw.ustring.match (item, '^%a+%s*[—–-]%s*%a+$') then -- letter hyphen letter
item = mw.ustring.gsub (item, '(%w+)%s*[—–-]%s*(%w+)', '<span class="nowrap">%1–</span>%2'); -- replace hyphen/emdash with endash, remove extraneous space characters
else
-- item = mw.ustring.gsub (item, '%s*[—–-]%s*', '–'); -- disabled; here when 'unlike' items so return <item> as is
end
end
table.insert (out, item); -- add the (possibly modified) item to the output table
end
local temp_str = ''; -- concatenate the output table into a comma separated string
temp_str, accept = has_accept_as_written (table.concat (out, ', ')); -- remove accept-this-as-written markup when it wraps all of concatenated out
if accept then
temp_str = has_accept_as_written (str); -- when global markup removed, return original str; do it this way to suppress boolean second return value
return temp_str:gsub(",", ","):gsub(";", ";");
else
return temp_str:gsub(",", ","):gsub(";", ";"); -- else, return assembled temp_str
end
end
--[=[-------------------------< M A K E _ W I K I L I N K >----------------------------------------------------
Makes a wikilink; when both link and display text is provided, returns a wikilink in the form [[L|D]]; if only
link is provided (or link and display are the same), returns a wikilink in the form [[L]]; if neither are
provided or link is omitted, returns an empty string.
]=]
local function make_wikilink (link, display)
if not is_set (link) then return '' end
if is_set (display) and link ~= display then
return table.concat ({'[[', link, '|', display, ']]'});
else
return table.concat ({'[[', link, ']]'});
end
end
--[[--------------------------< S E T _ M E S S A G E >----------------------------------------------------------
Sets an error message using the ~/Configuration error_conditions{} table along with arguments supplied in the function
call, inserts the resulting message in z.error_msgs_t{} sequence table, and returns the error message.
<error_id> – key value for appropriate error handler in ~/Configuration error_conditions{} table
<arguments> – may be a single string or a sequence table of multiple strings to be subsititued into error_conditions[error_id].message
<raw> – boolean
true – causes this function to return the error message not wrapped in visible-error, hidden-error span tag;
returns error_conditions[error_id].hidden as a second return value
does not add message to z.error_msgs_t sequence table
false, nil – adds message wrapped in visible-error, hidden-error span tag to z.error_msgs_t
returns the error message wrapped in visible-error, hidden-error span tag; there is no second return value
<prefix> – string to be prepended to <message> -- TODO: remove support for these unused(?) arguments?
<suffix> – string to be appended to <message>
TODO: change z.error_cats_t and z.maint_cats_t to have the form cat_name = true? this to avoid dups without having to have an extra table
]]
local added_maint_cats = {} -- list of maintenance categories that have been added to z.maint_cats_t; TODO: figure out how to delete this table
local function set_message (error_id, arguments, raw, prefix, suffix)
local error_state = cfg.error_conditions[error_id];
prefix = prefix or '';
suffix = suffix or '';
if error_state == nil then
error (cfg.messages['undefined_error'] .. ': ' .. error_id); -- because missing error handler in Module:Citation/CS1/Configuration
elseif is_set (error_state.category) then
if error_state.message then -- when error_state.message defined, this is an error message
table.insert (z.error_cats_t, error_state.category);
else
if not added_maint_cats[error_id] then
added_maint_cats[error_id] = true; -- note that we've added this category
table.insert (z.maint_cats_t, substitute (error_state.category, arguments)); -- make cat name then add to table
end
return; -- because no message, nothing more to do
end
end
local message = substitute (error_state.message, arguments);
message = table.concat (
{
message,
' (',
make_wikilink (
table.concat (
{
cfg.messages['help page link'],
'#',
error_state.anchor
}),
cfg.messages['help page label']),
')'
});
z.error_ids_t[error_id] = true;
if z.error_ids_t['err_citation_missing_title'] and -- if missing-title error already noted
in_array (error_id, {'err_bare_url_missing_title', 'err_trans_missing_title'}) then -- and this error is one of these
return '', false; -- don't bother because one flavor of missing title is sufficient
end
message = table.concat ({prefix, message, suffix});
if true == raw then
return message, error_state.hidden; -- return message not wrapped in visible-error, hidden-error span tag
end
message = error_comment (message, error_state.hidden); -- wrap message in visible-error, hidden-error span tag
table.insert (z.error_msgs_t, message); -- add it to the messages sequence table
return message; -- and done; return value generally not used but is used as a flag in various functions of ~/Identifiers
end
--[[-------------------------< I S _ A L I A S _ U S E D >-----------------------------------------------------
This function is used by select_one() to determine if one of a list of alias parameters is in the argument list
provided by the template.
Input:
args – pointer to the arguments table from calling template
alias – one of the list of possible aliases in the aliases lists from Module:Citation/CS1/Configuration
index – for enumerated parameters, identifies which one
enumerated – true/false flag used to choose how enumerated aliases are examined
value – value associated with an alias that has previously been selected; nil if not yet selected
selected – the alias that has previously been selected; nil if not yet selected
error_list – list of aliases that are duplicates of the alias already selected
Returns:
value – value associated with alias we selected or that was previously selected or nil if an alias not yet selected
selected – the alias we selected or the alias that was previously selected or nil if an alias not yet selected
]]
local function is_alias_used (args, alias, index, enumerated, value, selected, error_list)
if enumerated then -- is this a test for an enumerated parameters?
alias = alias:gsub ('#', index); -- replace '#' with the value in index
else
alias = alias:gsub ('#', ''); -- remove '#' if it exists
end
if is_set (args[alias]) then -- alias is in the template's argument list
if value ~= nil and selected ~= alias then -- if we have already selected one of the aliases
local skip;
for _, v in ipairs (error_list) do -- spin through the error list to see if we've added this alias
if v == alias then
skip = true;
break; -- has been added so stop looking
end
end
if not skip then -- has not been added so
table.insert (error_list, alias); -- add error alias to the error list
end
else
value = args[alias]; -- not yet selected an alias, so select this one
selected = alias;
end
end
return value, selected; -- return newly selected alias, or previously selected alias
end
--[[--------------------------< A D D _ M A I N T _ C A T >------------------------------------------------------
Adds a category to z.maint_cats_t using names from the configuration file with additional text if any.
To prevent duplication, the added_maint_cats table lists the categories by key that have been added to z.maint_cats_t.
]]
local function add_maint_cat (key, arguments)
if not added_maint_cats [key] then
added_maint_cats [key] = true; -- note that we've added this category
table.insert (z.maint_cats_t, substitute (cfg.maint_cats [key], arguments)); -- make name then add to table
end
end
--[[--------------------------< A D D _ P R O P _ C A T >--------------------------------------------------------
Adds a category to z.prop_cats_t using names from the configuration file with additional text if any.
foreign_lang_source and foreign_lang_source_2 keys have a language code appended to them so that multiple languages
may be categorized but multiples of the same language are not categorized.
added_prop_cats is a table declared in page scope variables above
]]
local added_prop_cats = {}; -- list of property categories that have been added to z.prop_cats_t
local function add_prop_cat (key, arguments, key_modifier)
local key_modified = key .. ((key_modifier and key_modifier) or ''); -- modify <key> with <key_modifier> if present and not nil
if not added_prop_cats [key_modified] then
added_prop_cats [key_modified] = true; -- note that we've added this category
table.insert (z.prop_cats_t, substitute (cfg.prop_cats [key], arguments)); -- make name then add to table
table.insert (z.prop_keys_t, 'cs1-prop-' .. key); -- convert key to class for use in the citation's <cite> tag
end
end
--[[--------------------------< S A F E _ F O R _ I T A L I C S >----------------------------------------------
Protects a string that will be wrapped in wiki italic markup '' ... ''
Note: We cannot use <i> for italics, as the expected behavior for italics specified by ''...'' in the title is that
they will be inverted (i.e. unitalicized) in the resulting references. In addition, <i> and '' tend to interact
poorly under Mediawiki's HTML tidy.
]]
local function safe_for_italics (str)
if not is_set (str) then return str end
if str:sub (1, 1) == "'" then str = "<span></span>" .. str; end
if str:sub (-1, -1) == "'" then str = str .. "<span></span>"; end
return str:gsub ('\n', ' '); -- Remove newlines as they break italics.
end
--[[--------------------------< W R A P _ S T Y L E >----------------------------------------------------------
Applies styling to various parameters. Supplied string is wrapped using a message_list configuration taking one
argument; protects italic styled parameters. Additional text taken from citation_config.presentation - the reason
this function is similar to but separate from wrap_msg().
]]
local function wrap_style (key, str)
if not is_set (str) then
return "";
elseif in_array (key, {'italic-title', 'trans-italic-title'}) then
str = safe_for_italics (str);
end
return substitute (cfg.presentation[key], {str});
end
--[[--------------------------< M A K E _ S E P _ L I S T >------------------------------------------------------------
make a separated list of items using provided separators.
<sep_list> - typically '<comma><space>'
<sep_list_pair> - typically '<space>and<space>'
<sep_list_end> - typically '<comma><space>and<space>' or '<comma><space>&<space>'
defaults to cfg.presentation['sep_list'], cfg.presentation['sep_list_pair'], and cfg.presentation['sep_list_end']
if <sep_list_end> is specified, <sep_list> and <sep_list_pair> must also be supplied
]]
local function make_sep_list (count, list_seq, sep_list, sep_list_pair, sep_list_end)
local list = '';
if not sep_list then -- set the defaults
sep_list = cfg.presentation['sep_list'];
sep_list_pair = cfg.presentation['sep_list_pair'];
sep_list_end = cfg.presentation['sep_list_end'];
end
if 2 >= count then
list = table.concat (list_seq, sep_list_pair); -- insert separator between two items; returns list_seq[1] then only one item
elseif 2 < count then
list = table.concat (list_seq, sep_list, 1, count - 1); -- concatenate all but last item with plain list separator
list = table.concat ({list, list_seq[count]}, sep_list_end); -- concatenate last item onto end of <list> with final separator
end
return list;
end
--[[--------------------------< S E L E C T _ O N E >----------------------------------------------------------
Chooses one matching parameter from a list of parameters to consider. The list of parameters to consider is just
names. For parameters that may be enumerated, the position of the numerator in the parameter name is identified
by the '#' so |author-last1= and |author1-last= are represented as 'author-last#' and 'author#-last'.
Because enumerated parameter |<param>1= is an alias of |<param>= we must test for both possibilities.
Generates an error if more than one match is present.
]]
local function select_one (args, aliases_list, error_condition, index)
local value = nil; -- the value assigned to the selected parameter
local selected = ''; -- the name of the parameter we have chosen
local error_list = {};
if index ~= nil then index = tostring(index); end
for _, alias in ipairs (aliases_list) do -- for each alias in the aliases list
if alias:match ('#') then -- if this alias can be enumerated
if '1' == index then -- when index is 1 test for enumerated and non-enumerated aliases
value, selected = is_alias_used (args, alias, index, false, value, selected, error_list); -- first test for non-enumerated alias
end
value, selected = is_alias_used (args, alias, index, true, value, selected, error_list); -- test for enumerated alias
else
value, selected = is_alias_used (args, alias, index, false, value, selected, error_list); -- test for non-enumerated alias
end
end
if #error_list > 0 and 'none' ~= error_condition then -- for cases where this code is used outside of extract_names()
for i, v in ipairs (error_list) do
error_list[i] = wrap_style ('parameter', v);
end
table.insert (error_list, wrap_style ('parameter', selected));
set_message (error_condition, {make_sep_list (#error_list, error_list)});
end
return value, selected;
end
--[=[-------------------------< R E M O V E _ W I K I _ L I N K >----------------------------------------------
Gets the display text from a wikilink like [[A|B]] or [[B]] gives B
The str:gsub() returns either A|B froma [[A|B]] or B from [[B]] or B from B (no wikilink markup).
In l(), l:gsub() removes the link and pipe (if they exist); the second :gsub() trims whitespace from the label
if str was wrapped in wikilink markup. Presumably, this is because without wikimarkup in str, there is no match
in the initial gsub, the replacement function l() doesn't get called.
]=]
local function remove_wiki_link (str)
return (str:gsub ("%[%[([^%[%]]*)%]%]", function(l)
return l:gsub ("^[^|]*|(.*)$", "%1" ):gsub ("^%s*(.-)%s*$", "%1");
end));
end
--[=[-------------------------< I S _ W I K I L I N K >--------------------------------------------------------
Determines if str is a wikilink, extracts, and returns the wikilink type, link text, and display text parts.
If str is a complex wikilink ([[L|D]]):
returns wl_type 2 and D and L from [[L|D]];
if str is a simple wikilink ([[D]])
returns wl_type 1 and D from [[D]] and L as empty string;
if not a wikilink:
returns wl_type 0, str as D, and L as empty string.
trims leading and trailing whitespace and pipes from L and D ([[L|]] and [[|D]] are accepted by MediaWiki and
treated like [[D]]; while [[|D|]] is not accepted by MediaWiki, here, we accept it and return D without the pipes).
]=]
local function is_wikilink (str)
local D, L
local wl_type = 2; -- assume that str is a complex wikilink [[L|D]]
if not str:match ('^%[%[[^%]]+%]%]$') then -- is str some sort of a wikilink (must have some sort of content)
return 0, str, ''; -- not a wikilink; return wl_type as 0, str as D, and empty string as L
end
L, D = str:match ('^%[%[([^|]+)|([^%]]+)%]%]$'); -- get L and D from [[L|D]]
if not is_set (D) then -- if no separate display
D = str:match ('^%[%[([^%]]*)|*%]%]$'); -- get D from [[D]] or [[D|]]
wl_type = 1;
end
D = mw.text.trim (D, '%s|'); -- trim white space and pipe characters
return wl_type, D, L or '';
end
--[[--------------------------< S T R I P _ A P O S T R O P H E _ M A R K U P >--------------------------------
Strip wiki italic and bold markup from argument so that it doesn't contaminate COinS metadata.
This function strips common patterns of apostrophe markup. We presume that editors who have taken the time to
markup a title have, as a result, provided valid markup. When they don't, some single apostrophes are left behind.
Returns the argument without wiki markup and a number; the number is more-or-less meaningless except as a flag
to indicate that markup was replaced; do not rely on it as an indicator of how many of any kind of markup was
removed; returns the argument and nil when no markup removed
]]
local function strip_apostrophe_markup (argument)
if not is_set (argument) then
return argument, nil; -- no argument, nothing to do
end
if nil == argument:find ( "''", 1, true ) then -- Is there at least one double apostrophe? If not, exit.
return argument, nil;
end
local flag;
while true do
if argument:find ("'''''", 1, true) then -- bold italic (5)
argument, flag = argument:gsub ("%'%'%'%'%'", ""); -- remove all instances of it
elseif argument:find ("''''", 1, true) then -- italic start and end without content (4)
argument, flag=argument:gsub ("%'%'%'%'", "");
elseif argument:find ("'''", 1, true) then -- bold (3)
argument, flag=argument:gsub ("%'%'%'", "");
elseif argument:find ("''", 1, true) then -- italic (2)
argument, flag = argument:gsub ("%'%'", "");
else
break;
end
end
return argument, flag; -- done
end
--[[--------------------------< S E T _ S E L E C T E D _ M O D U L E S >--------------------------------------
Sets local cfg table to same (live or sandbox) as that used by the other modules.
]]
local function set_selected_modules (cfg_table_ptr)
cfg = cfg_table_ptr;
end
--[[--------------------------< E X P O R T S >----------------------------------------------------------------
]]
return {
add_maint_cat = add_maint_cat, -- exported functions
add_prop_cat = add_prop_cat,
error_comment = error_comment,
has_accept_as_written = has_accept_as_written,
hyphen_to_dash = hyphen_to_dash,
in_array = in_array,
is_set = is_set,
is_wikilink = is_wikilink,
make_sep_list = make_sep_list,
make_wikilink = make_wikilink,
remove_wiki_link = remove_wiki_link,
safe_for_italics = safe_for_italics,
select_one = select_one,
set_message = set_message,
set_selected_modules = set_selected_modules,
strip_apostrophe_markup = strip_apostrophe_markup,
substitute = substitute,
wrap_style = wrap_style,
z = z, -- exported table
}
1rgyhapxi1dzuxmh3cqix2zrvvro8l2
886349
886348
2025-06-13T17:03:24Z
KartikMistry
10383
[[:en:Module:Citation/CS1/Utilities]] માંથી આયાત કરેલ ૧ પુનરાવર્તન
886348
Scribunto
text/plain
local z = {
error_cats_t = {}; -- for categorizing citations that contain errors
error_ids_t = {}; -- list of error identifiers; used to prevent duplication of certain errors; local to this module
error_msgs_t = {}; -- sequence table of error messages
maint_cats_t = {}; -- for categorizing citations that aren't erroneous per se, but could use a little work
prop_cats_t = {}; -- for categorizing citations based on certain properties, language of source for instance
prop_keys_t = {}; -- for adding classes to the citation's <cite> tag
};
--[[--------------------------< F O R W A R D D E C L A R A T I O N S >--------------------------------------
]]
local cfg; -- table of tables imported from selected Module:Citation/CS1/Configuration
--[[--------------------------< I S _ S E T >------------------------------------------------------------------
Returns true if argument is set; false otherwise. Argument is 'set' when it exists (not nil) or when it is not an empty string.
]]
local function is_set (var)
return not (var == nil or var == '');
end
--[[--------------------------< I N _ A R R A Y >--------------------------------------------------------------
Whether needle is in haystack
]]
local function in_array (needle, haystack)
if needle == nil then
return false;
end
for n, v in ipairs (haystack) do
if v == needle then
return n;
end
end
return false;
end
--[[--------------------------< H A S _ A C C E P T _ A S _ W R I T T E N >------------------------------------
When <str> is wholly wrapped in accept-as-written markup, return <str> without markup and true; return <str> and false else
with allow_empty = false, <str> must have at least one character inside the markup
with allow_empty = true, <str> the markup frame can be empty like (()) to distinguish an empty template parameter from the specific condition "has no applicable value" in citation-context.
After further evaluation the two cases might be merged at a later stage, but should be kept separated for now.
]]
local function has_accept_as_written (str, allow_empty)
if not is_set (str) then
return str, false;
end
local count;
if true == allow_empty then
str, count = str:gsub ('^%(%((.*)%)%)$', '%1'); -- allows (()) to be an empty set
else
str, count = str:gsub ('^%(%((.+)%)%)$', '%1');
end
return str, 0 ~= count;
end
--[[--------------------------< S U B S T I T U T E >----------------------------------------------------------
Populates numbered arguments in a message string using an argument table. <args> may be a single string or a
sequence table of multiple strings.
]]
local function substitute (msg, args)
return args and mw.message.newRawMessage (msg, args):plain() or msg;
end
--[[--------------------------< E R R O R _ C O M M E N T >----------------------------------------------------
Wraps error messages with CSS markup according to the state of hidden. <content> may be a single string or a
sequence table of multiple strings.
]]
local function error_comment (content, hidden)
return substitute (hidden and cfg.presentation['hidden-error'] or cfg.presentation['visible-error'], content);
end
--[[--------------------------< H Y P H E N _ T O _ D A S H >--------------------------------------------------
Converts a hyphen, endash, emdash to endash under certain conditions. The hyphen/en/em must separate
like items; unlike items are returned unmodified. These forms are modified:
letter - letter (A-B)
digit - digit (4-5)
digit separator digit - digit separator digit (4.1-4.5 or 4-1-4-5)
letterdigit - letterdigit (A1-A5) (an optional separator between letter and
digit is supported – a.1-a.5 or a-1-a-5)
digitletter - digitletter (5a-5d) (an optional separator between letter and
digit is supported – 5.a-5.d or 5-a-5-d)
any other forms are returned unmodified.
str may be a comma- or semicolon-separated list of page ranges with/without single pages
]]
local function hyphen_to_dash (str)
if not is_set (str) then
return str;
end
str = str:gsub ("(%(%(.-%)%))", function(m) return m:gsub(",", ","):gsub(";", ";") end) -- replace commas and semicolons in accept-as-written markup with similar unicode characters so they'll be ignored during the split
str = str:gsub ('&[nm]dash;', {['–'] = '–', ['—'] = '—'}); -- replace — and – entities with their characters; semicolon mucks up the text.split
str = str:gsub ('-', '-'); -- replace HTML numeric entity with hyphen character
str = str:gsub (' ', ' '); -- replace entity with generic keyboard space character
local out = {};
local list = mw.text.split (str, '%s*[,;]%s*'); -- split str at comma or semicolon separators if there are any
local accept; -- boolean
for _, item in ipairs (list) do -- for each item in the list
item, accept = has_accept_as_written (item); -- remove accept-this-as-written markup when it wraps all of item
if not accept and mw.ustring.match (item, '^%w*[%.%-]?%w+%s*[—–-]%s*%w*[%.%-]?%w+$') then -- if a hyphenated range or has endash or emdash separators
if mw.ustring.match (item, '^%a+[%.%-]?%d+%s*[—–-]%s*%a+[%.%-]?%d+$') or -- letterdigit hyphen letterdigit (optional separator between letter and digit)
mw.ustring.match (item, '^%d+[%.%-]?%a+%s*[—–-]%s*%d+[%.%-]?%a+$') or -- digitletter hyphen digitletter (optional separator between digit and letter)
mw.ustring.match (item, '^%d+[%.%-]%d+%s*[—–-]%s*%d+[%.%-]%d+$') then -- digit separator digit hyphen digit separator digit
item = mw.ustring.gsub (item, '(%w*[%.%-]?%w+)%s*[—–-]%s*(%w*[%.%-]?%w+)', '<span class="nowrap">%1 –</span> <span class="nowrap">%2</span>'); -- replace hyphen/dash, with spaced endash
elseif mw.ustring.match (item, '^%d+%s*[—–-]%s*%d+$') or -- digit hyphen digit
mw.ustring.match (item, '^%a+%s*[—–-]%s*%a+$') then -- letter hyphen letter
item = mw.ustring.gsub (item, '(%w+)%s*[—–-]%s*(%w+)', '<span class="nowrap">%1–</span>%2'); -- replace hyphen/emdash with endash, remove extraneous space characters
else
-- item = mw.ustring.gsub (item, '%s*[—–-]%s*', '–'); -- disabled; here when 'unlike' items so return <item> as is
end
end
table.insert (out, item); -- add the (possibly modified) item to the output table
end
local temp_str = ''; -- concatenate the output table into a comma separated string
temp_str, accept = has_accept_as_written (table.concat (out, ', ')); -- remove accept-this-as-written markup when it wraps all of concatenated out
if accept then
temp_str = has_accept_as_written (str); -- when global markup removed, return original str; do it this way to suppress boolean second return value
return temp_str:gsub(",", ","):gsub(";", ";");
else
return temp_str:gsub(",", ","):gsub(";", ";"); -- else, return assembled temp_str
end
end
--[=[-------------------------< M A K E _ W I K I L I N K >----------------------------------------------------
Makes a wikilink; when both link and display text is provided, returns a wikilink in the form [[L|D]]; if only
link is provided (or link and display are the same), returns a wikilink in the form [[L]]; if neither are
provided or link is omitted, returns an empty string.
]=]
local function make_wikilink (link, display)
if not is_set (link) then return '' end
if is_set (display) and link ~= display then
return table.concat ({'[[', link, '|', display, ']]'});
else
return table.concat ({'[[', link, ']]'});
end
end
--[[--------------------------< S E T _ M E S S A G E >----------------------------------------------------------
Sets an error message using the ~/Configuration error_conditions{} table along with arguments supplied in the function
call, inserts the resulting message in z.error_msgs_t{} sequence table, and returns the error message.
<error_id> – key value for appropriate error handler in ~/Configuration error_conditions{} table
<arguments> – may be a single string or a sequence table of multiple strings to be subsititued into error_conditions[error_id].message
<raw> – boolean
true – causes this function to return the error message not wrapped in visible-error, hidden-error span tag;
returns error_conditions[error_id].hidden as a second return value
does not add message to z.error_msgs_t sequence table
false, nil – adds message wrapped in visible-error, hidden-error span tag to z.error_msgs_t
returns the error message wrapped in visible-error, hidden-error span tag; there is no second return value
<prefix> – string to be prepended to <message> -- TODO: remove support for these unused(?) arguments?
<suffix> – string to be appended to <message>
TODO: change z.error_cats_t and z.maint_cats_t to have the form cat_name = true? this to avoid dups without having to have an extra table
]]
local added_maint_cats = {} -- list of maintenance categories that have been added to z.maint_cats_t; TODO: figure out how to delete this table
local function set_message (error_id, arguments, raw, prefix, suffix)
local error_state = cfg.error_conditions[error_id];
prefix = prefix or '';
suffix = suffix or '';
if error_state == nil then
error (cfg.messages['undefined_error'] .. ': ' .. error_id); -- because missing error handler in Module:Citation/CS1/Configuration
elseif is_set (error_state.category) then
if error_state.message then -- when error_state.message defined, this is an error message
table.insert (z.error_cats_t, error_state.category);
else
if not added_maint_cats[error_id] then
added_maint_cats[error_id] = true; -- note that we've added this category
table.insert (z.maint_cats_t, substitute (error_state.category, arguments)); -- make cat name then add to table
end
return; -- because no message, nothing more to do
end
end
local message = substitute (error_state.message, arguments);
message = table.concat (
{
message,
' (',
make_wikilink (
table.concat (
{
cfg.messages['help page link'],
'#',
error_state.anchor
}),
cfg.messages['help page label']),
')'
});
z.error_ids_t[error_id] = true;
if z.error_ids_t['err_citation_missing_title'] and -- if missing-title error already noted
in_array (error_id, {'err_bare_url_missing_title', 'err_trans_missing_title'}) then -- and this error is one of these
return '', false; -- don't bother because one flavor of missing title is sufficient
end
message = table.concat ({prefix, message, suffix});
if true == raw then
return message, error_state.hidden; -- return message not wrapped in visible-error, hidden-error span tag
end
message = error_comment (message, error_state.hidden); -- wrap message in visible-error, hidden-error span tag
table.insert (z.error_msgs_t, message); -- add it to the messages sequence table
return message; -- and done; return value generally not used but is used as a flag in various functions of ~/Identifiers
end
--[[-------------------------< I S _ A L I A S _ U S E D >-----------------------------------------------------
This function is used by select_one() to determine if one of a list of alias parameters is in the argument list
provided by the template.
Input:
args – pointer to the arguments table from calling template
alias – one of the list of possible aliases in the aliases lists from Module:Citation/CS1/Configuration
index – for enumerated parameters, identifies which one
enumerated – true/false flag used to choose how enumerated aliases are examined
value – value associated with an alias that has previously been selected; nil if not yet selected
selected – the alias that has previously been selected; nil if not yet selected
error_list – list of aliases that are duplicates of the alias already selected
Returns:
value – value associated with alias we selected or that was previously selected or nil if an alias not yet selected
selected – the alias we selected or the alias that was previously selected or nil if an alias not yet selected
]]
local function is_alias_used (args, alias, index, enumerated, value, selected, error_list)
if enumerated then -- is this a test for an enumerated parameters?
alias = alias:gsub ('#', index); -- replace '#' with the value in index
else
alias = alias:gsub ('#', ''); -- remove '#' if it exists
end
if is_set (args[alias]) then -- alias is in the template's argument list
if value ~= nil and selected ~= alias then -- if we have already selected one of the aliases
local skip;
for _, v in ipairs (error_list) do -- spin through the error list to see if we've added this alias
if v == alias then
skip = true;
break; -- has been added so stop looking
end
end
if not skip then -- has not been added so
table.insert (error_list, alias); -- add error alias to the error list
end
else
value = args[alias]; -- not yet selected an alias, so select this one
selected = alias;
end
end
return value, selected; -- return newly selected alias, or previously selected alias
end
--[[--------------------------< A D D _ M A I N T _ C A T >------------------------------------------------------
Adds a category to z.maint_cats_t using names from the configuration file with additional text if any.
To prevent duplication, the added_maint_cats table lists the categories by key that have been added to z.maint_cats_t.
]]
local function add_maint_cat (key, arguments)
if not added_maint_cats [key] then
added_maint_cats [key] = true; -- note that we've added this category
table.insert (z.maint_cats_t, substitute (cfg.maint_cats [key], arguments)); -- make name then add to table
end
end
--[[--------------------------< A D D _ P R O P _ C A T >--------------------------------------------------------
Adds a category to z.prop_cats_t using names from the configuration file with additional text if any.
foreign_lang_source and foreign_lang_source_2 keys have a language code appended to them so that multiple languages
may be categorized but multiples of the same language are not categorized.
added_prop_cats is a table declared in page scope variables above
]]
local added_prop_cats = {}; -- list of property categories that have been added to z.prop_cats_t
local function add_prop_cat (key, arguments, key_modifier)
local key_modified = key .. ((key_modifier and key_modifier) or ''); -- modify <key> with <key_modifier> if present and not nil
if not added_prop_cats [key_modified] then
added_prop_cats [key_modified] = true; -- note that we've added this category
table.insert (z.prop_cats_t, substitute (cfg.prop_cats [key], arguments)); -- make name then add to table
table.insert (z.prop_keys_t, 'cs1-prop-' .. key); -- convert key to class for use in the citation's <cite> tag
end
end
--[[--------------------------< S A F E _ F O R _ I T A L I C S >----------------------------------------------
Protects a string that will be wrapped in wiki italic markup '' ... ''
Note: We cannot use <i> for italics, as the expected behavior for italics specified by ''...'' in the title is that
they will be inverted (i.e. unitalicized) in the resulting references. In addition, <i> and '' tend to interact
poorly under Mediawiki's HTML tidy.
]]
local function safe_for_italics (str)
if not is_set (str) then return str end
if str:sub (1, 1) == "'" then str = "<span></span>" .. str; end
if str:sub (-1, -1) == "'" then str = str .. "<span></span>"; end
return str:gsub ('\n', ' '); -- Remove newlines as they break italics.
end
--[[--------------------------< W R A P _ S T Y L E >----------------------------------------------------------
Applies styling to various parameters. Supplied string is wrapped using a message_list configuration taking one
argument; protects italic styled parameters. Additional text taken from citation_config.presentation - the reason
this function is similar to but separate from wrap_msg().
]]
local function wrap_style (key, str)
if not is_set (str) then
return "";
elseif in_array (key, {'italic-title', 'trans-italic-title'}) then
str = safe_for_italics (str);
end
return substitute (cfg.presentation[key], {str});
end
--[[--------------------------< M A K E _ S E P _ L I S T >------------------------------------------------------------
make a separated list of items using provided separators.
<sep_list> - typically '<comma><space>'
<sep_list_pair> - typically '<space>and<space>'
<sep_list_end> - typically '<comma><space>and<space>' or '<comma><space>&<space>'
defaults to cfg.presentation['sep_list'], cfg.presentation['sep_list_pair'], and cfg.presentation['sep_list_end']
if <sep_list_end> is specified, <sep_list> and <sep_list_pair> must also be supplied
]]
local function make_sep_list (count, list_seq, sep_list, sep_list_pair, sep_list_end)
local list = '';
if not sep_list then -- set the defaults
sep_list = cfg.presentation['sep_list'];
sep_list_pair = cfg.presentation['sep_list_pair'];
sep_list_end = cfg.presentation['sep_list_end'];
end
if 2 >= count then
list = table.concat (list_seq, sep_list_pair); -- insert separator between two items; returns list_seq[1] then only one item
elseif 2 < count then
list = table.concat (list_seq, sep_list, 1, count - 1); -- concatenate all but last item with plain list separator
list = table.concat ({list, list_seq[count]}, sep_list_end); -- concatenate last item onto end of <list> with final separator
end
return list;
end
--[[--------------------------< S E L E C T _ O N E >----------------------------------------------------------
Chooses one matching parameter from a list of parameters to consider. The list of parameters to consider is just
names. For parameters that may be enumerated, the position of the numerator in the parameter name is identified
by the '#' so |author-last1= and |author1-last= are represented as 'author-last#' and 'author#-last'.
Because enumerated parameter |<param>1= is an alias of |<param>= we must test for both possibilities.
Generates an error if more than one match is present.
]]
local function select_one (args, aliases_list, error_condition, index)
local value = nil; -- the value assigned to the selected parameter
local selected = ''; -- the name of the parameter we have chosen
local error_list = {};
if index ~= nil then index = tostring(index); end
for _, alias in ipairs (aliases_list) do -- for each alias in the aliases list
if alias:match ('#') then -- if this alias can be enumerated
if '1' == index then -- when index is 1 test for enumerated and non-enumerated aliases
value, selected = is_alias_used (args, alias, index, false, value, selected, error_list); -- first test for non-enumerated alias
end
value, selected = is_alias_used (args, alias, index, true, value, selected, error_list); -- test for enumerated alias
else
value, selected = is_alias_used (args, alias, index, false, value, selected, error_list); -- test for non-enumerated alias
end
end
if #error_list > 0 and 'none' ~= error_condition then -- for cases where this code is used outside of extract_names()
for i, v in ipairs (error_list) do
error_list[i] = wrap_style ('parameter', v);
end
table.insert (error_list, wrap_style ('parameter', selected));
set_message (error_condition, {make_sep_list (#error_list, error_list)});
end
return value, selected;
end
--[=[-------------------------< R E M O V E _ W I K I _ L I N K >----------------------------------------------
Gets the display text from a wikilink like [[A|B]] or [[B]] gives B
The str:gsub() returns either A|B froma [[A|B]] or B from [[B]] or B from B (no wikilink markup).
In l(), l:gsub() removes the link and pipe (if they exist); the second :gsub() trims whitespace from the label
if str was wrapped in wikilink markup. Presumably, this is because without wikimarkup in str, there is no match
in the initial gsub, the replacement function l() doesn't get called.
]=]
local function remove_wiki_link (str)
return (str:gsub ("%[%[([^%[%]]*)%]%]", function(l)
return l:gsub ("^[^|]*|(.*)$", "%1" ):gsub ("^%s*(.-)%s*$", "%1");
end));
end
--[=[-------------------------< I S _ W I K I L I N K >--------------------------------------------------------
Determines if str is a wikilink, extracts, and returns the wikilink type, link text, and display text parts.
If str is a complex wikilink ([[L|D]]):
returns wl_type 2 and D and L from [[L|D]];
if str is a simple wikilink ([[D]])
returns wl_type 1 and D from [[D]] and L as empty string;
if not a wikilink:
returns wl_type 0, str as D, and L as empty string.
trims leading and trailing whitespace and pipes from L and D ([[L|]] and [[|D]] are accepted by MediaWiki and
treated like [[D]]; while [[|D|]] is not accepted by MediaWiki, here, we accept it and return D without the pipes).
]=]
local function is_wikilink (str)
local D, L
local wl_type = 2; -- assume that str is a complex wikilink [[L|D]]
if not str:match ('^%[%[[^%]]+%]%]$') then -- is str some sort of a wikilink (must have some sort of content)
return 0, str, ''; -- not a wikilink; return wl_type as 0, str as D, and empty string as L
end
L, D = str:match ('^%[%[([^|]+)|([^%]]+)%]%]$'); -- get L and D from [[L|D]]
if not is_set (D) then -- if no separate display
D = str:match ('^%[%[([^%]]*)|*%]%]$'); -- get D from [[D]] or [[D|]]
wl_type = 1;
end
D = mw.text.trim (D, '%s|'); -- trim white space and pipe characters
return wl_type, D, L or '';
end
--[[--------------------------< S T R I P _ A P O S T R O P H E _ M A R K U P >--------------------------------
Strip wiki italic and bold markup from argument so that it doesn't contaminate COinS metadata.
This function strips common patterns of apostrophe markup. We presume that editors who have taken the time to
markup a title have, as a result, provided valid markup. When they don't, some single apostrophes are left behind.
Returns the argument without wiki markup and a number; the number is more-or-less meaningless except as a flag
to indicate that markup was replaced; do not rely on it as an indicator of how many of any kind of markup was
removed; returns the argument and nil when no markup removed
]]
local function strip_apostrophe_markup (argument)
if not is_set (argument) then
return argument, nil; -- no argument, nothing to do
end
if nil == argument:find ( "''", 1, true ) then -- Is there at least one double apostrophe? If not, exit.
return argument, nil;
end
local flag;
while true do
if argument:find ("'''''", 1, true) then -- bold italic (5)
argument, flag = argument:gsub ("%'%'%'%'%'", ""); -- remove all instances of it
elseif argument:find ("''''", 1, true) then -- italic start and end without content (4)
argument, flag=argument:gsub ("%'%'%'%'", "");
elseif argument:find ("'''", 1, true) then -- bold (3)
argument, flag=argument:gsub ("%'%'%'", "");
elseif argument:find ("''", 1, true) then -- italic (2)
argument, flag = argument:gsub ("%'%'", "");
else
break;
end
end
return argument, flag; -- done
end
--[[--------------------------< S E T _ S E L E C T E D _ M O D U L E S >--------------------------------------
Sets local cfg table to same (live or sandbox) as that used by the other modules.
]]
local function set_selected_modules (cfg_table_ptr)
cfg = cfg_table_ptr;
end
--[[--------------------------< E X P O R T S >----------------------------------------------------------------
]]
return {
add_maint_cat = add_maint_cat, -- exported functions
add_prop_cat = add_prop_cat,
error_comment = error_comment,
has_accept_as_written = has_accept_as_written,
hyphen_to_dash = hyphen_to_dash,
in_array = in_array,
is_set = is_set,
is_wikilink = is_wikilink,
make_sep_list = make_sep_list,
make_wikilink = make_wikilink,
remove_wiki_link = remove_wiki_link,
safe_for_italics = safe_for_italics,
select_one = select_one,
set_message = set_message,
set_selected_modules = set_selected_modules,
strip_apostrophe_markup = strip_apostrophe_markup,
substitute = substitute,
wrap_style = wrap_style,
z = z, -- exported table
}
1rgyhapxi1dzuxmh3cqix2zrvvro8l2
વિભાગ:Citation/CS1/Identifiers
828
72170
886352
821286
2025-04-12T14:06:45Z
en>Trappist the monk
0
886352
Scribunto
text/plain
--[[--------------------------< F O R W A R D D E C L A R A T I O N S >--------------------------------------
]]
local has_accept_as_written, is_set, in_array, set_message, select_one, -- functions in Module:Citation/CS1/Utilities
substitute, make_wikilink;
local z; -- table of tables defined in Module:Citation/CS1/Utilities
local cfg; -- table of configuration tables that are defined in Module:Citation/CS1/Configuration
--[[--------------------------< 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 auto_link_urls = {}; -- holds identifier URLs for those identifiers that can auto-link |title=
--============================<< H E L P E R F U N C T I O N S >>============================================
--[[--------------------------< W I K I D A T A _ A R T I C L E _ N A M E _ G E T >----------------------------
as an aid to internationalizing identifier-label wikilinks, gets identifier article names from Wikidata.
returns :<lang code>:<article title> when <q> has an <article title> for <lang code>; nil else
for identifiers that do not have q, returns nil
for wikis that do not have mw.wikibase installed, returns nil
]]
local function wikidata_article_name_get (q)
if not is_set (q) or (q and not mw.wikibase) then -- when no q number or when a q number but mw.wikibase not installed on this wiki
return nil; -- abandon
end
local wd_article;
local this_wiki_code = cfg.this_wiki_code; -- Wikipedia subdomain; 'en' for en.wikipedia.org
wd_article = mw.wikibase.getSitelink (q, this_wiki_code .. 'wiki'); -- fetch article title from WD; nil when no title available at this wiki
if wd_article then
wd_article = table.concat ({':', this_wiki_code, ':', wd_article}); -- interwiki-style link without brackets if taken from WD; leading colon required
end
return wd_article; -- article title from WD; nil else
end
--[[--------------------------< L I N K _ L A B E L _ M A K E >------------------------------------------------
common function to create identifier link label from handler table or from Wikidata
returns the first available of
1. redirect from local wiki's handler table (if enabled)
2. Wikidata (if there is a Wikidata entry for this identifier in the local wiki's language)
3. label specified in the local wiki's handler table
]]
local function link_label_make (handler)
local wd_article;
if not (cfg.use_identifier_redirects and is_set (handler.redirect)) then -- redirect has priority so if enabled and available don't fetch from Wikidata because expensive
wd_article = wikidata_article_name_get (handler.q); -- if Wikidata has an article title for this wiki, get it;
end
return (cfg.use_identifier_redirects and is_set (handler.redirect) and handler.redirect) or wd_article or handler.link;
end
--[[--------------------------< E X T E R N A L _ L I N K _ I D >----------------------------------------------
Formats a wiki-style external link
]]
local function external_link_id (options)
local url_string = options.id;
local ext_link;
local this_wiki_code = cfg.this_wiki_code; -- Wikipedia subdomain; 'en' for en.wikipedia.org
local wd_article; -- article title from Wikidata
if options.encode == true or options.encode == nil then
url_string = mw.uri.encode (url_string, 'PATH');
end
if options.auto_link and is_set (options.access) then
auto_link_urls[options.auto_link] = table.concat ({options.prefix, url_string, options.suffix});
end
ext_link = mw.ustring.format ('[%s%s%s %s]', options.prefix, url_string, options.suffix or "", mw.text.nowiki (options.id));
if is_set (options.access) then
ext_link = substitute (cfg.presentation['ext-link-access-signal'], {cfg.presentation[options.access].class, cfg.presentation[options.access].title, ext_link}); -- add the free-to-read / paywall lock
end
return table.concat ({
make_wikilink (link_label_make (options), options.label), -- redirect, Wikidata link, or locally specified link (in that order)
options.separator or ' ',
ext_link
});
end
--[[--------------------------< I N T E R N A L _ L I N K _ I D >----------------------------------------------
Formats a wiki-style internal link
TODO: Does not currently need to support options.access, options.encode, auto-linking and COinS (as in external_link_id),
but may be needed in the future for :m:Interwiki_map custom-prefixes like :arxiv:, :bibcode:, :DOI:, :hdl:, :ISSN:,
:JSTOR:, :Openlibrary:, :PMID:, :RFC:.
]]
local function internal_link_id (options)
local id = mw.ustring.gsub (options.id, '%d', cfg.date_names.local_digits); -- translate 'local' digits to Western 0-9
return table.concat (
{
make_wikilink (link_label_make (options), options.label), -- wiki-link the identifier label
options.separator or ' ', -- add the separator
make_wikilink (
table.concat (
{
options.prefix,
id, -- translated to Western digits
options.suffix or ''
}),
substitute (cfg.presentation['bdi'], {'', mw.text.nowiki (options.id)}) -- bdi tags to prevent Latin script identifiers from being reversed at RTL language wikis
); -- nowiki because MediaWiki still has magic links for ISBN and the like; TODO: is it really required?
});
end
--[[--------------------------< I S _ E M B A R G O E D >------------------------------------------------------
Determines if a PMC identifier's online version is embargoed. Compares the date in |pmc-embargo-date= against
today's date. If embargo date is in the future, returns the content of |pmc-embargo-date=; otherwise, returns
an empty string because the embargo has expired or because |pmc-embargo-date= was not set in this cite.
]]
local function is_embargoed (embargo)
if is_set (embargo) then
local lang = mw.getContentLanguage();
local good1, embargo_date, todays_date;
good1, embargo_date = pcall (lang.formatDate, lang, 'U', embargo);
todays_date = lang:formatDate ('U');
if good1 then -- if embargo date is a good date
if tonumber (embargo_date) >= tonumber (todays_date) then -- is embargo date is in the future?
return embargo; -- still embargoed
else
set_message ('maint_pmc_embargo'); -- embargo has expired; add main cat
return ''; -- unset because embargo has expired
end
end
end
return ''; -- |pmc-embargo-date= not set return empty string
end
--[=[-------------------------< I S _ V A L I D _ R X I V _ D A T E >------------------------------------------
for biorxiv, returns true if:
2019-12-11T00:00Z <= biorxiv_date < today + 2 days
for medrxiv, returns true if:
2020-01-01T00:00Z <= medrxiv_date < today + 2 days
The dated form of biorxiv identifier has a start date of 2019-12-11. The Unix timestamp for that date is {{#time:U|2019-12-11}} = 1576022400
The medrxiv identifier has a start date of 2020-01-01. The Unix timestamp for that date is {{#time:U|2020-01-01}} = 1577836800
<rxiv_date> is the date provided in those |biorxiv= parameter values that are dated and in |medrxiv= parameter values at time 00:00:00 UTC
<today> is the current date at time 00:00:00 UTC plus 48 hours
if today's date is 2023-01-01T00:00:00 then
adding 24 hours gives 2023-01-02T00:00:00 – one second more than today
adding 24 hours gives 2023-01-03T00:00:00 – one second more than tomorrow
inputs:
<y>, <m>, <d> – year, month, day parts of the date from the birxiv or medrxiv identifier
<select> 'b' for biorxiv, 'm' for medrxiv; defaults to 'b'
]=]
local function is_valid_rxiv_date (y, m, d, select)
if 0 == tonumber (m) and 12 < tonumber (m) then -- <m> must be a number 1–12
return false;
end
if 0 == tonumber (d) and 31 < tonumber (d) then -- <d> must be a number 1–31; TODO: account for month length and leap yer?
return false;
end
local rxiv_date = table.concat ({y, m, d}, '-'); -- make ymd date string
local good1, good2;
local rxiv_ts, tomorrow_ts; -- to hold Unix timestamps representing the dates
local lang_object = mw.getContentLanguage();
good1, rxiv_ts = pcall (lang_object.formatDate, lang_object, 'U', rxiv_date); -- convert rxiv_date value to Unix timestamp
good2, tomorrow_ts = pcall (lang_object.formatDate, lang_object, 'U', 'today + 2 days' ); -- today midnight + 2 days is one second more than all day tomorrow
if good1 and good2 then -- lang.formatDate() returns a timestamp in the local script which tonumber() may not understand
rxiv_ts = tonumber (rxiv_ts) or lang_object:parseFormattedNumber (rxiv_ts); -- convert to numbers for the comparison;
tomorrow_ts = tonumber (tomorrow_ts) or lang_object:parseFormattedNumber (tomorrow_ts);
else
return false; -- one or both failed to convert to Unix timestamp
end
local limit_ts = ((select and ('m' == select)) and 1577836800) or 1576022400; -- choose the appropriate limit timesatmp
return ((limit_ts <= rxiv_ts) and (rxiv_ts < tomorrow_ts)) -- limit_ts <= rxiv_date < tomorrow's date
end
--[[--------------------------< IS _ V A L I D _ I S X N >-----------------------------------------------------
ISBN-10 and ISSN validator code calculates checksum across all ISBN/ISSN digits including the check digit.
ISBN-13 is checked in isbn().
If the number is valid the result will be 0. Before calling this function, ISBN/ISSN must be checked for length
and stripped of dashes, spaces and other non-ISxN characters.
]]
local function is_valid_isxn (isxn_str, len)
local temp = 0;
isxn_str = { isxn_str:byte(1, len) }; -- make a table of byte values '0' → 0x30 .. '9' → 0x39, 'X' → 0x58
len = len + 1; -- adjust to be a loop counter
for i, v in ipairs (isxn_str) do -- loop through all of the bytes and calculate the checksum
if v == string.byte ("X" ) then -- if checkdigit is X (compares the byte value of 'X' which is 0x58)
temp = temp + 10 * (len - i); -- it represents 10 decimal
else
temp = temp + tonumber (string.char (v) )*(len-i);
end
end
return temp % 11 == 0; -- returns true if calculation result is zero
end
--[[--------------------------< IS _ V A L I D _ I S X N _ 1 3 >-----------------------------------------------
ISBN-13 and ISMN validator code calculates checksum across all 13 ISBN/ISMN digits including the check digit.
If the number is valid, the result will be 0. Before calling this function, ISBN-13/ISMN must be checked for length
and stripped of dashes, spaces and other non-ISxN-13 characters.
]]
local function is_valid_isxn_13 (isxn_str)
local temp=0;
isxn_str = { isxn_str:byte(1, 13) }; -- make a table of byte values '0' → 0x30 .. '9' → 0x39
for i, v in ipairs (isxn_str) do
temp = temp + (3 - 2*(i % 2)) * tonumber (string.char (v) ); -- multiply odd index digits by 1, even index digits by 3 and sum; includes check digit
end
return temp % 10 == 0; -- sum modulo 10 is zero when ISBN-13/ISMN is correct
end
--[[--------------------------< N O R M A L I Z E _ L C C N >--------------------------------------------------
LCCN normalization (https://www.loc.gov/marc/lccn-namespace.html#normalization)
1. Remove all blanks.
2. If there is a forward slash (/) in the string, remove it, and remove all characters to the right of the forward slash.
3. If there is a hyphen in the string:
a. Remove it.
b. Inspect the substring following (to the right of) the (removed) hyphen. Then (and assuming that steps 1 and 2 have been carried out):
1. All these characters should be digits, and there should be six or less. (not done in this function)
2. If the length of the substring is less than 6, left-fill the substring with zeroes until the length is six.
Returns a normalized LCCN for lccn() to validate. There is no error checking (step 3.b.1) performed in this function.
]]
local function normalize_lccn (lccn)
lccn = lccn:gsub ("%s", ""); -- 1. strip whitespace
if nil ~= string.find (lccn, '/') then
lccn = lccn:match ("(.-)/"); -- 2. remove forward slash and all character to the right of it
end
local prefix
local suffix
prefix, suffix = lccn:match ("(.+)%-(.+)"); -- 3.a remove hyphen by splitting the string into prefix and suffix
if nil ~= suffix then -- if there was a hyphen
suffix = string.rep("0", 6-string.len (suffix)) .. suffix; -- 3.b.2 left fill the suffix with 0s if suffix length less than 6
lccn = prefix..suffix; -- reassemble the LCCN
end
return lccn;
end
--============================<< I D E N T I F I E R F U N C T I O N S >>====================================
--[[--------------------------< A R X I V >--------------------------------------------------------------------
See: https://arxiv.org/help/arxiv_identifier
format and error check arXiv identifier. There are three valid forms of the identifier:
the first form, valid only between date codes 9107 and 0703, is:
arXiv:<archive>.<class>/<date code><number><version>
where:
<archive> is a string of alpha characters - may be hyphenated; no other punctuation
<class> is a string of alpha characters - may be hyphenated; no other punctuation; not the same as |class= parameter which is not supported in this form
<date code> is four digits in the form YYMM where YY is the last two digits of the four-digit year and MM is the month number January = 01
first digit of YY for this form can only 9 and 0
<number> is a three-digit number
<version> is a 1 or more digit number preceded with a lowercase v; no spaces (undocumented)
the second form, valid from April 2007 through December 2014 is:
arXiv:<date code>.<number><version>
where:
<date code> is four digits in the form YYMM where YY is the last two digits of the four-digit year and MM is the month number January = 01
<number> is a four-digit number
<version> is a 1 or more digit number preceded with a lowercase v; no spaces
the third form, valid from January 2015 is:
arXiv:<date code>.<number><version>
where:
<date code> and <version> are as defined for 0704-1412
<number> is a five-digit number
]]
local function arxiv (options)
local id = options.id;
local class = options.Class; -- TODO: lowercase?
local handler = options.handler;
local year, month, version;
local err_msg = false; -- assume no error message
local text; -- output text
if id:match("^%a[%a%.%-]+/[90]%d[01]%d%d%d%d$") or id:match("^%a[%a%.%-]+/[90]%d[01]%d%d%d%dv%d+$") then -- test for the 9107-0703 format with or without version
year, month = id:match("^%a[%a%.%-]+/([90]%d)([01]%d)%d%d%d[v%d]*$");
year = tonumber (year);
month = tonumber (month);
if ((not (90 < year or 8 > year)) or (1 > month or 12 < month)) or -- if invalid year or invalid month
((91 == year and 7 > month) or (7 == year and 3 < month)) then -- if years ok, are starting and ending months ok?
err_msg = true; -- flag for error message
end
elseif id:match("^%d%d[01]%d%.%d%d%d%d$") or id:match("^%d%d[01]%d%.%d%d%d%dv%d+$") then -- test for the 0704-1412 with or without version
year, month = id:match("^(%d%d)([01]%d)%.%d%d%d%d[v%d]*$");
year = tonumber (year);
month = tonumber (month);
if ((7 > year) or (14 < year) or (1 > month or 12 < month)) or -- is year invalid or is month invalid? (doesn't test for future years)
((7 == year) and (4 > month)) then -- when year is 07, is month invalid (before April)?
err_msg = true; -- flag for error message
end
elseif id:match("^%d%d[01]%d%.%d%d%d%d%d$") or id:match("^%d%d[01]%d%.%d%d%d%d%dv%d+$") then -- test for the 1501- format with or without version
year, month = id:match("^(%d%d)([01]%d)%.%d%d%d%d%d[v%d]*$");
year = tonumber (year);
month = tonumber (month);
if ((15 > year) or (1 > month or 12 < month)) then -- is year invalid or is month invalid? (doesn't test for future years)
err_msg = true; -- flag for error message
end
else
err_msg = true; -- not a recognized format; flag for error message
end
if err_msg then
options.coins_list_t['ARXIV'] = nil; -- when error, unset so not included in COinS
end
local err_msg_t = {};
if err_msg then
set_message ('err_bad_arxiv');
end
text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = handler.access});
if is_set (class) then
if id:match ('^%d+') then
text = table.concat ({text, ' [[https://arxiv.org/archive/', class, ' ', class, ']]'}); -- external link within square brackets, not wikilink
else
set_message ('err_class_ignored');
end
end
return text;
end
--[[--------------------------< B I B C O D E >--------------------------------------------------------------------
Validates (sort of) and formats a bibcode ID.
Format for bibcodes is specified here: https://adsabs.harvard.edu/abs_doc/help_pages/data.html#bibcodes
But, this: 2015arXiv151206696F is apparently valid so apparently, the only things that really matter are length, 19 characters
and first four digits must be a year. This function makes these tests:
length must be 19 characters
characters in position
1–4 must be digits and must represent a year in the range of 1000 – next year
5 must be a letter
6–8 must be letter, digit, ampersand, or dot (ampersand cannot directly precede a dot; &. )
9–18 must be letter, digit, or dot
19 must be a letter or dot
]]
local function bibcode (options)
local id = options.id;
local access = options.access;
local handler = options.handler;
local ignore_invalid = options.accept;
local err_type;
local err_msg = '';
local year;
local text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode,
access = access});
if 19 ~= id:len() then
err_type = cfg.err_msg_supl.length;
else
year = id:match ("^(%d%d%d%d)[%a][%w&%.][%w&%.][%w&%.][%w.]+[%a%.]$");
if not year then -- if nil then no pattern match
err_type = cfg.err_msg_supl.value; -- so value error
else
local next_year = tonumber (os.date ('%Y')) + 1; -- get the current year as a number and add one for next year
year = tonumber (year); -- convert year portion of bibcode to a number
if (1000 > year) or (year > next_year) then
err_type = cfg.err_msg_supl.year; -- year out of bounds
end
if id:find('&%.') then
err_type = cfg.err_msg_supl.journal; -- journal abbreviation must not have '&.' (if it does it's missing a letter)
end
if id:match ('.........%.tmp%.') then -- temporary bibcodes when positions 10–14 are '.tmp.'
set_message ('maint_bibcode');
end
end
end
if is_set (err_type) and not ignore_invalid then -- if there was an error detected and accept-as-written markup not used
set_message ('err_bad_bibcode', {err_type});
options.coins_list_t['BIBCODE'] = nil; -- when error, unset so not included in COinS
end
return text;
end
--[[--------------------------< B I O R X I V >-----------------------------------------------------------------
Format bioRxiv ID and do simple error checking. Before 2019-12-11, biorXiv IDs were 10.1101/ followed by exactly
6 digits. After 2019-12-11, biorXiv IDs retained the six-digit identifier but prefixed that with a yyyy.mm.dd.
date and suffixed with an optional version identifier.
The bioRxiv ID is the string of characters:
https://doi.org/10.1101/078733 -> 10.1101/078733
or a date followed by a six-digit number followed by an optional version indicator 'v' and one or more digits:
https://www.biorxiv.org/content/10.1101/2019.12.11.123456v2 -> 10.1101/2019.12.11.123456v2
see https://www.biorxiv.org/about-biorxiv
]]
local function biorxiv (options)
local id = options.id;
local handler = options.handler;
local err_msg = true; -- flag; assume that there will be an error
local patterns = {
'^10%.1101/%d%d%d%d%d%d$', -- simple 6-digit identifier (before 2019-12-11)
'^10%.1101/(20%d%d)%.(%d%d)%.(%d%d)%.%d%d%d%d%d%dv%d+$', -- y.m.d. date + 6-digit identifier + version (after 2019-12-11)
'^10%.1101/(20%d%d)%.(%d%d)%.(%d%d)%.%d%d%d%d%d%d$', -- y.m.d. date + 6-digit identifier (after 2019-12-11)
}
for _, pattern in ipairs (patterns) do -- spin through the patterns looking for a match
if id:match (pattern) then
local y, m, d = id:match (pattern); -- found a match, attempt to get year, month and date from the identifier
if m then -- m is nil when id is the six-digit form
if not is_valid_rxiv_date (y, m, d, 'b') then -- validate the encoded date; 'b' for biorxiv limit
break; -- date fail; break out early so we don't unset the error message
end
end
err_msg = nil; -- we found a match so unset the error message
break; -- and done
end
end -- err_cat remains set here when no match
if err_msg then
options.coins_list_t['BIORXIV'] = nil; -- when error, unset so not included in COinS
set_message ('err_bad_biorxiv'); -- and set the error message
end
return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
prefix = handler.prefix, id = id, separator = handler.separator,
encode = handler.encode, access = handler.access});
end
--[[--------------------------< C I T E S E E R X >------------------------------------------------------------
CiteSeerX use their own notion of "doi" (not to be confused with the identifiers resolved via doi.org).
The description of the structure of this identifier can be found at Help_talk:Citation_Style_1/Archive_26#CiteSeerX_id_structure
]]
local function citeseerx (options)
local id = options.id;
local handler = options.handler;
local matched;
local text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode,
access = handler.access});
matched = id:match ("^10%.1%.1%.[1-9]%d?%d?%d?%.[1-9]%d?%d?%d?$");
if not matched then
set_message ('err_bad_citeseerx' );
options.coins_list_t['CITESEERX'] = nil; -- when error, unset so not included in COinS
end
return text;
end
--[[--------------------------< D O I >------------------------------------------------------------------------
Formats a DOI and checks for DOI errors.
DOI names contain two parts: prefix and suffix separated by a forward slash.
Prefix: directory indicator '10.' followed by a registrant code
Suffix: character string of any length chosen by the registrant
This function checks a DOI name for: prefix/suffix. If the DOI name contains spaces or endashes, or, if it ends
with a period or a comma, this function will emit a bad_doi error message.
DOI names are case-insensitive and can incorporate any printable Unicode characters so the test for spaces, endash,
and terminal punctuation may not be technically correct but it appears, that in practice these characters are rarely
if ever used in DOI names.
https://www.doi.org/doi_handbook/2_Numbering.html -- 2.2 Syntax of a DOI name
https://www.doi.org/doi_handbook/2_Numbering.html#2.2.2 -- 2.2.2 DOI prefix
]]
local function doi (options)
local id = options.id;
local inactive = options.DoiBroken
local access = options.access;
local ignore_invalid = options.accept;
local handler = options.handler;
local err_flag;
local function is_extended_free (registrant, suffix) -- local function to check those few registrants that are mixed; identifiable by the doi suffix <incipit>
if cfg.extended_registrants_t[registrant] then -- if this registrant has known free-to-read extentions
for _, incipit in ipairs (cfg.extended_registrants_t[registrant]) do -- loop through the registrant's incipits
if mw.ustring.find (suffix, '^' .. incipit) then -- if found
return true;
end
end
end
end
local text;
if is_set (inactive) then
local inactive_year = inactive:match("%d%d%d%d"); -- try to get the year portion from the inactive date
local inactive_month, good;
if is_set (inactive_year) then
if 4 < inactive:len() then -- inactive date has more than just a year (could be anything)
local lang_obj = mw.getContentLanguage(); -- get a language object for this wiki
good, inactive_month = pcall (lang_obj.formatDate, lang_obj, 'F', inactive); -- try to get the month name from the inactive date
if not good then
inactive_month = nil; -- something went wrong so make sure this is unset
end
end
end -- otherwise, |doi-broken-date= has something but it isn't a date
if is_set (inactive_year) and is_set (inactive_month) then
set_message ('maint_doi_inactive_dated', {inactive_year, inactive_month, ' '});
elseif is_set (inactive_year) then
set_message ('maint_doi_inactive_dated', {inactive_year, '', ''});
else
set_message ('maint_doi_inactive');
end
inactive = " (" .. cfg.messages['inactive'] .. ' ' .. inactive .. ')';
end
local suffix;
local registrant, suffix = mw.ustring.match (id, '^10%.([^/]+)/([^%s–]-[^%.,])$'); -- registrant and suffix set when DOI has the proper basic form
local registrant_err_patterns = { -- these patterns are for code ranges that are not supported
'^[^1-3]%d%d%d%d%.%d+$', -- 5 digits with subcode (0xxxx, 40000+); accepts: 10000–39999
'^[^1-7]%d%d%d%d$', -- 5 digits without subcode (0xxxx, 60000+); accepts: 10000–69999
'^[^1-9]%d%d%d%.%d+$', -- 4 digits with subcode (0xxx); accepts: 1000–9999
'^[^1-9]%d%d%d$', -- 4 digits without subcode (0xxx); accepts: 1000–9999
'^%d%d%d%d%d%d+', -- 6 or more digits
'^%d%d?%d?$', -- less than 4 digits without subcode (3 digits with subcode is legitimate)
'^%d%d?%.[%d%.]+', -- 1 or 2 digits with subcode
'^5555$', -- test registrant will never resolve
'[^%d%.]', -- any character that isn't a digit or a dot
}
if not ignore_invalid then
if registrant then -- when DOI has proper form
for i, pattern in ipairs (registrant_err_patterns) do -- spin through error patterns
if registrant:match (pattern) then -- to validate registrant codes
err_flag = set_message ('err_bad_doi'); -- when found, mark this DOI as bad
break; -- and done
end
end
else
err_flag = set_message ('err_bad_doi'); -- invalid directory or malformed
end
else
set_message ('maint_doi_ignore');
end
if err_flag then
options.coins_list_t['DOI'] = nil; -- when error, unset so not included in COinS
else
if not access and (cfg.known_free_doi_registrants_t[registrant] or is_extended_free (registrant, suffix)) then -- |doi-access=free not set and <registrant> is known to be free
set_message ('maint_doi_unflagged_free'); -- set a maint cat
end
end
text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = access,
auto_link = not (err_flag or is_set (inactive) or ignore_invalid) and 'doi' or nil -- do not auto-link when |doi-broken-date= has a value or when there is a DOI error or (to play it safe, after all, auto-linking is not essential) when invalid DOIs are ignored
}) .. (inactive or '');
return text;
end
--[[--------------------------< H D L >------------------------------------------------------------------------
Formats an HDL with minor error checking.
HDL names contain two parts: prefix and suffix separated by a forward slash.
Prefix: character string using any character in the UCS-2 character set except '/'
Suffix: character string of any length using any character in the UCS-2 character set chosen by the registrant
This function checks a HDL name for: prefix/suffix. If the HDL name contains spaces, endashes, or, if it ends
with a period or a comma, this function will emit a bad_hdl error message.
HDL names are case-insensitive and can incorporate any printable Unicode characters so the test for endashes and
terminal punctuation may not be technically correct but it appears, that in practice these characters are rarely
if ever used in HDLs.
Query string parameters are named here: https://www.handle.net/proxy_servlet.html. query strings are not displayed
but since '?' is an allowed character in an HDL, '?' followed by one of the query parameters is the only way we
have to detect the query string so that it isn't URL-encoded with the rest of the identifier.
]]
local function hdl (options)
local id = options.id;
local access = options.access;
local handler = options.handler;
local query_params = { -- list of known query parameters from https://www.handle.net/proxy_servlet.html
'noredirect',
'ignore_aliases',
'auth',
'cert',
'index',
'type',
'urlappend',
'locatt',
'action',
}
local hdl, suffix, param = id:match ('(.-)(%?(%a+).+)$'); -- look for query string
local found;
if hdl then -- when there are query strings, this is the handle identifier portion
for _, q in ipairs (query_params) do -- spin through the list of query parameters
if param:match ('^' .. q) then -- if the query string begins with one of the parameters
found = true; -- announce a find
break; -- and stop looking
end
end
end
if found then
id = hdl; -- found so replace id with the handle portion; this will be URL-encoded, suffix will not
else
suffix = ''; -- make sure suffix is empty string for concatenation else
end
local text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
prefix = handler.prefix, id = id, suffix = suffix, separator = handler.separator, encode = handler.encode, access = access})
if nil == id:match("^[^%s–]-/[^%s–]-[^%.,]$") then -- HDL must contain a forward slash, must not contain spaces, endashes, and must not end with period or comma
set_message ('err_bad_hdl' );
options.coins_list_t['HDL'] = nil; -- when error, unset so not included in COinS
end
return text;
end
--[[--------------------------< I S B N >----------------------------------------------------------------------
Determines whether an ISBN string is valid
]]
local function isbn (options_t)
local isbn_str = options_t.id;
local ignore_invalid = options_t.accept;
local handler = options_t.handler;
local year = options_t.Year; -- when set, valid anchor_year; may have a disambiguator which must be removed
local function return_result (check, err_type) -- local function to handle the various returns
local ISBN = internal_link_id ({link = handler.link, label = handler.label, redirect = handler.redirect,
prefix = handler.prefix, id = isbn_str, separator = handler.separator});
if ignore_invalid then -- if ignoring ISBN errors
set_message ('maint_isbn_ignore'); -- add a maint category even when there is no error
else -- here when not ignoring
if not check then -- and there is an error
options_t.coins_list_t['ISBN'] = nil; -- when error, unset so not included in COinS
set_message ('err_bad_isbn', err_type); -- set an error message
return ISBN; -- return id text
end
end
return ISBN; -- return id text
end
if year and not ignore_invalid then --
year = year:match ('%d%d%d%d?'); -- strip disambiguator if present
if year and (1965 > tonumber(year)) then
set_message ('err_invalid_isbn_date'); -- set an error message
return internal_link_id ({link = handler.link, label = handler.label, redirect = handler.redirect,
prefix = handler.prefix, id = isbn_str, separator = handler.separator});
end
end
if nil ~= isbn_str:match ('[^%s-0-9X]') then
return return_result (false, cfg.err_msg_supl.char); -- fail if isbn_str contains anything but digits, hyphens, or the uppercase X
end
local id = isbn_str:gsub ('[%s-]', ''); -- remove hyphens and whitespace
local len = id:len();
if len ~= 10 and len ~= 13 then
return return_result (false, cfg.err_msg_supl.length); -- fail if incorrect length
end
if len == 10 then
if id:match ('^%d*X?$') == nil then -- fail if isbn_str has 'X' anywhere but last position
return return_result (false, cfg.err_msg_supl.form);
end
if not is_valid_isxn (id, 10) then -- test isbn-10 for numerical validity
return return_result (false, cfg.err_msg_supl.check); -- fail if isbn-10 is not numerically valid
end
if id:find ('^63[01]') then -- 630xxxxxxx and 631xxxxxxx are (apparently) not valid isbn group ids but are used by amazon as numeric identifiers (asin)
return return_result (false, cfg.err_msg_supl.group); -- fail if isbn-10 begins with 630/1
end
return return_result (true, cfg.err_msg_supl.check); -- pass if isbn-10 is numerically valid
else
if id:match ('^%d+$') == nil then
return return_result (false, cfg.err_msg_supl.char); -- fail if ISBN-13 is not all digits
end
if id:match ('^97[89]%d*$') == nil then
return return_result (false, cfg.err_msg_supl.prefix); -- fail when ISBN-13 does not begin with 978 or 979
end
if id:match ('^9790') then
return return_result (false, cfg.err_msg_supl.group); -- group identifier '0' is reserved to ISMN
end
return return_result (is_valid_isxn_13 (id), cfg.err_msg_supl.check);
end
end
--[[--------------------------< A S I N >----------------------------------------------------------------------
Formats a link to Amazon. Do simple error checking: ASIN must be mix of 10 numeric or uppercase alpha
characters. If a mix, first character must be uppercase alpha; if all numeric, ASINs must be 10-digit
ISBN. If 10-digit ISBN, add a maintenance category so a bot or AWB script can replace |asin= with |isbn=.
Error message if not 10 characters, if not ISBN-10, if mixed and first character is a digit.
|asin=630....... and |asin=631....... are (apparently) not a legitimate ISBN though it checksums as one; these
do not cause this function to emit the maint_asin message
This function is positioned here because it calls isbn()
]]
local function asin (options)
local id = options.id;
local domain = options.ASINTLD;
local err_flag;
if not id:match("^[%d%u][%d%u][%d%u][%d%u][%d%u][%d%u][%d%u][%d%u][%d%u][%d%u]$") then
err_flag = set_message ('err_bad_asin'); -- ASIN is not a mix of 10 uppercase alpha and numeric characters
else
if id:match("^%d%d%d%d%d%d%d%d%d[%dX]$") then -- if 10-digit numeric (or 9 digits with terminal X)
if is_valid_isxn (id, 10) then -- see if ASIN value is or validates as ISBN-10
if not id:find ('^63[01]') then -- 630xxxxxxx and 631xxxxxxx are (apparently) not a valid isbn prefixes but are used by amazon as a numeric identifier
err_flag = set_message ('err_bad_asin'); -- ASIN has ISBN-10 form but begins with something other than 630/1 so probably an isbn
end
elseif not is_set (err_flag) then
err_flag = set_message ('err_bad_asin'); -- ASIN is not ISBN-10
end
elseif not id:match("^%u[%d%u]+$") then
err_flag = set_message ('err_bad_asin'); -- asin doesn't begin with uppercase alpha
end
end
if (not is_set (domain)) or in_array (domain, {'us'}) then -- default: United States
domain = "com";
elseif in_array (domain, {'jp', 'uk'}) then -- Japan, United Kingdom
domain = "co." .. domain;
elseif in_array (domain, {'z.cn'}) then -- China
domain = "cn";
elseif in_array (domain, {'au', 'br', 'mx', 'sg', 'tr'}) then -- Australia, Brazil, Mexico, Singapore, Turkey
domain = "com." .. domain;
elseif not in_array (domain, {'ae', 'ca', 'cn', 'de', 'es', 'fr', 'in', 'it', 'nl', 'pl', 'sa', 'se', 'co.jp', 'co.uk', 'com', 'com.au', 'com.br', 'com.mx', 'com.sg', 'com.tr'}) then -- Arabic Emirates, Canada, China, Germany, Spain, France, Indonesia, Italy, Netherlands, Poland, Saudi Arabia, Sweden (as of 2021-03 Austria (.at), Liechtenstein (.li) and Switzerland (.ch) still redirect to the German site (.de) with special settings, so don't maintain local ASINs for them)
err_flag = set_message ('err_bad_asin_tld'); -- unsupported asin-tld value
end
local handler = options.handler;
if not is_set (err_flag) then
options.coins_list_t['ASIN'] = handler.prefix .. domain .. "/dp/" .. id; -- asin for coins
else
options.coins_list_t['ASIN'] = nil; -- when error, unset so not included in COinS
end
return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
prefix = handler.prefix .. domain .. "/dp/",
id = id, encode = handler.encode, separator = handler.separator})
end
--[[--------------------------< I S M N >----------------------------------------------------------------------
Determines whether an ISMN string is valid. Similar to ISBN-13, ISMN is 13 digits beginning 979-0-... and uses the
same check digit calculations. See https://www.ismn-international.org/download/Web_ISMN_Users_Manual_2008-6.pdf
section 2, pages 9–12.
ismn value not made part of COinS metadata because we don't have a url or isn't a COinS-defined identifier (rft.xxx)
or an identifier registered at info-uri.info (info:)
]]
local function ismn (options)
local id = options.id;
local handler = options.handler;
local text;
local valid_ismn = true;
local id_copy;
id_copy = id; -- save a copy because this testing is destructive
id = id:gsub ('[%s-]', ''); -- remove hyphens and white space
if 13 ~= id:len() or id:match ("^9790%d*$" ) == nil then -- ISMN must be 13 digits and begin with 9790
valid_ismn = false;
else
valid_ismn=is_valid_isxn_13 (id); -- validate ISMN
end
-- text = internal_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, -- use this (or external version) when there is some place to link to
-- prefix = handler.prefix, id = id_copy, separator = handler.separator, encode = handler.encode})
text = table.concat ( -- because no place to link to yet
{
make_wikilink (link_label_make (handler), handler.label),
handler.separator,
id_copy
});
if false == valid_ismn then
options.coins_list_t['ISMN'] = nil; -- when error, unset so not included in COinS; not really necessary here because ismn not made part of COinS
set_message ('err_bad_ismn'); -- create an error message if the ISMN is invalid
end
return text;
end
--[[--------------------------< I S S N >----------------------------------------------------------------------
Validate and format an ISSN. This code fixes the case where an editor has included an ISSN in the citation but
has separated the two groups of four digits with a space. When that condition occurred, the resulting link looked
like this:
|issn=0819 4327 gives: [https://www.worldcat.org/issn/0819 4327 0819 4327] -- can't have spaces in an external link
This code now prevents that by inserting a hyphen at the ISSN midpoint. It also validates the ISSN for length
and makes sure that the checkdigit agrees with the calculated value. Incorrect length (8 digits), characters
other than 0-9 and X, or checkdigit / calculated value mismatch will all cause a check ISSN error message. The
ISSN is always displayed with a hyphen, even if the ISSN was given as a single group of 8 digits.
]]
local function issn (options)
local id = options.id;
local handler = options.handler;
local ignore_invalid = options.accept;
local issn_copy = id; -- save a copy of unadulterated ISSN; use this version for display if ISSN does not validate
local text;
local valid_issn = true;
id = id:gsub ('[%s-]', ''); -- remove hyphens and whitespace
if 8 ~= id:len() or nil == id:match ("^%d*X?$" ) then -- validate the ISSN: 8 digits long, containing only 0-9 or X in the last position
valid_issn = false; -- wrong length or improper character
else
valid_issn = is_valid_isxn (id, 8); -- validate ISSN
end
if true == valid_issn then
id = string.sub (id, 1, 4 ) .. "-" .. string.sub (id, 5 ); -- if valid, display correctly formatted version
else
id = issn_copy; -- if not valid, show the invalid ISSN with error message
end
text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode})
if ignore_invalid then
set_message ('maint_issn_ignore');
else
if false == valid_issn then
options.coins_list_t['ISSN'] = nil; -- when error, unset so not included in COinS
set_message ('err_bad_issn', (options.hkey == 'EISSN') and 'e' or ''); -- create an error message if the ISSN is invalid
end
end
return text;
end
--[[--------------------------< J F M >-----------------------------------------------------------------------
A numerical identifier in the form nn.nnnn.nn
]]
local function jfm (options)
local id = options.id;
local handler = options.handler;
local id_num;
id_num = id:match ('^[Jj][Ff][Mm](.*)$'); -- identifier with jfm prefix; extract identifier
if is_set (id_num) then
set_message ('maint_jfm_format');
else -- plain number without JFM prefix
id_num = id; -- if here id does not have prefix
end
if id_num and id_num:match('^%d%d%.%d%d%d%d%.%d%d$') then
id = id_num; -- jfm matches pattern
else
set_message ('err_bad_jfm' ); -- set an error message
options.coins_list_t['JFM'] = nil; -- when error, unset so not included in COinS
end
return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode});
end
--[[--------------------------< J S T O R >--------------------------------------------------------------------
Format a JSTOR with some error checking
]]
local function jstor (options)
local id = options.id;
local access = options.access;
local handler = options.handler;
if id:find ('[Jj][Ss][Tt][Oo][Rr]') or id:find ('^https?://') or id:find ('%s') then
set_message ('err_bad_jstor'); -- set an error message
options.coins_list_t['JSTOR'] = nil; -- when error, unset so not included in COinS
end
return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = access});
end
--[[--------------------------< L C C N >----------------------------------------------------------------------
Format LCCN link and do simple error checking. LCCN is a character string 8-12 characters long. The length of
the LCCN dictates the character type of the first 1-3 characters; the rightmost eight are always digits.
https://oclc-research.github.io/infoURI-Frozen/info-uri.info/info:lccn/reg.html
length = 8 then all digits
length = 9 then lccn[1] is lowercase alpha
length = 10 then lccn[1] and lccn[2] are both lowercase alpha or both digits
length = 11 then lccn[1] is lower case alpha, lccn[2] and lccn[3] are both lowercase alpha or both digits
length = 12 then lccn[1] and lccn[2] are both lowercase alpha
]]
local function lccn (options)
local lccn = options.id;
local handler = options.handler;
local err_flag; -- presume that LCCN is valid
local id = lccn; -- local copy of the LCCN
id = normalize_lccn (id); -- get canonical form (no whitespace, hyphens, forward slashes)
local len = id:len(); -- get the length of the LCCN
if 8 == len then
if id:match("[^%d]") then -- if LCCN has anything but digits (nil if only digits)
err_flag = set_message ('err_bad_lccn'); -- set an error message
end
elseif 9 == len then -- LCCN should be adddddddd
if nil == id:match("%l%d%d%d%d%d%d%d%d") then -- does it match our pattern?
err_flag = set_message ('err_bad_lccn'); -- set an error message
end
elseif 10 == len then -- LCCN should be aadddddddd or dddddddddd
if id:match("[^%d]") then -- if LCCN has anything but digits (nil if only digits) ...
if nil == id:match("^%l%l%d%d%d%d%d%d%d%d") then -- ... see if it matches our pattern
err_flag = set_message ('err_bad_lccn'); -- no match, set an error message
end
end
elseif 11 == len then -- LCCN should be aaadddddddd or adddddddddd
if not (id:match("^%l%l%l%d%d%d%d%d%d%d%d") or id:match("^%l%d%d%d%d%d%d%d%d%d%d")) then -- see if it matches one of our patterns
err_flag = set_message ('err_bad_lccn'); -- no match, set an error message
end
elseif 12 == len then -- LCCN should be aadddddddddd
if not id:match("^%l%l%d%d%d%d%d%d%d%d%d%d") then -- see if it matches our pattern
err_flag = set_message ('err_bad_lccn'); -- no match, set an error message
end
else
err_flag = set_message ('err_bad_lccn'); -- wrong length, set an error message
end
if not is_set (err_flag) and nil ~= lccn:find ('%s') then
err_flag = set_message ('err_bad_lccn'); -- lccn contains a space, set an error message
end
if is_set (err_flag) then
options.coins_list_t['LCCN'] = nil; -- when error, unset so not included in COinS
end
return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
prefix = handler.prefix, id = lccn, separator = handler.separator, encode = handler.encode});
end
--[[--------------------------< M E D R X I V >-----------------------------------------------------------------
Format medRxiv ID and do simple error checking. Similar to later bioRxiv IDs, medRxiv IDs are prefixed with a
yyyy.mm.dd. date and suffixed with an optional version identifier. Ealiest date accepted is 2020.01.01
The medRxiv ID is a date followed by an eight-digit number followed by an optional version indicator 'v' and one or more digits:
https://www.medrxiv.org/content/10.1101/2020.11.16.20232009v2 -> 10.1101/2020.11.16.20232009v2
]]
local function medrxiv (options)
local id = options.id;
local handler = options.handler;
local err_msg_flag = true; -- flag; assume that there will be an error
local patterns = {
'%d%d%d%d%d%d%d%d$', -- simple 8-digit identifier; these should be relatively rare
'^10%.1101/(20%d%d)%.(%d%d)%.(%d%d)%.%d%d%d%d%d%d%d%dv%d+$', -- y.m.d. date + 8-digit identifier + version (2020-01-01 and later)
'^10%.1101/(20%d%d)%.(%d%d)%.(%d%d)%.%d%d%d%d%d%d%d%d$', -- y.m.d. date + 8-digit identifier (2020-01-01 and later)
}
for _, pattern in ipairs (patterns) do -- spin through the patterns looking for a match
if id:match (pattern) then
local y, m, d = id:match (pattern); -- found a match, attempt to get year, month and date from the identifier
if m then -- m is nil when id is the 8-digit form
if not is_valid_rxiv_date (y, m, d, 'b') then -- validate the encoded date; 'b' for medrxiv limit
break; -- date fail; break out early so we don't unset the error message
end
end
err_msg_flag = nil; -- we found a match so unset the error message
break; -- and done
end
end -- <err_msg_flag> remains set here when no match
if err_msg_flag then
options.coins_list_t['MEDRXIV'] = nil; -- when error, unset so not included in COinS
set_message ('err_bad_medrxiv'); -- and set the error message
end
return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
prefix = handler.prefix, id = id, separator = handler.separator,
encode = handler.encode, access = handler.access});
end
--[[--------------------------< M R >--------------------------------------------------------------------------
A seven digit number; if not seven digits, zero-fill leading digits to make seven digits.
]]
local function mr (options)
local id = options.id;
local handler = options.handler;
local id_num;
local id_len;
id_num = id:match ('^[Mm][Rr](%d+)$'); -- identifier with mr prefix
if is_set (id_num) then
set_message ('maint_mr_format'); -- add maint cat
else -- plain number without mr prefix
id_num = id:match ('^%d+$'); -- if here id is all digits
end
id_len = id_num and id_num:len() or 0;
if (7 >= id_len) and (0 ~= id_len) then
id = string.rep ('0', 7-id_len) .. id_num; -- zero-fill leading digits
else
set_message ('err_bad_mr'); -- set an error message
options.coins_list_t['MR'] = nil; -- when error, unset so not included in COinS
end
return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode});
end
--[[--------------------------< O C L C >----------------------------------------------------------------------
Validate and format an OCLC ID. https://www.oclc.org/batchload/controlnumber.en.html {{dead link}}
archived at: https://web.archive.org/web/20161228233804/https://www.oclc.org/batchload/controlnumber.en.html
]]
local function oclc (options)
local id = options.id;
local handler = options.handler;
local number;
if id:match('^ocm%d%d%d%d%d%d%d%d$') then -- ocm prefix and 8 digits; 001 field (12 characters)
number = id:match('ocm(%d+)'); -- get the number
elseif id:match('^ocn%d%d%d%d%d%d%d%d%d$') then -- ocn prefix and 9 digits; 001 field (12 characters)
number = id:match('ocn(%d+)'); -- get the number
elseif id:match('^on%d%d%d%d%d%d%d%d%d%d+$') then -- on prefix and 10 or more digits; 001 field (12 characters)
number = id:match('^on(%d%d%d%d%d%d%d%d%d%d+)$'); -- get the number
elseif id:match('^%(OCoLC%)[1-9]%d*$') then -- (OCoLC) prefix and variable number digits; no leading zeros; 035 field
number = id:match('%(OCoLC%)([1-9]%d*)'); -- get the number
if 9 < number:len() then
number = nil; -- constrain to 1 to 9 digits; change this when OCLC issues 10-digit numbers
end
elseif id:match('^%d+$') then -- no prefix
number = id; -- get the number
if tonumber (id) > handler.id_limit then
number = nil; -- unset when id value exceeds the limit
end
end
if number then -- proper format
id = number; -- exclude prefix, if any, from external link
else
set_message ('err_bad_oclc') -- add an error message if the id is malformed
options.coins_list_t['OCLC'] = nil; -- when error, unset so not included in COinS
end
return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode});
end
--[[--------------------------< O P E N L I B R A R Y >--------------------------------------------------------
Formats an OpenLibrary link, and checks for associated errors.
]]
local function openlibrary (options)
local id = options.id;
local access = options.access;
local handler = options.handler;
local ident, code = id:gsub('^OL', ''):match("^(%d+([AMW]))$"); -- strip optional OL prefix followed immediately by digits followed by 'A', 'M', or 'W';
local err_flag;
local prefix = { -- these are appended to the handler.prefix according to code
['A']='authors/OL',
['M']='books/OL',
['W']='works/OL',
['X']='OL' -- not a code; spoof when 'code' in id is invalid
};
if not ident then
code = 'X'; -- no code or id completely invalid
ident = id; -- copy id to ident so that we display the flawed identifier
err_flag = set_message ('err_bad_ol');
end
if not is_set (err_flag) then
options.coins_list_t['OL'] = handler.prefix .. prefix[code] .. ident; -- experiment for ol coins
else
options.coins_list_t['OL'] = nil; -- when error, unset so not included in COinS
end
return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
prefix = handler.prefix .. prefix[code],
id = ident, separator = handler.separator, encode = handler.encode,
access = access});
end
--[[--------------------------< O S T I >----------------------------------------------------------------------
Format OSTI and do simple error checking. OSTIs are sequential numbers beginning at 1 and counting up. This
code checks the OSTI to see that it contains only digits and is less than test_limit specified in the configuration;
the value in test_limit will need to be updated periodically as more OSTIs are issued.
NB. 1018 is the lowest OSTI number found in the wild (so far) and resolving OK on the OSTI site
]]
local function osti (options)
local id = options.id;
local access = options.access;
local handler = options.handler;
if id:match("[^%d]") then -- if OSTI has anything but digits
set_message ('err_bad_osti'); -- set an error message
options.coins_list_t['OSTI'] = nil; -- when error, unset so not included in COinS
else -- OSTI is only digits
local id_num = tonumber (id); -- convert id to a number for range testing
if 1018 > id_num or handler.id_limit < id_num then -- if OSTI is outside test limit boundaries
set_message ('err_bad_osti'); -- set an error message
options.coins_list_t['OSTI'] = nil; -- when error, unset so not included in COinS
end
end
return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = access});
end
--[[--------------------------< P M C >------------------------------------------------------------------------
Format a PMC, do simple error checking, and check for embargoed articles.
The embargo parameter takes a date for a value. If the embargo date is in the future the PMC identifier will not
be linked to the article. If the embargo date is today or in the past, or if it is empty or omitted, then the
PMC identifier is linked to the article through the link at cfg.id_handlers['PMC'].prefix.
PMC embargo date testing is done in function is_embargoed () which is called earlier because when the citation
has |pmc=<value> but does not have a |url= then |title= is linked with the PMC link. Function is_embargoed ()
returns the embargo date if the PMC article is still embargoed, otherwise it returns an empty string.
PMCs are sequential numbers beginning at 1 and counting up. This code checks the PMC to see that it contains only digits and is less
than test_limit; the value in local variable test_limit will need to be updated periodically as more PMCs are issued.
]]
local function pmc (options)
local id = options.id;
local embargo = options.Embargo; -- TODO: lowercase?
local handler = options.handler;
local err_flag;
local id_num;
local text;
id_num = id:match ('^[Pp][Mm][Cc](%d+)$'); -- identifier with PMC prefix
if is_set (id_num) then
set_message ('maint_pmc_format');
else -- plain number without PMC prefix
id_num = id:match ('^%d+$'); -- if here id is all digits
end
if is_set (id_num) then -- id_num has a value so test it
id_num = tonumber (id_num); -- convert id_num to a number for range testing
if 1 > id_num or handler.id_limit < id_num then -- if PMC is outside test limit boundaries
err_flag = set_message ('err_bad_pmc'); -- set an error message
else
id = tostring (id_num); -- make sure id is a string
end
else -- when id format incorrect
err_flag = set_message ('err_bad_pmc'); -- set an error message
end
if is_set (embargo) and is_set (is_embargoed (embargo)) then -- is PMC is still embargoed?
text = table.concat ( -- still embargoed so no external link
{
make_wikilink (link_label_make (handler), handler.label),
handler.separator,
id,
});
else
text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, -- no embargo date or embargo has expired, ok to link to article
prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = handler.access,
auto_link = not err_flag and 'pmc' or nil -- do not auto-link when PMC has error
});
end
if err_flag then
options.coins_list_t['PMC'] = nil; -- when error, unset so not included in COinS
end
return text;
end
--[[--------------------------< P M I D >----------------------------------------------------------------------
Format PMID and do simple error checking. PMIDs are sequential numbers beginning at 1 and counting up. This
code checks the PMID to see that it contains only digits and is less than test_limit; the value in local variable
test_limit will need to be updated periodically as more PMIDs are issued.
]]
local function pmid (options)
local id = options.id;
local handler = options.handler;
if id:match("[^%d]") then -- if PMID has anything but digits
set_message ('err_bad_pmid'); -- set an error message
options.coins_list_t['PMID'] = nil; -- when error, unset so not included in COinS
else -- PMID is only digits
local id_num = tonumber (id); -- convert id to a number for range testing
if 1 > id_num or handler.id_limit < id_num then -- if PMID is outside test limit boundaries
set_message ('err_bad_pmid'); -- set an error message
options.coins_list_t['PMID'] = nil; -- when error, unset so not included in COinS
end
end
return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode});
end
--[[--------------------------< R F C >------------------------------------------------------------------------
Format RFC and do simple error checking. RFCs are sequential numbers beginning at 1 and counting up. This
code checks the RFC to see that it contains only digits and is less than test_limit specified in the configuration;
the value in test_limit will need to be updated periodically as more RFCs are issued.
An index of all RFCs is here: https://tools.ietf.org/rfc/
]]
local function rfc (options)
local id = options.id;
local handler = options.handler;
if id:match("[^%d]") then -- if RFC has anything but digits
set_message ('err_bad_rfc'); -- set an error message
options.coins_list_t['RFC'] = nil; -- when error, unset so not included in COinS
else -- RFC is only digits
local id_num = tonumber (id); -- convert id to a number for range testing
if 1 > id_num or handler.id_limit < id_num then -- if RFC is outside test limit boundaries
set_message ('err_bad_rfc'); -- set an error message
options.coins_list_t['RFC'] = nil; -- when error, unset so not included in COinS
end
end
return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = handler.access});
end
--[[--------------------------< S 2 C I D >--------------------------------------------------------------------
Format an S2CID, do simple error checking
S2CIDs are sequential numbers beginning at 1 and counting up. This code checks the S2CID to see that it is only
digits and is less than test_limit; the value in local variable test_limit will need to be updated periodically
as more S2CIDs are issued.
]]
local function s2cid (options)
local id = options.id;
local access = options.access;
local handler = options.handler;
local id_num;
local text;
id_num = id:match ('^[1-9]%d*$'); -- id must be all digits; must not begin with 0; no open access flag
if is_set (id_num) then -- id_num has a value so test it
id_num = tonumber (id_num); -- convert id_num to a number for range testing
if handler.id_limit < id_num then -- if S2CID is outside test limit boundaries
set_message ('err_bad_s2cid'); -- set an error message
options.coins_list_t['S2CID'] = nil; -- when error, unset so not included in COinS
end
else -- when id format incorrect
set_message ('err_bad_s2cid'); -- set an error message
options.coins_list_t['S2CID'] = nil; -- when error, unset so not included in COinS
end
text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = access});
return text;
end
--[[--------------------------< S B N >------------------------------------------------------------------------
9-digit form of ISBN-10; uses same check-digit validation when SBN is prefixed with an additional '0' to make 10 digits
sbn value not made part of COinS metadata because we don't have a url or isn't a COinS-defined identifier (rft.xxx)
or an identifier registered at info-uri.info (info:)
]]
local function sbn (options)
local id = options.id;
local ignore_invalid = options.accept;
local handler = options.handler;
local function return_result (check, err_type) -- local function to handle the various returns
local SBN = internal_link_id ({link = handler.link, label = handler.label, redirect = handler.redirect,
prefix = handler.prefix, id = id, separator = handler.separator});
if not ignore_invalid then -- if not ignoring SBN errors
if not check then
options.coins_list_t['SBN'] = nil; -- when error, unset so not included in COinS; not really necessary here because sbn not made part of COinS
set_message ('err_bad_sbn', {err_type}); -- display an error message
return SBN;
end
else
set_message ('maint_isbn_ignore'); -- add a maint category even when there is no error (ToDo: Possibly switch to separate message for SBNs only)
end
return SBN;
end
if id:match ('[^%s-0-9X]') then
return return_result (false, cfg.err_msg_supl.char); -- fail if SBN contains anything but digits, hyphens, or the uppercase X
end
local ident = id:gsub ('[%s-]', ''); -- remove hyphens and whitespace; they interfere with the rest of the tests
if 9 ~= ident:len() then
return return_result (false, cfg.err_msg_supl.length); -- fail if incorrect length
end
if ident:match ('^%d*X?$') == nil then
return return_result (false, cfg.err_msg_supl.form); -- fail if SBN has 'X' anywhere but last position
end
return return_result (is_valid_isxn ('0' .. ident, 10), cfg.err_msg_supl.check);
end
--[[--------------------------< S S R N >----------------------------------------------------------------------
Format an SSRN, do simple error checking
SSRNs are sequential numbers beginning at 100? and counting up. This code checks the SSRN to see that it is
only digits and is greater than 99 and less than test_limit; the value in local variable test_limit will need
to be updated periodically as more SSRNs are issued.
]]
local function ssrn (options)
local id = options.id;
local handler = options.handler;
local id_num;
local text;
id_num = id:match ('^%d+$'); -- id must be all digits
if is_set (id_num) then -- id_num has a value so test it
id_num = tonumber (id_num); -- convert id_num to a number for range testing
if 100 > id_num or handler.id_limit < id_num then -- if SSRN is outside test limit boundaries
set_message ('err_bad_ssrn'); -- set an error message
options.coins_list_t['SSRN'] = nil; -- when error, unset so not included in COinS
end
else -- when id format incorrect
set_message ('err_bad_ssrn'); -- set an error message
options.coins_list_t['SSRN'] = nil; -- when error, unset so not included in COinS
end
text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = options.access});
return text;
end
--[[--------------------------< U S E N E T _ I D >------------------------------------------------------------
Validate and format a usenet message id. Simple error checking, looks for 'id-left@id-right' not enclosed in
'<' and/or '>' angle brackets.
]]
local function usenet_id (options)
local id = options.id;
local handler = options.handler;
local text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode})
if not id:match('^.+@.+$') or not id:match('^[^<].*[^>]$') then -- doesn't have '@' or has one or first or last character is '< or '>'
set_message ('err_bad_usenet_id') -- add an error message if the message id is invalid
options.coins_list_t['USENETID'] = nil; -- when error, unset so not included in COinS
end
return text;
end
--[[--------------------------< Z B L >-----------------------------------------------------------------------
A numerical identifier in the form nnnn.nnnnn - leading zeros in the first quartet optional
format described here: http://emis.mi.sanu.ac.rs/ZMATH/zmath/en/help/search/
temporary format is apparently eight digits. Anything else is an error
]]
local function zbl (options)
local id = options.id;
local handler = options.handler;
if id:match('^%d%d%d%d%d%d%d%d$') then -- is this identifier using temporary format?
set_message ('maint_zbl'); -- yes, add maint cat
elseif not id:match('^%d?%d?%d?%d%.%d%d%d%d%d$') then -- not temporary, is it normal format?
set_message ('err_bad_zbl'); -- no, set an error message
options.coins_list_t['ZBL'] = nil; -- when error, unset so not included in COinS
end
return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode});
end
--============================<< I N T E R F A C E F U N C T I O N S >>==========================================
--[[--------------------------< E X T R A C T _ I D S >------------------------------------------------------------
Populates ID table from arguments using configuration settings. Loops through cfg.id_handlers and searches args for
any of the parameters listed in each cfg.id_handlers['...'].parameters. If found, adds the parameter and value to
the identifier list. Emits redundant error message if more than one alias exists in args
]]
local function extract_ids (args)
local id_list = {}; -- list of identifiers found in args
for k, v in pairs (cfg.id_handlers) do -- k is uppercase identifier name as index to cfg.id_handlers; e.g. cfg.id_handlers['ISBN'], v is a table
v = select_one (args, v.parameters, 'err_redundant_parameters' ); -- v.parameters is a table of aliases for k; here we pick one from args if present
if is_set (v) then id_list[k] = v; end -- if found in args, add identifier to our list
end
return id_list;
end
--[[--------------------------< E X T R A C T _ I D _ A C C E S S _ L E V E L S >--------------------------------------
Fetches custom id access levels from arguments using configuration settings. Parameters which have a predefined access
level (e.g. arxiv) do not use this function as they are directly rendered as free without using an additional parameter.
returns a table of k/v pairs where k is same as the identifier's key in cfg.id_handlers and v is the assigned (valid) keyword
access-level values must match the case used in cfg.keywords_lists['id-access'] (lowercase unless there is some special reason for something else)
]]
local function extract_id_access_levels (args, id_list)
local id_accesses_list = {};
for k, v in pairs (cfg.id_handlers) do
local access_param = v.custom_access; -- name of identifier's access-level parameter
if is_set (access_param) then
local access_level = args[access_param]; -- get the assigned value if there is one
if is_set (access_level) then
if not in_array (access_level, cfg.keywords_lists['id-access']) then -- exact match required
set_message ('err_invalid_param_val', {access_param, access_level});
access_level = nil; -- invalid so unset
end
if not is_set (id_list[k]) then -- identifier access-level must have a matching identifier
set_message ('err_param_access_requires_param', {k:lower()}); -- parameter name is uppercase in cfg.id_handlers (k); lowercase for error message
end
id_accesses_list[k] = cfg.keywords_xlate[access_level]; -- get translated keyword
end
end
end
return id_accesses_list;
end
--[[--------------------------< B U I L D _ I D _ L I S T >----------------------------------------------------
render the identifiers into a sorted sequence table
<ID_list_coins_t> is a table of k/v pairs where k is same as key in cfg.id_handlers and v is the assigned value
<options_t> is a table of various k/v option pairs provided in the call to new_build_id_list();
modified by this function and passed to all identifier rendering functions
<access_levels_t> is a table of k/v pairs where k is same as key in cfg.id_handlers and v is the assigned value (if valid)
returns a sequence table of sorted (by hkey - 'handler' key) rendered identifier strings
]]
local function build_id_list (ID_list_coins_t, options_t, access_levels_t)
local ID_list_t = {};
local accept;
local func_map = { --function map points to functions associated with hkey identifier
['ARXIV'] = arxiv,
['ASIN'] = asin,
['BIBCODE'] = bibcode,
['BIORXIV'] = biorxiv,
['CITESEERX'] = citeseerx,
['DOI'] = doi,
['EISSN'] = issn,
['HDL'] = hdl,
['ISBN'] = isbn,
['ISMN'] = ismn,
['ISSN'] = issn,
['JFM'] = jfm,
['JSTOR'] = jstor,
['LCCN'] = lccn,
['MEDRXIV'] = medrxiv,
['MR'] = mr,
['OCLC'] = oclc,
['OL'] = openlibrary,
['OSTI'] = osti,
['PMC'] = pmc,
['PMID'] = pmid,
['RFC'] = rfc,
['S2CID'] = s2cid,
['SBN'] = sbn,
['SSRN'] = ssrn,
['USENETID'] = usenet_id,
['ZBL'] = zbl,
}
for hkey, v in pairs (ID_list_coins_t) do
v, accept = has_accept_as_written (v); -- remove accept-as-written markup if present; accept is boolean true when markup removed; false else
-- every function gets the options table with value v and accept boolean
options_t.hkey = hkey; -- ~/Configuration handler key
options_t.id = v; -- add that identifier value to the options table
options_t.accept = accept; -- add the accept boolean flag
options_t.access = access_levels_t[hkey]; -- add the access level for those that have an |<identifier-access= parameter
options_t.handler = cfg.id_handlers[hkey];
options_t.coins_list_t = ID_list_coins_t; -- pointer to ID_list_coins_t; for |asin= and |ol=; also to keep erroneous values out of the citation's metadata
options_t.coins_list_t[hkey] = v; -- id value without accept-as-written markup for metadata
if options_t.handler.access and not in_array (options_t.handler.access, cfg.keywords_lists['id-access']) then
error (cfg.messages['unknown_ID_access'] .. options_t.handler.access); -- here when handler access key set to a value not listed in list of allowed id access keywords
end
if func_map[hkey] then
local id_text = func_map[hkey] (options_t); -- call the function to get identifier text and any error message
table.insert (ID_list_t, {hkey, id_text}); -- add identifier text to the output sequence table
else
error (cfg.messages['unknown_ID_key'] .. hkey); -- here when func_map doesn't have a function for hkey
end
end
local function comp (a, b) -- used by following table.sort()
return a[1]:lower() < b[1]:lower(); -- sort by hkey
end
table.sort (ID_list_t, comp); -- sequence table of tables sort
for k, v in ipairs (ID_list_t) do -- convert sequence table of tables to simple sequence table of strings
ID_list_t[k] = v[2]; -- v[2] is the identifier rendering from the call to the various functions in func_map{}
end
return ID_list_t;
end
--[[--------------------------< O P T I O N S _ C H E C K >----------------------------------------------------
check that certain option parameters have their associated identifier parameters with values
<ID_list_coins_t> is a table of k/v pairs where k is same as key in cfg.id_handlers and v is the assigned value
<ID_support_t> is a sequence table of tables created in citation0() where each subtable has four elements:
[1] is the support parameter's assigned value; empty string if not set
[2] is a text string same as key in cfg.id_handlers
[3] is cfg.error_conditions key used to create error message
[4] is original ID support parameter name used to create error message
returns nothing; on error emits an appropriate error message
]]
local function options_check (ID_list_coins_t, ID_support_t)
for _, v in ipairs (ID_support_t) do
if is_set (v[1]) and not ID_list_coins_t[v[2]] then -- when support parameter has a value but matching identifier parameter is missing or empty
set_message (v[3], (v[4])); -- emit the appropriate error message
end
end
end
--[[--------------------------< I D E N T I F I E R _ L I S T S _ G E T >--------------------------------------
Creates two identifier lists: a k/v table of identifiers and their values to be used locally and for use in the
COinS metadata, and a sequence table of the rendered identifier strings that will be included in the rendered
citation.
]]
local function identifier_lists_get (args_t, options_t, ID_support_t)
local ID_list_coins_t = extract_ids (args_t); -- get a table of identifiers and their values for use locally and for use in COinS
options_check (ID_list_coins_t, ID_support_t); -- ID support parameters must have matching identifier parameters
local ID_access_levels_t = extract_id_access_levels (args_t, ID_list_coins_t); -- get a table of identifier access levels
local ID_list_t = build_id_list (ID_list_coins_t, options_t, ID_access_levels_t); -- get a sequence table of rendered identifier strings
return ID_list_t, ID_list_coins_t; -- return the tables
end
--[[--------------------------< S E T _ S E L E C T E D _ M O D U L E S >--------------------------------------
Sets local cfg table and imported functions table to same (live or sandbox) as that used by the other modules.
]]
local function set_selected_modules (cfg_table_ptr, utilities_page_ptr)
cfg = cfg_table_ptr;
has_accept_as_written = utilities_page_ptr.has_accept_as_written; -- import functions from select Module:Citation/CS1/Utilities module
is_set = utilities_page_ptr.is_set;
in_array = utilities_page_ptr.in_array;
set_message = utilities_page_ptr.set_message;
select_one = utilities_page_ptr.select_one;
substitute = utilities_page_ptr.substitute;
make_wikilink = utilities_page_ptr.make_wikilink;
z = utilities_page_ptr.z; -- table of tables in Module:Citation/CS1/Utilities
end
--[[--------------------------< E X P O R T E D F U N C T I O N S >------------------------------------------
]]
return {
auto_link_urls = auto_link_urls, -- table of identifier URLs to be used when auto-linking |title=
identifier_lists_get = identifier_lists_get, -- experiment to replace individual calls to build_id_list(), extract_ids, extract_id_access_levels
is_embargoed = is_embargoed;
set_selected_modules = set_selected_modules;
}
k1dr68mpitw4fjf8xuqkl74nhwowac7
886353
886352
2025-06-13T17:03:24Z
KartikMistry
10383
[[:en:Module:Citation/CS1/Identifiers]] માંથી આયાત કરેલ ૧ પુનરાવર્તન
886352
Scribunto
text/plain
--[[--------------------------< F O R W A R D D E C L A R A T I O N S >--------------------------------------
]]
local has_accept_as_written, is_set, in_array, set_message, select_one, -- functions in Module:Citation/CS1/Utilities
substitute, make_wikilink;
local z; -- table of tables defined in Module:Citation/CS1/Utilities
local cfg; -- table of configuration tables that are defined in Module:Citation/CS1/Configuration
--[[--------------------------< 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 auto_link_urls = {}; -- holds identifier URLs for those identifiers that can auto-link |title=
--============================<< H E L P E R F U N C T I O N S >>============================================
--[[--------------------------< W I K I D A T A _ A R T I C L E _ N A M E _ G E T >----------------------------
as an aid to internationalizing identifier-label wikilinks, gets identifier article names from Wikidata.
returns :<lang code>:<article title> when <q> has an <article title> for <lang code>; nil else
for identifiers that do not have q, returns nil
for wikis that do not have mw.wikibase installed, returns nil
]]
local function wikidata_article_name_get (q)
if not is_set (q) or (q and not mw.wikibase) then -- when no q number or when a q number but mw.wikibase not installed on this wiki
return nil; -- abandon
end
local wd_article;
local this_wiki_code = cfg.this_wiki_code; -- Wikipedia subdomain; 'en' for en.wikipedia.org
wd_article = mw.wikibase.getSitelink (q, this_wiki_code .. 'wiki'); -- fetch article title from WD; nil when no title available at this wiki
if wd_article then
wd_article = table.concat ({':', this_wiki_code, ':', wd_article}); -- interwiki-style link without brackets if taken from WD; leading colon required
end
return wd_article; -- article title from WD; nil else
end
--[[--------------------------< L I N K _ L A B E L _ M A K E >------------------------------------------------
common function to create identifier link label from handler table or from Wikidata
returns the first available of
1. redirect from local wiki's handler table (if enabled)
2. Wikidata (if there is a Wikidata entry for this identifier in the local wiki's language)
3. label specified in the local wiki's handler table
]]
local function link_label_make (handler)
local wd_article;
if not (cfg.use_identifier_redirects and is_set (handler.redirect)) then -- redirect has priority so if enabled and available don't fetch from Wikidata because expensive
wd_article = wikidata_article_name_get (handler.q); -- if Wikidata has an article title for this wiki, get it;
end
return (cfg.use_identifier_redirects and is_set (handler.redirect) and handler.redirect) or wd_article or handler.link;
end
--[[--------------------------< E X T E R N A L _ L I N K _ I D >----------------------------------------------
Formats a wiki-style external link
]]
local function external_link_id (options)
local url_string = options.id;
local ext_link;
local this_wiki_code = cfg.this_wiki_code; -- Wikipedia subdomain; 'en' for en.wikipedia.org
local wd_article; -- article title from Wikidata
if options.encode == true or options.encode == nil then
url_string = mw.uri.encode (url_string, 'PATH');
end
if options.auto_link and is_set (options.access) then
auto_link_urls[options.auto_link] = table.concat ({options.prefix, url_string, options.suffix});
end
ext_link = mw.ustring.format ('[%s%s%s %s]', options.prefix, url_string, options.suffix or "", mw.text.nowiki (options.id));
if is_set (options.access) then
ext_link = substitute (cfg.presentation['ext-link-access-signal'], {cfg.presentation[options.access].class, cfg.presentation[options.access].title, ext_link}); -- add the free-to-read / paywall lock
end
return table.concat ({
make_wikilink (link_label_make (options), options.label), -- redirect, Wikidata link, or locally specified link (in that order)
options.separator or ' ',
ext_link
});
end
--[[--------------------------< I N T E R N A L _ L I N K _ I D >----------------------------------------------
Formats a wiki-style internal link
TODO: Does not currently need to support options.access, options.encode, auto-linking and COinS (as in external_link_id),
but may be needed in the future for :m:Interwiki_map custom-prefixes like :arxiv:, :bibcode:, :DOI:, :hdl:, :ISSN:,
:JSTOR:, :Openlibrary:, :PMID:, :RFC:.
]]
local function internal_link_id (options)
local id = mw.ustring.gsub (options.id, '%d', cfg.date_names.local_digits); -- translate 'local' digits to Western 0-9
return table.concat (
{
make_wikilink (link_label_make (options), options.label), -- wiki-link the identifier label
options.separator or ' ', -- add the separator
make_wikilink (
table.concat (
{
options.prefix,
id, -- translated to Western digits
options.suffix or ''
}),
substitute (cfg.presentation['bdi'], {'', mw.text.nowiki (options.id)}) -- bdi tags to prevent Latin script identifiers from being reversed at RTL language wikis
); -- nowiki because MediaWiki still has magic links for ISBN and the like; TODO: is it really required?
});
end
--[[--------------------------< I S _ E M B A R G O E D >------------------------------------------------------
Determines if a PMC identifier's online version is embargoed. Compares the date in |pmc-embargo-date= against
today's date. If embargo date is in the future, returns the content of |pmc-embargo-date=; otherwise, returns
an empty string because the embargo has expired or because |pmc-embargo-date= was not set in this cite.
]]
local function is_embargoed (embargo)
if is_set (embargo) then
local lang = mw.getContentLanguage();
local good1, embargo_date, todays_date;
good1, embargo_date = pcall (lang.formatDate, lang, 'U', embargo);
todays_date = lang:formatDate ('U');
if good1 then -- if embargo date is a good date
if tonumber (embargo_date) >= tonumber (todays_date) then -- is embargo date is in the future?
return embargo; -- still embargoed
else
set_message ('maint_pmc_embargo'); -- embargo has expired; add main cat
return ''; -- unset because embargo has expired
end
end
end
return ''; -- |pmc-embargo-date= not set return empty string
end
--[=[-------------------------< I S _ V A L I D _ R X I V _ D A T E >------------------------------------------
for biorxiv, returns true if:
2019-12-11T00:00Z <= biorxiv_date < today + 2 days
for medrxiv, returns true if:
2020-01-01T00:00Z <= medrxiv_date < today + 2 days
The dated form of biorxiv identifier has a start date of 2019-12-11. The Unix timestamp for that date is {{#time:U|2019-12-11}} = 1576022400
The medrxiv identifier has a start date of 2020-01-01. The Unix timestamp for that date is {{#time:U|2020-01-01}} = 1577836800
<rxiv_date> is the date provided in those |biorxiv= parameter values that are dated and in |medrxiv= parameter values at time 00:00:00 UTC
<today> is the current date at time 00:00:00 UTC plus 48 hours
if today's date is 2023-01-01T00:00:00 then
adding 24 hours gives 2023-01-02T00:00:00 – one second more than today
adding 24 hours gives 2023-01-03T00:00:00 – one second more than tomorrow
inputs:
<y>, <m>, <d> – year, month, day parts of the date from the birxiv or medrxiv identifier
<select> 'b' for biorxiv, 'm' for medrxiv; defaults to 'b'
]=]
local function is_valid_rxiv_date (y, m, d, select)
if 0 == tonumber (m) and 12 < tonumber (m) then -- <m> must be a number 1–12
return false;
end
if 0 == tonumber (d) and 31 < tonumber (d) then -- <d> must be a number 1–31; TODO: account for month length and leap yer?
return false;
end
local rxiv_date = table.concat ({y, m, d}, '-'); -- make ymd date string
local good1, good2;
local rxiv_ts, tomorrow_ts; -- to hold Unix timestamps representing the dates
local lang_object = mw.getContentLanguage();
good1, rxiv_ts = pcall (lang_object.formatDate, lang_object, 'U', rxiv_date); -- convert rxiv_date value to Unix timestamp
good2, tomorrow_ts = pcall (lang_object.formatDate, lang_object, 'U', 'today + 2 days' ); -- today midnight + 2 days is one second more than all day tomorrow
if good1 and good2 then -- lang.formatDate() returns a timestamp in the local script which tonumber() may not understand
rxiv_ts = tonumber (rxiv_ts) or lang_object:parseFormattedNumber (rxiv_ts); -- convert to numbers for the comparison;
tomorrow_ts = tonumber (tomorrow_ts) or lang_object:parseFormattedNumber (tomorrow_ts);
else
return false; -- one or both failed to convert to Unix timestamp
end
local limit_ts = ((select and ('m' == select)) and 1577836800) or 1576022400; -- choose the appropriate limit timesatmp
return ((limit_ts <= rxiv_ts) and (rxiv_ts < tomorrow_ts)) -- limit_ts <= rxiv_date < tomorrow's date
end
--[[--------------------------< IS _ V A L I D _ I S X N >-----------------------------------------------------
ISBN-10 and ISSN validator code calculates checksum across all ISBN/ISSN digits including the check digit.
ISBN-13 is checked in isbn().
If the number is valid the result will be 0. Before calling this function, ISBN/ISSN must be checked for length
and stripped of dashes, spaces and other non-ISxN characters.
]]
local function is_valid_isxn (isxn_str, len)
local temp = 0;
isxn_str = { isxn_str:byte(1, len) }; -- make a table of byte values '0' → 0x30 .. '9' → 0x39, 'X' → 0x58
len = len + 1; -- adjust to be a loop counter
for i, v in ipairs (isxn_str) do -- loop through all of the bytes and calculate the checksum
if v == string.byte ("X" ) then -- if checkdigit is X (compares the byte value of 'X' which is 0x58)
temp = temp + 10 * (len - i); -- it represents 10 decimal
else
temp = temp + tonumber (string.char (v) )*(len-i);
end
end
return temp % 11 == 0; -- returns true if calculation result is zero
end
--[[--------------------------< IS _ V A L I D _ I S X N _ 1 3 >-----------------------------------------------
ISBN-13 and ISMN validator code calculates checksum across all 13 ISBN/ISMN digits including the check digit.
If the number is valid, the result will be 0. Before calling this function, ISBN-13/ISMN must be checked for length
and stripped of dashes, spaces and other non-ISxN-13 characters.
]]
local function is_valid_isxn_13 (isxn_str)
local temp=0;
isxn_str = { isxn_str:byte(1, 13) }; -- make a table of byte values '0' → 0x30 .. '9' → 0x39
for i, v in ipairs (isxn_str) do
temp = temp + (3 - 2*(i % 2)) * tonumber (string.char (v) ); -- multiply odd index digits by 1, even index digits by 3 and sum; includes check digit
end
return temp % 10 == 0; -- sum modulo 10 is zero when ISBN-13/ISMN is correct
end
--[[--------------------------< N O R M A L I Z E _ L C C N >--------------------------------------------------
LCCN normalization (https://www.loc.gov/marc/lccn-namespace.html#normalization)
1. Remove all blanks.
2. If there is a forward slash (/) in the string, remove it, and remove all characters to the right of the forward slash.
3. If there is a hyphen in the string:
a. Remove it.
b. Inspect the substring following (to the right of) the (removed) hyphen. Then (and assuming that steps 1 and 2 have been carried out):
1. All these characters should be digits, and there should be six or less. (not done in this function)
2. If the length of the substring is less than 6, left-fill the substring with zeroes until the length is six.
Returns a normalized LCCN for lccn() to validate. There is no error checking (step 3.b.1) performed in this function.
]]
local function normalize_lccn (lccn)
lccn = lccn:gsub ("%s", ""); -- 1. strip whitespace
if nil ~= string.find (lccn, '/') then
lccn = lccn:match ("(.-)/"); -- 2. remove forward slash and all character to the right of it
end
local prefix
local suffix
prefix, suffix = lccn:match ("(.+)%-(.+)"); -- 3.a remove hyphen by splitting the string into prefix and suffix
if nil ~= suffix then -- if there was a hyphen
suffix = string.rep("0", 6-string.len (suffix)) .. suffix; -- 3.b.2 left fill the suffix with 0s if suffix length less than 6
lccn = prefix..suffix; -- reassemble the LCCN
end
return lccn;
end
--============================<< I D E N T I F I E R F U N C T I O N S >>====================================
--[[--------------------------< A R X I V >--------------------------------------------------------------------
See: https://arxiv.org/help/arxiv_identifier
format and error check arXiv identifier. There are three valid forms of the identifier:
the first form, valid only between date codes 9107 and 0703, is:
arXiv:<archive>.<class>/<date code><number><version>
where:
<archive> is a string of alpha characters - may be hyphenated; no other punctuation
<class> is a string of alpha characters - may be hyphenated; no other punctuation; not the same as |class= parameter which is not supported in this form
<date code> is four digits in the form YYMM where YY is the last two digits of the four-digit year and MM is the month number January = 01
first digit of YY for this form can only 9 and 0
<number> is a three-digit number
<version> is a 1 or more digit number preceded with a lowercase v; no spaces (undocumented)
the second form, valid from April 2007 through December 2014 is:
arXiv:<date code>.<number><version>
where:
<date code> is four digits in the form YYMM where YY is the last two digits of the four-digit year and MM is the month number January = 01
<number> is a four-digit number
<version> is a 1 or more digit number preceded with a lowercase v; no spaces
the third form, valid from January 2015 is:
arXiv:<date code>.<number><version>
where:
<date code> and <version> are as defined for 0704-1412
<number> is a five-digit number
]]
local function arxiv (options)
local id = options.id;
local class = options.Class; -- TODO: lowercase?
local handler = options.handler;
local year, month, version;
local err_msg = false; -- assume no error message
local text; -- output text
if id:match("^%a[%a%.%-]+/[90]%d[01]%d%d%d%d$") or id:match("^%a[%a%.%-]+/[90]%d[01]%d%d%d%dv%d+$") then -- test for the 9107-0703 format with or without version
year, month = id:match("^%a[%a%.%-]+/([90]%d)([01]%d)%d%d%d[v%d]*$");
year = tonumber (year);
month = tonumber (month);
if ((not (90 < year or 8 > year)) or (1 > month or 12 < month)) or -- if invalid year or invalid month
((91 == year and 7 > month) or (7 == year and 3 < month)) then -- if years ok, are starting and ending months ok?
err_msg = true; -- flag for error message
end
elseif id:match("^%d%d[01]%d%.%d%d%d%d$") or id:match("^%d%d[01]%d%.%d%d%d%dv%d+$") then -- test for the 0704-1412 with or without version
year, month = id:match("^(%d%d)([01]%d)%.%d%d%d%d[v%d]*$");
year = tonumber (year);
month = tonumber (month);
if ((7 > year) or (14 < year) or (1 > month or 12 < month)) or -- is year invalid or is month invalid? (doesn't test for future years)
((7 == year) and (4 > month)) then -- when year is 07, is month invalid (before April)?
err_msg = true; -- flag for error message
end
elseif id:match("^%d%d[01]%d%.%d%d%d%d%d$") or id:match("^%d%d[01]%d%.%d%d%d%d%dv%d+$") then -- test for the 1501- format with or without version
year, month = id:match("^(%d%d)([01]%d)%.%d%d%d%d%d[v%d]*$");
year = tonumber (year);
month = tonumber (month);
if ((15 > year) or (1 > month or 12 < month)) then -- is year invalid or is month invalid? (doesn't test for future years)
err_msg = true; -- flag for error message
end
else
err_msg = true; -- not a recognized format; flag for error message
end
if err_msg then
options.coins_list_t['ARXIV'] = nil; -- when error, unset so not included in COinS
end
local err_msg_t = {};
if err_msg then
set_message ('err_bad_arxiv');
end
text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = handler.access});
if is_set (class) then
if id:match ('^%d+') then
text = table.concat ({text, ' [[https://arxiv.org/archive/', class, ' ', class, ']]'}); -- external link within square brackets, not wikilink
else
set_message ('err_class_ignored');
end
end
return text;
end
--[[--------------------------< B I B C O D E >--------------------------------------------------------------------
Validates (sort of) and formats a bibcode ID.
Format for bibcodes is specified here: https://adsabs.harvard.edu/abs_doc/help_pages/data.html#bibcodes
But, this: 2015arXiv151206696F is apparently valid so apparently, the only things that really matter are length, 19 characters
and first four digits must be a year. This function makes these tests:
length must be 19 characters
characters in position
1–4 must be digits and must represent a year in the range of 1000 – next year
5 must be a letter
6–8 must be letter, digit, ampersand, or dot (ampersand cannot directly precede a dot; &. )
9–18 must be letter, digit, or dot
19 must be a letter or dot
]]
local function bibcode (options)
local id = options.id;
local access = options.access;
local handler = options.handler;
local ignore_invalid = options.accept;
local err_type;
local err_msg = '';
local year;
local text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode,
access = access});
if 19 ~= id:len() then
err_type = cfg.err_msg_supl.length;
else
year = id:match ("^(%d%d%d%d)[%a][%w&%.][%w&%.][%w&%.][%w.]+[%a%.]$");
if not year then -- if nil then no pattern match
err_type = cfg.err_msg_supl.value; -- so value error
else
local next_year = tonumber (os.date ('%Y')) + 1; -- get the current year as a number and add one for next year
year = tonumber (year); -- convert year portion of bibcode to a number
if (1000 > year) or (year > next_year) then
err_type = cfg.err_msg_supl.year; -- year out of bounds
end
if id:find('&%.') then
err_type = cfg.err_msg_supl.journal; -- journal abbreviation must not have '&.' (if it does it's missing a letter)
end
if id:match ('.........%.tmp%.') then -- temporary bibcodes when positions 10–14 are '.tmp.'
set_message ('maint_bibcode');
end
end
end
if is_set (err_type) and not ignore_invalid then -- if there was an error detected and accept-as-written markup not used
set_message ('err_bad_bibcode', {err_type});
options.coins_list_t['BIBCODE'] = nil; -- when error, unset so not included in COinS
end
return text;
end
--[[--------------------------< B I O R X I V >-----------------------------------------------------------------
Format bioRxiv ID and do simple error checking. Before 2019-12-11, biorXiv IDs were 10.1101/ followed by exactly
6 digits. After 2019-12-11, biorXiv IDs retained the six-digit identifier but prefixed that with a yyyy.mm.dd.
date and suffixed with an optional version identifier.
The bioRxiv ID is the string of characters:
https://doi.org/10.1101/078733 -> 10.1101/078733
or a date followed by a six-digit number followed by an optional version indicator 'v' and one or more digits:
https://www.biorxiv.org/content/10.1101/2019.12.11.123456v2 -> 10.1101/2019.12.11.123456v2
see https://www.biorxiv.org/about-biorxiv
]]
local function biorxiv (options)
local id = options.id;
local handler = options.handler;
local err_msg = true; -- flag; assume that there will be an error
local patterns = {
'^10%.1101/%d%d%d%d%d%d$', -- simple 6-digit identifier (before 2019-12-11)
'^10%.1101/(20%d%d)%.(%d%d)%.(%d%d)%.%d%d%d%d%d%dv%d+$', -- y.m.d. date + 6-digit identifier + version (after 2019-12-11)
'^10%.1101/(20%d%d)%.(%d%d)%.(%d%d)%.%d%d%d%d%d%d$', -- y.m.d. date + 6-digit identifier (after 2019-12-11)
}
for _, pattern in ipairs (patterns) do -- spin through the patterns looking for a match
if id:match (pattern) then
local y, m, d = id:match (pattern); -- found a match, attempt to get year, month and date from the identifier
if m then -- m is nil when id is the six-digit form
if not is_valid_rxiv_date (y, m, d, 'b') then -- validate the encoded date; 'b' for biorxiv limit
break; -- date fail; break out early so we don't unset the error message
end
end
err_msg = nil; -- we found a match so unset the error message
break; -- and done
end
end -- err_cat remains set here when no match
if err_msg then
options.coins_list_t['BIORXIV'] = nil; -- when error, unset so not included in COinS
set_message ('err_bad_biorxiv'); -- and set the error message
end
return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
prefix = handler.prefix, id = id, separator = handler.separator,
encode = handler.encode, access = handler.access});
end
--[[--------------------------< C I T E S E E R X >------------------------------------------------------------
CiteSeerX use their own notion of "doi" (not to be confused with the identifiers resolved via doi.org).
The description of the structure of this identifier can be found at Help_talk:Citation_Style_1/Archive_26#CiteSeerX_id_structure
]]
local function citeseerx (options)
local id = options.id;
local handler = options.handler;
local matched;
local text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode,
access = handler.access});
matched = id:match ("^10%.1%.1%.[1-9]%d?%d?%d?%.[1-9]%d?%d?%d?$");
if not matched then
set_message ('err_bad_citeseerx' );
options.coins_list_t['CITESEERX'] = nil; -- when error, unset so not included in COinS
end
return text;
end
--[[--------------------------< D O I >------------------------------------------------------------------------
Formats a DOI and checks for DOI errors.
DOI names contain two parts: prefix and suffix separated by a forward slash.
Prefix: directory indicator '10.' followed by a registrant code
Suffix: character string of any length chosen by the registrant
This function checks a DOI name for: prefix/suffix. If the DOI name contains spaces or endashes, or, if it ends
with a period or a comma, this function will emit a bad_doi error message.
DOI names are case-insensitive and can incorporate any printable Unicode characters so the test for spaces, endash,
and terminal punctuation may not be technically correct but it appears, that in practice these characters are rarely
if ever used in DOI names.
https://www.doi.org/doi_handbook/2_Numbering.html -- 2.2 Syntax of a DOI name
https://www.doi.org/doi_handbook/2_Numbering.html#2.2.2 -- 2.2.2 DOI prefix
]]
local function doi (options)
local id = options.id;
local inactive = options.DoiBroken
local access = options.access;
local ignore_invalid = options.accept;
local handler = options.handler;
local err_flag;
local function is_extended_free (registrant, suffix) -- local function to check those few registrants that are mixed; identifiable by the doi suffix <incipit>
if cfg.extended_registrants_t[registrant] then -- if this registrant has known free-to-read extentions
for _, incipit in ipairs (cfg.extended_registrants_t[registrant]) do -- loop through the registrant's incipits
if mw.ustring.find (suffix, '^' .. incipit) then -- if found
return true;
end
end
end
end
local text;
if is_set (inactive) then
local inactive_year = inactive:match("%d%d%d%d"); -- try to get the year portion from the inactive date
local inactive_month, good;
if is_set (inactive_year) then
if 4 < inactive:len() then -- inactive date has more than just a year (could be anything)
local lang_obj = mw.getContentLanguage(); -- get a language object for this wiki
good, inactive_month = pcall (lang_obj.formatDate, lang_obj, 'F', inactive); -- try to get the month name from the inactive date
if not good then
inactive_month = nil; -- something went wrong so make sure this is unset
end
end
end -- otherwise, |doi-broken-date= has something but it isn't a date
if is_set (inactive_year) and is_set (inactive_month) then
set_message ('maint_doi_inactive_dated', {inactive_year, inactive_month, ' '});
elseif is_set (inactive_year) then
set_message ('maint_doi_inactive_dated', {inactive_year, '', ''});
else
set_message ('maint_doi_inactive');
end
inactive = " (" .. cfg.messages['inactive'] .. ' ' .. inactive .. ')';
end
local suffix;
local registrant, suffix = mw.ustring.match (id, '^10%.([^/]+)/([^%s–]-[^%.,])$'); -- registrant and suffix set when DOI has the proper basic form
local registrant_err_patterns = { -- these patterns are for code ranges that are not supported
'^[^1-3]%d%d%d%d%.%d+$', -- 5 digits with subcode (0xxxx, 40000+); accepts: 10000–39999
'^[^1-7]%d%d%d%d$', -- 5 digits without subcode (0xxxx, 60000+); accepts: 10000–69999
'^[^1-9]%d%d%d%.%d+$', -- 4 digits with subcode (0xxx); accepts: 1000–9999
'^[^1-9]%d%d%d$', -- 4 digits without subcode (0xxx); accepts: 1000–9999
'^%d%d%d%d%d%d+', -- 6 or more digits
'^%d%d?%d?$', -- less than 4 digits without subcode (3 digits with subcode is legitimate)
'^%d%d?%.[%d%.]+', -- 1 or 2 digits with subcode
'^5555$', -- test registrant will never resolve
'[^%d%.]', -- any character that isn't a digit or a dot
}
if not ignore_invalid then
if registrant then -- when DOI has proper form
for i, pattern in ipairs (registrant_err_patterns) do -- spin through error patterns
if registrant:match (pattern) then -- to validate registrant codes
err_flag = set_message ('err_bad_doi'); -- when found, mark this DOI as bad
break; -- and done
end
end
else
err_flag = set_message ('err_bad_doi'); -- invalid directory or malformed
end
else
set_message ('maint_doi_ignore');
end
if err_flag then
options.coins_list_t['DOI'] = nil; -- when error, unset so not included in COinS
else
if not access and (cfg.known_free_doi_registrants_t[registrant] or is_extended_free (registrant, suffix)) then -- |doi-access=free not set and <registrant> is known to be free
set_message ('maint_doi_unflagged_free'); -- set a maint cat
end
end
text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = access,
auto_link = not (err_flag or is_set (inactive) or ignore_invalid) and 'doi' or nil -- do not auto-link when |doi-broken-date= has a value or when there is a DOI error or (to play it safe, after all, auto-linking is not essential) when invalid DOIs are ignored
}) .. (inactive or '');
return text;
end
--[[--------------------------< H D L >------------------------------------------------------------------------
Formats an HDL with minor error checking.
HDL names contain two parts: prefix and suffix separated by a forward slash.
Prefix: character string using any character in the UCS-2 character set except '/'
Suffix: character string of any length using any character in the UCS-2 character set chosen by the registrant
This function checks a HDL name for: prefix/suffix. If the HDL name contains spaces, endashes, or, if it ends
with a period or a comma, this function will emit a bad_hdl error message.
HDL names are case-insensitive and can incorporate any printable Unicode characters so the test for endashes and
terminal punctuation may not be technically correct but it appears, that in practice these characters are rarely
if ever used in HDLs.
Query string parameters are named here: https://www.handle.net/proxy_servlet.html. query strings are not displayed
but since '?' is an allowed character in an HDL, '?' followed by one of the query parameters is the only way we
have to detect the query string so that it isn't URL-encoded with the rest of the identifier.
]]
local function hdl (options)
local id = options.id;
local access = options.access;
local handler = options.handler;
local query_params = { -- list of known query parameters from https://www.handle.net/proxy_servlet.html
'noredirect',
'ignore_aliases',
'auth',
'cert',
'index',
'type',
'urlappend',
'locatt',
'action',
}
local hdl, suffix, param = id:match ('(.-)(%?(%a+).+)$'); -- look for query string
local found;
if hdl then -- when there are query strings, this is the handle identifier portion
for _, q in ipairs (query_params) do -- spin through the list of query parameters
if param:match ('^' .. q) then -- if the query string begins with one of the parameters
found = true; -- announce a find
break; -- and stop looking
end
end
end
if found then
id = hdl; -- found so replace id with the handle portion; this will be URL-encoded, suffix will not
else
suffix = ''; -- make sure suffix is empty string for concatenation else
end
local text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
prefix = handler.prefix, id = id, suffix = suffix, separator = handler.separator, encode = handler.encode, access = access})
if nil == id:match("^[^%s–]-/[^%s–]-[^%.,]$") then -- HDL must contain a forward slash, must not contain spaces, endashes, and must not end with period or comma
set_message ('err_bad_hdl' );
options.coins_list_t['HDL'] = nil; -- when error, unset so not included in COinS
end
return text;
end
--[[--------------------------< I S B N >----------------------------------------------------------------------
Determines whether an ISBN string is valid
]]
local function isbn (options_t)
local isbn_str = options_t.id;
local ignore_invalid = options_t.accept;
local handler = options_t.handler;
local year = options_t.Year; -- when set, valid anchor_year; may have a disambiguator which must be removed
local function return_result (check, err_type) -- local function to handle the various returns
local ISBN = internal_link_id ({link = handler.link, label = handler.label, redirect = handler.redirect,
prefix = handler.prefix, id = isbn_str, separator = handler.separator});
if ignore_invalid then -- if ignoring ISBN errors
set_message ('maint_isbn_ignore'); -- add a maint category even when there is no error
else -- here when not ignoring
if not check then -- and there is an error
options_t.coins_list_t['ISBN'] = nil; -- when error, unset so not included in COinS
set_message ('err_bad_isbn', err_type); -- set an error message
return ISBN; -- return id text
end
end
return ISBN; -- return id text
end
if year and not ignore_invalid then --
year = year:match ('%d%d%d%d?'); -- strip disambiguator if present
if year and (1965 > tonumber(year)) then
set_message ('err_invalid_isbn_date'); -- set an error message
return internal_link_id ({link = handler.link, label = handler.label, redirect = handler.redirect,
prefix = handler.prefix, id = isbn_str, separator = handler.separator});
end
end
if nil ~= isbn_str:match ('[^%s-0-9X]') then
return return_result (false, cfg.err_msg_supl.char); -- fail if isbn_str contains anything but digits, hyphens, or the uppercase X
end
local id = isbn_str:gsub ('[%s-]', ''); -- remove hyphens and whitespace
local len = id:len();
if len ~= 10 and len ~= 13 then
return return_result (false, cfg.err_msg_supl.length); -- fail if incorrect length
end
if len == 10 then
if id:match ('^%d*X?$') == nil then -- fail if isbn_str has 'X' anywhere but last position
return return_result (false, cfg.err_msg_supl.form);
end
if not is_valid_isxn (id, 10) then -- test isbn-10 for numerical validity
return return_result (false, cfg.err_msg_supl.check); -- fail if isbn-10 is not numerically valid
end
if id:find ('^63[01]') then -- 630xxxxxxx and 631xxxxxxx are (apparently) not valid isbn group ids but are used by amazon as numeric identifiers (asin)
return return_result (false, cfg.err_msg_supl.group); -- fail if isbn-10 begins with 630/1
end
return return_result (true, cfg.err_msg_supl.check); -- pass if isbn-10 is numerically valid
else
if id:match ('^%d+$') == nil then
return return_result (false, cfg.err_msg_supl.char); -- fail if ISBN-13 is not all digits
end
if id:match ('^97[89]%d*$') == nil then
return return_result (false, cfg.err_msg_supl.prefix); -- fail when ISBN-13 does not begin with 978 or 979
end
if id:match ('^9790') then
return return_result (false, cfg.err_msg_supl.group); -- group identifier '0' is reserved to ISMN
end
return return_result (is_valid_isxn_13 (id), cfg.err_msg_supl.check);
end
end
--[[--------------------------< A S I N >----------------------------------------------------------------------
Formats a link to Amazon. Do simple error checking: ASIN must be mix of 10 numeric or uppercase alpha
characters. If a mix, first character must be uppercase alpha; if all numeric, ASINs must be 10-digit
ISBN. If 10-digit ISBN, add a maintenance category so a bot or AWB script can replace |asin= with |isbn=.
Error message if not 10 characters, if not ISBN-10, if mixed and first character is a digit.
|asin=630....... and |asin=631....... are (apparently) not a legitimate ISBN though it checksums as one; these
do not cause this function to emit the maint_asin message
This function is positioned here because it calls isbn()
]]
local function asin (options)
local id = options.id;
local domain = options.ASINTLD;
local err_flag;
if not id:match("^[%d%u][%d%u][%d%u][%d%u][%d%u][%d%u][%d%u][%d%u][%d%u][%d%u]$") then
err_flag = set_message ('err_bad_asin'); -- ASIN is not a mix of 10 uppercase alpha and numeric characters
else
if id:match("^%d%d%d%d%d%d%d%d%d[%dX]$") then -- if 10-digit numeric (or 9 digits with terminal X)
if is_valid_isxn (id, 10) then -- see if ASIN value is or validates as ISBN-10
if not id:find ('^63[01]') then -- 630xxxxxxx and 631xxxxxxx are (apparently) not a valid isbn prefixes but are used by amazon as a numeric identifier
err_flag = set_message ('err_bad_asin'); -- ASIN has ISBN-10 form but begins with something other than 630/1 so probably an isbn
end
elseif not is_set (err_flag) then
err_flag = set_message ('err_bad_asin'); -- ASIN is not ISBN-10
end
elseif not id:match("^%u[%d%u]+$") then
err_flag = set_message ('err_bad_asin'); -- asin doesn't begin with uppercase alpha
end
end
if (not is_set (domain)) or in_array (domain, {'us'}) then -- default: United States
domain = "com";
elseif in_array (domain, {'jp', 'uk'}) then -- Japan, United Kingdom
domain = "co." .. domain;
elseif in_array (domain, {'z.cn'}) then -- China
domain = "cn";
elseif in_array (domain, {'au', 'br', 'mx', 'sg', 'tr'}) then -- Australia, Brazil, Mexico, Singapore, Turkey
domain = "com." .. domain;
elseif not in_array (domain, {'ae', 'ca', 'cn', 'de', 'es', 'fr', 'in', 'it', 'nl', 'pl', 'sa', 'se', 'co.jp', 'co.uk', 'com', 'com.au', 'com.br', 'com.mx', 'com.sg', 'com.tr'}) then -- Arabic Emirates, Canada, China, Germany, Spain, France, Indonesia, Italy, Netherlands, Poland, Saudi Arabia, Sweden (as of 2021-03 Austria (.at), Liechtenstein (.li) and Switzerland (.ch) still redirect to the German site (.de) with special settings, so don't maintain local ASINs for them)
err_flag = set_message ('err_bad_asin_tld'); -- unsupported asin-tld value
end
local handler = options.handler;
if not is_set (err_flag) then
options.coins_list_t['ASIN'] = handler.prefix .. domain .. "/dp/" .. id; -- asin for coins
else
options.coins_list_t['ASIN'] = nil; -- when error, unset so not included in COinS
end
return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
prefix = handler.prefix .. domain .. "/dp/",
id = id, encode = handler.encode, separator = handler.separator})
end
--[[--------------------------< I S M N >----------------------------------------------------------------------
Determines whether an ISMN string is valid. Similar to ISBN-13, ISMN is 13 digits beginning 979-0-... and uses the
same check digit calculations. See https://www.ismn-international.org/download/Web_ISMN_Users_Manual_2008-6.pdf
section 2, pages 9–12.
ismn value not made part of COinS metadata because we don't have a url or isn't a COinS-defined identifier (rft.xxx)
or an identifier registered at info-uri.info (info:)
]]
local function ismn (options)
local id = options.id;
local handler = options.handler;
local text;
local valid_ismn = true;
local id_copy;
id_copy = id; -- save a copy because this testing is destructive
id = id:gsub ('[%s-]', ''); -- remove hyphens and white space
if 13 ~= id:len() or id:match ("^9790%d*$" ) == nil then -- ISMN must be 13 digits and begin with 9790
valid_ismn = false;
else
valid_ismn=is_valid_isxn_13 (id); -- validate ISMN
end
-- text = internal_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, -- use this (or external version) when there is some place to link to
-- prefix = handler.prefix, id = id_copy, separator = handler.separator, encode = handler.encode})
text = table.concat ( -- because no place to link to yet
{
make_wikilink (link_label_make (handler), handler.label),
handler.separator,
id_copy
});
if false == valid_ismn then
options.coins_list_t['ISMN'] = nil; -- when error, unset so not included in COinS; not really necessary here because ismn not made part of COinS
set_message ('err_bad_ismn'); -- create an error message if the ISMN is invalid
end
return text;
end
--[[--------------------------< I S S N >----------------------------------------------------------------------
Validate and format an ISSN. This code fixes the case where an editor has included an ISSN in the citation but
has separated the two groups of four digits with a space. When that condition occurred, the resulting link looked
like this:
|issn=0819 4327 gives: [https://www.worldcat.org/issn/0819 4327 0819 4327] -- can't have spaces in an external link
This code now prevents that by inserting a hyphen at the ISSN midpoint. It also validates the ISSN for length
and makes sure that the checkdigit agrees with the calculated value. Incorrect length (8 digits), characters
other than 0-9 and X, or checkdigit / calculated value mismatch will all cause a check ISSN error message. The
ISSN is always displayed with a hyphen, even if the ISSN was given as a single group of 8 digits.
]]
local function issn (options)
local id = options.id;
local handler = options.handler;
local ignore_invalid = options.accept;
local issn_copy = id; -- save a copy of unadulterated ISSN; use this version for display if ISSN does not validate
local text;
local valid_issn = true;
id = id:gsub ('[%s-]', ''); -- remove hyphens and whitespace
if 8 ~= id:len() or nil == id:match ("^%d*X?$" ) then -- validate the ISSN: 8 digits long, containing only 0-9 or X in the last position
valid_issn = false; -- wrong length or improper character
else
valid_issn = is_valid_isxn (id, 8); -- validate ISSN
end
if true == valid_issn then
id = string.sub (id, 1, 4 ) .. "-" .. string.sub (id, 5 ); -- if valid, display correctly formatted version
else
id = issn_copy; -- if not valid, show the invalid ISSN with error message
end
text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode})
if ignore_invalid then
set_message ('maint_issn_ignore');
else
if false == valid_issn then
options.coins_list_t['ISSN'] = nil; -- when error, unset so not included in COinS
set_message ('err_bad_issn', (options.hkey == 'EISSN') and 'e' or ''); -- create an error message if the ISSN is invalid
end
end
return text;
end
--[[--------------------------< J F M >-----------------------------------------------------------------------
A numerical identifier in the form nn.nnnn.nn
]]
local function jfm (options)
local id = options.id;
local handler = options.handler;
local id_num;
id_num = id:match ('^[Jj][Ff][Mm](.*)$'); -- identifier with jfm prefix; extract identifier
if is_set (id_num) then
set_message ('maint_jfm_format');
else -- plain number without JFM prefix
id_num = id; -- if here id does not have prefix
end
if id_num and id_num:match('^%d%d%.%d%d%d%d%.%d%d$') then
id = id_num; -- jfm matches pattern
else
set_message ('err_bad_jfm' ); -- set an error message
options.coins_list_t['JFM'] = nil; -- when error, unset so not included in COinS
end
return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode});
end
--[[--------------------------< J S T O R >--------------------------------------------------------------------
Format a JSTOR with some error checking
]]
local function jstor (options)
local id = options.id;
local access = options.access;
local handler = options.handler;
if id:find ('[Jj][Ss][Tt][Oo][Rr]') or id:find ('^https?://') or id:find ('%s') then
set_message ('err_bad_jstor'); -- set an error message
options.coins_list_t['JSTOR'] = nil; -- when error, unset so not included in COinS
end
return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = access});
end
--[[--------------------------< L C C N >----------------------------------------------------------------------
Format LCCN link and do simple error checking. LCCN is a character string 8-12 characters long. The length of
the LCCN dictates the character type of the first 1-3 characters; the rightmost eight are always digits.
https://oclc-research.github.io/infoURI-Frozen/info-uri.info/info:lccn/reg.html
length = 8 then all digits
length = 9 then lccn[1] is lowercase alpha
length = 10 then lccn[1] and lccn[2] are both lowercase alpha or both digits
length = 11 then lccn[1] is lower case alpha, lccn[2] and lccn[3] are both lowercase alpha or both digits
length = 12 then lccn[1] and lccn[2] are both lowercase alpha
]]
local function lccn (options)
local lccn = options.id;
local handler = options.handler;
local err_flag; -- presume that LCCN is valid
local id = lccn; -- local copy of the LCCN
id = normalize_lccn (id); -- get canonical form (no whitespace, hyphens, forward slashes)
local len = id:len(); -- get the length of the LCCN
if 8 == len then
if id:match("[^%d]") then -- if LCCN has anything but digits (nil if only digits)
err_flag = set_message ('err_bad_lccn'); -- set an error message
end
elseif 9 == len then -- LCCN should be adddddddd
if nil == id:match("%l%d%d%d%d%d%d%d%d") then -- does it match our pattern?
err_flag = set_message ('err_bad_lccn'); -- set an error message
end
elseif 10 == len then -- LCCN should be aadddddddd or dddddddddd
if id:match("[^%d]") then -- if LCCN has anything but digits (nil if only digits) ...
if nil == id:match("^%l%l%d%d%d%d%d%d%d%d") then -- ... see if it matches our pattern
err_flag = set_message ('err_bad_lccn'); -- no match, set an error message
end
end
elseif 11 == len then -- LCCN should be aaadddddddd or adddddddddd
if not (id:match("^%l%l%l%d%d%d%d%d%d%d%d") or id:match("^%l%d%d%d%d%d%d%d%d%d%d")) then -- see if it matches one of our patterns
err_flag = set_message ('err_bad_lccn'); -- no match, set an error message
end
elseif 12 == len then -- LCCN should be aadddddddddd
if not id:match("^%l%l%d%d%d%d%d%d%d%d%d%d") then -- see if it matches our pattern
err_flag = set_message ('err_bad_lccn'); -- no match, set an error message
end
else
err_flag = set_message ('err_bad_lccn'); -- wrong length, set an error message
end
if not is_set (err_flag) and nil ~= lccn:find ('%s') then
err_flag = set_message ('err_bad_lccn'); -- lccn contains a space, set an error message
end
if is_set (err_flag) then
options.coins_list_t['LCCN'] = nil; -- when error, unset so not included in COinS
end
return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
prefix = handler.prefix, id = lccn, separator = handler.separator, encode = handler.encode});
end
--[[--------------------------< M E D R X I V >-----------------------------------------------------------------
Format medRxiv ID and do simple error checking. Similar to later bioRxiv IDs, medRxiv IDs are prefixed with a
yyyy.mm.dd. date and suffixed with an optional version identifier. Ealiest date accepted is 2020.01.01
The medRxiv ID is a date followed by an eight-digit number followed by an optional version indicator 'v' and one or more digits:
https://www.medrxiv.org/content/10.1101/2020.11.16.20232009v2 -> 10.1101/2020.11.16.20232009v2
]]
local function medrxiv (options)
local id = options.id;
local handler = options.handler;
local err_msg_flag = true; -- flag; assume that there will be an error
local patterns = {
'%d%d%d%d%d%d%d%d$', -- simple 8-digit identifier; these should be relatively rare
'^10%.1101/(20%d%d)%.(%d%d)%.(%d%d)%.%d%d%d%d%d%d%d%dv%d+$', -- y.m.d. date + 8-digit identifier + version (2020-01-01 and later)
'^10%.1101/(20%d%d)%.(%d%d)%.(%d%d)%.%d%d%d%d%d%d%d%d$', -- y.m.d. date + 8-digit identifier (2020-01-01 and later)
}
for _, pattern in ipairs (patterns) do -- spin through the patterns looking for a match
if id:match (pattern) then
local y, m, d = id:match (pattern); -- found a match, attempt to get year, month and date from the identifier
if m then -- m is nil when id is the 8-digit form
if not is_valid_rxiv_date (y, m, d, 'b') then -- validate the encoded date; 'b' for medrxiv limit
break; -- date fail; break out early so we don't unset the error message
end
end
err_msg_flag = nil; -- we found a match so unset the error message
break; -- and done
end
end -- <err_msg_flag> remains set here when no match
if err_msg_flag then
options.coins_list_t['MEDRXIV'] = nil; -- when error, unset so not included in COinS
set_message ('err_bad_medrxiv'); -- and set the error message
end
return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
prefix = handler.prefix, id = id, separator = handler.separator,
encode = handler.encode, access = handler.access});
end
--[[--------------------------< M R >--------------------------------------------------------------------------
A seven digit number; if not seven digits, zero-fill leading digits to make seven digits.
]]
local function mr (options)
local id = options.id;
local handler = options.handler;
local id_num;
local id_len;
id_num = id:match ('^[Mm][Rr](%d+)$'); -- identifier with mr prefix
if is_set (id_num) then
set_message ('maint_mr_format'); -- add maint cat
else -- plain number without mr prefix
id_num = id:match ('^%d+$'); -- if here id is all digits
end
id_len = id_num and id_num:len() or 0;
if (7 >= id_len) and (0 ~= id_len) then
id = string.rep ('0', 7-id_len) .. id_num; -- zero-fill leading digits
else
set_message ('err_bad_mr'); -- set an error message
options.coins_list_t['MR'] = nil; -- when error, unset so not included in COinS
end
return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode});
end
--[[--------------------------< O C L C >----------------------------------------------------------------------
Validate and format an OCLC ID. https://www.oclc.org/batchload/controlnumber.en.html {{dead link}}
archived at: https://web.archive.org/web/20161228233804/https://www.oclc.org/batchload/controlnumber.en.html
]]
local function oclc (options)
local id = options.id;
local handler = options.handler;
local number;
if id:match('^ocm%d%d%d%d%d%d%d%d$') then -- ocm prefix and 8 digits; 001 field (12 characters)
number = id:match('ocm(%d+)'); -- get the number
elseif id:match('^ocn%d%d%d%d%d%d%d%d%d$') then -- ocn prefix and 9 digits; 001 field (12 characters)
number = id:match('ocn(%d+)'); -- get the number
elseif id:match('^on%d%d%d%d%d%d%d%d%d%d+$') then -- on prefix and 10 or more digits; 001 field (12 characters)
number = id:match('^on(%d%d%d%d%d%d%d%d%d%d+)$'); -- get the number
elseif id:match('^%(OCoLC%)[1-9]%d*$') then -- (OCoLC) prefix and variable number digits; no leading zeros; 035 field
number = id:match('%(OCoLC%)([1-9]%d*)'); -- get the number
if 9 < number:len() then
number = nil; -- constrain to 1 to 9 digits; change this when OCLC issues 10-digit numbers
end
elseif id:match('^%d+$') then -- no prefix
number = id; -- get the number
if tonumber (id) > handler.id_limit then
number = nil; -- unset when id value exceeds the limit
end
end
if number then -- proper format
id = number; -- exclude prefix, if any, from external link
else
set_message ('err_bad_oclc') -- add an error message if the id is malformed
options.coins_list_t['OCLC'] = nil; -- when error, unset so not included in COinS
end
return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode});
end
--[[--------------------------< O P E N L I B R A R Y >--------------------------------------------------------
Formats an OpenLibrary link, and checks for associated errors.
]]
local function openlibrary (options)
local id = options.id;
local access = options.access;
local handler = options.handler;
local ident, code = id:gsub('^OL', ''):match("^(%d+([AMW]))$"); -- strip optional OL prefix followed immediately by digits followed by 'A', 'M', or 'W';
local err_flag;
local prefix = { -- these are appended to the handler.prefix according to code
['A']='authors/OL',
['M']='books/OL',
['W']='works/OL',
['X']='OL' -- not a code; spoof when 'code' in id is invalid
};
if not ident then
code = 'X'; -- no code or id completely invalid
ident = id; -- copy id to ident so that we display the flawed identifier
err_flag = set_message ('err_bad_ol');
end
if not is_set (err_flag) then
options.coins_list_t['OL'] = handler.prefix .. prefix[code] .. ident; -- experiment for ol coins
else
options.coins_list_t['OL'] = nil; -- when error, unset so not included in COinS
end
return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
prefix = handler.prefix .. prefix[code],
id = ident, separator = handler.separator, encode = handler.encode,
access = access});
end
--[[--------------------------< O S T I >----------------------------------------------------------------------
Format OSTI and do simple error checking. OSTIs are sequential numbers beginning at 1 and counting up. This
code checks the OSTI to see that it contains only digits and is less than test_limit specified in the configuration;
the value in test_limit will need to be updated periodically as more OSTIs are issued.
NB. 1018 is the lowest OSTI number found in the wild (so far) and resolving OK on the OSTI site
]]
local function osti (options)
local id = options.id;
local access = options.access;
local handler = options.handler;
if id:match("[^%d]") then -- if OSTI has anything but digits
set_message ('err_bad_osti'); -- set an error message
options.coins_list_t['OSTI'] = nil; -- when error, unset so not included in COinS
else -- OSTI is only digits
local id_num = tonumber (id); -- convert id to a number for range testing
if 1018 > id_num or handler.id_limit < id_num then -- if OSTI is outside test limit boundaries
set_message ('err_bad_osti'); -- set an error message
options.coins_list_t['OSTI'] = nil; -- when error, unset so not included in COinS
end
end
return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = access});
end
--[[--------------------------< P M C >------------------------------------------------------------------------
Format a PMC, do simple error checking, and check for embargoed articles.
The embargo parameter takes a date for a value. If the embargo date is in the future the PMC identifier will not
be linked to the article. If the embargo date is today or in the past, or if it is empty or omitted, then the
PMC identifier is linked to the article through the link at cfg.id_handlers['PMC'].prefix.
PMC embargo date testing is done in function is_embargoed () which is called earlier because when the citation
has |pmc=<value> but does not have a |url= then |title= is linked with the PMC link. Function is_embargoed ()
returns the embargo date if the PMC article is still embargoed, otherwise it returns an empty string.
PMCs are sequential numbers beginning at 1 and counting up. This code checks the PMC to see that it contains only digits and is less
than test_limit; the value in local variable test_limit will need to be updated periodically as more PMCs are issued.
]]
local function pmc (options)
local id = options.id;
local embargo = options.Embargo; -- TODO: lowercase?
local handler = options.handler;
local err_flag;
local id_num;
local text;
id_num = id:match ('^[Pp][Mm][Cc](%d+)$'); -- identifier with PMC prefix
if is_set (id_num) then
set_message ('maint_pmc_format');
else -- plain number without PMC prefix
id_num = id:match ('^%d+$'); -- if here id is all digits
end
if is_set (id_num) then -- id_num has a value so test it
id_num = tonumber (id_num); -- convert id_num to a number for range testing
if 1 > id_num or handler.id_limit < id_num then -- if PMC is outside test limit boundaries
err_flag = set_message ('err_bad_pmc'); -- set an error message
else
id = tostring (id_num); -- make sure id is a string
end
else -- when id format incorrect
err_flag = set_message ('err_bad_pmc'); -- set an error message
end
if is_set (embargo) and is_set (is_embargoed (embargo)) then -- is PMC is still embargoed?
text = table.concat ( -- still embargoed so no external link
{
make_wikilink (link_label_make (handler), handler.label),
handler.separator,
id,
});
else
text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect, -- no embargo date or embargo has expired, ok to link to article
prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = handler.access,
auto_link = not err_flag and 'pmc' or nil -- do not auto-link when PMC has error
});
end
if err_flag then
options.coins_list_t['PMC'] = nil; -- when error, unset so not included in COinS
end
return text;
end
--[[--------------------------< P M I D >----------------------------------------------------------------------
Format PMID and do simple error checking. PMIDs are sequential numbers beginning at 1 and counting up. This
code checks the PMID to see that it contains only digits and is less than test_limit; the value in local variable
test_limit will need to be updated periodically as more PMIDs are issued.
]]
local function pmid (options)
local id = options.id;
local handler = options.handler;
if id:match("[^%d]") then -- if PMID has anything but digits
set_message ('err_bad_pmid'); -- set an error message
options.coins_list_t['PMID'] = nil; -- when error, unset so not included in COinS
else -- PMID is only digits
local id_num = tonumber (id); -- convert id to a number for range testing
if 1 > id_num or handler.id_limit < id_num then -- if PMID is outside test limit boundaries
set_message ('err_bad_pmid'); -- set an error message
options.coins_list_t['PMID'] = nil; -- when error, unset so not included in COinS
end
end
return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode});
end
--[[--------------------------< R F C >------------------------------------------------------------------------
Format RFC and do simple error checking. RFCs are sequential numbers beginning at 1 and counting up. This
code checks the RFC to see that it contains only digits and is less than test_limit specified in the configuration;
the value in test_limit will need to be updated periodically as more RFCs are issued.
An index of all RFCs is here: https://tools.ietf.org/rfc/
]]
local function rfc (options)
local id = options.id;
local handler = options.handler;
if id:match("[^%d]") then -- if RFC has anything but digits
set_message ('err_bad_rfc'); -- set an error message
options.coins_list_t['RFC'] = nil; -- when error, unset so not included in COinS
else -- RFC is only digits
local id_num = tonumber (id); -- convert id to a number for range testing
if 1 > id_num or handler.id_limit < id_num then -- if RFC is outside test limit boundaries
set_message ('err_bad_rfc'); -- set an error message
options.coins_list_t['RFC'] = nil; -- when error, unset so not included in COinS
end
end
return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = handler.access});
end
--[[--------------------------< S 2 C I D >--------------------------------------------------------------------
Format an S2CID, do simple error checking
S2CIDs are sequential numbers beginning at 1 and counting up. This code checks the S2CID to see that it is only
digits and is less than test_limit; the value in local variable test_limit will need to be updated periodically
as more S2CIDs are issued.
]]
local function s2cid (options)
local id = options.id;
local access = options.access;
local handler = options.handler;
local id_num;
local text;
id_num = id:match ('^[1-9]%d*$'); -- id must be all digits; must not begin with 0; no open access flag
if is_set (id_num) then -- id_num has a value so test it
id_num = tonumber (id_num); -- convert id_num to a number for range testing
if handler.id_limit < id_num then -- if S2CID is outside test limit boundaries
set_message ('err_bad_s2cid'); -- set an error message
options.coins_list_t['S2CID'] = nil; -- when error, unset so not included in COinS
end
else -- when id format incorrect
set_message ('err_bad_s2cid'); -- set an error message
options.coins_list_t['S2CID'] = nil; -- when error, unset so not included in COinS
end
text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = access});
return text;
end
--[[--------------------------< S B N >------------------------------------------------------------------------
9-digit form of ISBN-10; uses same check-digit validation when SBN is prefixed with an additional '0' to make 10 digits
sbn value not made part of COinS metadata because we don't have a url or isn't a COinS-defined identifier (rft.xxx)
or an identifier registered at info-uri.info (info:)
]]
local function sbn (options)
local id = options.id;
local ignore_invalid = options.accept;
local handler = options.handler;
local function return_result (check, err_type) -- local function to handle the various returns
local SBN = internal_link_id ({link = handler.link, label = handler.label, redirect = handler.redirect,
prefix = handler.prefix, id = id, separator = handler.separator});
if not ignore_invalid then -- if not ignoring SBN errors
if not check then
options.coins_list_t['SBN'] = nil; -- when error, unset so not included in COinS; not really necessary here because sbn not made part of COinS
set_message ('err_bad_sbn', {err_type}); -- display an error message
return SBN;
end
else
set_message ('maint_isbn_ignore'); -- add a maint category even when there is no error (ToDo: Possibly switch to separate message for SBNs only)
end
return SBN;
end
if id:match ('[^%s-0-9X]') then
return return_result (false, cfg.err_msg_supl.char); -- fail if SBN contains anything but digits, hyphens, or the uppercase X
end
local ident = id:gsub ('[%s-]', ''); -- remove hyphens and whitespace; they interfere with the rest of the tests
if 9 ~= ident:len() then
return return_result (false, cfg.err_msg_supl.length); -- fail if incorrect length
end
if ident:match ('^%d*X?$') == nil then
return return_result (false, cfg.err_msg_supl.form); -- fail if SBN has 'X' anywhere but last position
end
return return_result (is_valid_isxn ('0' .. ident, 10), cfg.err_msg_supl.check);
end
--[[--------------------------< S S R N >----------------------------------------------------------------------
Format an SSRN, do simple error checking
SSRNs are sequential numbers beginning at 100? and counting up. This code checks the SSRN to see that it is
only digits and is greater than 99 and less than test_limit; the value in local variable test_limit will need
to be updated periodically as more SSRNs are issued.
]]
local function ssrn (options)
local id = options.id;
local handler = options.handler;
local id_num;
local text;
id_num = id:match ('^%d+$'); -- id must be all digits
if is_set (id_num) then -- id_num has a value so test it
id_num = tonumber (id_num); -- convert id_num to a number for range testing
if 100 > id_num or handler.id_limit < id_num then -- if SSRN is outside test limit boundaries
set_message ('err_bad_ssrn'); -- set an error message
options.coins_list_t['SSRN'] = nil; -- when error, unset so not included in COinS
end
else -- when id format incorrect
set_message ('err_bad_ssrn'); -- set an error message
options.coins_list_t['SSRN'] = nil; -- when error, unset so not included in COinS
end
text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = options.access});
return text;
end
--[[--------------------------< U S E N E T _ I D >------------------------------------------------------------
Validate and format a usenet message id. Simple error checking, looks for 'id-left@id-right' not enclosed in
'<' and/or '>' angle brackets.
]]
local function usenet_id (options)
local id = options.id;
local handler = options.handler;
local text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode})
if not id:match('^.+@.+$') or not id:match('^[^<].*[^>]$') then -- doesn't have '@' or has one or first or last character is '< or '>'
set_message ('err_bad_usenet_id') -- add an error message if the message id is invalid
options.coins_list_t['USENETID'] = nil; -- when error, unset so not included in COinS
end
return text;
end
--[[--------------------------< Z B L >-----------------------------------------------------------------------
A numerical identifier in the form nnnn.nnnnn - leading zeros in the first quartet optional
format described here: http://emis.mi.sanu.ac.rs/ZMATH/zmath/en/help/search/
temporary format is apparently eight digits. Anything else is an error
]]
local function zbl (options)
local id = options.id;
local handler = options.handler;
if id:match('^%d%d%d%d%d%d%d%d$') then -- is this identifier using temporary format?
set_message ('maint_zbl'); -- yes, add maint cat
elseif not id:match('^%d?%d?%d?%d%.%d%d%d%d%d$') then -- not temporary, is it normal format?
set_message ('err_bad_zbl'); -- no, set an error message
options.coins_list_t['ZBL'] = nil; -- when error, unset so not included in COinS
end
return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,
prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode});
end
--============================<< I N T E R F A C E F U N C T I O N S >>==========================================
--[[--------------------------< E X T R A C T _ I D S >------------------------------------------------------------
Populates ID table from arguments using configuration settings. Loops through cfg.id_handlers and searches args for
any of the parameters listed in each cfg.id_handlers['...'].parameters. If found, adds the parameter and value to
the identifier list. Emits redundant error message if more than one alias exists in args
]]
local function extract_ids (args)
local id_list = {}; -- list of identifiers found in args
for k, v in pairs (cfg.id_handlers) do -- k is uppercase identifier name as index to cfg.id_handlers; e.g. cfg.id_handlers['ISBN'], v is a table
v = select_one (args, v.parameters, 'err_redundant_parameters' ); -- v.parameters is a table of aliases for k; here we pick one from args if present
if is_set (v) then id_list[k] = v; end -- if found in args, add identifier to our list
end
return id_list;
end
--[[--------------------------< E X T R A C T _ I D _ A C C E S S _ L E V E L S >--------------------------------------
Fetches custom id access levels from arguments using configuration settings. Parameters which have a predefined access
level (e.g. arxiv) do not use this function as they are directly rendered as free without using an additional parameter.
returns a table of k/v pairs where k is same as the identifier's key in cfg.id_handlers and v is the assigned (valid) keyword
access-level values must match the case used in cfg.keywords_lists['id-access'] (lowercase unless there is some special reason for something else)
]]
local function extract_id_access_levels (args, id_list)
local id_accesses_list = {};
for k, v in pairs (cfg.id_handlers) do
local access_param = v.custom_access; -- name of identifier's access-level parameter
if is_set (access_param) then
local access_level = args[access_param]; -- get the assigned value if there is one
if is_set (access_level) then
if not in_array (access_level, cfg.keywords_lists['id-access']) then -- exact match required
set_message ('err_invalid_param_val', {access_param, access_level});
access_level = nil; -- invalid so unset
end
if not is_set (id_list[k]) then -- identifier access-level must have a matching identifier
set_message ('err_param_access_requires_param', {k:lower()}); -- parameter name is uppercase in cfg.id_handlers (k); lowercase for error message
end
id_accesses_list[k] = cfg.keywords_xlate[access_level]; -- get translated keyword
end
end
end
return id_accesses_list;
end
--[[--------------------------< B U I L D _ I D _ L I S T >----------------------------------------------------
render the identifiers into a sorted sequence table
<ID_list_coins_t> is a table of k/v pairs where k is same as key in cfg.id_handlers and v is the assigned value
<options_t> is a table of various k/v option pairs provided in the call to new_build_id_list();
modified by this function and passed to all identifier rendering functions
<access_levels_t> is a table of k/v pairs where k is same as key in cfg.id_handlers and v is the assigned value (if valid)
returns a sequence table of sorted (by hkey - 'handler' key) rendered identifier strings
]]
local function build_id_list (ID_list_coins_t, options_t, access_levels_t)
local ID_list_t = {};
local accept;
local func_map = { --function map points to functions associated with hkey identifier
['ARXIV'] = arxiv,
['ASIN'] = asin,
['BIBCODE'] = bibcode,
['BIORXIV'] = biorxiv,
['CITESEERX'] = citeseerx,
['DOI'] = doi,
['EISSN'] = issn,
['HDL'] = hdl,
['ISBN'] = isbn,
['ISMN'] = ismn,
['ISSN'] = issn,
['JFM'] = jfm,
['JSTOR'] = jstor,
['LCCN'] = lccn,
['MEDRXIV'] = medrxiv,
['MR'] = mr,
['OCLC'] = oclc,
['OL'] = openlibrary,
['OSTI'] = osti,
['PMC'] = pmc,
['PMID'] = pmid,
['RFC'] = rfc,
['S2CID'] = s2cid,
['SBN'] = sbn,
['SSRN'] = ssrn,
['USENETID'] = usenet_id,
['ZBL'] = zbl,
}
for hkey, v in pairs (ID_list_coins_t) do
v, accept = has_accept_as_written (v); -- remove accept-as-written markup if present; accept is boolean true when markup removed; false else
-- every function gets the options table with value v and accept boolean
options_t.hkey = hkey; -- ~/Configuration handler key
options_t.id = v; -- add that identifier value to the options table
options_t.accept = accept; -- add the accept boolean flag
options_t.access = access_levels_t[hkey]; -- add the access level for those that have an |<identifier-access= parameter
options_t.handler = cfg.id_handlers[hkey];
options_t.coins_list_t = ID_list_coins_t; -- pointer to ID_list_coins_t; for |asin= and |ol=; also to keep erroneous values out of the citation's metadata
options_t.coins_list_t[hkey] = v; -- id value without accept-as-written markup for metadata
if options_t.handler.access and not in_array (options_t.handler.access, cfg.keywords_lists['id-access']) then
error (cfg.messages['unknown_ID_access'] .. options_t.handler.access); -- here when handler access key set to a value not listed in list of allowed id access keywords
end
if func_map[hkey] then
local id_text = func_map[hkey] (options_t); -- call the function to get identifier text and any error message
table.insert (ID_list_t, {hkey, id_text}); -- add identifier text to the output sequence table
else
error (cfg.messages['unknown_ID_key'] .. hkey); -- here when func_map doesn't have a function for hkey
end
end
local function comp (a, b) -- used by following table.sort()
return a[1]:lower() < b[1]:lower(); -- sort by hkey
end
table.sort (ID_list_t, comp); -- sequence table of tables sort
for k, v in ipairs (ID_list_t) do -- convert sequence table of tables to simple sequence table of strings
ID_list_t[k] = v[2]; -- v[2] is the identifier rendering from the call to the various functions in func_map{}
end
return ID_list_t;
end
--[[--------------------------< O P T I O N S _ C H E C K >----------------------------------------------------
check that certain option parameters have their associated identifier parameters with values
<ID_list_coins_t> is a table of k/v pairs where k is same as key in cfg.id_handlers and v is the assigned value
<ID_support_t> is a sequence table of tables created in citation0() where each subtable has four elements:
[1] is the support parameter's assigned value; empty string if not set
[2] is a text string same as key in cfg.id_handlers
[3] is cfg.error_conditions key used to create error message
[4] is original ID support parameter name used to create error message
returns nothing; on error emits an appropriate error message
]]
local function options_check (ID_list_coins_t, ID_support_t)
for _, v in ipairs (ID_support_t) do
if is_set (v[1]) and not ID_list_coins_t[v[2]] then -- when support parameter has a value but matching identifier parameter is missing or empty
set_message (v[3], (v[4])); -- emit the appropriate error message
end
end
end
--[[--------------------------< I D E N T I F I E R _ L I S T S _ G E T >--------------------------------------
Creates two identifier lists: a k/v table of identifiers and their values to be used locally and for use in the
COinS metadata, and a sequence table of the rendered identifier strings that will be included in the rendered
citation.
]]
local function identifier_lists_get (args_t, options_t, ID_support_t)
local ID_list_coins_t = extract_ids (args_t); -- get a table of identifiers and their values for use locally and for use in COinS
options_check (ID_list_coins_t, ID_support_t); -- ID support parameters must have matching identifier parameters
local ID_access_levels_t = extract_id_access_levels (args_t, ID_list_coins_t); -- get a table of identifier access levels
local ID_list_t = build_id_list (ID_list_coins_t, options_t, ID_access_levels_t); -- get a sequence table of rendered identifier strings
return ID_list_t, ID_list_coins_t; -- return the tables
end
--[[--------------------------< S E T _ S E L E C T E D _ M O D U L E S >--------------------------------------
Sets local cfg table and imported functions table to same (live or sandbox) as that used by the other modules.
]]
local function set_selected_modules (cfg_table_ptr, utilities_page_ptr)
cfg = cfg_table_ptr;
has_accept_as_written = utilities_page_ptr.has_accept_as_written; -- import functions from select Module:Citation/CS1/Utilities module
is_set = utilities_page_ptr.is_set;
in_array = utilities_page_ptr.in_array;
set_message = utilities_page_ptr.set_message;
select_one = utilities_page_ptr.select_one;
substitute = utilities_page_ptr.substitute;
make_wikilink = utilities_page_ptr.make_wikilink;
z = utilities_page_ptr.z; -- table of tables in Module:Citation/CS1/Utilities
end
--[[--------------------------< E X P O R T E D F U N C T I O N S >------------------------------------------
]]
return {
auto_link_urls = auto_link_urls, -- table of identifier URLs to be used when auto-linking |title=
identifier_lists_get = identifier_lists_get, -- experiment to replace individual calls to build_id_list(), extract_ids, extract_id_access_levels
is_embargoed = is_embargoed;
set_selected_modules = set_selected_modules;
}
k1dr68mpitw4fjf8xuqkl74nhwowac7
વિભાગ:Citation/CS1/COinS
828
72171
886354
821287
2025-04-12T13:48:47Z
en>Trappist the monk
0
sync from sandbox;
886354
Scribunto
text/plain
--[[--------------------------< F O R W A R D D E C L A R A T I O N S >--------------------------------------
]]
local has_accept_as_written, is_set, in_array, remove_wiki_link, strip_apostrophe_markup; -- functions in Module:Citation/CS1/Utilities
local cfg; -- table of configuration tables that are defined in Module:Citation/CS1/Configuration
--[[--------------------------< M A K E _ C O I N S _ T I T L E >----------------------------------------------
Makes a title for COinS from Title and / or ScriptTitle (or any other name-script pairs)
Apostrophe markup (bold, italics) is stripped from each value so that the COinS metadata isn't corrupted with strings
of %27%27...
]]
local function make_coins_title (title, script)
title = has_accept_as_written (title);
if is_set (title) then
title = strip_apostrophe_markup (title); -- strip any apostrophe markup
else
title = ''; -- if not set, make sure title is an empty string
end
if is_set (script) then
script = script:gsub ('^%l%l%s*:%s*', ''); -- remove language prefix if present (script value may now be empty string)
script = strip_apostrophe_markup (script); -- strip any apostrophe markup
else
script = ''; -- if not set, make sure script is an empty string
end
if is_set (title) and is_set (script) then
script = ' ' .. script; -- add a space before we concatenate
end
return title .. script; -- return the concatenation
end
--[[--------------------------< E S C A P E _ L U A _ M A G I C _ C H A R S >----------------------------------
Returns a string where all of Lua's magic characters have been escaped. This is important because functions like
string.gsub() treat their pattern and replace strings as patterns, not literal strings.
]]
local function escape_lua_magic_chars (argument)
argument = argument:gsub("%%", "%%%%"); -- replace % with %%
argument = argument:gsub("([%^%$%(%)%.%[%]%*%+%-%?])", "%%%1"); -- replace all other Lua magic pattern characters
return argument;
end
--[[--------------------------< G E T _ C O I N S _ P A G E S >------------------------------------------------
Extract page numbers from external wikilinks in any of the |page=, |pages=, or |at= parameters for use in COinS.
]]
local function get_coins_pages (pages)
local pattern;
if not is_set (pages) then return pages; end -- if no page numbers then we're done
while true do
pattern = pages:match("%[(%w*:?//[^ ]+%s+)[%w%d].*%]"); -- pattern is the opening bracket, the URL and following space(s): "[url "
if nil == pattern then break; end -- no more URLs
pattern = escape_lua_magic_chars (pattern); -- pattern is not a literal string; escape Lua's magic pattern characters
pages = pages:gsub(pattern, ""); -- remove as many instances of pattern as possible
end
pages = pages:gsub("[%[%]]", ""); -- remove the brackets
pages = pages:gsub("–", "-" ); -- replace endashes with hyphens
pages = pages:gsub("&%w+;", "-" ); -- and replace HTML entities (– etc.) with hyphens; do we need to replace numerical entities like   and the like?
pages = pages:gsub ('%b<>', ''); -- remove html-like tags; spans are added to <Pages> by utilities.hyphen_to_dash() which should not appear in COinS metadata
return pages;
end
--[=[-------------------------< C O I N S _ R E P L A C E _ M A T H _ S T R I P M A R K E R >------------------
There are three options for math markup rendering that depend on the editor's math preference settings. These
settings are at [[Special:Preferences#mw-prefsection-rendering]] and are
PNG images
TeX source
MathML with SVG or PNG fallback
All three are heavy with HTML and CSS which doesn't belong in the metadata.
Without this function, the metadata saved in the raw wikitext contained the rendering determined by the settings
of the last editor to save the page.
This function gets the rendered form of an equation according to the editor's preference before the page is saved. It
then searches the rendering for the text equivalent of the rendered equation and replaces the rendering with that so
that the page is saved without extraneous HTML/CSS markup and with a reasonably readable text form of the equation.
When a replacement is made, this function returns true and the value with replacement; otherwise false and the initial
value. To replace multipe equations it is necessary to call this function from within a loop.
]=]
local function coins_replace_math_stripmarker (value)
local stripmarker = cfg.stripmarkers['math'];
local rendering = value:match (stripmarker); -- is there a math stripmarker
if not rendering then -- when value doesn't have a math stripmarker, abandon this test
return false, value;
end
rendering = mw.text.unstripNoWiki (rendering); -- convert stripmarker into rendered value (or nil? ''? when math render error)
if rendering:match ('alt="[^"]+"') then -- if PNG math option
rendering = rendering:match ('alt="([^"]+)"'); -- extract just the math text
elseif rendering:match ('$%s+.+%s+%$') then -- if TeX math option; $ is legit character that is escapes as \$
rendering = rendering:match ('$%s+(.+)%s+%$') -- extract just the math text
elseif rendering:match ('<annotation[^>]+>.+</annotation>') then -- if MathML math option
rendering = rendering:match ('<annotation[^>]+>(.+)</annotation>') -- extract just the math text
else
return false, value; -- had math stripmarker but not one of the three defined forms
end
return true, value:gsub (stripmarker, rendering, 1);
end
--[[--------------------------< C O I N S _ C L E A N U P >----------------------------------------------------
Cleanup parameter values for the metadata by removing or replacing invisible characters and certain HTML entities.
2015-12-10: there is a bug in mw.text.unstripNoWiki (). It replaces math stripmarkers with the appropriate content
when it shouldn't. See https://phabricator.wikimedia.org/T121085 and Wikipedia_talk:Lua#stripmarkers_and_mw.text.unstripNoWiki.28.29
TODO: move the replacement patterns and replacement values into a table in /Configuration similar to the invisible
characters table?
]]
local function coins_cleanup (value)
local replaced = true; -- default state to get the do loop running
while replaced do -- loop until all math stripmarkers replaced
replaced, value = coins_replace_math_stripmarker (value); -- replace math stripmarker with text representation of the equation
end
value = value:gsub (cfg.stripmarkers['math'], "MATH RENDER ERROR"); -- one or more couldn't be replaced; insert vague error message
value = mw.text.unstripNoWiki (value); -- replace nowiki stripmarkers with their content
value = value:gsub ('<span class="nowrap" style="padding%-left:0%.1em;">'(s?)</span>', "'%1"); -- replace {{'}} or {{'s}} with simple apostrophe or apostrophe-s
value = value:gsub (' ', ' '); -- replace entity with plain space
value = value:gsub ('\226\128\138', ' '); -- replace hair space with plain space
if not mw.ustring.find (value, cfg.indic_script) then -- don't remove zero-width joiner characters from indic script
value = value:gsub ('‍', ''); -- remove ‍ entities
value = mw.ustring.gsub (value, '[\226\128\141\226\128\139\194\173]', ''); -- remove zero-width joiner, zero-width space, soft hyphen
end
value = value:gsub ('[\009\010\013 ]+', ' '); -- replace horizontal tab, line feed, carriage return with plain space
return value;
end
--[[--------------------------< C O I N S >--------------------------------------------------------------------
COinS metadata (see <http://ocoins.info/>) allows automated tools to parse the citation information.
]]
local function COinS(data, class)
if 'table' ~= type(data) or nil == next(data) then
return '';
end
for k, v in pairs (data) do -- spin through all of the metadata parameter values
if 'ID_list' ~= k and 'Authors' ~= k then -- except the ID_list and Author tables (author nowiki stripmarker done when Author table processed)
data[k] = coins_cleanup (v);
end
end
local ctx_ver = "Z39.88-2004";
-- treat table strictly as an array with only set values.
local OCinSoutput = setmetatable( {}, {
__newindex = function(self, key, value)
if is_set(value) then
rawset( self, #self+1, table.concat{ key, '=', mw.uri.encode( remove_wiki_link( value ) ) } );
end
end
});
if in_array (class, {'arxiv', 'biorxiv', 'citeseerx', 'medrxiv', 'ssrn', 'journal', 'news', 'magazine'}) or
(in_array (class, {'conference', 'interview', 'map', 'press release', 'web'}) and is_set(data.Periodical)) or
('citation' == class and is_set(data.Periodical) and not is_set (data.Encyclopedia)) then
OCinSoutput.rft_val_fmt = "info:ofi/fmt:kev:mtx:journal"; -- journal metadata identifier
if in_array (class, {'arxiv', 'biorxiv', 'citeseerx', 'medrxiv', 'ssrn'}) then -- set genre according to the type of citation template we are rendering
OCinSoutput["rft.genre"] = "preprint"; -- cite arxiv, cite biorxiv, cite citeseerx, cite medrxiv, cite ssrn
elseif 'conference' == class then
OCinSoutput["rft.genre"] = "conference"; -- cite conference (when Periodical set)
elseif 'web' == class then
OCinSoutput["rft.genre"] = "unknown"; -- cite web (when Periodical set)
else
OCinSoutput["rft.genre"] = "article"; -- journal and other 'periodical' articles
end
OCinSoutput["rft.jtitle"] = data.Periodical; -- journal only
OCinSoutput["rft.atitle"] = data.Title; -- 'periodical' article titles
-- these used only for periodicals
OCinSoutput["rft.ssn"] = data.Season; -- keywords: winter, spring, summer, fall
OCinSoutput["rft.quarter"] = data.Quarter; -- single digits 1->first quarter, etc.
OCinSoutput["rft.chron"] = data.Chron; -- free-form date components
OCinSoutput["rft.volume"] = data.Volume; -- does not apply to books
OCinSoutput["rft.issue"] = data.Issue;
OCinSoutput['rft.artnum'] = data.ArticleNumber; -- {{cite journal}} only
OCinSoutput["rft.pages"] = data.Pages; -- also used in book metadata
elseif 'thesis' ~= class then -- all others except cite thesis are treated as 'book' metadata; genre distinguishes
OCinSoutput.rft_val_fmt = "info:ofi/fmt:kev:mtx:book"; -- book metadata identifier
if 'report' == class or 'techreport' == class then -- cite report and cite techreport
OCinSoutput["rft.genre"] = "report";
elseif 'conference' == class then -- cite conference when Periodical not set
OCinSoutput["rft.genre"] = "conference";
OCinSoutput["rft.atitle"] = data.Chapter; -- conference paper as chapter in proceedings (book)
elseif in_array (class, {'book', 'citation', 'encyclopaedia', 'interview', 'map'}) then
if is_set (data.Chapter) then
OCinSoutput["rft.genre"] = "bookitem";
OCinSoutput["rft.atitle"] = data.Chapter; -- book chapter, encyclopedia article, interview in a book, or map title
else
if 'map' == class or 'interview' == class then
OCinSoutput["rft.genre"] = 'unknown'; -- standalone map or interview
else
OCinSoutput["rft.genre"] = 'book'; -- book and encyclopedia
end
end
else -- {'audio-visual', 'AV-media-notes', 'DVD-notes', 'episode', 'interview', 'mailinglist', 'map', 'newsgroup', 'podcast', 'press release', 'serial', 'sign', 'speech', 'web'}
OCinSoutput["rft.genre"] = "unknown";
end
OCinSoutput["rft.btitle"] = data.Title; -- book only
OCinSoutput["rft.place"] = data.PublicationPlace; -- book only
OCinSoutput["rft.series"] = data.Series; -- book only
OCinSoutput["rft.pages"] = data.Pages; -- book, journal
OCinSoutput["rft.edition"] = data.Edition; -- book only
OCinSoutput["rft.pub"] = data.PublisherName; -- book and dissertation
else -- cite thesis
OCinSoutput.rft_val_fmt = "info:ofi/fmt:kev:mtx:dissertation"; -- dissertation metadata identifier
OCinSoutput["rft.title"] = data.Title; -- dissertation (also patent but that is not yet supported)
OCinSoutput["rft.degree"] = data.Degree; -- dissertation only
OCinSoutput['rft.inst'] = data.PublisherName; -- book and dissertation
end
-- NB. Not currently supported are "info:ofi/fmt:kev:mtx:patent", "info:ofi/fmt:kev:mtx:dc", "info:ofi/fmt:kev:mtx:sch_svc", "info:ofi/fmt:kev:mtx:ctx"
-- and now common parameters (as much as possible)
OCinSoutput["rft.date"] = data.Date; -- book, journal, dissertation
for k, v in pairs( data.ID_list ) do -- what to do about these? For now assume that they are common to all?
if k == 'ISBN' then v = v:gsub( "[^-0-9X]", "" ); end
local id = cfg.id_handlers[k].COinS;
if string.sub( id or "", 1, 4 ) == 'info' then -- for ids that are in the info:registry
OCinSoutput["rft_id"] = table.concat{ id, "/", v };
elseif string.sub (id or "", 1, 3 ) == 'rft' then -- for isbn, issn, eissn, etc. that have defined COinS keywords
OCinSoutput[ id ] = v;
elseif 'url' == id then -- for urls that are assembled in ~/Identifiers; |asin= and |ol=
OCinSoutput["rft_id"] = table.concat ({data.ID_list[k], "#id-name=", cfg.id_handlers[k].label});
elseif id then -- when cfg.id_handlers[k].COinS is not nil so urls created here
OCinSoutput["rft_id"] = table.concat{ cfg.id_handlers[k].prefix, v, cfg.id_handlers[k].suffix or '', "#id-name=", cfg.id_handlers[k].label }; -- others; provide a URL and indicate identifier name as #fragment (human-readable, but transparent to browsers)
end
end
local last, first;
for k, v in ipairs( data.Authors ) do
last, first = coins_cleanup (v.last), coins_cleanup (v.first or ''); -- replace any nowiki stripmarkers, non-printing or invisible characters
if k == 1 then -- for the first author name only
if is_set(last) and is_set(first) then -- set these COinS values if |first= and |last= specify the first author name
OCinSoutput["rft.aulast"] = last; -- book, journal, dissertation
OCinSoutput["rft.aufirst"] = first; -- book, journal, dissertation
elseif is_set(last) then
OCinSoutput["rft.au"] = last; -- book, journal, dissertation -- otherwise use this form for the first name
end
else -- for all other authors
if is_set(last) and is_set(first) then
OCinSoutput["rft.au"] = table.concat{ last, ", ", first }; -- book, journal, dissertation
elseif is_set(last) then
OCinSoutput["rft.au"] = last; -- book, journal, dissertation
end
-- TODO: At present we do not report "et al.". Add anything special if this condition applies?
end
end
OCinSoutput.rft_id = data.URL;
OCinSoutput.rfr_id = table.concat{ "info:sid/", mw.site.server:match( "[^/]*$" ), ":", data.RawPage };
-- TODO: Add optional extra info:
-- rfr_dat=#REVISION<version> (referrer private data)
-- ctx_id=<data.RawPage>#<ref> (identifier for the context object)
-- ctx_tim=<ts> (timestamp in format yyyy-mm-ddThh:mm:ssTZD or yyyy-mm-dd)
-- ctx_enc=info:ofi/enc:UTF-8 (character encoding)
OCinSoutput = setmetatable( OCinSoutput, nil );
-- sort with version string always first, and combine.
-- table.sort( OCinSoutput );
table.insert( OCinSoutput, 1, "ctx_ver=" .. ctx_ver ); -- such as "Z39.88-2004"
return table.concat(OCinSoutput, "&");
end
--[[--------------------------< S E T _ S E L E C T E D _ M O D U L E S >--------------------------------------
Sets local cfg table and imported functions table to same (live or sandbox) as that used by the other modules.
]]
local function set_selected_modules (cfg_table_ptr, utilities_page_ptr)
cfg = cfg_table_ptr;
has_accept_as_written = utilities_page_ptr.has_accept_as_written; -- import functions from selected Module:Citation/CS1/Utilities module
is_set = utilities_page_ptr.is_set;
in_array = utilities_page_ptr.in_array;
remove_wiki_link = utilities_page_ptr.remove_wiki_link;
strip_apostrophe_markup = utilities_page_ptr.strip_apostrophe_markup;
end
--[[--------------------------< E X P O R T E D F U N C T I O N S >------------------------------------------
]]
return {
make_coins_title = make_coins_title,
get_coins_pages = get_coins_pages,
COinS = COinS,
set_selected_modules = set_selected_modules,
}
2eba9cn0u4yseyeujt1vmrglxrgcllo
886355
886354
2025-06-13T17:03:24Z
KartikMistry
10383
[[:en:Module:Citation/CS1/COinS]] માંથી આયાત કરેલ ૧ પુનરાવર્તન
886354
Scribunto
text/plain
--[[--------------------------< F O R W A R D D E C L A R A T I O N S >--------------------------------------
]]
local has_accept_as_written, is_set, in_array, remove_wiki_link, strip_apostrophe_markup; -- functions in Module:Citation/CS1/Utilities
local cfg; -- table of configuration tables that are defined in Module:Citation/CS1/Configuration
--[[--------------------------< M A K E _ C O I N S _ T I T L E >----------------------------------------------
Makes a title for COinS from Title and / or ScriptTitle (or any other name-script pairs)
Apostrophe markup (bold, italics) is stripped from each value so that the COinS metadata isn't corrupted with strings
of %27%27...
]]
local function make_coins_title (title, script)
title = has_accept_as_written (title);
if is_set (title) then
title = strip_apostrophe_markup (title); -- strip any apostrophe markup
else
title = ''; -- if not set, make sure title is an empty string
end
if is_set (script) then
script = script:gsub ('^%l%l%s*:%s*', ''); -- remove language prefix if present (script value may now be empty string)
script = strip_apostrophe_markup (script); -- strip any apostrophe markup
else
script = ''; -- if not set, make sure script is an empty string
end
if is_set (title) and is_set (script) then
script = ' ' .. script; -- add a space before we concatenate
end
return title .. script; -- return the concatenation
end
--[[--------------------------< E S C A P E _ L U A _ M A G I C _ C H A R S >----------------------------------
Returns a string where all of Lua's magic characters have been escaped. This is important because functions like
string.gsub() treat their pattern and replace strings as patterns, not literal strings.
]]
local function escape_lua_magic_chars (argument)
argument = argument:gsub("%%", "%%%%"); -- replace % with %%
argument = argument:gsub("([%^%$%(%)%.%[%]%*%+%-%?])", "%%%1"); -- replace all other Lua magic pattern characters
return argument;
end
--[[--------------------------< G E T _ C O I N S _ P A G E S >------------------------------------------------
Extract page numbers from external wikilinks in any of the |page=, |pages=, or |at= parameters for use in COinS.
]]
local function get_coins_pages (pages)
local pattern;
if not is_set (pages) then return pages; end -- if no page numbers then we're done
while true do
pattern = pages:match("%[(%w*:?//[^ ]+%s+)[%w%d].*%]"); -- pattern is the opening bracket, the URL and following space(s): "[url "
if nil == pattern then break; end -- no more URLs
pattern = escape_lua_magic_chars (pattern); -- pattern is not a literal string; escape Lua's magic pattern characters
pages = pages:gsub(pattern, ""); -- remove as many instances of pattern as possible
end
pages = pages:gsub("[%[%]]", ""); -- remove the brackets
pages = pages:gsub("–", "-" ); -- replace endashes with hyphens
pages = pages:gsub("&%w+;", "-" ); -- and replace HTML entities (– etc.) with hyphens; do we need to replace numerical entities like   and the like?
pages = pages:gsub ('%b<>', ''); -- remove html-like tags; spans are added to <Pages> by utilities.hyphen_to_dash() which should not appear in COinS metadata
return pages;
end
--[=[-------------------------< C O I N S _ R E P L A C E _ M A T H _ S T R I P M A R K E R >------------------
There are three options for math markup rendering that depend on the editor's math preference settings. These
settings are at [[Special:Preferences#mw-prefsection-rendering]] and are
PNG images
TeX source
MathML with SVG or PNG fallback
All three are heavy with HTML and CSS which doesn't belong in the metadata.
Without this function, the metadata saved in the raw wikitext contained the rendering determined by the settings
of the last editor to save the page.
This function gets the rendered form of an equation according to the editor's preference before the page is saved. It
then searches the rendering for the text equivalent of the rendered equation and replaces the rendering with that so
that the page is saved without extraneous HTML/CSS markup and with a reasonably readable text form of the equation.
When a replacement is made, this function returns true and the value with replacement; otherwise false and the initial
value. To replace multipe equations it is necessary to call this function from within a loop.
]=]
local function coins_replace_math_stripmarker (value)
local stripmarker = cfg.stripmarkers['math'];
local rendering = value:match (stripmarker); -- is there a math stripmarker
if not rendering then -- when value doesn't have a math stripmarker, abandon this test
return false, value;
end
rendering = mw.text.unstripNoWiki (rendering); -- convert stripmarker into rendered value (or nil? ''? when math render error)
if rendering:match ('alt="[^"]+"') then -- if PNG math option
rendering = rendering:match ('alt="([^"]+)"'); -- extract just the math text
elseif rendering:match ('$%s+.+%s+%$') then -- if TeX math option; $ is legit character that is escapes as \$
rendering = rendering:match ('$%s+(.+)%s+%$') -- extract just the math text
elseif rendering:match ('<annotation[^>]+>.+</annotation>') then -- if MathML math option
rendering = rendering:match ('<annotation[^>]+>(.+)</annotation>') -- extract just the math text
else
return false, value; -- had math stripmarker but not one of the three defined forms
end
return true, value:gsub (stripmarker, rendering, 1);
end
--[[--------------------------< C O I N S _ C L E A N U P >----------------------------------------------------
Cleanup parameter values for the metadata by removing or replacing invisible characters and certain HTML entities.
2015-12-10: there is a bug in mw.text.unstripNoWiki (). It replaces math stripmarkers with the appropriate content
when it shouldn't. See https://phabricator.wikimedia.org/T121085 and Wikipedia_talk:Lua#stripmarkers_and_mw.text.unstripNoWiki.28.29
TODO: move the replacement patterns and replacement values into a table in /Configuration similar to the invisible
characters table?
]]
local function coins_cleanup (value)
local replaced = true; -- default state to get the do loop running
while replaced do -- loop until all math stripmarkers replaced
replaced, value = coins_replace_math_stripmarker (value); -- replace math stripmarker with text representation of the equation
end
value = value:gsub (cfg.stripmarkers['math'], "MATH RENDER ERROR"); -- one or more couldn't be replaced; insert vague error message
value = mw.text.unstripNoWiki (value); -- replace nowiki stripmarkers with their content
value = value:gsub ('<span class="nowrap" style="padding%-left:0%.1em;">'(s?)</span>', "'%1"); -- replace {{'}} or {{'s}} with simple apostrophe or apostrophe-s
value = value:gsub (' ', ' '); -- replace entity with plain space
value = value:gsub ('\226\128\138', ' '); -- replace hair space with plain space
if not mw.ustring.find (value, cfg.indic_script) then -- don't remove zero-width joiner characters from indic script
value = value:gsub ('‍', ''); -- remove ‍ entities
value = mw.ustring.gsub (value, '[\226\128\141\226\128\139\194\173]', ''); -- remove zero-width joiner, zero-width space, soft hyphen
end
value = value:gsub ('[\009\010\013 ]+', ' '); -- replace horizontal tab, line feed, carriage return with plain space
return value;
end
--[[--------------------------< C O I N S >--------------------------------------------------------------------
COinS metadata (see <http://ocoins.info/>) allows automated tools to parse the citation information.
]]
local function COinS(data, class)
if 'table' ~= type(data) or nil == next(data) then
return '';
end
for k, v in pairs (data) do -- spin through all of the metadata parameter values
if 'ID_list' ~= k and 'Authors' ~= k then -- except the ID_list and Author tables (author nowiki stripmarker done when Author table processed)
data[k] = coins_cleanup (v);
end
end
local ctx_ver = "Z39.88-2004";
-- treat table strictly as an array with only set values.
local OCinSoutput = setmetatable( {}, {
__newindex = function(self, key, value)
if is_set(value) then
rawset( self, #self+1, table.concat{ key, '=', mw.uri.encode( remove_wiki_link( value ) ) } );
end
end
});
if in_array (class, {'arxiv', 'biorxiv', 'citeseerx', 'medrxiv', 'ssrn', 'journal', 'news', 'magazine'}) or
(in_array (class, {'conference', 'interview', 'map', 'press release', 'web'}) and is_set(data.Periodical)) or
('citation' == class and is_set(data.Periodical) and not is_set (data.Encyclopedia)) then
OCinSoutput.rft_val_fmt = "info:ofi/fmt:kev:mtx:journal"; -- journal metadata identifier
if in_array (class, {'arxiv', 'biorxiv', 'citeseerx', 'medrxiv', 'ssrn'}) then -- set genre according to the type of citation template we are rendering
OCinSoutput["rft.genre"] = "preprint"; -- cite arxiv, cite biorxiv, cite citeseerx, cite medrxiv, cite ssrn
elseif 'conference' == class then
OCinSoutput["rft.genre"] = "conference"; -- cite conference (when Periodical set)
elseif 'web' == class then
OCinSoutput["rft.genre"] = "unknown"; -- cite web (when Periodical set)
else
OCinSoutput["rft.genre"] = "article"; -- journal and other 'periodical' articles
end
OCinSoutput["rft.jtitle"] = data.Periodical; -- journal only
OCinSoutput["rft.atitle"] = data.Title; -- 'periodical' article titles
-- these used only for periodicals
OCinSoutput["rft.ssn"] = data.Season; -- keywords: winter, spring, summer, fall
OCinSoutput["rft.quarter"] = data.Quarter; -- single digits 1->first quarter, etc.
OCinSoutput["rft.chron"] = data.Chron; -- free-form date components
OCinSoutput["rft.volume"] = data.Volume; -- does not apply to books
OCinSoutput["rft.issue"] = data.Issue;
OCinSoutput['rft.artnum'] = data.ArticleNumber; -- {{cite journal}} only
OCinSoutput["rft.pages"] = data.Pages; -- also used in book metadata
elseif 'thesis' ~= class then -- all others except cite thesis are treated as 'book' metadata; genre distinguishes
OCinSoutput.rft_val_fmt = "info:ofi/fmt:kev:mtx:book"; -- book metadata identifier
if 'report' == class or 'techreport' == class then -- cite report and cite techreport
OCinSoutput["rft.genre"] = "report";
elseif 'conference' == class then -- cite conference when Periodical not set
OCinSoutput["rft.genre"] = "conference";
OCinSoutput["rft.atitle"] = data.Chapter; -- conference paper as chapter in proceedings (book)
elseif in_array (class, {'book', 'citation', 'encyclopaedia', 'interview', 'map'}) then
if is_set (data.Chapter) then
OCinSoutput["rft.genre"] = "bookitem";
OCinSoutput["rft.atitle"] = data.Chapter; -- book chapter, encyclopedia article, interview in a book, or map title
else
if 'map' == class or 'interview' == class then
OCinSoutput["rft.genre"] = 'unknown'; -- standalone map or interview
else
OCinSoutput["rft.genre"] = 'book'; -- book and encyclopedia
end
end
else -- {'audio-visual', 'AV-media-notes', 'DVD-notes', 'episode', 'interview', 'mailinglist', 'map', 'newsgroup', 'podcast', 'press release', 'serial', 'sign', 'speech', 'web'}
OCinSoutput["rft.genre"] = "unknown";
end
OCinSoutput["rft.btitle"] = data.Title; -- book only
OCinSoutput["rft.place"] = data.PublicationPlace; -- book only
OCinSoutput["rft.series"] = data.Series; -- book only
OCinSoutput["rft.pages"] = data.Pages; -- book, journal
OCinSoutput["rft.edition"] = data.Edition; -- book only
OCinSoutput["rft.pub"] = data.PublisherName; -- book and dissertation
else -- cite thesis
OCinSoutput.rft_val_fmt = "info:ofi/fmt:kev:mtx:dissertation"; -- dissertation metadata identifier
OCinSoutput["rft.title"] = data.Title; -- dissertation (also patent but that is not yet supported)
OCinSoutput["rft.degree"] = data.Degree; -- dissertation only
OCinSoutput['rft.inst'] = data.PublisherName; -- book and dissertation
end
-- NB. Not currently supported are "info:ofi/fmt:kev:mtx:patent", "info:ofi/fmt:kev:mtx:dc", "info:ofi/fmt:kev:mtx:sch_svc", "info:ofi/fmt:kev:mtx:ctx"
-- and now common parameters (as much as possible)
OCinSoutput["rft.date"] = data.Date; -- book, journal, dissertation
for k, v in pairs( data.ID_list ) do -- what to do about these? For now assume that they are common to all?
if k == 'ISBN' then v = v:gsub( "[^-0-9X]", "" ); end
local id = cfg.id_handlers[k].COinS;
if string.sub( id or "", 1, 4 ) == 'info' then -- for ids that are in the info:registry
OCinSoutput["rft_id"] = table.concat{ id, "/", v };
elseif string.sub (id or "", 1, 3 ) == 'rft' then -- for isbn, issn, eissn, etc. that have defined COinS keywords
OCinSoutput[ id ] = v;
elseif 'url' == id then -- for urls that are assembled in ~/Identifiers; |asin= and |ol=
OCinSoutput["rft_id"] = table.concat ({data.ID_list[k], "#id-name=", cfg.id_handlers[k].label});
elseif id then -- when cfg.id_handlers[k].COinS is not nil so urls created here
OCinSoutput["rft_id"] = table.concat{ cfg.id_handlers[k].prefix, v, cfg.id_handlers[k].suffix or '', "#id-name=", cfg.id_handlers[k].label }; -- others; provide a URL and indicate identifier name as #fragment (human-readable, but transparent to browsers)
end
end
local last, first;
for k, v in ipairs( data.Authors ) do
last, first = coins_cleanup (v.last), coins_cleanup (v.first or ''); -- replace any nowiki stripmarkers, non-printing or invisible characters
if k == 1 then -- for the first author name only
if is_set(last) and is_set(first) then -- set these COinS values if |first= and |last= specify the first author name
OCinSoutput["rft.aulast"] = last; -- book, journal, dissertation
OCinSoutput["rft.aufirst"] = first; -- book, journal, dissertation
elseif is_set(last) then
OCinSoutput["rft.au"] = last; -- book, journal, dissertation -- otherwise use this form for the first name
end
else -- for all other authors
if is_set(last) and is_set(first) then
OCinSoutput["rft.au"] = table.concat{ last, ", ", first }; -- book, journal, dissertation
elseif is_set(last) then
OCinSoutput["rft.au"] = last; -- book, journal, dissertation
end
-- TODO: At present we do not report "et al.". Add anything special if this condition applies?
end
end
OCinSoutput.rft_id = data.URL;
OCinSoutput.rfr_id = table.concat{ "info:sid/", mw.site.server:match( "[^/]*$" ), ":", data.RawPage };
-- TODO: Add optional extra info:
-- rfr_dat=#REVISION<version> (referrer private data)
-- ctx_id=<data.RawPage>#<ref> (identifier for the context object)
-- ctx_tim=<ts> (timestamp in format yyyy-mm-ddThh:mm:ssTZD or yyyy-mm-dd)
-- ctx_enc=info:ofi/enc:UTF-8 (character encoding)
OCinSoutput = setmetatable( OCinSoutput, nil );
-- sort with version string always first, and combine.
-- table.sort( OCinSoutput );
table.insert( OCinSoutput, 1, "ctx_ver=" .. ctx_ver ); -- such as "Z39.88-2004"
return table.concat(OCinSoutput, "&");
end
--[[--------------------------< S E T _ S E L E C T E D _ M O D U L E S >--------------------------------------
Sets local cfg table and imported functions table to same (live or sandbox) as that used by the other modules.
]]
local function set_selected_modules (cfg_table_ptr, utilities_page_ptr)
cfg = cfg_table_ptr;
has_accept_as_written = utilities_page_ptr.has_accept_as_written; -- import functions from selected Module:Citation/CS1/Utilities module
is_set = utilities_page_ptr.is_set;
in_array = utilities_page_ptr.in_array;
remove_wiki_link = utilities_page_ptr.remove_wiki_link;
strip_apostrophe_markup = utilities_page_ptr.strip_apostrophe_markup;
end
--[[--------------------------< E X P O R T E D F U N C T I O N S >------------------------------------------
]]
return {
make_coins_title = make_coins_title,
get_coins_pages = get_coins_pages,
COinS = COinS,
set_selected_modules = set_selected_modules,
}
2eba9cn0u4yseyeujt1vmrglxrgcllo
વિજય રૂપાણી
0
75647
886426
886324
2025-06-14T05:22:31Z
KartikMistry
10383
સા. સાફ-સફાઇ.
886426
wikitext
text/x-wiki
{{સુધારો}}
{{Infobox officeholder
| image = ભવનાથ_શિવરાત્રી_મેલા_ના_મુખ્ય_અતિથિ_શ્રી_વિજયભાઈ_રૂપાણી_સર.jpg
| caption = વિજય રૂપાણી, [[ભવનાથ]]ના શિવરાત્રી મેળા દરમિયાન, ૨૦૧૮
| alt = વિજય રૂપાણી
| constituency = રાજકોટ પશ્ચિમ
| term_start = [[ડિસેમ્બર ૨૬ | ૨૬ ડિસેમ્બર ૨૦૧૭]]
| term_end = [[સપ્ટેમ્બર ૧૧ | ૧૧ સપ્ટેમ્બર ૨૦૨૧]]
| order = [[ગુજરાતના મુખ્યમંત્રીઓ|ગુજરાતના ૧૬માં મુખ્યમંત્રી]]
| assembly = ગુજરાત વિધાન સભા
| predecessor = [[આનંદીબેન પટેલ]]
| successor = [[ભૂપેન્દ્ર પટેલ]]
| parliament =
| majority =
| constituency_AM1 = રાજકોટ પશ્ચિમ
| term_start1 = ૧૯ ઓક્ટોબર ૨૦૧૪
| term_end1 =
| office5 = રાજ્ય સભાના સભ્ય, ગુજરાત માટે
| term_start5 = ૨૦૦૬
| term_end5 = ૨૦૧૨
| birth_date = ૨ ઓગસ્ટ ૧૯૫૬<ref name="IA2007"/>
| birth_place = રંગૂન, [[મ્યાનમાર|બર્મા]]<ref name="IA2007"/>
| death_date = ૧૨ જૂન ૨૦૨૫<ref name="ગુ.સ.૧૨ જૂન" />
| death_place = [[અમદાવાદ]]
| nationality = ભારતીય
| party = [[ભારતીય જનતા પાર્ટી|ભારતીય જનતા પક્ષ]]
| spouse = અંજલી રૂપાણી
| children = એક પુત્ર, એક પુત્રી
| parents = રમણિકલાલ, માયાબેન
| residence = રાજકોટ
| occupation = રાજકારણી
| cabinet = [[ગુજરાત સરકાર]]
| portfolio = વાહનવ્યવહાર, જળ પુરવઠો, શ્રમ અને રોજગાર (નવેમ્બર ૨૦૧૪ - ઓગસ્ટ ૨૦૧૬)
}}
'''વિજય રૂપાણી''' [[ભારતીય જનતા પાર્ટી|ભારતીય જનતા પક્ષના]] રાજનેતા હતા. પશ્ચિમ રાજકોટનાં પ્રતિનિધિરૂપે એ ગુજરાત વિધાનસભાના સદસ્ય હતા. એમણે ૭ ઓગસ્ટ ૨૦૧૬ ના રોજ ગુજરાત રાજ્યના સોળમા મુખ્યમંત્રી તરીકે શપથ ગ્રહણ કર્યા હતા.<ref name="TOI1">{{Cite web|url=http://timesofindia.indiatimes.com/india/Vijay-Rupani-sworn-in-as-new-Gujarat-Chief-Minister/articleshow/53582905.cms|title=Vijay Rupani sworn in as new Gujarat Chief Minister|date=૭ ઓગસ્ટ ૨૦૧૬|access-date= ૭ ઓગસ્ટ ૨૦૧૬|website=The Times of India}}</ref> મુખ્યમંત્રી બન્યા પહેલાં તેઓ ભારતીય જનતા પક્ષના રાજ્યાધ્યક્ષ અને રાજ્યસભાના સભ્ય પણ હતા.<ref name="p">{{Cite web|url=http://www.govtofficials.com/rajya_name_wiseto.asp?name=VIJAY%20KUMAR%20RAMNIKLAL%20RUPANI|title=MEMBERS OF PARLIAMENT|access-date=૧૪ ડિસેમ્બર ૨૦૧૪|archive-date=2018-12-26|archive-url=https://web.archive.org/web/20181226121048/http://www.govtofficials.com/rajya_name_wiseto.asp?name=VIJAY%20KUMAR%20RAMNIKLAL%20RUPANI|url-status=dead}}</ref> ૧૨મી જૂન ૨૦૨૫ના રોજ સર્જાયેલી [[૨૦૨૫ અમદાવાદ વિમાન દુર્ઘટના]]માં તેમનું નિધન થયું હતું.<ref name="ગુ.સ.૧૨ જૂન">{{Cite web|title=અમદાવાદ લંડન વિમાન દુર્ઘટનામાં ગુજરાતના પૂર્વ મુખ્યમંત્રી વિજય રૂપાણીનું નિધન, નેતાઓએ ટ્વિટ કરી વ્યક્ત કર્યું દુ:ખ|url=https://www.gujaratsamachar.com/news/gujarat/former-gujarat-cm-vijay-rupani-death-in-ahmedabad-london-plane-crash|access-date=2025-06-13|website=www.gujaratsamachar.com|language=gu}}</ref>
== પ્રારંભિક જીવન ==
૨ ઓગસ્ટ ૧૯૫૬ના રોજ [[મ્યાનમાર|બર્મા]] દેશના રંગૂન મહાનગરમાં વિજયભાઈનો જન્મ થયો હતો.<ref name="p"></ref> તેમના પિતાનું નામ રમણિકલાલ અને માતાનું નામ માયાબેન હતું. તેઓ જૈનધર્મનાં અનુયાયી હતા.<ref name="toi0816">{{Citation|url=http://m.timesofindia.com/india/How-Vijay-Rupani-pipped-Nitin-Patel-to-become-Gujarat-chief-minister/articleshow/53563396.cms|title=How Vijay Rupani pipped Nitin Patel to become Gujarat chief minister|work=The Times of India|date=૫ ઓગસ્ટ ૨૦૧૬}}</ref><ref name="et">{{Cite news|title=Saurashtra strongman Vijay Rupani in Gujarat Cabinet|url=http://articles.economictimes.indiatimes.com/2014-11-20/news/56304021_1_babku-undhad-cabinet-expansion-state-bjp|access-date=૧૪ ડિસેમ્બર ૨૦૧૪|publisher=Economic Times|date=૨૦ નવેમ્બર ૨૦૧૪|archive-date=2016-08-23|archive-url=https://web.archive.org/web/20160823005608/http://articles.economictimes.indiatimes.com/2014-11-20/news/56304021_1_babku-undhad-cabinet-expansion-state-bjp|url-status=dead}}</ref> રમણિકલાલ સપરિવાર ૧૯૬૦ માં બર્માને છોડીને હંમેશને માટે ભારત આવ્યા. પછી તેઓ ગુજરાતનાં રાજકોટમાં રહેવા લાગ્યા. વિજયભાઈ ધર્મેન્દ્રસિંહ મહાવિદ્યાલયમાંથી સ્નાતક થયા અને પછી સૌરાષ્ટ્ર વિશ્વવિદ્યાલયથી એલ.એલ.બી થયા.<ref name="IA2007">{{Cite web|url=http://164.100.24.167:8080/members/website/Mainweb.asp?mpcode=2008|title=Vijay Rupani: Member's Web Site|date=૩૦ સપ્ટેમ્બર ૨૦૦૭|access-date=૫ ઓગસ્ટ ૨૦૧૬|website=Internet Archive|archive-date=2007-09-30|archive-url=https://web.archive.org/web/20070930201434/http://164.100.24.167:8080/members/website/Mainweb.asp?mpcode=2008|url-status=dead}}</ref><ref name="p"></ref><ref name="Indiex2016">{{Cite web|url=http://indianexpress.com/article/india/india-news-india/vijay-rupani-gujarat-cm-profile-2956811/|title=Vijay Rupani: A swayamsevak, stock broker and founder of a trust for poor|date=૬ ઓગસ્ટ ૨૦૧૬|access-date= ૬ ઓગસ્ટ ૨૦૧૬|website=The Indian Express}}</ref><ref name="vrnx2016">{{Cite web|url=http://timesofindia.indiatimes.com/india/How-Vijay-Rupani-pipped-Nitin-Patel-to-become-Gujarat-chief-minister/articleshow/53563396.cms|title=How Vijay Rupani pipped Nitin Patel to become Gujarat chief minister|date=૫ ઓગસ્ટ ૨૦૧૬|access-date=૬ ઓગસ્ટ ૨૦૧૬|website=The Times of India|archive-url=https://web.archive.org/web/20160806042050/http://timesofindia.indiatimes.com/india/How-Vijay-Rupani-pipped-Nitin-Patel-to-become-Gujarat-chief-minister/articleshow/53563396.cms|archive-date=2016-08-06|url-status=live}}</ref>
== કારકિર્દી ==
અખિલ ભારતીય વિદ્યાર્થી પરિષદમાં સક્રિય હતાં ત્યારથી વિજય રૂપાણી પોતાના જીવનને સાવર્જનિકપણે જીવવા લાગ્યા.<ref name="et"></ref> પછી તે [[રાષ્ટ્રીય સ્વયંસેવક સંઘ|રાષ્ટ્રિય સ્વયંસેવક સંઘ]] સાથે અને જનસંઘ સાથે જોડાયા. તેઓ ૧૯૭૧થી ભારતીય જનતા પક્ષનાં કાર્યકર્તા રહ્યા હતા. ૧૯૭૬ વર્ષમાં [[કટોકટી કાળ (ભારત)|ભારતની કટોકટી]] વખતે વિજય રૂપાણી ભાવનગરનાં અને ભુજ-મહાનગરનાં કારાગારમાં બંદી હતા. એ બંને કારાગરોમાં એ ૧૧ માસ સુધી હતાં. વિજય રૂપાણી ૧૯૭૮ વર્ષથી ૧૯૮૧ સુધી રાષ્ટ્રિય સ્વયંસેવક સંઘના પ્રચારક પણ હતાં. ૧૯૮૭ વર્ષે રાજકોટ મહાનગરપાલિકામાં સભ્ય તરીકે ચુંટાયા અને જલ નિકાસ સમિતિનાં અધ્યક્ષ બન્યા. સમનન્તર વર્ષે એ રાજકોટ મહાનગરપાલિકાનાં સ્થાયી સમિતિનાં અધ્યક્ષ બન્યા. એ પદ પર તેઓ ૧૯૮૮ થી ૧૯૯૬ સુધી આરૂઢ હતા. વચ્ચે ૧૯૯૫ માં વર્ષે એમનું રાજકોટ મહાનગરપાલિકાનાં સ્થાયી સમિતિના અધ્યક્ષત્વે પુનઃ ચયન થયું. પછી એમણે ૧૯૯૬ થી ૧૯૯૭ પર્યન્ત રાજકોટ મહાનગરનાં મેયર તરીકે પણ કાર્ય કર્યું. પછી ૧૯૯૮ વર્ષે ભાજપ-પક્ષનાં ગુજરાત રાજ્ય વિભાગનાં વિભાગાધ્યક્ષ થયા. જ્યારે કેશુભાઈ પટેલ ગુજરાત રાજ્યનાં મુખ્યમંત્રી હતા, ત્યારે વિજય રૂપાણીએ ઘોષણાપત્ર સમિતિનાં આધ્યક્ષનું વહન કર્યું. ૨૦૦૬ વર્ષે ગુજરાત પર્યટન વિભાગનાં અધ્યક્ષ બન્યાં. ૨૦૦૬ - ૨૦૧૨ એ રાજ્યસભાનાં સદસ્ય હતા. જ્યારે નરેન્દ્ર મોદી ગુજરાત રાજ્યનાં મુખ્યમંત્રી હતા, ત્યારે વિજય રૂપાણી ભાજપનાં ગુજરાત વિભાગનાં ચાર વાર અધ્યક્ષ, ગુજરાત મહાનગરપાલિકાનાં વિત્તવિભાગનાં એકવાર અધ્યક્ષ (૨૦૧૩) બન્યા. ૧૯ ફેબ્રુઆરી ૨૦૧૬ ના રોજ વિજય રૂપાણી ભાજપનાં ગુજરાત વિભાગનાં અધ્યક્ષ બન્યા. એમનાં પૂર્વ એ પદે આર. સી. ફલ્દુ આરૂઢ હતા. ૨૦૧૪ ઓગસ્ટ માસમાં જ્યારે ગુજરાત વિધાનસભાનાં વક્તા વજુભાઈ પશ્ચિમ રાજકોટનાં વિધાયકત્વે ત્યાગપત્ર આપી [[કર્ણાટક]] રાજ્યનાં રાજ્યપાલ બન્યા, ત્યારે ભાજપ-દળ દ્વારા વિજયનું નામાંકન એ રિક્ત સ્થાનની પૂર્તિ માટે થયું. ૧૯ ઓક્ટોબર ૨૦૧૪ ના રોજ વિધાયક પદનાં નિર્વાચનમાં વિજય રૂપાણી બહુમતે જિત્યા.
૧૧ સપ્ટેમ્બર ૨૦૨૧ ના રોજ એમણે ગુજરાતના મુખ્યમંત્રી પદેથી રાજીનામું આપ્યું હતું.<ref>{{cite web|author=દિવ્ય ભાસ્કર ઓનલાઈન પૂર્તિ|date=૧૨ સપ્ટેમ્બર ૨૦૨૧|title=ગુજરાતના મુખ્યમંત્રી પદેથી શ્રી વિજય રૂપાણીએ રાજીનામું આપ્યું|url=https://www.divyabhaskar.co.in/local/gujarat/ahmedabad/news/divyabhaskar-morning-podcast-vijay-rupani-resigns-as-cm-new-gujarat-cm-to-be-annouced-today-rainfall-forecast-128913842.html?ref=inbound_More_News|url-status=live|work=|publisher=દિવ્ય ભાસ્કર|accessdate=૧૨ સપ્ટેમ્બર ૨૦૨૧}}</ref>
== સંદર્ભ ==
{{Reflist}}
{{ગુજરાતના મુખ્યમંત્રીઓ}}
[[શ્રેણી:ગુજરાતના મુખ્યમંત્રી]]
[[શ્રેણી:રાજકારણી]]
[[શ્રેણી:વ્યક્તિત્વ]]
h0i6q6vqa18liublxozwse5fttj7wqk
વિભાગ:Hatnote list
828
76246
886382
810416
2023-11-13T21:00:31Z
en>Nihiltres
0
Updated from sandbox: added support for punctuation collapse when text is italicized. The update's content includes changes by users Johnuniq, Dexxor, and Nihiltres.
886382
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote list --
-- --
-- This module produces and formats lists for use in hatnotes. In particular, --
-- it implements the for-see list, i.e. lists of "For X, see Y" statements, --
-- as used in {{about}}, {{redirect}}, and their variants. Also introduced --
-- are andList & orList helpers for formatting lists with those conjunctions. --
--------------------------------------------------------------------------------
local mArguments --initialize lazily
local mFormatLink = require('Module:Format link')
local mHatnote = require('Module:Hatnote')
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local p = {}
--------------------------------------------------------------------------------
-- List stringification helper functions
--
-- These functions are used for stringifying lists, usually page lists inside
-- the "Y" portion of "For X, see Y" for-see items.
--------------------------------------------------------------------------------
--default options table used across the list stringification functions
local stringifyListDefaultOptions = {
conjunction = "and",
separator = ",",
altSeparator = ";",
space = " ",
formatted = false
}
--Searches display text only
local function searchDisp(haystack, needle)
return string.find(
string.sub(haystack, (string.find(haystack, '|') or 0) + 1), needle
)
end
-- Stringifies a list generically; probably shouldn't be used directly
local function stringifyList(list, options)
-- Type-checks, defaults, and a shortcut
checkType("stringifyList", 1, list, "table")
if #list == 0 then return nil end
checkType("stringifyList", 2, options, "table", true)
options = options or {}
for k, v in pairs(stringifyListDefaultOptions) do
if options[k] == nil then options[k] = v end
end
local s = options.space
-- Format the list if requested
if options.formatted then
list = mFormatLink.formatPages(
{categorizeMissing = mHatnote.missingTargetCat}, list
)
end
-- Set the separator; if any item contains it, use the alternate separator
local separator = options.separator
for k, v in pairs(list) do
if searchDisp(v, separator) then
separator = options.altSeparator
break
end
end
-- Set the conjunction, apply Oxford comma, and force a comma if #1 has "§"
local conjunction = s .. options.conjunction .. s
if #list == 2 and searchDisp(list[1], "§") or #list > 2 then
conjunction = separator .. conjunction
end
-- Return the formatted string
return mw.text.listToText(list, separator .. s, conjunction)
end
--DRY function
function p.conjList (conj, list, fmt)
return stringifyList(list, {conjunction = conj, formatted = fmt})
end
-- Stringifies lists with "and" or "or"
function p.andList (...) return p.conjList("and", ...) end
function p.orList (...) return p.conjList("or", ...) end
--------------------------------------------------------------------------------
-- For see
--
-- Makes a "For X, see [[Y]]." list from raw parameters. Intended for the
-- {{about}} and {{redirect}} templates and their variants.
--------------------------------------------------------------------------------
--default options table used across the forSee family of functions
local forSeeDefaultOptions = {
andKeyword = 'and',
title = mw.title.getCurrentTitle().text,
otherText = 'other uses',
forSeeForm = 'For %s, see %s.',
}
--Collapses duplicate punctuation at end of string, ignoring italics and links
local function punctuationCollapse (text)
return text:match("[.?!]('?)%1(%]?)%2%.$") and text:sub(1, -2) or text
end
-- Structures arguments into a table for stringification, & options
function p.forSeeArgsToTable (args, from, options)
-- Type-checks and defaults
checkType("forSeeArgsToTable", 1, args, 'table')
checkType("forSeeArgsToTable", 2, from, 'number', true)
from = from or 1
checkType("forSeeArgsToTable", 3, options, 'table', true)
options = options or {}
for k, v in pairs(forSeeDefaultOptions) do
if options[k] == nil then options[k] = v end
end
-- maxArg's gotten manually because getArgs() and table.maxn aren't friends
local maxArg = 0
for k, v in pairs(args) do
if type(k) == 'number' and k > maxArg then maxArg = k end
end
-- Structure the data out from the parameter list:
-- * forTable is the wrapper table, with forRow rows
-- * Rows are tables of a "use" string & a "pages" table of pagename strings
-- * Blanks are left empty for defaulting elsewhere, but can terminate list
local forTable = {}
local i = from
local terminated = false
-- If there is extra text, and no arguments are given, give nil value
-- to not produce default of "For other uses, see foo (disambiguation)"
if options.extratext and i > maxArg then return nil end
-- Loop to generate rows
repeat
-- New empty row
local forRow = {}
-- On blank use, assume list's ended & break at end of this loop
forRow.use = args[i]
if not args[i] then terminated = true end
-- New empty list of pages
forRow.pages = {}
-- Insert first pages item if present
table.insert(forRow.pages, args[i + 1])
-- If the param after next is "and", do inner loop to collect params
-- until the "and"'s stop. Blanks are ignored: "1|and||and|3" → {1, 3}
while args[i + 2] == options.andKeyword do
if args[i + 3] then
table.insert(forRow.pages, args[i + 3])
end
-- Increment to next "and"
i = i + 2
end
-- Increment to next use
i = i + 2
-- Append the row
table.insert(forTable, forRow)
until terminated or i > maxArg
return forTable
end
-- Stringifies a table as formatted by forSeeArgsToTable
function p.forSeeTableToString (forSeeTable, options)
-- Type-checks and defaults
checkType("forSeeTableToString", 1, forSeeTable, "table", true)
checkType("forSeeTableToString", 2, options, "table", true)
options = options or {}
for k, v in pairs(forSeeDefaultOptions) do
if options[k] == nil then options[k] = v end
end
-- Stringify each for-see item into a list
local strList = {}
if forSeeTable then
for k, v in pairs(forSeeTable) do
local useStr = v.use or options.otherText
local pagesStr =
p.andList(v.pages, true) or
mFormatLink._formatLink{
categorizeMissing = mHatnote.missingTargetCat,
link = mHatnote.disambiguate(options.title)
}
local forSeeStr = string.format(options.forSeeForm, useStr, pagesStr)
forSeeStr = punctuationCollapse(forSeeStr)
table.insert(strList, forSeeStr)
end
end
if options.extratext then table.insert(strList, punctuationCollapse(options.extratext..'.')) end
-- Return the concatenated list
return table.concat(strList, ' ')
end
-- Produces a "For X, see [[Y]]" string from arguments. Expects index gaps
-- but not blank/whitespace values. Ignores named args and args < "from".
function p._forSee (args, from, options)
local forSeeTable = p.forSeeArgsToTable(args, from, options)
return p.forSeeTableToString(forSeeTable, options)
end
-- As _forSee, but uses the frame.
function p.forSee (frame, from, options)
mArguments = require('Module:Arguments')
return p._forSee(mArguments.getArgs(frame), from, options)
end
return p
3c31ivxc2t731ceyfvqcqlnp9acgg3s
886383
886382
2025-06-13T17:03:26Z
KartikMistry
10383
[[:en:Module:Hatnote_list]] માંથી આયાત કરેલ ૧ પુનરાવર્તન
886382
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote list --
-- --
-- This module produces and formats lists for use in hatnotes. In particular, --
-- it implements the for-see list, i.e. lists of "For X, see Y" statements, --
-- as used in {{about}}, {{redirect}}, and their variants. Also introduced --
-- are andList & orList helpers for formatting lists with those conjunctions. --
--------------------------------------------------------------------------------
local mArguments --initialize lazily
local mFormatLink = require('Module:Format link')
local mHatnote = require('Module:Hatnote')
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local p = {}
--------------------------------------------------------------------------------
-- List stringification helper functions
--
-- These functions are used for stringifying lists, usually page lists inside
-- the "Y" portion of "For X, see Y" for-see items.
--------------------------------------------------------------------------------
--default options table used across the list stringification functions
local stringifyListDefaultOptions = {
conjunction = "and",
separator = ",",
altSeparator = ";",
space = " ",
formatted = false
}
--Searches display text only
local function searchDisp(haystack, needle)
return string.find(
string.sub(haystack, (string.find(haystack, '|') or 0) + 1), needle
)
end
-- Stringifies a list generically; probably shouldn't be used directly
local function stringifyList(list, options)
-- Type-checks, defaults, and a shortcut
checkType("stringifyList", 1, list, "table")
if #list == 0 then return nil end
checkType("stringifyList", 2, options, "table", true)
options = options or {}
for k, v in pairs(stringifyListDefaultOptions) do
if options[k] == nil then options[k] = v end
end
local s = options.space
-- Format the list if requested
if options.formatted then
list = mFormatLink.formatPages(
{categorizeMissing = mHatnote.missingTargetCat}, list
)
end
-- Set the separator; if any item contains it, use the alternate separator
local separator = options.separator
for k, v in pairs(list) do
if searchDisp(v, separator) then
separator = options.altSeparator
break
end
end
-- Set the conjunction, apply Oxford comma, and force a comma if #1 has "§"
local conjunction = s .. options.conjunction .. s
if #list == 2 and searchDisp(list[1], "§") or #list > 2 then
conjunction = separator .. conjunction
end
-- Return the formatted string
return mw.text.listToText(list, separator .. s, conjunction)
end
--DRY function
function p.conjList (conj, list, fmt)
return stringifyList(list, {conjunction = conj, formatted = fmt})
end
-- Stringifies lists with "and" or "or"
function p.andList (...) return p.conjList("and", ...) end
function p.orList (...) return p.conjList("or", ...) end
--------------------------------------------------------------------------------
-- For see
--
-- Makes a "For X, see [[Y]]." list from raw parameters. Intended for the
-- {{about}} and {{redirect}} templates and their variants.
--------------------------------------------------------------------------------
--default options table used across the forSee family of functions
local forSeeDefaultOptions = {
andKeyword = 'and',
title = mw.title.getCurrentTitle().text,
otherText = 'other uses',
forSeeForm = 'For %s, see %s.',
}
--Collapses duplicate punctuation at end of string, ignoring italics and links
local function punctuationCollapse (text)
return text:match("[.?!]('?)%1(%]?)%2%.$") and text:sub(1, -2) or text
end
-- Structures arguments into a table for stringification, & options
function p.forSeeArgsToTable (args, from, options)
-- Type-checks and defaults
checkType("forSeeArgsToTable", 1, args, 'table')
checkType("forSeeArgsToTable", 2, from, 'number', true)
from = from or 1
checkType("forSeeArgsToTable", 3, options, 'table', true)
options = options or {}
for k, v in pairs(forSeeDefaultOptions) do
if options[k] == nil then options[k] = v end
end
-- maxArg's gotten manually because getArgs() and table.maxn aren't friends
local maxArg = 0
for k, v in pairs(args) do
if type(k) == 'number' and k > maxArg then maxArg = k end
end
-- Structure the data out from the parameter list:
-- * forTable is the wrapper table, with forRow rows
-- * Rows are tables of a "use" string & a "pages" table of pagename strings
-- * Blanks are left empty for defaulting elsewhere, but can terminate list
local forTable = {}
local i = from
local terminated = false
-- If there is extra text, and no arguments are given, give nil value
-- to not produce default of "For other uses, see foo (disambiguation)"
if options.extratext and i > maxArg then return nil end
-- Loop to generate rows
repeat
-- New empty row
local forRow = {}
-- On blank use, assume list's ended & break at end of this loop
forRow.use = args[i]
if not args[i] then terminated = true end
-- New empty list of pages
forRow.pages = {}
-- Insert first pages item if present
table.insert(forRow.pages, args[i + 1])
-- If the param after next is "and", do inner loop to collect params
-- until the "and"'s stop. Blanks are ignored: "1|and||and|3" → {1, 3}
while args[i + 2] == options.andKeyword do
if args[i + 3] then
table.insert(forRow.pages, args[i + 3])
end
-- Increment to next "and"
i = i + 2
end
-- Increment to next use
i = i + 2
-- Append the row
table.insert(forTable, forRow)
until terminated or i > maxArg
return forTable
end
-- Stringifies a table as formatted by forSeeArgsToTable
function p.forSeeTableToString (forSeeTable, options)
-- Type-checks and defaults
checkType("forSeeTableToString", 1, forSeeTable, "table", true)
checkType("forSeeTableToString", 2, options, "table", true)
options = options or {}
for k, v in pairs(forSeeDefaultOptions) do
if options[k] == nil then options[k] = v end
end
-- Stringify each for-see item into a list
local strList = {}
if forSeeTable then
for k, v in pairs(forSeeTable) do
local useStr = v.use or options.otherText
local pagesStr =
p.andList(v.pages, true) or
mFormatLink._formatLink{
categorizeMissing = mHatnote.missingTargetCat,
link = mHatnote.disambiguate(options.title)
}
local forSeeStr = string.format(options.forSeeForm, useStr, pagesStr)
forSeeStr = punctuationCollapse(forSeeStr)
table.insert(strList, forSeeStr)
end
end
if options.extratext then table.insert(strList, punctuationCollapse(options.extratext..'.')) end
-- Return the concatenated list
return table.concat(strList, ' ')
end
-- Produces a "For X, see [[Y]]" string from arguments. Expects index gaps
-- but not blank/whitespace values. Ignores named args and args < "from".
function p._forSee (args, from, options)
local forSeeTable = p.forSeeArgsToTable(args, from, options)
return p.forSeeTableToString(forSeeTable, options)
end
-- As _forSee, but uses the frame.
function p.forSee (frame, from, options)
mArguments = require('Module:Arguments')
return p._forSee(mArguments.getArgs(frame), from, options)
end
return p
3c31ivxc2t731ceyfvqcqlnp9acgg3s
વિભાગ:String2
828
80840
886372
869063
2025-02-19T12:24:59Z
en>Gonnym
0
fix
886372
Scribunto
text/plain
require ('strict');
local p = {}
p.trim = function(frame)
return mw.text.trim(frame.args[1] or "")
end
p.sentence = function (frame)
-- {{lc:}} is strip-marker safe, string.lower is not.
frame.args[1] = frame:callParserFunction('lc', frame.args[1])
return p.ucfirst(frame)
end
p.ucfirst = function (frame)
local s = frame.args[1];
if not s or '' == s or s:match ('^%s+$') then -- when <s> is nil, empty, or only whitespace
return s; -- abandon because nothing to do
end
s = mw.text.trim( frame.args[1] or "" )
local s1 = ""
local prefix_patterns_t = { -- sequence of prefix patterns
'^\127[^\127]*UNIQ%-%-%a+%-%x+%-QINU[^\127]*\127', -- stripmarker
'^([%*;:#]+)', -- various list markup
'^(\'\'\'*)', -- bold / italic markup
'^(%b<>)', -- html-like tags because some templates render these
'^(&%a+;)', -- html character entities because some templates render these
'^(&#%d+;)', -- html numeric (decimal) entities because some templates render these
'^(&#x%x+;)', -- html numeric (hexadecimal) entities because some templates render these
'^(%s+)', -- any whitespace characters
'^([%(%)%-%+%?%.%%!~!@%$%^&_={}/`,‘’„“”ʻ|\"\'\\]+)', -- miscellaneous punctuation
}
local prefixes_t = {}; -- list, bold/italic, and html-like markup, & whitespace saved here
local function prefix_strip (s) -- local function to strip prefixes from <s>
for _, pattern in ipairs (prefix_patterns_t) do -- spin through <prefix_patterns_t>
if s:match (pattern) then -- when there is a match
local prefix = s:match (pattern); -- get a copy of the matched prefix
table.insert (prefixes_t, prefix); -- save it
s = s:sub (prefix:len() + 1); -- remove the prefix from <s>
return s, true; -- return <s> without prefix and flag; force restart at top of sequence because misc punct removal can break stripmarker
end
end
return s; -- no prefix found; return <s> with nil flag
end
local prefix_removed; -- flag; boolean true as long as prefix_strip() finds and removes a prefix
repeat -- one by one remove list, bold/italic, html-like markup, whitespace, etc from start of <s>
s, prefix_removed = prefix_strip (s);
until (not prefix_removed); -- until <prefix_removed> is nil
s1 = table.concat (prefixes_t); -- recreate the prefix string for later reattachment
local first_text = mw.ustring.match (s, '^%[%[[^%]]+%]%]'); -- extract wikilink at start of string if present; TODO: this can be string.match()?
local upcased;
if first_text then
if first_text:match ('^%[%[[^|]+|[^%]]+%]%]') then -- if <first_text> is a piped link
upcased = mw.ustring.match (s, '^%[%[[^|]+|%W*(%w)'); -- get first letter character
upcased = mw.ustring.upper (upcased); -- upcase first letter character
s = mw.ustring.gsub (s, '^(%[%[[^|]+|%W*)%w', '%1' .. upcased); -- replace
else -- here when <first_text> is a wikilink but not a piped link
upcased = mw.ustring.match (s, '^%[%[%W*%w'); -- get '[[' and first letter
upcased = mw.ustring.upper (upcased); -- upcase first letter character
s = mw.ustring.gsub (s, '^%[%[%W*%w', upcased); -- replace; no capture needed here
end
elseif s:match ('^%[%S+%s+[^%]]+%]') then -- if <s> is a ext link of some sort; must have label text
upcased = mw.ustring.match (s, '^%[%S+%s+%W*(%w)'); -- get first letter character
upcased = mw.ustring.upper (upcased); -- upcase first letter character
s = mw.ustring.gsub (s, '^(%[%S+%s+%W*)%w', '%1' .. upcased); -- replace
elseif s:match ('^%[%S+%s*%]') then -- if <s> is a ext link without label text; nothing to do
return s1 .. s; -- reattach prefix string (if present) and done
else -- <s> is not a wikilink or ext link; assume plain text
upcased = mw.ustring.match (s, '^%W*%w'); -- get the first letter character
upcased = mw.ustring.upper (upcased); -- upcase first letter character
s = mw.ustring.gsub (s, '^%W*%w', upcased); -- replace; no capture needed here
end
return s1 .. s; -- reattach prefix string (if present) and done
end
p.title = function (frame)
-- http://grammar.yourdictionary.com/capitalization/rules-for-capitalization-in-titles.html
-- recommended by The U.S. Government Printing Office Style Manual:
-- "Capitalize all words in titles of publications and documents,
-- except a, an, the, at, by, for, in, of, on, to, up, and, as, but, or, and nor."
local alwayslower = {['a'] = 1, ['an'] = 1, ['the'] = 1,
['and'] = 1, ['but'] = 1, ['or'] = 1, ['for'] = 1,
['nor'] = 1, ['on'] = 1, ['in'] = 1, ['at'] = 1, ['to'] = 1,
['from'] = 1, ['by'] = 1, ['of'] = 1, ['up'] = 1 }
local res = ''
local s = mw.text.trim( frame.args[1] or "" )
local words = mw.text.split( s, " ")
for i, s in ipairs(words) do
-- {{lc:}} is strip-marker safe, string.lower is not.
s = frame:callParserFunction('lc', s)
if i == 1 or alwayslower[s] ~= 1 then
s = mw.getContentLanguage():ucfirst(s)
end
words[i] = s
end
return table.concat(words, " ")
end
-- findlast finds the last item in a list
-- the first unnamed parameter is the list
-- the second, optional unnamed parameter is the list separator (default = comma space)
-- returns the whole list if separator not found
p.findlast = function(frame)
local s = mw.text.trim( frame.args[1] or "" )
local sep = frame.args[2] or ""
if sep == "" then sep = ", " end
local pattern = ".*" .. sep .. "(.*)"
local a, b, last = s:find(pattern)
if a then
return last
else
return s
end
end
-- stripZeros finds the first number and strips leading zeros (apart from units)
-- e.g "0940" -> "940"; "Year: 0023" -> "Year: 23"; "00.12" -> "0.12"
p.stripZeros = function(frame)
local s = mw.text.trim(frame.args[1] or "")
local n = tonumber( string.match( s, "%d+" ) ) or ""
s = string.gsub( s, "%d+", n, 1 )
return s
end
-- nowiki ensures that a string of text is treated by the MediaWiki software as just a string
-- it takes an unnamed parameter and trims whitespace, then removes any wikicode
p.nowiki = function(frame)
local str = mw.text.trim(frame.args[1] or "")
return mw.text.nowiki(str)
end
-- split splits text at boundaries specified by separator
-- and returns the chunk for the index idx (starting at 1)
-- #invoke:String2 |split |text |separator |index |true/false
-- #invoke:String2 |split |txt=text |sep=separator |idx=index |plain=true/false
-- if plain is false/no/0 then separator is treated as a Lua pattern - defaults to plain=true
p.split = function(frame)
local args = frame.args
if not(args[1] or args.txt) then args = frame:getParent().args end
local txt = args[1] or args.txt or ""
if txt == "" then return nil end
local sep = (args[2] or args.sep or ""):gsub('"', '')
local idx = tonumber(args[3] or args.idx) or 1
local plain = (args[4] or args.plain or "true"):sub(1,1)
plain = (plain ~= "f" and plain ~= "n" and plain ~= "0")
local splittbl = mw.text.split( txt, sep, plain )
if idx < 0 then idx = #splittbl + idx + 1 end
return splittbl[idx]
end
-- val2percent scans through a string, passed as either the first unnamed parameter or |txt=
-- it converts each number it finds into a percentage and returns the resultant string.
p.val2percent = function(frame)
local args = frame.args
if not(args[1] or args.txt) then args = frame:getParent().args end
local txt = mw.text.trim(args[1] or args.txt or "")
if txt == "" then return nil end
local function v2p (x)
x = (tonumber(x) or 0) * 100
if x == math.floor(x) then x = math.floor(x) end
return x .. "%"
end
txt = txt:gsub("%d[%d%.]*", v2p) -- store just the string
return txt
end
-- one2a scans through a string, passed as either the first unnamed parameter or |txt=
-- it converts each occurrence of 'one ' into either 'a ' or 'an ' and returns the resultant string.
p.one2a = function(frame)
local args = frame.args
if not(args[1] or args.txt) then args = frame:getParent().args end
local txt = mw.text.trim(args[1] or args.txt or "")
if txt == "" then return nil end
txt = txt:gsub(" one ", " a "):gsub("^one", "a"):gsub("One ", "A "):gsub("a ([aeiou])", "an %1"):gsub("A ([aeiou])", "An %1")
return txt
end
-- findpagetext returns the position of a piece of text in a page
-- First positional parameter or |text is the search text
-- Optional parameter |title is the page title, defaults to current page
-- Optional parameter |plain is either true for plain search (default) or false for Lua pattern search
-- Optional parameter |nomatch is the return value when no match is found; default is nil
p._findpagetext = function(args)
-- process parameters
local nomatch = args.nomatch or ""
if nomatch == "" then nomatch = nil end
--
local text = mw.text.trim(args[1] or args.text or "")
if text == "" then return nil end
--
local title = args.title or ""
local titleobj
if title == "" then
titleobj = mw.title.getCurrentTitle()
else
titleobj = mw.title.new(title)
end
--
local plain = args.plain or ""
if plain:sub(1, 1) == "f" then plain = false else plain = true end
-- get the page content and look for 'text' - return position or nomatch
local content = titleobj and titleobj:getContent()
return content and mw.ustring.find(content, text, 1, plain) or nomatch
end
p.findpagetext = function(frame)
local args = frame.args
local pargs = frame:getParent().args
for k, v in pairs(pargs) do
args[k] = v
end
if not (args[1] or args.text) then return nil end
-- just the first value
return (p._findpagetext(args))
end
-- returns the decoded url. Inverse of parser function {{urlencode:val|TYPE}}
-- Type is:
-- QUERY decodes + to space (default)
-- PATH does no extra decoding
-- WIKI decodes _ to space
p._urldecode = function(url, type)
url = url or ""
type = (type == "PATH" or type == "WIKI") and type
return mw.uri.decode( url, type )
end
-- {{#invoke:String2|urldecode|url=url|type=type}}
p.urldecode = function(frame)
return mw.uri.decode( frame.args.url, frame.args.type )
end
-- what follows was merged from Module:StringFunc
-- helper functions
p._GetParameters = require('Module:GetParameters')
-- Argument list helper function, as per Module:String
p._getParameters = p._GetParameters.getParameters
-- Escape Pattern helper function so that all characters are treated as plain text, as per Module:String
function p._escapePattern( pattern_str )
return mw.ustring.gsub( pattern_str, "([%(%)%.%%%+%-%*%?%[%^%$%]])", "%%%1" )
end
-- Helper Function to interpret boolean strings, as per Module:String
p._getBoolean = p._GetParameters.getBoolean
--[[
Strip
This function Strips characters from string
Usage:
{{#invoke:String2|strip|source_string|characters_to_strip|plain_flag}}
Parameters
source: The string to strip
chars: The pattern or list of characters to strip from string, replaced with ''
plain: A flag indicating that the chars should be understood as plain text. defaults to true.
Leading and trailing whitespace is also automatically stripped from the string.
]]
function p.strip( frame )
local new_args = p._getParameters( frame.args, {'source', 'chars', 'plain'} )
local source_str = new_args['source'] or ''
local chars = new_args['chars'] or '' or 'characters'
source_str = mw.text.trim(source_str)
if source_str == '' or chars == '' then
return source_str
end
local l_plain = p._getBoolean( new_args['plain'] or true )
if l_plain then
chars = p._escapePattern( chars )
end
local result
result = mw.ustring.gsub(source_str, "["..chars.."]", '')
return result
end
--[[
Match any
Returns the index of the first given pattern to match the input. Patterns must be consecutively numbered.
Returns the empty string if nothing matches for use in {{#if:}}
Usage:
{{#invoke:String2|matchAll|source=123 abc|456|abc}} returns '2'.
Parameters:
source: the string to search
plain: A flag indicating that the patterns should be understood as plain text. defaults to true.
1, 2, 3, ...: the patterns to search for
]]
function p.matchAny(frame)
local source_str = frame.args['source'] or error('The source parameter is mandatory.')
local l_plain = p._getBoolean( frame.args['plain'] or true )
for i = 1, math.huge do
local pattern = frame.args[i]
if not pattern then return '' end
if mw.ustring.find(source_str, pattern, 1, l_plain) then
return tostring(i)
end
end
end
--[[--------------------------< H Y P H E N _ T O _ D A S H >--------------------------------------------------
Converts a hyphen to a dash under certain conditions. The hyphen must separate
like items; unlike items are returned unmodified. These forms are modified:
letter - letter (A - B)
digit - digit (4-5)
digit separator digit - digit separator digit (4.1-4.5 or 4-1-4-5)
letterdigit - letterdigit (A1-A5) (an optional separator between letter and
digit is supported – a.1-a.5 or a-1-a-5)
digitletter - digitletter (5a - 5d) (an optional separator between letter and
digit is supported – 5.a-5.d or 5-a-5-d)
any other forms are returned unmodified.
str may be a comma- or semicolon-separated list
]]
function p.hyphen_to_dash( str, spacing )
if (str == nil or str == '') then
return str
end
local accept
str = mw.text.decode(str, true ) -- replace html entities with their characters; semicolon mucks up the text.split
local out = {}
local list = mw.text.split (str, '%s*[,;]%s*') -- split str at comma or semicolon separators if there are any
for _, item in ipairs (list) do -- for each item in the list
item = mw.text.trim(item) -- trim whitespace
item, accept = item:gsub ('^%(%((.+)%)%)$', '%1')
if accept == 0 and mw.ustring.match (item, '^%w*[%.%-]?%w+%s*[%-–—]%s*%w*[%.%-]?%w+$') then -- if a hyphenated range or has endash or emdash separators
if item:match ('^%a+[%.%-]?%d+%s*%-%s*%a+[%.%-]?%d+$') or -- letterdigit hyphen letterdigit (optional separator between letter and digit)
item:match ('^%d+[%.%-]?%a+%s*%-%s*%d+[%.%-]?%a+$') or -- digitletter hyphen digitletter (optional separator between digit and letter)
item:match ('^%d+[%.%-]%d+%s*%-%s*%d+[%.%-]%d+$') or -- digit separator digit hyphen digit separator digit
item:match ('^%d+%s*%-%s*%d+$') or -- digit hyphen digit
item:match ('^%a+%s*%-%s*%a+$') then -- letter hyphen letter
item = item:gsub ('(%w*[%.%-]?%w+)%s*%-%s*(%w*[%.%-]?%w+)', '%1–%2') -- replace hyphen, remove extraneous space characters
else
item = mw.ustring.gsub (item, '%s*[–—]%s*', '–') -- for endash or emdash separated ranges, replace em with en, remove extraneous whitespace
end
end
table.insert (out, item) -- add the (possibly modified) item to the output table
end
local temp_str = table.concat (out, ',' .. spacing) -- concatenate the output table into a comma separated string
temp_str, accept = temp_str:gsub ('^%(%((.+)%)%)$', '%1') -- remove accept-this-as-written markup when it wraps all of concatenated out
if accept ~= 0 then
temp_str = str:gsub ('^%(%((.+)%)%)$', '%1') -- when global markup removed, return original str; do it this way to suppress boolean second return value
end
return temp_str
end
function p.hyphen2dash( frame )
local str = frame.args[1] or ''
local spacing = frame.args[2] or ' ' -- space is part of the standard separator for normal spacing (but in conjunction with templates r/rp/ran we may need a narrower spacing
return p.hyphen_to_dash(str, spacing)
end
-- Similar to [[Module:String#endswith]]
function p.startswith(frame)
return (frame.args[1]:sub(1, frame.args[2]:len()) == frame.args[2]) and 'yes' or ''
end
-- Implements [[Template:Isnumeric]]
function p.isnumeric(frame)
local s = frame.args[1] or frame:getParent().args[1]
local boolean = (frame.args.boolean or frame:getParent().args.boolean) == 'true'
if type(s) == 'string' and mw.getContentLanguage():parseFormattedNumber( s ) then
return boolean and 1 or s
end
return boolean and 0 or ''
end
-- Checks if a value in a group of numbers is not an interger.
-- Allows usage of an |empty= parameter to allow empty values to be skipped.
function p.isInteger(frame)
local values = frame.args or frame:getParent().args
local allow_empty = frame.args.empty or frame:getParent().args.empty
for _, value in ipairs(values) do
-- Trim spaces
value = value and value:gsub("^%s*(.-)%s*$", "%1")
if value == "" or value == nil then
if not allow_empty then
return false -- Empty values are not allowed
end
else
value = tonumber(value)
if not (type(value) == "number" and value == math.floor(value)) then
return false
end
end
end
return true
end
-- Returns an error found in a string.
function p.getError(frame)
local text = frame.args[1] or frame:getParent().args[1]
local error_message = text:match('(<strong class="error">.-</strong>)')
return error_message or nil
end
return p
8jl034otxz47vwg2szaa5x536gkylpd
886373
886372
2025-06-13T17:03:26Z
KartikMistry
10383
[[:en:Module:String2]] માંથી આયાત કરેલ ૧ પુનરાવર્તન
886372
Scribunto
text/plain
require ('strict');
local p = {}
p.trim = function(frame)
return mw.text.trim(frame.args[1] or "")
end
p.sentence = function (frame)
-- {{lc:}} is strip-marker safe, string.lower is not.
frame.args[1] = frame:callParserFunction('lc', frame.args[1])
return p.ucfirst(frame)
end
p.ucfirst = function (frame)
local s = frame.args[1];
if not s or '' == s or s:match ('^%s+$') then -- when <s> is nil, empty, or only whitespace
return s; -- abandon because nothing to do
end
s = mw.text.trim( frame.args[1] or "" )
local s1 = ""
local prefix_patterns_t = { -- sequence of prefix patterns
'^\127[^\127]*UNIQ%-%-%a+%-%x+%-QINU[^\127]*\127', -- stripmarker
'^([%*;:#]+)', -- various list markup
'^(\'\'\'*)', -- bold / italic markup
'^(%b<>)', -- html-like tags because some templates render these
'^(&%a+;)', -- html character entities because some templates render these
'^(&#%d+;)', -- html numeric (decimal) entities because some templates render these
'^(&#x%x+;)', -- html numeric (hexadecimal) entities because some templates render these
'^(%s+)', -- any whitespace characters
'^([%(%)%-%+%?%.%%!~!@%$%^&_={}/`,‘’„“”ʻ|\"\'\\]+)', -- miscellaneous punctuation
}
local prefixes_t = {}; -- list, bold/italic, and html-like markup, & whitespace saved here
local function prefix_strip (s) -- local function to strip prefixes from <s>
for _, pattern in ipairs (prefix_patterns_t) do -- spin through <prefix_patterns_t>
if s:match (pattern) then -- when there is a match
local prefix = s:match (pattern); -- get a copy of the matched prefix
table.insert (prefixes_t, prefix); -- save it
s = s:sub (prefix:len() + 1); -- remove the prefix from <s>
return s, true; -- return <s> without prefix and flag; force restart at top of sequence because misc punct removal can break stripmarker
end
end
return s; -- no prefix found; return <s> with nil flag
end
local prefix_removed; -- flag; boolean true as long as prefix_strip() finds and removes a prefix
repeat -- one by one remove list, bold/italic, html-like markup, whitespace, etc from start of <s>
s, prefix_removed = prefix_strip (s);
until (not prefix_removed); -- until <prefix_removed> is nil
s1 = table.concat (prefixes_t); -- recreate the prefix string for later reattachment
local first_text = mw.ustring.match (s, '^%[%[[^%]]+%]%]'); -- extract wikilink at start of string if present; TODO: this can be string.match()?
local upcased;
if first_text then
if first_text:match ('^%[%[[^|]+|[^%]]+%]%]') then -- if <first_text> is a piped link
upcased = mw.ustring.match (s, '^%[%[[^|]+|%W*(%w)'); -- get first letter character
upcased = mw.ustring.upper (upcased); -- upcase first letter character
s = mw.ustring.gsub (s, '^(%[%[[^|]+|%W*)%w', '%1' .. upcased); -- replace
else -- here when <first_text> is a wikilink but not a piped link
upcased = mw.ustring.match (s, '^%[%[%W*%w'); -- get '[[' and first letter
upcased = mw.ustring.upper (upcased); -- upcase first letter character
s = mw.ustring.gsub (s, '^%[%[%W*%w', upcased); -- replace; no capture needed here
end
elseif s:match ('^%[%S+%s+[^%]]+%]') then -- if <s> is a ext link of some sort; must have label text
upcased = mw.ustring.match (s, '^%[%S+%s+%W*(%w)'); -- get first letter character
upcased = mw.ustring.upper (upcased); -- upcase first letter character
s = mw.ustring.gsub (s, '^(%[%S+%s+%W*)%w', '%1' .. upcased); -- replace
elseif s:match ('^%[%S+%s*%]') then -- if <s> is a ext link without label text; nothing to do
return s1 .. s; -- reattach prefix string (if present) and done
else -- <s> is not a wikilink or ext link; assume plain text
upcased = mw.ustring.match (s, '^%W*%w'); -- get the first letter character
upcased = mw.ustring.upper (upcased); -- upcase first letter character
s = mw.ustring.gsub (s, '^%W*%w', upcased); -- replace; no capture needed here
end
return s1 .. s; -- reattach prefix string (if present) and done
end
p.title = function (frame)
-- http://grammar.yourdictionary.com/capitalization/rules-for-capitalization-in-titles.html
-- recommended by The U.S. Government Printing Office Style Manual:
-- "Capitalize all words in titles of publications and documents,
-- except a, an, the, at, by, for, in, of, on, to, up, and, as, but, or, and nor."
local alwayslower = {['a'] = 1, ['an'] = 1, ['the'] = 1,
['and'] = 1, ['but'] = 1, ['or'] = 1, ['for'] = 1,
['nor'] = 1, ['on'] = 1, ['in'] = 1, ['at'] = 1, ['to'] = 1,
['from'] = 1, ['by'] = 1, ['of'] = 1, ['up'] = 1 }
local res = ''
local s = mw.text.trim( frame.args[1] or "" )
local words = mw.text.split( s, " ")
for i, s in ipairs(words) do
-- {{lc:}} is strip-marker safe, string.lower is not.
s = frame:callParserFunction('lc', s)
if i == 1 or alwayslower[s] ~= 1 then
s = mw.getContentLanguage():ucfirst(s)
end
words[i] = s
end
return table.concat(words, " ")
end
-- findlast finds the last item in a list
-- the first unnamed parameter is the list
-- the second, optional unnamed parameter is the list separator (default = comma space)
-- returns the whole list if separator not found
p.findlast = function(frame)
local s = mw.text.trim( frame.args[1] or "" )
local sep = frame.args[2] or ""
if sep == "" then sep = ", " end
local pattern = ".*" .. sep .. "(.*)"
local a, b, last = s:find(pattern)
if a then
return last
else
return s
end
end
-- stripZeros finds the first number and strips leading zeros (apart from units)
-- e.g "0940" -> "940"; "Year: 0023" -> "Year: 23"; "00.12" -> "0.12"
p.stripZeros = function(frame)
local s = mw.text.trim(frame.args[1] or "")
local n = tonumber( string.match( s, "%d+" ) ) or ""
s = string.gsub( s, "%d+", n, 1 )
return s
end
-- nowiki ensures that a string of text is treated by the MediaWiki software as just a string
-- it takes an unnamed parameter and trims whitespace, then removes any wikicode
p.nowiki = function(frame)
local str = mw.text.trim(frame.args[1] or "")
return mw.text.nowiki(str)
end
-- split splits text at boundaries specified by separator
-- and returns the chunk for the index idx (starting at 1)
-- #invoke:String2 |split |text |separator |index |true/false
-- #invoke:String2 |split |txt=text |sep=separator |idx=index |plain=true/false
-- if plain is false/no/0 then separator is treated as a Lua pattern - defaults to plain=true
p.split = function(frame)
local args = frame.args
if not(args[1] or args.txt) then args = frame:getParent().args end
local txt = args[1] or args.txt or ""
if txt == "" then return nil end
local sep = (args[2] or args.sep or ""):gsub('"', '')
local idx = tonumber(args[3] or args.idx) or 1
local plain = (args[4] or args.plain or "true"):sub(1,1)
plain = (plain ~= "f" and plain ~= "n" and plain ~= "0")
local splittbl = mw.text.split( txt, sep, plain )
if idx < 0 then idx = #splittbl + idx + 1 end
return splittbl[idx]
end
-- val2percent scans through a string, passed as either the first unnamed parameter or |txt=
-- it converts each number it finds into a percentage and returns the resultant string.
p.val2percent = function(frame)
local args = frame.args
if not(args[1] or args.txt) then args = frame:getParent().args end
local txt = mw.text.trim(args[1] or args.txt or "")
if txt == "" then return nil end
local function v2p (x)
x = (tonumber(x) or 0) * 100
if x == math.floor(x) then x = math.floor(x) end
return x .. "%"
end
txt = txt:gsub("%d[%d%.]*", v2p) -- store just the string
return txt
end
-- one2a scans through a string, passed as either the first unnamed parameter or |txt=
-- it converts each occurrence of 'one ' into either 'a ' or 'an ' and returns the resultant string.
p.one2a = function(frame)
local args = frame.args
if not(args[1] or args.txt) then args = frame:getParent().args end
local txt = mw.text.trim(args[1] or args.txt or "")
if txt == "" then return nil end
txt = txt:gsub(" one ", " a "):gsub("^one", "a"):gsub("One ", "A "):gsub("a ([aeiou])", "an %1"):gsub("A ([aeiou])", "An %1")
return txt
end
-- findpagetext returns the position of a piece of text in a page
-- First positional parameter or |text is the search text
-- Optional parameter |title is the page title, defaults to current page
-- Optional parameter |plain is either true for plain search (default) or false for Lua pattern search
-- Optional parameter |nomatch is the return value when no match is found; default is nil
p._findpagetext = function(args)
-- process parameters
local nomatch = args.nomatch or ""
if nomatch == "" then nomatch = nil end
--
local text = mw.text.trim(args[1] or args.text or "")
if text == "" then return nil end
--
local title = args.title or ""
local titleobj
if title == "" then
titleobj = mw.title.getCurrentTitle()
else
titleobj = mw.title.new(title)
end
--
local plain = args.plain or ""
if plain:sub(1, 1) == "f" then plain = false else plain = true end
-- get the page content and look for 'text' - return position or nomatch
local content = titleobj and titleobj:getContent()
return content and mw.ustring.find(content, text, 1, plain) or nomatch
end
p.findpagetext = function(frame)
local args = frame.args
local pargs = frame:getParent().args
for k, v in pairs(pargs) do
args[k] = v
end
if not (args[1] or args.text) then return nil end
-- just the first value
return (p._findpagetext(args))
end
-- returns the decoded url. Inverse of parser function {{urlencode:val|TYPE}}
-- Type is:
-- QUERY decodes + to space (default)
-- PATH does no extra decoding
-- WIKI decodes _ to space
p._urldecode = function(url, type)
url = url or ""
type = (type == "PATH" or type == "WIKI") and type
return mw.uri.decode( url, type )
end
-- {{#invoke:String2|urldecode|url=url|type=type}}
p.urldecode = function(frame)
return mw.uri.decode( frame.args.url, frame.args.type )
end
-- what follows was merged from Module:StringFunc
-- helper functions
p._GetParameters = require('Module:GetParameters')
-- Argument list helper function, as per Module:String
p._getParameters = p._GetParameters.getParameters
-- Escape Pattern helper function so that all characters are treated as plain text, as per Module:String
function p._escapePattern( pattern_str )
return mw.ustring.gsub( pattern_str, "([%(%)%.%%%+%-%*%?%[%^%$%]])", "%%%1" )
end
-- Helper Function to interpret boolean strings, as per Module:String
p._getBoolean = p._GetParameters.getBoolean
--[[
Strip
This function Strips characters from string
Usage:
{{#invoke:String2|strip|source_string|characters_to_strip|plain_flag}}
Parameters
source: The string to strip
chars: The pattern or list of characters to strip from string, replaced with ''
plain: A flag indicating that the chars should be understood as plain text. defaults to true.
Leading and trailing whitespace is also automatically stripped from the string.
]]
function p.strip( frame )
local new_args = p._getParameters( frame.args, {'source', 'chars', 'plain'} )
local source_str = new_args['source'] or ''
local chars = new_args['chars'] or '' or 'characters'
source_str = mw.text.trim(source_str)
if source_str == '' or chars == '' then
return source_str
end
local l_plain = p._getBoolean( new_args['plain'] or true )
if l_plain then
chars = p._escapePattern( chars )
end
local result
result = mw.ustring.gsub(source_str, "["..chars.."]", '')
return result
end
--[[
Match any
Returns the index of the first given pattern to match the input. Patterns must be consecutively numbered.
Returns the empty string if nothing matches for use in {{#if:}}
Usage:
{{#invoke:String2|matchAll|source=123 abc|456|abc}} returns '2'.
Parameters:
source: the string to search
plain: A flag indicating that the patterns should be understood as plain text. defaults to true.
1, 2, 3, ...: the patterns to search for
]]
function p.matchAny(frame)
local source_str = frame.args['source'] or error('The source parameter is mandatory.')
local l_plain = p._getBoolean( frame.args['plain'] or true )
for i = 1, math.huge do
local pattern = frame.args[i]
if not pattern then return '' end
if mw.ustring.find(source_str, pattern, 1, l_plain) then
return tostring(i)
end
end
end
--[[--------------------------< H Y P H E N _ T O _ D A S H >--------------------------------------------------
Converts a hyphen to a dash under certain conditions. The hyphen must separate
like items; unlike items are returned unmodified. These forms are modified:
letter - letter (A - B)
digit - digit (4-5)
digit separator digit - digit separator digit (4.1-4.5 or 4-1-4-5)
letterdigit - letterdigit (A1-A5) (an optional separator between letter and
digit is supported – a.1-a.5 or a-1-a-5)
digitletter - digitletter (5a - 5d) (an optional separator between letter and
digit is supported – 5.a-5.d or 5-a-5-d)
any other forms are returned unmodified.
str may be a comma- or semicolon-separated list
]]
function p.hyphen_to_dash( str, spacing )
if (str == nil or str == '') then
return str
end
local accept
str = mw.text.decode(str, true ) -- replace html entities with their characters; semicolon mucks up the text.split
local out = {}
local list = mw.text.split (str, '%s*[,;]%s*') -- split str at comma or semicolon separators if there are any
for _, item in ipairs (list) do -- for each item in the list
item = mw.text.trim(item) -- trim whitespace
item, accept = item:gsub ('^%(%((.+)%)%)$', '%1')
if accept == 0 and mw.ustring.match (item, '^%w*[%.%-]?%w+%s*[%-–—]%s*%w*[%.%-]?%w+$') then -- if a hyphenated range or has endash or emdash separators
if item:match ('^%a+[%.%-]?%d+%s*%-%s*%a+[%.%-]?%d+$') or -- letterdigit hyphen letterdigit (optional separator between letter and digit)
item:match ('^%d+[%.%-]?%a+%s*%-%s*%d+[%.%-]?%a+$') or -- digitletter hyphen digitletter (optional separator between digit and letter)
item:match ('^%d+[%.%-]%d+%s*%-%s*%d+[%.%-]%d+$') or -- digit separator digit hyphen digit separator digit
item:match ('^%d+%s*%-%s*%d+$') or -- digit hyphen digit
item:match ('^%a+%s*%-%s*%a+$') then -- letter hyphen letter
item = item:gsub ('(%w*[%.%-]?%w+)%s*%-%s*(%w*[%.%-]?%w+)', '%1–%2') -- replace hyphen, remove extraneous space characters
else
item = mw.ustring.gsub (item, '%s*[–—]%s*', '–') -- for endash or emdash separated ranges, replace em with en, remove extraneous whitespace
end
end
table.insert (out, item) -- add the (possibly modified) item to the output table
end
local temp_str = table.concat (out, ',' .. spacing) -- concatenate the output table into a comma separated string
temp_str, accept = temp_str:gsub ('^%(%((.+)%)%)$', '%1') -- remove accept-this-as-written markup when it wraps all of concatenated out
if accept ~= 0 then
temp_str = str:gsub ('^%(%((.+)%)%)$', '%1') -- when global markup removed, return original str; do it this way to suppress boolean second return value
end
return temp_str
end
function p.hyphen2dash( frame )
local str = frame.args[1] or ''
local spacing = frame.args[2] or ' ' -- space is part of the standard separator for normal spacing (but in conjunction with templates r/rp/ran we may need a narrower spacing
return p.hyphen_to_dash(str, spacing)
end
-- Similar to [[Module:String#endswith]]
function p.startswith(frame)
return (frame.args[1]:sub(1, frame.args[2]:len()) == frame.args[2]) and 'yes' or ''
end
-- Implements [[Template:Isnumeric]]
function p.isnumeric(frame)
local s = frame.args[1] or frame:getParent().args[1]
local boolean = (frame.args.boolean or frame:getParent().args.boolean) == 'true'
if type(s) == 'string' and mw.getContentLanguage():parseFormattedNumber( s ) then
return boolean and 1 or s
end
return boolean and 0 or ''
end
-- Checks if a value in a group of numbers is not an interger.
-- Allows usage of an |empty= parameter to allow empty values to be skipped.
function p.isInteger(frame)
local values = frame.args or frame:getParent().args
local allow_empty = frame.args.empty or frame:getParent().args.empty
for _, value in ipairs(values) do
-- Trim spaces
value = value and value:gsub("^%s*(.-)%s*$", "%1")
if value == "" or value == nil then
if not allow_empty then
return false -- Empty values are not allowed
end
else
value = tonumber(value)
if not (type(value) == "number" and value == math.floor(value)) then
return false
end
end
end
return true
end
-- Returns an error found in a string.
function p.getError(frame)
local text = frame.args[1] or frame:getParent().args[1]
local error_message = text:match('(<strong class="error">.-</strong>)')
return error_message or nil
end
return p
8jl034otxz47vwg2szaa5x536gkylpd
બોસ્નિયા અને હર્ઝેગોવિના
0
82110
886326
806069
2025-06-13T12:52:26Z
CommonsDelinker
264
Replacing Europe-Bosnia_and_Herzegovina.svg with [[File:Map_of_Bosnia_and_Herzegovina_in_Europe.svg]] (by [[:c:User:CommonsDelinker|CommonsDelinker]] because: [[:c:COM:FR|File renamed]]: [[:c:COM:FR#FR2|Criterion 2]]).
886326
wikitext
text/x-wiki
{{Infobox country
|conventional_long_name = બોસ્નિયા અને હર્ઝેગોવિના
|native_name = ''Bosna i Hercegovina''<br/>Босна и Херцеговина
|common_name = બોસ્નિયા અને હર્ઝેગોવિના
|image_flag = Flag of Bosnia and Herzegovina.svg
|image_coat = Coat of arms of Bosnia and Herzegovina.svg
|national_anthem = {{nowrap|<br/>''Državna himna Bosne i Hercegovine''<br/>{{small|બોસ્નિયા અને હર્ઝેગોવિનાનું રાષ્ટ્રગીત}}}}
|image_map = Map of Bosnia and Herzegovina in Europe.svg
|languages_type = અધિકૃત ભાષાઓ
|languages = બોસ્નિયન,<br/>ક્રોએશિયન અને<br/>સર્બિયન
|ethnic_groups = {{vunblist|50.11% બોસ્નિઆક|30.78% સર્બ્સ|15.43% ક્રોએટ્સ |2.73% અન્ય}}
|ethnic_groups_year = ૨૦૧૩ વસ્તી ગણતરી<ref name=Popis2013>{{cite web|title=Census of population, households and dwellings in Bosnia and Herzegovina, 2013: Final results|url=http://www.popis2013.ba/popis2013/doc/Popis2013prvoIzdanje.pdf|publisher=Agency for Statistics of Bosnia and Herzegovina|date=જૂન ૨૦૧૬|access-date=૧ જુલાઇ ૨૦૧૬|archive-date=2017-12-24|archive-url=https://web.archive.org/web/20171224103940/http://www.popis2013.ba/popis2013/doc/Popis2013prvoIzdanje.pdf|url-status=dead}}</ref>
|demonym = {{vunblist|બોસ્નિયન|હર્ઝેગોવિઅન}}
|capital = સારાયેવો<ref name="Constitution">{{cite web|url=http://www.ccbh.ba/public/down/USTAV_BOSNE_I_HERCEGOVINE_engl.pdf|title=Constitution of Bosnia and Herzegovina|access-date=૬ માર્ચ ૨૦૧૫|archive-date=2015-10-28|archive-url=https://web.archive.org/web/20151028162530/http://www.ccbh.ba/public/down/USTAV_BOSNE_I_HERCEGOVINE_engl.pdf|url-status=dead}}</ref>
|coordinates = {{Coord|43|52|N|18|25|E|type:city}}
|largest_city = રાજધાની
|government_type = {{nowrap|ફેડરલ સંસદીય<br/>પ્રજાસત્તાક}}{{sfn|CIA}}
|leader_title1 = {{nowrap|મુખ્ય પ્રતિનિધી}}
|leader_name1 = {{nowrap|વેલેન્ટિન ઇંઝકો}}<sup>a</sup>
|leader_title2 = પ્રમુખપદના ચેરમેન
|leader_name2 = {{nowrap|મ્લાડેન ઇવાનિક<sup>b</sup>}}
|leader_title3 = પ્રમુખપદના સભ્યો
|leader_name3 = {{nowrap|ડ્રેગાન કોવિક<sup>c</sup><br/>બાકિર ઇઝેટ્બેગોવિક<sup>d</sup>}}
|leader_title4 = પ્રધાન મંત્રી
|leader_name4 = ડેનિશ ઝ્વઝડિક
|legislature = સંસદ
|upper_house = લોક સભા
|lower_house = પ્રતિનિધી ગૃહ
|area_rank = ૧૨૭મો
|area_km2 = 51,197
|area_sq_mi = 19,741 <!--Do not remove per [[WP:MOSNUM]]-->
|percent_water = 0.8%
|population_estimate =
|population_estimate_rank =
|population_estimate_year =
|population_census = 3,531,159<ref>{{cite news|publisher=Politico|title=Bosnia releases disputed census results|url=http://www.politico.eu/article/bosnia-releases-disputed-census-results/|date=૧ જુલાઇ ૨૦૧૬|access-date=૧ જુલાઇ ૨૦૧૬}}</ref>
|population_census_year = ૨૦૧૩
|population_density_km2 = 68.97
|population_density_sq_mi =
|population_density_rank =
|GDP_PPP = $41.127 બિલિયન<ref name="imf.org">{{cite web|url=https://www.imf.org/external/pubs/ft/weo/2015/02/weodata/weorept.aspx?pr.x=62&pr.y=7&sy=2015&ey=2016&scsm=1&ssd=1&sort=country&ds=.&br=1&c=963&s=NGDPD%2CNGDPDPC%2CPPPGDP%2CPPPPC&grp=0&a=|title=Report for Selected Countries and Subjects|publisher=}}</ref>
|GDP_PPP_rank =
|GDP_PPP_year = ૨૦૧૬
|GDP_PPP_per_capita = $11,647<ref name="imf.org"/>
|GDP_PPP_per_capita_rank =
|GDP_nominal_year = ૨૦૧૬
|GDP_nominal = $16.306 બિલિયન<ref name=imf2>{{cite web |url=https://www.imf.org/external/pubs/ft/weo/2015/02/weodata/weorept.aspx?pr.x=62&pr.y=7&sy=2015&ey=2016&scsm=1&ssd=1&sort=country&ds=.&br=1&c=963&s=NGDPD%2CNGDPDPC%2CPPPGDP%2CPPPPC&grp=0&a=
|title=Bosnia and Herzegovina |publisher=International Monetary Fund |access-date=૫ ઓક્ટોબર ૨૦૧૫}}</ref>
|GDP_nominal_rank =
|GDP_nominal_per_capita = $4,617.75<ref name=imf2/>
|GDP_nominal_per_capita_rank =
|sovereignty_type = સ્થાપના ઇતિહાસ
|established_event2 = બોસ્નિયા બેનેટ
|established_date2 = c. ૧૧૫૪
|established_event3 = બોસ્નિયા રાજ્ય
|established_date3 = c. ૧૩૭૭
|established_event4 = ઓટોમન આક્રમણ
|established_date4 = c. ૧૪૬૩
|established_event5 = ઓસ્ટ્રિયા-હંગેરીમાંથી અલગ
|established_date5 = ૨૯ ઓક્ટોબર ૧૯૧૮
|established_event6 = યુગોસ્લોવિઆની સ્થાપના
|established_date6 = ૪ ડિસેમ્બર ૧૯૧૮
|established_event7 = રાષ્ટ્રીય દિવસ
|established_date7 = ૨૫ નવેમ્બર ૧૯૪૩
|established_event8 = યુગોસ્લોવિયામાંથી સ્વતંત્રતા
|established_date8 = ૧ માર્ચ ૧૯૯૨
|established_event9 = અધિકૃત<ref>Peace Implementation Council, High Representative for Bosnia and Herzegovina, Constitutional Court of Bosnia and Herzegovina#Composition of the court, European Union Police Mission in Bosnia and Herzegovina, EUFOR Althea</ref>
|established_date9 = ૬ એપ્રિલ ૧૯૯૨
|established_event10 = બંધારણ
|established_date10 = ૧૪ ડિસેમ્બર ૧૯૯૫
|Gini_year = ૨૦૧૩
|Gini_change = 36 → 58<!--increase/decrease/steady-->
|Gini = 36.2 <!--number only-->
|Gini_ref = <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 |access-date=૧ સપ્ટેમ્બર ૨૦૦૯ |archive-date=2007-06-13 |archive-url=https://web.archive.org/web/20070613005439/https://www.cia.gov/library/publications/the-world-factbook/fields/2172.html |url-status=dead }}</ref>
|Gini_rank =
|HDI_year = ૨૦૧૪<!-- Please use the year to which the data refers, not the publication year-->
|HDI_change = increase<!--increase/decrease/steady-->
|HDI = 0.733 <!--number only-->
|HDI_ref = <ref name="HDI">{{cite web |url=http://hdr.undp.org/sites/default/files/hdr_2015_statistical_annex.pdf |title=Human Development Report 2015 |year=૨૦૧૪ |publisher=United Nations |access-date=૧૪ ડિસેમ્બર ૨૦૧૫}}</ref>
|HDI_rank = 85th
|currency = બોસ્નિયા અને હર્ઝેગોવિના માર્ક
|currency_code = BAM
|country_code =
|time_zone = મધ્ય યુરોપિયન સમય
|utc_offset = +1
|time_zone_DST = મધ્ય યુરોપિયન ઉનાળુ સમય
|utc_offset_DST = +2
|date_format = dd. mm. yyyy. ([[Common Era|CE]])
|drives_on = right
|calling_code = ૩૮૭
|cctld = [[.ba]]
|footnote_a = સરકારના સભ્ય નથી; ઉચ્ચ પ્રતિનિધી ડાયટોન કરાર મુજબ ચૂંટાયેલા અથવા ન ચૂંટાયેલા સભ્યોને દૂર કરી શકે છે.
|footnote_b = હાલના પ્રમુખપદના ચેરમેન, સર્બ
|footnote_c = હાલના પ્રમુખપદના સભ્ય, ક્રોએટ્સ
|footnote_d = હાલના પ્રમુખપદના સભ્ય, બોસ્નિક
}}
'''બોસ્નિયા અને હર્ઝેગોવિના''' ({{IPAc-en|audio=En-us-Bosnia and Herzegovina.ogg|ˈ|b|ɒ|z|n|i|ə|_|ə|n|d|_|ˌ|h|ɛər|t|s|ə|ɡ|oʊ|ˈ|v|iː|n|ə|,_|-|ˌ|h|ɜːr|t|-|,_|-|ɡ|ə|-}} or {{IPAc-en|ˌ|h|ɜːr|t|s|ə|ˈ|ɡ|ɒ|v|ᵻ|n|ə}};{{refn|{{Citation |last=Jones |first=Daniel |author-link=Daniel Jones (phonetician) |title=English Pronouncing Dictionary |editors=Peter Roach, James Hartmann and Jane Setter |place=Cambridge |publisher=Cambridge University Press |orig-year=૧૯૧૭ |year=૨૦૦૩ |isbn=3-12-539683-2}}}}{{refn|{{MerriamWebsterDictionary|Bosnia}}, {{MerriamWebsterDictionary|Herzegovina}}.}} '''B&H'''; બોસ્નિયન, ક્રોએશિયન, સર્બિયન: ''Bosna i Hercegovina'' / Босна и Херцеговина {{IPA-sh|bôsna i xěrt͡seɡoʋina|}}), જે કેટલીક વખત '''બોસ્નિયા-હર્ઝેગોવિના''' ('''BiH'''), અને, ટૂંકમાં, મોટાભાગે '''બોસ્નિયા''' તરીકે ઓળખાય છે, દક્ષિણ-પૂર્વ યુરોપમાં બાલ્કન પ્રદેશમાં આવેલો દેશ છે. સારાયેવો સૌથી મોટું શહેર અને દેશની રાજધાની છે. તેની ઉત્તર, પશ્ચિમ અને દક્ષિણમાં ક્રોએશિયા અને પૂર્વમાં સર્બિયા, દક્ષિણ-પૂર્વમાં મોન્ટેન્ગ્રો અને દક્ષિણમાં એડ્રિયાટિક સમુદ્ર આવેલો છે, જે {{convert|20|km|abbr=off}} લાંબો સમુદ્ર કિનારો નેઉમ શહેરમાં ધરાવે છે. દેશનો મધ્ય અને પૂર્વ ભાગ પર્વતીય ભૂગોળ ધરાવે છે અને ઉત્તર-પશ્ચિમ મોટાભાગે ટેકરીઓ આવેલી છે. જ્યારે ઉત્તર-પૂર્વ મોટાભાગે મેદાનો ધારેવ છે. દેશનો મધ્ય ભાગ ખંડીય હવામાન ધરાવે છે, જેમાં ઉનાળો ગરમ અને શિયાળો ઠંડો અને બરફ વાળો હોય છે. દેશનો દક્ષિણ ભાગ ભૂમધ્ય તાપમાન ધરાવે છે.
== સંદર્ભ ==
{{Reflist}}
{{યુરોપ}}
{{સ્ટબ}}
[[શ્રેણી:દેશ]]
or8m3eut40e4k1a1k6diptiwkrxa0cd
વિભાગ:Delink
828
82602
886364
843972
2024-02-17T04:47:33Z
en>Pppery
0
Changed protection settings for "[[Module:Delink]]": Dependency of fully-protected (and on [[WP:CASC]]) [[Template:Fix]] ([Edit=Require administrator access] (indefinite) [Move=Require administrator access] (indefinite))
843971
Scribunto
text/plain
-- This module de-links most wikitext.
require("strict")
local p = {}
local getArgs
local function delinkReversePipeTrick(s)
if s:match("^%[%[|.*[|\n]") then -- Check for newlines or multiple pipes.
return s
end
return s:match("%[%[|(.*)%]%]")
end
local function delinkPipeTrick(s)
-- We need to deal with colons, brackets, and commas, per [[Help:Pipe trick]].
-- First, remove the text before the first colon, if any.
if s:match(":") then
s = s:match("%[%[.-:(.*)|%]%]")
-- If there are no colons, grab all of the text apart from the square brackets and the pipe.
else
s = s:match("%[%[(.*)|%]%]")
end
-- Next up, brackets and commas.
if s:match("%(.-%)$") then -- Brackets trump commas.
s = s:match("(.-) ?%(.-%)$")
elseif s:match(",") then -- If there are no brackets, display only the text before the first comma.
s = s:match("(.-),.*$")
end
return s
end
-- Return wikilink target |wikilinks=target
local function getDelinkedTarget(s)
local result = s
-- Deal with the reverse pipe trick.
if result:match("%[%[|") then
return delinkReversePipeTrick(result)
end
result = mw.uri.decode(result, "PATH") -- decode percent-encoded entities. Leave underscores and plus signs.
result = mw.text.decode(result, true) -- decode HTML entities.
-- Check for bad titles. To do this we need to find the
-- title area of the link, i.e. the part before any pipes.
local target_area
if result:match("|") then -- Find if we're dealing with a piped link.
target_area = result:match("^%[%[(.-)|.*%]%]")
else
target_area = result:match("^%[%[(.-)%]%]")
end
-- Check for bad characters.
if mw.ustring.match(target_area, "[%[%]<>{}%%%c\n]") and mw.ustring.match(target_area, "[%[%]<>{}%%%c\n]") ~= "?" then
return s
end
return target_area
end
local function getDelinkedLabel(s)
local result = s
-- Deal with the reverse pipe trick.
if result:match("%[%[|") then
return delinkReversePipeTrick(result)
end
result = mw.uri.decode(result, "PATH") -- decode percent-encoded entities. Leave underscores and plus signs.
result = mw.text.decode(result, true) -- decode HTML entities.
-- Check for bad titles. To do this we need to find the
-- title area of the link, i.e. the part before any pipes.
local target_area
if result:match("|") then -- Find if we're dealing with a piped link.
target_area = result:match("^%[%[(.-)|.*%]%]")
else
target_area = result:match("^%[%[(.-)%]%]")
end
-- Check for bad characters.
if mw.ustring.match(target_area, "[%[%]<>{}%%%c\n]") and mw.ustring.match(target_area, "[%[%]<>{}%%%c\n]") ~= "?" then
return s
end
-- Check for categories, interwikis, and files.
local colon_prefix = result:match("%[%[(.-):.*%]%]") or "" -- Get the text before the first colon.
local ns = mw.site.namespaces[colon_prefix] -- see if this is a known namespace
if mw.language.isKnownLanguageTag(colon_prefix) or (ns and (ns.canonicalName == "File" or ns.canonicalName == "Category")) then
return ""
end
-- Remove the colon if the link is using the [[Help:Colon trick]].
if result:match("%[%[:") then
result = "[[" .. result:match("%[%[:(.*%]%])")
end
-- Deal with links using the [[Help:Pipe trick]].
if mw.ustring.match(result, "^%[%[[^|]*|%]%]") then
return delinkPipeTrick(result)
end
-- Find the display area of the wikilink
if result:match("|") then -- Find if we're dealing with a piped link.
result = result:match("^%[%[.-|(.+)%]%]")
-- Remove new lines from the display of multiline piped links,
-- where the pipe is before the first new line.
result = result:gsub("\n", "")
else
result = result:match("^%[%[(.-)%]%]")
end
return result
end
local function delinkURL(s)
-- Assume we have already delinked internal wikilinks, and that
-- we have been passed some text between two square brackets [foo].
-- If the text contains a line break it is not formatted as a URL, regardless of other content.
if s:match("\n") then
return s
end
-- Check if the text has a valid URL prefix and at least one valid URL character.
local valid_url_prefixes = {"//", "http://", "https://", "ftp://", "gopher://", "mailto:", "news:", "irc://"}
local url_prefix
for _ ,v in ipairs(valid_url_prefixes) do
if mw.ustring.match(s, '^%[' .. v ..'[^"%s].*%]' ) then
url_prefix = v
break
end
end
-- Get display text
if not url_prefix then
return s
end
s = s:match("^%[" .. url_prefix .. "(.*)%]") -- Grab all of the text after the URL prefix and before the final square bracket.
s = s:match('^.-(["<> ].*)') or "" -- Grab all of the text after the first URL separator character ("<> ).
s = mw.ustring.match(s, "^%s*(%S.*)$") or "" -- If the separating character was a space, trim it off.
local s_decoded = mw.text.decode(s, true)
if mw.ustring.match(s_decoded, "%c") then
return s
end
return s_decoded
end
local function delinkLinkClass(text, pattern, delinkFunction)
if type(text) ~= "string" then
error("Attempt to de-link non-string input.", 2)
end
if type(pattern) ~= "string" or mw.ustring.sub(pattern, 1, 1) ~= "^" then
error('Invalid pattern detected. Patterns must begin with "^".', 2)
end
-- Iterate over the text string, and replace any matched text. using the
-- delink function. We need to iterate character by character rather
-- than just use gsub, otherwise nested links aren't detected properly.
local result = ""
while text ~= "" do
-- Replace text using one iteration of gsub.
text = mw.ustring.gsub(text, pattern, delinkFunction, 1)
-- Append the left-most character to the result string.
result = result .. mw.ustring.sub(text, 1, 1)
text = mw.ustring.sub(text, 2, -1)
end
return result
end
function p._delink(args)
local text = args[1] or ""
if args.refs == "yes" then
-- Remove any [[Help:Strip markers]] representing ref tags. In most situations
-- this is not a good idea - only use it if you know what you are doing!
text = mw.ustring.gsub(text, "UNIQ%w*%-ref%-%d*%-QINU", "")
end
if args.comments ~= "no" then
text = text:gsub("<!%-%-.-%-%->", "") -- Remove html comments.
end
if args.wikilinks ~= "no" and args.wikilinks ~= "target" then
-- De-link wikilinks and return the label portion of the wikilink.
text = delinkLinkClass(text, "^%[%[.-%]%]", getDelinkedLabel)
elseif args.wikilinks == "target" then
-- De-link wikilinks and return the target portions of the wikilink.
text = delinkLinkClass(text, "^%[%[.-%]%]", getDelinkedTarget)
end
if args.urls ~= "no" then
text = delinkLinkClass(text, "^%[.-%]", delinkURL) -- De-link URLs.
end
if args.whitespace ~= "no" then
-- Replace single new lines with a single space, but leave double new lines
-- and new lines only containing spaces or tabs before a second new line.
text = mw.ustring.gsub(text, "([^\n \t][ \t]*)\n([ \t]*[^\n \t])", "%1 %2")
text = text:gsub("[ \t]+", " ") -- Remove extra tabs and spaces.
end
return text
end
function p.delink(frame)
if not getArgs then
getArgs = require('Module:Arguments').getArgs
end
return p._delink(getArgs(frame, {wrappers = 'Template:Delink'}))
end
return p
ap0hgbdtouyp0g9x21nujuwhyky0459
886365
886364
2025-06-13T17:03:25Z
KartikMistry
10383
[[:en:Module:Delink]] માંથી આયાત કરેલ ૧ પુનરાવર્તન
843971
Scribunto
text/plain
-- This module de-links most wikitext.
require("strict")
local p = {}
local getArgs
local function delinkReversePipeTrick(s)
if s:match("^%[%[|.*[|\n]") then -- Check for newlines or multiple pipes.
return s
end
return s:match("%[%[|(.*)%]%]")
end
local function delinkPipeTrick(s)
-- We need to deal with colons, brackets, and commas, per [[Help:Pipe trick]].
-- First, remove the text before the first colon, if any.
if s:match(":") then
s = s:match("%[%[.-:(.*)|%]%]")
-- If there are no colons, grab all of the text apart from the square brackets and the pipe.
else
s = s:match("%[%[(.*)|%]%]")
end
-- Next up, brackets and commas.
if s:match("%(.-%)$") then -- Brackets trump commas.
s = s:match("(.-) ?%(.-%)$")
elseif s:match(",") then -- If there are no brackets, display only the text before the first comma.
s = s:match("(.-),.*$")
end
return s
end
-- Return wikilink target |wikilinks=target
local function getDelinkedTarget(s)
local result = s
-- Deal with the reverse pipe trick.
if result:match("%[%[|") then
return delinkReversePipeTrick(result)
end
result = mw.uri.decode(result, "PATH") -- decode percent-encoded entities. Leave underscores and plus signs.
result = mw.text.decode(result, true) -- decode HTML entities.
-- Check for bad titles. To do this we need to find the
-- title area of the link, i.e. the part before any pipes.
local target_area
if result:match("|") then -- Find if we're dealing with a piped link.
target_area = result:match("^%[%[(.-)|.*%]%]")
else
target_area = result:match("^%[%[(.-)%]%]")
end
-- Check for bad characters.
if mw.ustring.match(target_area, "[%[%]<>{}%%%c\n]") and mw.ustring.match(target_area, "[%[%]<>{}%%%c\n]") ~= "?" then
return s
end
return target_area
end
local function getDelinkedLabel(s)
local result = s
-- Deal with the reverse pipe trick.
if result:match("%[%[|") then
return delinkReversePipeTrick(result)
end
result = mw.uri.decode(result, "PATH") -- decode percent-encoded entities. Leave underscores and plus signs.
result = mw.text.decode(result, true) -- decode HTML entities.
-- Check for bad titles. To do this we need to find the
-- title area of the link, i.e. the part before any pipes.
local target_area
if result:match("|") then -- Find if we're dealing with a piped link.
target_area = result:match("^%[%[(.-)|.*%]%]")
else
target_area = result:match("^%[%[(.-)%]%]")
end
-- Check for bad characters.
if mw.ustring.match(target_area, "[%[%]<>{}%%%c\n]") and mw.ustring.match(target_area, "[%[%]<>{}%%%c\n]") ~= "?" then
return s
end
-- Check for categories, interwikis, and files.
local colon_prefix = result:match("%[%[(.-):.*%]%]") or "" -- Get the text before the first colon.
local ns = mw.site.namespaces[colon_prefix] -- see if this is a known namespace
if mw.language.isKnownLanguageTag(colon_prefix) or (ns and (ns.canonicalName == "File" or ns.canonicalName == "Category")) then
return ""
end
-- Remove the colon if the link is using the [[Help:Colon trick]].
if result:match("%[%[:") then
result = "[[" .. result:match("%[%[:(.*%]%])")
end
-- Deal with links using the [[Help:Pipe trick]].
if mw.ustring.match(result, "^%[%[[^|]*|%]%]") then
return delinkPipeTrick(result)
end
-- Find the display area of the wikilink
if result:match("|") then -- Find if we're dealing with a piped link.
result = result:match("^%[%[.-|(.+)%]%]")
-- Remove new lines from the display of multiline piped links,
-- where the pipe is before the first new line.
result = result:gsub("\n", "")
else
result = result:match("^%[%[(.-)%]%]")
end
return result
end
local function delinkURL(s)
-- Assume we have already delinked internal wikilinks, and that
-- we have been passed some text between two square brackets [foo].
-- If the text contains a line break it is not formatted as a URL, regardless of other content.
if s:match("\n") then
return s
end
-- Check if the text has a valid URL prefix and at least one valid URL character.
local valid_url_prefixes = {"//", "http://", "https://", "ftp://", "gopher://", "mailto:", "news:", "irc://"}
local url_prefix
for _ ,v in ipairs(valid_url_prefixes) do
if mw.ustring.match(s, '^%[' .. v ..'[^"%s].*%]' ) then
url_prefix = v
break
end
end
-- Get display text
if not url_prefix then
return s
end
s = s:match("^%[" .. url_prefix .. "(.*)%]") -- Grab all of the text after the URL prefix and before the final square bracket.
s = s:match('^.-(["<> ].*)') or "" -- Grab all of the text after the first URL separator character ("<> ).
s = mw.ustring.match(s, "^%s*(%S.*)$") or "" -- If the separating character was a space, trim it off.
local s_decoded = mw.text.decode(s, true)
if mw.ustring.match(s_decoded, "%c") then
return s
end
return s_decoded
end
local function delinkLinkClass(text, pattern, delinkFunction)
if type(text) ~= "string" then
error("Attempt to de-link non-string input.", 2)
end
if type(pattern) ~= "string" or mw.ustring.sub(pattern, 1, 1) ~= "^" then
error('Invalid pattern detected. Patterns must begin with "^".', 2)
end
-- Iterate over the text string, and replace any matched text. using the
-- delink function. We need to iterate character by character rather
-- than just use gsub, otherwise nested links aren't detected properly.
local result = ""
while text ~= "" do
-- Replace text using one iteration of gsub.
text = mw.ustring.gsub(text, pattern, delinkFunction, 1)
-- Append the left-most character to the result string.
result = result .. mw.ustring.sub(text, 1, 1)
text = mw.ustring.sub(text, 2, -1)
end
return result
end
function p._delink(args)
local text = args[1] or ""
if args.refs == "yes" then
-- Remove any [[Help:Strip markers]] representing ref tags. In most situations
-- this is not a good idea - only use it if you know what you are doing!
text = mw.ustring.gsub(text, "UNIQ%w*%-ref%-%d*%-QINU", "")
end
if args.comments ~= "no" then
text = text:gsub("<!%-%-.-%-%->", "") -- Remove html comments.
end
if args.wikilinks ~= "no" and args.wikilinks ~= "target" then
-- De-link wikilinks and return the label portion of the wikilink.
text = delinkLinkClass(text, "^%[%[.-%]%]", getDelinkedLabel)
elseif args.wikilinks == "target" then
-- De-link wikilinks and return the target portions of the wikilink.
text = delinkLinkClass(text, "^%[%[.-%]%]", getDelinkedTarget)
end
if args.urls ~= "no" then
text = delinkLinkClass(text, "^%[.-%]", delinkURL) -- De-link URLs.
end
if args.whitespace ~= "no" then
-- Replace single new lines with a single space, but leave double new lines
-- and new lines only containing spaces or tabs before a second new line.
text = mw.ustring.gsub(text, "([^\n \t][ \t]*)\n([ \t]*[^\n \t])", "%1 %2")
text = text:gsub("[ \t]+", " ") -- Remove extra tabs and spaces.
end
return text
end
function p.delink(frame)
if not getArgs then
getArgs = require('Module:Arguments').getArgs
end
return p._delink(getArgs(frame, {wrappers = 'Template:Delink'}))
end
return p
ap0hgbdtouyp0g9x21nujuwhyky0459
વિભાગ:Labelled list hatnote
828
87236
886384
543021
2025-05-28T18:18:35Z
en>Ahecht
0
Exclude section anchors from ifexists, properly show "no input" error message when ifexists=true
886384
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Labelled list --
-- --
-- This module does the core work of creating a hatnote composed of a list --
-- prefixed by a colon-terminated label, i.e. "LABEL: [andList of pages]", --
-- for {{see also}} and similar templates. --
--------------------------------------------------------------------------------
local mHatnote = require('Module:Hatnote')
local mHatlist = require('Module:Hatnote list')
local mArguments --initialize lazily
local yesno --initialize lazily
local p = {}
-- Defaults global to this module
local defaults = {
label = 'See also', --Final fallback for label argument
labelForm = '%s: %s',
prefixes = {'label', 'label ', 'l'},
template = 'Module:Labelled list hatnote'
}
-- Localizable message strings
local msg = {
errorSuffix = '#Errors',
noInputWarning = 'no page names specified',
noOutputWarning =
"'''[[%s]] — no output: none of the target pages exist.'''"
}
-- Helper function that pre-combines display parameters into page arguments.
-- Also compresses sparse arrays, as a desirable side-effect.
function p.preprocessDisplays (args, prefixes)
-- Prefixes specify which parameters, in order, to check for display options
-- They each have numbers auto-appended, e.g. 'label1', 'label 1', & 'l1'
prefixes = prefixes or defaults.prefixes
local indices = {}
local sparsePages = {}
for k, v in pairs(args) do
if type(k) == 'number' then
indices[#indices + 1] = k
local display
for i = 1, #prefixes do
display = args[prefixes[i] .. k]
if display then break end
end
sparsePages[k] = display and
string.format('%s|%s', string.gsub(v, '|.*$', ''), display) or v
end
end
table.sort(indices)
local pages = {}
for k, v in ipairs(indices) do pages[#pages + 1] = sparsePages[v] end
return pages
end
--Helper function to get a page target from a processed page string
--e.g. "Page|Label" → "Page" or "Target" → "Target"
local function getTarget(pagename)
local pipe = string.find(pagename, '|')
return string.sub(pagename, 0, pipe and pipe - 1 or nil)
end
-- Produces a labelled pages-list hatnote.
-- The main frame (template definition) takes 1 or 2 arguments, for a singular
-- and (optionally) plural label respectively:
-- * {{#invoke:Labelled list hatnote|labelledList|Singular label|Plural label}}
-- The resulting template takes pagename & label parameters normally.
function p.labelledList (frame)
mArguments = require('Module:Arguments')
yesno = require('Module:Yesno')
local labels = {frame.args[1] or defaults.label}
labels[2] = frame.args[2] or labels[1]
labels[3] = frame.args[3] --no defaulting
labels[4] = frame.args[4] --no defaulting
local template = frame:getParent():getTitle()
local args = mArguments.getArgs(frame, {parentOnly = true})
local pages = p.preprocessDisplays(args)
local options = {
category = yesno(args.category),
extraclasses = frame.args.extraclasses,
ifexists = yesno(frame.args.ifexists),
namespace = frame.args.namespace or args.namespace,
selfref = yesno(frame.args.selfref or args.selfref),
template = template
}
return p._labelledList(pages, labels, options)
end
local function exists(title)
local success, result = pcall(function() return title.exists end)
if success then
return result
else
return true
end
end
function p._labelledList (pages, labels, options)
local removednonexist = false
if options.ifexists then
for k = #pages, 1, -1 do --iterate backwards to allow smooth removals
local v = pages[k]
if mw.ustring.sub(mw.text.trim(v), 1, 1) ~= "#" then
local title = mw.title.new(getTarget(v), namespace)
if (v == '') or (title == nil) or not exists(title) then
table.remove(pages, k)
removednonexist = true
end
end
end
end
labels = labels or {}
label = (#pages == 1 and labels[1] or labels[2]) or defaults.label
for k, v in pairs(pages) do
if mHatnote.findNamespaceId(v) ~= 0 then
label =
(
#pages == 1 and
(labels[3] or labels[1] or defaults.label) or
(labels[4] or labels[2] or defaults.label)
) or defaults.label
end
end
if #pages == 0 then
if removednonexist then
mw.addWarning(
string.format(
msg.noOutputWarning, options.template or defaults.template
)
)
return ''
else
return mHatnote.makeWikitextError(
msg.noInputWarning,
(options.template or defaults.template) .. msg.errorSuffix,
options.category
)
end
end
local text = string.format(
options.labelForm or defaults.labelForm,
label,
mHatlist.andList(pages, true)
)
local hnOptions = {
extraclasses = options.extraclasses,
selfref = options.selfref
}
return mHatnote._hatnote(text, hnOptions)
end
return p
eppzjpd24ihnmcl0ag7d4r08nhgbt7c
886385
886384
2025-06-13T17:03:27Z
KartikMistry
10383
[[:en:Module:Labelled_list_hatnote]] માંથી આયાત કરેલ ૧ પુનરાવર્તન
886384
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Labelled list --
-- --
-- This module does the core work of creating a hatnote composed of a list --
-- prefixed by a colon-terminated label, i.e. "LABEL: [andList of pages]", --
-- for {{see also}} and similar templates. --
--------------------------------------------------------------------------------
local mHatnote = require('Module:Hatnote')
local mHatlist = require('Module:Hatnote list')
local mArguments --initialize lazily
local yesno --initialize lazily
local p = {}
-- Defaults global to this module
local defaults = {
label = 'See also', --Final fallback for label argument
labelForm = '%s: %s',
prefixes = {'label', 'label ', 'l'},
template = 'Module:Labelled list hatnote'
}
-- Localizable message strings
local msg = {
errorSuffix = '#Errors',
noInputWarning = 'no page names specified',
noOutputWarning =
"'''[[%s]] — no output: none of the target pages exist.'''"
}
-- Helper function that pre-combines display parameters into page arguments.
-- Also compresses sparse arrays, as a desirable side-effect.
function p.preprocessDisplays (args, prefixes)
-- Prefixes specify which parameters, in order, to check for display options
-- They each have numbers auto-appended, e.g. 'label1', 'label 1', & 'l1'
prefixes = prefixes or defaults.prefixes
local indices = {}
local sparsePages = {}
for k, v in pairs(args) do
if type(k) == 'number' then
indices[#indices + 1] = k
local display
for i = 1, #prefixes do
display = args[prefixes[i] .. k]
if display then break end
end
sparsePages[k] = display and
string.format('%s|%s', string.gsub(v, '|.*$', ''), display) or v
end
end
table.sort(indices)
local pages = {}
for k, v in ipairs(indices) do pages[#pages + 1] = sparsePages[v] end
return pages
end
--Helper function to get a page target from a processed page string
--e.g. "Page|Label" → "Page" or "Target" → "Target"
local function getTarget(pagename)
local pipe = string.find(pagename, '|')
return string.sub(pagename, 0, pipe and pipe - 1 or nil)
end
-- Produces a labelled pages-list hatnote.
-- The main frame (template definition) takes 1 or 2 arguments, for a singular
-- and (optionally) plural label respectively:
-- * {{#invoke:Labelled list hatnote|labelledList|Singular label|Plural label}}
-- The resulting template takes pagename & label parameters normally.
function p.labelledList (frame)
mArguments = require('Module:Arguments')
yesno = require('Module:Yesno')
local labels = {frame.args[1] or defaults.label}
labels[2] = frame.args[2] or labels[1]
labels[3] = frame.args[3] --no defaulting
labels[4] = frame.args[4] --no defaulting
local template = frame:getParent():getTitle()
local args = mArguments.getArgs(frame, {parentOnly = true})
local pages = p.preprocessDisplays(args)
local options = {
category = yesno(args.category),
extraclasses = frame.args.extraclasses,
ifexists = yesno(frame.args.ifexists),
namespace = frame.args.namespace or args.namespace,
selfref = yesno(frame.args.selfref or args.selfref),
template = template
}
return p._labelledList(pages, labels, options)
end
local function exists(title)
local success, result = pcall(function() return title.exists end)
if success then
return result
else
return true
end
end
function p._labelledList (pages, labels, options)
local removednonexist = false
if options.ifexists then
for k = #pages, 1, -1 do --iterate backwards to allow smooth removals
local v = pages[k]
if mw.ustring.sub(mw.text.trim(v), 1, 1) ~= "#" then
local title = mw.title.new(getTarget(v), namespace)
if (v == '') or (title == nil) or not exists(title) then
table.remove(pages, k)
removednonexist = true
end
end
end
end
labels = labels or {}
label = (#pages == 1 and labels[1] or labels[2]) or defaults.label
for k, v in pairs(pages) do
if mHatnote.findNamespaceId(v) ~= 0 then
label =
(
#pages == 1 and
(labels[3] or labels[1] or defaults.label) or
(labels[4] or labels[2] or defaults.label)
) or defaults.label
end
end
if #pages == 0 then
if removednonexist then
mw.addWarning(
string.format(
msg.noOutputWarning, options.template or defaults.template
)
)
return ''
else
return mHatnote.makeWikitextError(
msg.noInputWarning,
(options.template or defaults.template) .. msg.errorSuffix,
options.category
)
end
end
local text = string.format(
options.labelForm or defaults.labelForm,
label,
mHatlist.andList(pages, true)
)
local hnOptions = {
extraclasses = options.extraclasses,
selfref = options.selfref
}
return mHatnote._hatnote(text, hnOptions)
end
return p
eppzjpd24ihnmcl0ag7d4r08nhgbt7c
વિભાગ:Citation/CS1/styles.css
828
99245
886356
821257
2024-08-02T18:48:11Z
en>Izno
0
nix notes
886356
sanitized-css
text/css
/* Protection icon
the following line controls the page-protection icon in the upper right corner
it must remain within this comment
{{sandbox other||{{pp-template}}}}
*/
/* Overrides
Some wikis do not override user agent default styles for HTML <cite> and <q>,
unlike en.wp. On en.wp, keep these the same as [[MediaWiki:Common.css]].
The word-wrap and :target styles were moved here from Common.css.
On en.wp, keep these the same as [[Template:Citation/styles.css]].
*/
cite.citation {
font-style: inherit; /* Remove italics for <cite> */
/* Break long urls, etc., rather than overflowing box */
word-wrap: break-word;
}
.citation q {
quotes: '"' '"' "'" "'"; /* Straight quote marks for <q> */
}
/* Highlight linked elements (such as clicked references) in blue */
.citation:target {
/* ignore the linter - all browsers of interest implement this */
background-color: rgba(0, 127, 255, 0.133);
}
/* ID and URL access
Both core and Common.css have selector .mw-parser-output a[href$=".pdf"].external
for PDF pages. All TemplateStyles pages are hoisted to .mw-parser-output. We need
to have specificity equal to a[href$=".pdf"].external for locks to override PDF icon.
That's essentially 2 classes and 1 element.
the .id-lock-... selectors are for use by non-citation templates like
{{Catalog lookup link}}
bg-size `contain` in Minerva and Timeless is too large, so we set a size for them
and then exclude them later
*/
.id-lock-free.id-lock-free a {
background: url(//upload.wikimedia.org/wikipedia/commons/6/65/Lock-green.svg)
right 0.1em center/9px no-repeat;
}
.id-lock-limited.id-lock-limited a,
.id-lock-registration.id-lock-registration a {
background: url(//upload.wikimedia.org/wikipedia/commons/d/d6/Lock-gray-alt-2.svg)
right 0.1em center/9px no-repeat;
}
.id-lock-subscription.id-lock-subscription a {
background: url(//upload.wikimedia.org/wikipedia/commons/a/aa/Lock-red-alt-2.svg)
right 0.1em center/9px no-repeat;
}
/* Wikisource
Wikisource icon when |chapter= or |title= is wikilinked to Wikisource
as in cite wikisource
*/
.cs1-ws-icon a {
background: url(//upload.wikimedia.org/wikipedia/commons/4/4c/Wikisource-logo.svg)
right 0.1em center/12px no-repeat;
}
body:not(.skin-timeless):not(.skin-minerva) .id-lock-free a,
body:not(.skin-timeless):not(.skin-minerva) .id-lock-limited a,
body:not(.skin-timeless):not(.skin-minerva) .id-lock-registration a,
body:not(.skin-timeless):not(.skin-minerva) .id-lock-subscription a,
body:not(.skin-timeless):not(.skin-minerva) .cs1-ws-icon a {
background-size: contain;
/* Common.css has a padding set for PDF URLs. bg-contain expands to the
* size of the padding which makes the icons very large. we "reset" the
* padding here. 1em picked out of a hat based on console having a similar
* rule from elsewhere
*/
padding: 0 1em 0 0;
}
/* Errors and maintenance */
.cs1-code {
/* <code>...</code> style override: mediawiki's css definition is specified here:
https://git.wikimedia.org/blob/mediawiki%2Fcore.git/
69cd73811f7aadd093050dbf20ed70ef0b42a713/skins%2Fcommon%2FcommonElements.css#L199
*/
color: inherit;
background: inherit;
border: none;
padding: inherit;
}
.cs1-hidden-error {
display: none;
color: var(--color-error, #d33);
}
.cs1-visible-error {
color: var(--color-error, #d33);
}
.cs1-maint {
display: none;
color: #085;
margin-left: 0.3em;
}
/* kerning */
.cs1-kern-left {
padding-left: 0.2em;
}
.cs1-kern-right {
padding-right: 0.2em;
}
/* selflinks – avoid bold font style when cs1|2 template links to the current page */
.citation .mw-selflink {
font-weight: inherit;
}
@media screen {
/* Small text size
Set small text size in one place. 0.95 (here) * 0.9 (from references list) is
~0.85, which is the lower bound for size for accessibility. Old styling for this
was just 0.85. We could write the rule so that when this template is inside
references/reflist, only then does it multiply by 0.95; else multiply by 0.85 */
.cs1-format {
font-size: 95%;
}
html.skin-theme-clientpref-night .cs1-maint {
color: #18911f;
}
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .cs1-maint {
color: #18911f;
}
}
4vx8grkh20viq5i9qf98wumgw6uva7x
886357
886356
2025-06-13T17:03:25Z
KartikMistry
10383
[[:en:Module:Citation/CS1/styles.css]] માંથી આયાત કરેલ ૧ પુનરાવર્તન
886356
sanitized-css
text/css
/* Protection icon
the following line controls the page-protection icon in the upper right corner
it must remain within this comment
{{sandbox other||{{pp-template}}}}
*/
/* Overrides
Some wikis do not override user agent default styles for HTML <cite> and <q>,
unlike en.wp. On en.wp, keep these the same as [[MediaWiki:Common.css]].
The word-wrap and :target styles were moved here from Common.css.
On en.wp, keep these the same as [[Template:Citation/styles.css]].
*/
cite.citation {
font-style: inherit; /* Remove italics for <cite> */
/* Break long urls, etc., rather than overflowing box */
word-wrap: break-word;
}
.citation q {
quotes: '"' '"' "'" "'"; /* Straight quote marks for <q> */
}
/* Highlight linked elements (such as clicked references) in blue */
.citation:target {
/* ignore the linter - all browsers of interest implement this */
background-color: rgba(0, 127, 255, 0.133);
}
/* ID and URL access
Both core and Common.css have selector .mw-parser-output a[href$=".pdf"].external
for PDF pages. All TemplateStyles pages are hoisted to .mw-parser-output. We need
to have specificity equal to a[href$=".pdf"].external for locks to override PDF icon.
That's essentially 2 classes and 1 element.
the .id-lock-... selectors are for use by non-citation templates like
{{Catalog lookup link}}
bg-size `contain` in Minerva and Timeless is too large, so we set a size for them
and then exclude them later
*/
.id-lock-free.id-lock-free a {
background: url(//upload.wikimedia.org/wikipedia/commons/6/65/Lock-green.svg)
right 0.1em center/9px no-repeat;
}
.id-lock-limited.id-lock-limited a,
.id-lock-registration.id-lock-registration a {
background: url(//upload.wikimedia.org/wikipedia/commons/d/d6/Lock-gray-alt-2.svg)
right 0.1em center/9px no-repeat;
}
.id-lock-subscription.id-lock-subscription a {
background: url(//upload.wikimedia.org/wikipedia/commons/a/aa/Lock-red-alt-2.svg)
right 0.1em center/9px no-repeat;
}
/* Wikisource
Wikisource icon when |chapter= or |title= is wikilinked to Wikisource
as in cite wikisource
*/
.cs1-ws-icon a {
background: url(//upload.wikimedia.org/wikipedia/commons/4/4c/Wikisource-logo.svg)
right 0.1em center/12px no-repeat;
}
body:not(.skin-timeless):not(.skin-minerva) .id-lock-free a,
body:not(.skin-timeless):not(.skin-minerva) .id-lock-limited a,
body:not(.skin-timeless):not(.skin-minerva) .id-lock-registration a,
body:not(.skin-timeless):not(.skin-minerva) .id-lock-subscription a,
body:not(.skin-timeless):not(.skin-minerva) .cs1-ws-icon a {
background-size: contain;
/* Common.css has a padding set for PDF URLs. bg-contain expands to the
* size of the padding which makes the icons very large. we "reset" the
* padding here. 1em picked out of a hat based on console having a similar
* rule from elsewhere
*/
padding: 0 1em 0 0;
}
/* Errors and maintenance */
.cs1-code {
/* <code>...</code> style override: mediawiki's css definition is specified here:
https://git.wikimedia.org/blob/mediawiki%2Fcore.git/
69cd73811f7aadd093050dbf20ed70ef0b42a713/skins%2Fcommon%2FcommonElements.css#L199
*/
color: inherit;
background: inherit;
border: none;
padding: inherit;
}
.cs1-hidden-error {
display: none;
color: var(--color-error, #d33);
}
.cs1-visible-error {
color: var(--color-error, #d33);
}
.cs1-maint {
display: none;
color: #085;
margin-left: 0.3em;
}
/* kerning */
.cs1-kern-left {
padding-left: 0.2em;
}
.cs1-kern-right {
padding-right: 0.2em;
}
/* selflinks – avoid bold font style when cs1|2 template links to the current page */
.citation .mw-selflink {
font-weight: inherit;
}
@media screen {
/* Small text size
Set small text size in one place. 0.95 (here) * 0.9 (from references list) is
~0.85, which is the lower bound for size for accessibility. Old styling for this
was just 0.85. We could write the rule so that when this template is inside
references/reflist, only then does it multiply by 0.95; else multiply by 0.85 */
.cs1-format {
font-size: 95%;
}
html.skin-theme-clientpref-night .cs1-maint {
color: #18911f;
}
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .cs1-maint {
color: #18911f;
}
}
4vx8grkh20viq5i9qf98wumgw6uva7x
ઢાંચો:Reflist-talk
10
108546
886398
677623
2021-09-16T21:54:12Z
en>Izno
0
tstyles rlt
886398
wikitext
text/x-wiki
<templatestyles src="Reflist-talk/styles.css"/><div class="reflist-talk" {{#if:{{{style|}}}|style="{{{style}}}"}}>
<p class="reflist-talk-title">{{{title|References}}}</p>
{{reflist|{{{1|{{{colwidth|}}}}}}|refs={{{refs|}}}|group={{{group|}}}}}
</div><noinclude>
{{Documentation}}
</noinclude>
aqzv8kw2m0hjdgvx7cwu2eej2pvduqm
886399
886398
2025-06-13T17:03:30Z
KartikMistry
10383
[[:en:Template:Reflist-talk]] માંથી આયાત કરેલ ૧ પુનરાવર્તન
886398
wikitext
text/x-wiki
<templatestyles src="Reflist-talk/styles.css"/><div class="reflist-talk" {{#if:{{{style|}}}|style="{{{style}}}"}}>
<p class="reflist-talk-title">{{{title|References}}}</p>
{{reflist|{{{1|{{{colwidth|}}}}}}|refs={{{refs|}}}|group={{{group|}}}}}
</div><noinclude>
{{Documentation}}
</noinclude>
aqzv8kw2m0hjdgvx7cwu2eej2pvduqm
વિભાગ:High-use
828
111708
886408
869666
2024-12-17T19:28:55Z
en>Ahecht
0
Fix fetch=no and no-percent=yes
886408
Scribunto
text/plain
local p = {}
local getArgs = require('Module:Arguments').getArgs
-- _fetch looks at the "demo" argument.
local _fetch = require('Module:Transclusion_count')._fetch
local yesno = require('Module:Yesno')
function p._num(args, count, no_percent)
if count == nil then
if yesno(args['fetch']) == false then
if (args[1] or '') ~= '' then count = tonumber(args[1]) end
else
count = _fetch(args)
end
end
-- Build output string
local return_value = ""
if count == nil then
if args[1] == "risk" then
return "a very large number of"
else
return "many"
end
else
-- Use 2 significant figures for smaller numbers and 3 for larger ones
local sigfig = 2
if count >= 100000 then
sigfig = 3
end
-- Prepare to round to appropriate number of sigfigs
local f = math.floor(math.log10(count)) - sigfig + 1
-- Round and insert "approximately" or "+" when appropriate
if (args[2] == "yes") or (type(args[1]) == 'string' and (mw.ustring.sub(args[1],-1) == "+")) then
-- Round down
return_value = string.format("%s+", mw.getContentLanguage():formatNum(math.floor( (count / 10^(f)) ) * (10^(f))) )
else
-- Round to nearest
return_value = string.format("approximately %s", mw.getContentLanguage():formatNum(math.floor( (count / 10^(f)) + 0.5) * (10^(f))) )
end
-- Insert percentage of pages if that is likely to be >= 1% and when |no-percent= not set to yes
no_percent = no_percent or args['no-percent']
if count and count > 250000 and not yesno (no_percent) then
local numpages = mw.getCurrentFrame():callParserFunction('NUMBEROFPAGES', 'R')
local percent = math.floor( ( ( count/numpages ) * 100) + 0.5)
if percent >= 1 then
return_value = string.format("%s pages, or roughly %s%% of all", return_value, percent)
end
end
end
return return_value
end
function p.num(frame, count)
return p._num(getArgs(frame), count)
end
-- Actions if there is a large (greater than or equal to 100,000) transclusion count
function p._risk(args)
if args[1] == "risk" then
return "risk"
else
local count = _fetch(args)
if count and count >= 100000 then
return "risk"
end
end
return ""
end
function p.risk(frame)
return p._risk(getArgs(frame))
end
function p._text(args, count)
-- Only show the information about how this template gets updated if someone
-- is actually editing the page and maybe trying to update the count.
local bot_text = (mw.getCurrentFrame():preprocess("{{REVISIONID}}") == "") and "\n\n----\n'''Preview message''': Transclusion count updated automatically ([[Template:High-use/doc#Technical details|see documentation]])." or ''
if count == nil then
if yesno(args['fetch']) == false then
if (args[1] or '') ~= '' then count = tonumber(args[1]) end
else
count = _fetch(args)
end
end
local title = mw.title.getCurrentTitle()
if ( (args.demo or '' ~= '') and mw.title.new(args.demo, 10) ) then
title = mw.title.new(args.demo, 10)
end
if title.subpageText == "doc" or title.subpageText == "sandbox" then
title = title.basePageTitle
end
local systemMessages = (args['system'] or '') ~= ''
-- This retrieves the project URL automatically to simplify localization.
local templateCount = ('on [https://linkcount.toolforge.org/?project=%s&page=%s#transclusions %s pages]'):format(
title:fullUrl():gsub('//(.-)/.*', '%1'),
mw.uri.encode(title.fullText), p._num(args, count))
local used_on_text = "'''This " .. (title.namespace == 828 and "Lua module" or "template") .. ' is used ';
if systemMessages then
used_on_text = used_on_text .. args['system'] ..
((count and count > 2000) and ("''', and " .. templateCount) or ("'''"))
else
used_on_text = used_on_text .. templateCount .. "'''"
end
local sandbox_text = ("%s's [[%s/sandbox|/sandbox]] or [[%s/testcases|/testcases]] subpages, or in your own [[%s]]. "):format(
(title.namespace == 828 and "module" or "template"),
title.fullText, title.fullText,
title.namespace == 828 and "Module:Sandbox|module sandbox" or "Wikipedia:User pages#SUB|user subpage"
)
local infoArg = args["info"] ~= "" and args["info"]
if (systemMessages or args[1] == "risk" or (count and count >= 100000) ) then
local info = systemMessages and '.<br/>Changes to it can cause immediate changes to the Wikipedia user interface.' or '.'
if infoArg then
info = info .. "<br />" .. infoArg
end
sandbox_text = info .. '<br /> To avoid major disruption' ..
(count and count >= 100000 and ' and server load' or '') ..
', any changes should be tested in the ' .. sandbox_text ..
'The tested changes can be added to this page in a single edit. '
else
sandbox_text = (infoArg and ('.<br />' .. infoArg .. ' C') or ' and c') ..
'hanges may be widely noticed. Test changes in the ' .. sandbox_text
end
local discussion_text = systemMessages and 'Please discuss changes ' or 'Consider discussing changes '
if args["2"] and args["2"] ~= "" and args["2"] ~= "yes" then
discussion_text = string.format("%sat [[%s]]", discussion_text, args["2"])
else
discussion_text = string.format("%son the [[%s|talk page]]", discussion_text, title.talkPageTitle.fullText )
end
return used_on_text .. sandbox_text .. discussion_text .. " before implementing them." .. bot_text
end
function p.text(frame, count)
return p._text(getArgs(frame), count)
end
function p._main(args, nocat)
local count = nil
if yesno(args['fetch']) == false then
if (args[1] or '') ~= '' then count = tonumber(args[1]) end
else
count = _fetch(args)
end
local image = "[[File:Ambox warning yellow.svg|40px|alt=Warning|link=]]"
local type_param = "style"
local epilogue = ''
if args['system'] and args['system'] ~= '' then
image = "[[File:Ambox important.svg|40px|alt=Warning|link=]]"
type_param = "content"
nocat = nocat or args['nocat']
local categorise = (nocat == '' or not yesno(nocat))
if categorise and not mw.title.getCurrentTitle().isRedirect then
epilogue = mw.getCurrentFrame():preprocess('{{Sandbox other||{{#switch:{{#invoke:Effective protection level|{{#switch:{{NAMESPACE}}|File=upload|#default=edit}}|{{FULLPAGENAME}}}}|sysop|templateeditor|interfaceadmin=|#default=[[Category:Pages used in system messages needing protection]]}}}}')
end
elseif (args[1] == "risk" or (count and count >= 100000)) then
image = "[[File:Ambox warning orange.svg|40px|alt=Warning|link=]]"
type_param = "content"
end
if args["form"] == "editnotice" then
return mw.getCurrentFrame():expandTemplate{
title = 'editnotice',
args = {
["image"] = image,
["text"] = p._text(args, count),
["expiry"] = (args["expiry"] or "")
}
} .. epilogue
else
return require('Module:Message box').main('ombox', {
type = type_param,
image = image,
text = p._text(args, count),
expiry = (args["expiry"] or "")
}) .. epilogue
end
end
function p.main(frame)
return p._main(getArgs(frame))
end
return p
7u5ehuisjaqc8500quv6bo138vry5dv
886409
886408
2025-06-13T17:03:31Z
KartikMistry
10383
[[:en:Module:High-use]] માંથી આયાત કરેલ ૧ પુનરાવર્તન
886408
Scribunto
text/plain
local p = {}
local getArgs = require('Module:Arguments').getArgs
-- _fetch looks at the "demo" argument.
local _fetch = require('Module:Transclusion_count')._fetch
local yesno = require('Module:Yesno')
function p._num(args, count, no_percent)
if count == nil then
if yesno(args['fetch']) == false then
if (args[1] or '') ~= '' then count = tonumber(args[1]) end
else
count = _fetch(args)
end
end
-- Build output string
local return_value = ""
if count == nil then
if args[1] == "risk" then
return "a very large number of"
else
return "many"
end
else
-- Use 2 significant figures for smaller numbers and 3 for larger ones
local sigfig = 2
if count >= 100000 then
sigfig = 3
end
-- Prepare to round to appropriate number of sigfigs
local f = math.floor(math.log10(count)) - sigfig + 1
-- Round and insert "approximately" or "+" when appropriate
if (args[2] == "yes") or (type(args[1]) == 'string' and (mw.ustring.sub(args[1],-1) == "+")) then
-- Round down
return_value = string.format("%s+", mw.getContentLanguage():formatNum(math.floor( (count / 10^(f)) ) * (10^(f))) )
else
-- Round to nearest
return_value = string.format("approximately %s", mw.getContentLanguage():formatNum(math.floor( (count / 10^(f)) + 0.5) * (10^(f))) )
end
-- Insert percentage of pages if that is likely to be >= 1% and when |no-percent= not set to yes
no_percent = no_percent or args['no-percent']
if count and count > 250000 and not yesno (no_percent) then
local numpages = mw.getCurrentFrame():callParserFunction('NUMBEROFPAGES', 'R')
local percent = math.floor( ( ( count/numpages ) * 100) + 0.5)
if percent >= 1 then
return_value = string.format("%s pages, or roughly %s%% of all", return_value, percent)
end
end
end
return return_value
end
function p.num(frame, count)
return p._num(getArgs(frame), count)
end
-- Actions if there is a large (greater than or equal to 100,000) transclusion count
function p._risk(args)
if args[1] == "risk" then
return "risk"
else
local count = _fetch(args)
if count and count >= 100000 then
return "risk"
end
end
return ""
end
function p.risk(frame)
return p._risk(getArgs(frame))
end
function p._text(args, count)
-- Only show the information about how this template gets updated if someone
-- is actually editing the page and maybe trying to update the count.
local bot_text = (mw.getCurrentFrame():preprocess("{{REVISIONID}}") == "") and "\n\n----\n'''Preview message''': Transclusion count updated automatically ([[Template:High-use/doc#Technical details|see documentation]])." or ''
if count == nil then
if yesno(args['fetch']) == false then
if (args[1] or '') ~= '' then count = tonumber(args[1]) end
else
count = _fetch(args)
end
end
local title = mw.title.getCurrentTitle()
if ( (args.demo or '' ~= '') and mw.title.new(args.demo, 10) ) then
title = mw.title.new(args.demo, 10)
end
if title.subpageText == "doc" or title.subpageText == "sandbox" then
title = title.basePageTitle
end
local systemMessages = (args['system'] or '') ~= ''
-- This retrieves the project URL automatically to simplify localization.
local templateCount = ('on [https://linkcount.toolforge.org/?project=%s&page=%s#transclusions %s pages]'):format(
title:fullUrl():gsub('//(.-)/.*', '%1'),
mw.uri.encode(title.fullText), p._num(args, count))
local used_on_text = "'''This " .. (title.namespace == 828 and "Lua module" or "template") .. ' is used ';
if systemMessages then
used_on_text = used_on_text .. args['system'] ..
((count and count > 2000) and ("''', and " .. templateCount) or ("'''"))
else
used_on_text = used_on_text .. templateCount .. "'''"
end
local sandbox_text = ("%s's [[%s/sandbox|/sandbox]] or [[%s/testcases|/testcases]] subpages, or in your own [[%s]]. "):format(
(title.namespace == 828 and "module" or "template"),
title.fullText, title.fullText,
title.namespace == 828 and "Module:Sandbox|module sandbox" or "Wikipedia:User pages#SUB|user subpage"
)
local infoArg = args["info"] ~= "" and args["info"]
if (systemMessages or args[1] == "risk" or (count and count >= 100000) ) then
local info = systemMessages and '.<br/>Changes to it can cause immediate changes to the Wikipedia user interface.' or '.'
if infoArg then
info = info .. "<br />" .. infoArg
end
sandbox_text = info .. '<br /> To avoid major disruption' ..
(count and count >= 100000 and ' and server load' or '') ..
', any changes should be tested in the ' .. sandbox_text ..
'The tested changes can be added to this page in a single edit. '
else
sandbox_text = (infoArg and ('.<br />' .. infoArg .. ' C') or ' and c') ..
'hanges may be widely noticed. Test changes in the ' .. sandbox_text
end
local discussion_text = systemMessages and 'Please discuss changes ' or 'Consider discussing changes '
if args["2"] and args["2"] ~= "" and args["2"] ~= "yes" then
discussion_text = string.format("%sat [[%s]]", discussion_text, args["2"])
else
discussion_text = string.format("%son the [[%s|talk page]]", discussion_text, title.talkPageTitle.fullText )
end
return used_on_text .. sandbox_text .. discussion_text .. " before implementing them." .. bot_text
end
function p.text(frame, count)
return p._text(getArgs(frame), count)
end
function p._main(args, nocat)
local count = nil
if yesno(args['fetch']) == false then
if (args[1] or '') ~= '' then count = tonumber(args[1]) end
else
count = _fetch(args)
end
local image = "[[File:Ambox warning yellow.svg|40px|alt=Warning|link=]]"
local type_param = "style"
local epilogue = ''
if args['system'] and args['system'] ~= '' then
image = "[[File:Ambox important.svg|40px|alt=Warning|link=]]"
type_param = "content"
nocat = nocat or args['nocat']
local categorise = (nocat == '' or not yesno(nocat))
if categorise and not mw.title.getCurrentTitle().isRedirect then
epilogue = mw.getCurrentFrame():preprocess('{{Sandbox other||{{#switch:{{#invoke:Effective protection level|{{#switch:{{NAMESPACE}}|File=upload|#default=edit}}|{{FULLPAGENAME}}}}|sysop|templateeditor|interfaceadmin=|#default=[[Category:Pages used in system messages needing protection]]}}}}')
end
elseif (args[1] == "risk" or (count and count >= 100000)) then
image = "[[File:Ambox warning orange.svg|40px|alt=Warning|link=]]"
type_param = "content"
end
if args["form"] == "editnotice" then
return mw.getCurrentFrame():expandTemplate{
title = 'editnotice',
args = {
["image"] = image,
["text"] = p._text(args, count),
["expiry"] = (args["expiry"] or "")
}
} .. epilogue
else
return require('Module:Message box').main('ombox', {
type = type_param,
image = image,
text = p._text(args, count),
expiry = (args["expiry"] or "")
}) .. epilogue
end
end
function p.main(frame)
return p._main(getArgs(frame))
end
return p
7u5ehuisjaqc8500quv6bo138vry5dv
વિભાગ:Transclusion count
828
111709
886410
869670
2024-12-02T02:58:40Z
en>Ahecht
0
access _fetch without passing frame
886410
Scribunto
text/plain
local p = {}
function p._fetch(args)
local template = nil
local return_value = nil
-- Use demo parameter if it exists, otherwise use current template name
local namespace = mw.title.getCurrentTitle().namespace
if args["demo"] and args["demo"] ~= "" then
template = mw.ustring.gsub(args["demo"],"^[Tt]emplate:","")
elseif namespace == 10 then -- Template namespace
template = mw.title.getCurrentTitle().text
elseif namespace == 828 then -- Module namespace
template = (mw.site.namespaces[828].name .. ":" .. mw.title.getCurrentTitle().text)
end
-- If in template or module namespace, look up count in /data
if template ~= nil then
namespace = mw.title.new(template, "Template").namespace
if namespace == 10 or namespace == 828 then
template = mw.ustring.gsub(template, "/doc$", "") -- strip /doc from end
template = mw.ustring.gsub(template, "/sandbox$", "") -- strip /sandbox from end
local index = mw.ustring.sub(mw.title.new(template).text,1,1)
local status, data = pcall(function ()
return(mw.loadData('Module:Transclusion_count/data/' .. (mw.ustring.find(index, "%a") and index or "other")))
end)
if status then
return_value = tonumber(data[mw.ustring.gsub(template, " ", "_")])
end
end
end
-- If database value doesn't exist, use value passed to template
if return_value == nil and args[1] ~= nil then
local arg1=mw.ustring.match(args[1], '[%d,]+')
if arg1 and arg1 ~= '' then
return_value = tonumber(mw.getCurrentFrame():callParserFunction('formatnum', arg1, 'R'))
end
end
return return_value
end
function p.fetch(frame)
return p._fetch(frame.args)
end
-- Tabulate this data for [[Wikipedia:Database reports/Templates transcluded on the most pages]]
function p.tabulate()
local list = {}
for i = 65, 91 do
local data = mw.loadData('Module:Transclusion count/data/' .. ((i == 91) and 'other' or string.char(i)))
for name, count in pairs(data) do
table.insert(list, {mw.title.new(name, "Template").fullText, count})
end
end
table.sort(list, function(a, b)
return (a[2] == b[2]) and (a[1] < b[1]) or (a[2] > b[2])
end)
local lang = mw.getContentLanguage();
for i = 1, #list do
list[i] = ('|-\n| %d || [[%s]] || %s\n'):format(i, list[i][1]:gsub('_', ' '), lang:formatNum(list[i][2]))
end
return table.concat(list)
end
return p
cvwrrf1z7y5db6z5bajpjay525izf50
886411
886410
2025-06-13T17:03:31Z
KartikMistry
10383
[[:en:Module:Transclusion_count]] માંથી આયાત કરેલ ૧ પુનરાવર્તન
886410
Scribunto
text/plain
local p = {}
function p._fetch(args)
local template = nil
local return_value = nil
-- Use demo parameter if it exists, otherwise use current template name
local namespace = mw.title.getCurrentTitle().namespace
if args["demo"] and args["demo"] ~= "" then
template = mw.ustring.gsub(args["demo"],"^[Tt]emplate:","")
elseif namespace == 10 then -- Template namespace
template = mw.title.getCurrentTitle().text
elseif namespace == 828 then -- Module namespace
template = (mw.site.namespaces[828].name .. ":" .. mw.title.getCurrentTitle().text)
end
-- If in template or module namespace, look up count in /data
if template ~= nil then
namespace = mw.title.new(template, "Template").namespace
if namespace == 10 or namespace == 828 then
template = mw.ustring.gsub(template, "/doc$", "") -- strip /doc from end
template = mw.ustring.gsub(template, "/sandbox$", "") -- strip /sandbox from end
local index = mw.ustring.sub(mw.title.new(template).text,1,1)
local status, data = pcall(function ()
return(mw.loadData('Module:Transclusion_count/data/' .. (mw.ustring.find(index, "%a") and index or "other")))
end)
if status then
return_value = tonumber(data[mw.ustring.gsub(template, " ", "_")])
end
end
end
-- If database value doesn't exist, use value passed to template
if return_value == nil and args[1] ~= nil then
local arg1=mw.ustring.match(args[1], '[%d,]+')
if arg1 and arg1 ~= '' then
return_value = tonumber(mw.getCurrentFrame():callParserFunction('formatnum', arg1, 'R'))
end
end
return return_value
end
function p.fetch(frame)
return p._fetch(frame.args)
end
-- Tabulate this data for [[Wikipedia:Database reports/Templates transcluded on the most pages]]
function p.tabulate()
local list = {}
for i = 65, 91 do
local data = mw.loadData('Module:Transclusion count/data/' .. ((i == 91) and 'other' or string.char(i)))
for name, count in pairs(data) do
table.insert(list, {mw.title.new(name, "Template").fullText, count})
end
end
table.sort(list, function(a, b)
return (a[2] == b[2]) and (a[1] < b[1]) or (a[2] > b[2])
end)
local lang = mw.getContentLanguage();
for i = 1, #list do
list[i] = ('|-\n| %d || [[%s]] || %s\n'):format(i, list[i][1]:gsub('_', ' '), lang:formatNum(list[i][2]))
end
return table.concat(list)
end
return p
cvwrrf1z7y5db6z5bajpjay525izf50
વિભાગ:Template link general
828
121648
886366
869246
2024-12-24T19:31:50Z
en>Primefac
0
put subst on if nolink
886366
Scribunto
text/plain
-- This implements Template:Template link general and various other templates in its family
local getArgs = require('Module:Arguments').getArgs
local p = {}
-- Is a string non-empty?
local function _ne(s)
return s ~= nil and s ~= ""
end
local nw = mw.text.nowiki
local function addTemplate(s)
local i, _ = s:find(':', 1, true)
if i == nil then
return 'Template:' .. s
end
local ns = s:sub(1, i - 1)
if ns == '' or mw.site.namespaces[ns] then
return s
else
return 'Template:' .. s
end
end
local function trimTemplate(s)
local needle = 'template:'
if s:sub(1, needle:len()):lower() == needle then
return s:sub(needle:len() + 1)
else
return s
end
end
local function linkTitle(args)
if _ne(args.nolink) then
if _ne(args.subst) then
return 'subst:' .. args['1']
else
return args['1']
end
end
local titleObj
local titlePart = '[['
if args['1'] then
-- This handles :Page and other NS
titleObj = mw.title.new(args['1'], 'Template')
else
titleObj = mw.title.getCurrentTitle()
end
titlePart = titlePart .. (titleObj ~= nil and titleObj.fullText or
addTemplate(args['1']))
local textPart = args.alttext
if not _ne(textPart) then
if titleObj ~= nil then
textPart = titleObj:inNamespace("Template") and args['1'] or titleObj.fullText
else
-- redlink
textPart = args['1']
end
end
if _ne(args.subst) then
-- HACK: the ns thing above is probably broken
textPart = 'subst:' .. textPart
end
if _ne(args.brace) then
textPart = nw('{{') .. textPart .. nw('}}')
elseif _ne(args.braceinside) then
textPart = nw('{') .. textPart .. nw('}')
end
titlePart = titlePart .. '|' .. textPart .. ']]'
if _ne(args.braceinside) then
titlePart = nw('{') .. titlePart .. nw('}')
end
return titlePart
end
function p.main(frame)
local args = getArgs(frame, {
trim = true,
removeBlanks = false
})
return p._main(args)
end
function p._main(args)
local bold = _ne(args.bold) or _ne(args.boldlink) or _ne(args.boldname)
local italic = _ne(args.italic) or _ne(args.italics)
local dontBrace = _ne(args.brace) or _ne(args.braceinside)
local code = _ne(args.code) or _ne(args.tt)
local show_result = _ne(args._show_result)
local expand = _ne(args._expand)
-- Build the link part
local titlePart = linkTitle(args)
if bold then titlePart = "'''" .. titlePart .. "'''" end
if _ne(args.nowrapname) then titlePart = '<span class="nowrap">' .. titlePart .. '</span>' end
-- Build the arguments
local textPart = ""
local textPartBuffer = "|"
local codeArguments = {}
local codeArgumentsString = ""
local i = 2
local j = 1
while args[i] do
local val = args[i]
if val ~= "" then
if _ne(args.nowiki) then
-- Unstrip nowiki tags first because calling nw on something that already contains nowiki tags will
-- mangle the nowiki strip marker and result in literal UNIQ...QINU showing up
val = nw(mw.text.unstripNoWiki(val))
end
local k, v = string.match(val, "(.*)=(.*)")
if not k then
codeArguments[j] = val
j = j + 1
else
codeArguments[k] = v
end
codeArgumentsString = codeArgumentsString .. textPartBuffer .. val
if italic then
val = '<span style="font-style:italic;">' .. val .. '</span>'
end
textPart = textPart .. textPartBuffer .. val
end
i = i + 1
end
-- final wrap
local ret = titlePart .. textPart
if not dontBrace then ret = nw('{{') .. ret .. nw('}}') end
if _ne(args.a) then ret = nw('*') .. ' ' .. ret end
if _ne(args.kbd) then ret = '<kbd>' .. ret .. '</kbd>' end
if code then
ret = '<code>' .. ret .. '</code>'
elseif _ne(args.plaincode) then
ret = '<code style="border:none;background:transparent;color:inherit">' .. ret .. '</code>'
end
if _ne(args.nowrap) then ret = '<span class="nowrap">' .. ret .. '</span>' end
--[[ Wrap as html??
local span = mw.html.create('span')
span:wikitext(ret)
--]]
if _ne(args.debug) then ret = ret .. '\n<pre>' .. mw.text.encode(mw.dumpObject(args)) .. '</pre>' end
if show_result then
local result = mw.getCurrentFrame():expandTemplate{title = addTemplate(args[1]), args = codeArguments}
ret = ret .. " → " .. result
end
if expand then
local query = mw.text.encode('{{' .. addTemplate(args[1]) .. string.gsub(codeArgumentsString, textPartBuffer, "|") .. '}}')
local url = mw.uri.fullUrl('special:ExpandTemplates', 'wpInput=' .. query)
mw.log()
ret = ret .. " [" .. tostring(url) .. "]"
end
return ret
end
return p
kj8qd6uwqhx6n0gkkkslldw0pq00bgy
886367
886366
2025-06-13T17:03:25Z
KartikMistry
10383
[[:en:Module:Template_link_general]] માંથી આયાત કરેલ ૧ પુનરાવર્તન
886366
Scribunto
text/plain
-- This implements Template:Template link general and various other templates in its family
local getArgs = require('Module:Arguments').getArgs
local p = {}
-- Is a string non-empty?
local function _ne(s)
return s ~= nil and s ~= ""
end
local nw = mw.text.nowiki
local function addTemplate(s)
local i, _ = s:find(':', 1, true)
if i == nil then
return 'Template:' .. s
end
local ns = s:sub(1, i - 1)
if ns == '' or mw.site.namespaces[ns] then
return s
else
return 'Template:' .. s
end
end
local function trimTemplate(s)
local needle = 'template:'
if s:sub(1, needle:len()):lower() == needle then
return s:sub(needle:len() + 1)
else
return s
end
end
local function linkTitle(args)
if _ne(args.nolink) then
if _ne(args.subst) then
return 'subst:' .. args['1']
else
return args['1']
end
end
local titleObj
local titlePart = '[['
if args['1'] then
-- This handles :Page and other NS
titleObj = mw.title.new(args['1'], 'Template')
else
titleObj = mw.title.getCurrentTitle()
end
titlePart = titlePart .. (titleObj ~= nil and titleObj.fullText or
addTemplate(args['1']))
local textPart = args.alttext
if not _ne(textPart) then
if titleObj ~= nil then
textPart = titleObj:inNamespace("Template") and args['1'] or titleObj.fullText
else
-- redlink
textPart = args['1']
end
end
if _ne(args.subst) then
-- HACK: the ns thing above is probably broken
textPart = 'subst:' .. textPart
end
if _ne(args.brace) then
textPart = nw('{{') .. textPart .. nw('}}')
elseif _ne(args.braceinside) then
textPart = nw('{') .. textPart .. nw('}')
end
titlePart = titlePart .. '|' .. textPart .. ']]'
if _ne(args.braceinside) then
titlePart = nw('{') .. titlePart .. nw('}')
end
return titlePart
end
function p.main(frame)
local args = getArgs(frame, {
trim = true,
removeBlanks = false
})
return p._main(args)
end
function p._main(args)
local bold = _ne(args.bold) or _ne(args.boldlink) or _ne(args.boldname)
local italic = _ne(args.italic) or _ne(args.italics)
local dontBrace = _ne(args.brace) or _ne(args.braceinside)
local code = _ne(args.code) or _ne(args.tt)
local show_result = _ne(args._show_result)
local expand = _ne(args._expand)
-- Build the link part
local titlePart = linkTitle(args)
if bold then titlePart = "'''" .. titlePart .. "'''" end
if _ne(args.nowrapname) then titlePart = '<span class="nowrap">' .. titlePart .. '</span>' end
-- Build the arguments
local textPart = ""
local textPartBuffer = "|"
local codeArguments = {}
local codeArgumentsString = ""
local i = 2
local j = 1
while args[i] do
local val = args[i]
if val ~= "" then
if _ne(args.nowiki) then
-- Unstrip nowiki tags first because calling nw on something that already contains nowiki tags will
-- mangle the nowiki strip marker and result in literal UNIQ...QINU showing up
val = nw(mw.text.unstripNoWiki(val))
end
local k, v = string.match(val, "(.*)=(.*)")
if not k then
codeArguments[j] = val
j = j + 1
else
codeArguments[k] = v
end
codeArgumentsString = codeArgumentsString .. textPartBuffer .. val
if italic then
val = '<span style="font-style:italic;">' .. val .. '</span>'
end
textPart = textPart .. textPartBuffer .. val
end
i = i + 1
end
-- final wrap
local ret = titlePart .. textPart
if not dontBrace then ret = nw('{{') .. ret .. nw('}}') end
if _ne(args.a) then ret = nw('*') .. ' ' .. ret end
if _ne(args.kbd) then ret = '<kbd>' .. ret .. '</kbd>' end
if code then
ret = '<code>' .. ret .. '</code>'
elseif _ne(args.plaincode) then
ret = '<code style="border:none;background:transparent;color:inherit">' .. ret .. '</code>'
end
if _ne(args.nowrap) then ret = '<span class="nowrap">' .. ret .. '</span>' end
--[[ Wrap as html??
local span = mw.html.create('span')
span:wikitext(ret)
--]]
if _ne(args.debug) then ret = ret .. '\n<pre>' .. mw.text.encode(mw.dumpObject(args)) .. '</pre>' end
if show_result then
local result = mw.getCurrentFrame():expandTemplate{title = addTemplate(args[1]), args = codeArguments}
ret = ret .. " → " .. result
end
if expand then
local query = mw.text.encode('{{' .. addTemplate(args[1]) .. string.gsub(codeArgumentsString, textPartBuffer, "|") .. '}}')
local url = mw.uri.fullUrl('special:ExpandTemplates', 'wpInput=' .. query)
mw.log()
ret = ret .. " [" .. tostring(url) .. "]"
end
return ret
end
return p
kj8qd6uwqhx6n0gkkkslldw0pq00bgy
ઢાંચો:Template link
10
124580
886338
869055
2025-01-21T19:19:39Z
en>Ahecht
0
Reduce [[WP:PEIS]]
886338
wikitext
text/x-wiki
<span class="nowrap">{{</span>[[Template:{{{1}}}|{{{1}}}]]<span class="nowrap">}}</span><noinclude>
{{documentation}}
<!-- Categories go on the /doc subpage and interwikis go on Wikidata. -->
</noinclude>
kkhg4xclyf9r0bcl7ypdd0yuh41mfu9
886339
886338
2025-06-13T17:03:22Z
KartikMistry
10383
[[:en:Template:Template_link]] માંથી આયાત કરેલ ૧ પુનરાવર્તન
886338
wikitext
text/x-wiki
<span class="nowrap">{{</span>[[Template:{{{1}}}|{{{1}}}]]<span class="nowrap">}}</span><noinclude>
{{documentation}}
<!-- Categories go on the /doc subpage and interwikis go on Wikidata. -->
</noinclude>
kkhg4xclyf9r0bcl7ypdd0yuh41mfu9
વિભાગ:Coordinates/styles.css
828
124974
886392
832413
2023-05-24T21:17:17Z
en>Izno
0
per tper
886392
sanitized-css
text/css
/* {{pp-template}} */
/* Geographical coordinates defaults. The classes "geo", "longitude", and
"latitude" are used by the [[Geo microformat]]. */
.geo-default,
.geo-dms,
.geo-dec {
display: inline;
}
.geo-nondefault,
.geo-multi-punct,
.geo-inline-hidden {
display: none;
}
.longitude,
.latitude {
white-space: nowrap;
}
372gk2cap99kasnuwl2084jfakikzgw
886393
886392
2025-06-13T17:03:29Z
KartikMistry
10383
[[:en:Module:Coordinates/styles.css]] માંથી આયાત કરેલ ૧ પુનરાવર્તન
886392
sanitized-css
text/css
/* {{pp-template}} */
/* Geographical coordinates defaults. The classes "geo", "longitude", and
"latitude" are used by the [[Geo microformat]]. */
.geo-default,
.geo-dms,
.geo-dec {
display: inline;
}
.geo-nondefault,
.geo-multi-punct,
.geo-inline-hidden {
display: none;
}
.longitude,
.latitude {
white-space: nowrap;
}
372gk2cap99kasnuwl2084jfakikzgw
ઢાંચો:Reflist/styles.css
10
124981
886368
759898
2024-08-09T23:01:26Z
en>Izno
0
move font-size to @media screen so we can remove it from MediaWiki:Print.css
886368
sanitized-css
text/css
/* {{pp|small=yes}} */
.reflist {
margin-bottom: 0.5em;
list-style-type: decimal;
}
@media screen {
/* can we remove the font size declarations? .references gets a font-size in
* common.css that is always 90, and there is nothing else in reflist out in
* the wild. May affect column sizes.
*/
/* make smaller only on screens */
.reflist {
font-size: 90%; /* Default font-size */
}
}
.reflist .references {
font-size: 100%; /* Reset font-size when nested in .reflist */
margin-bottom: 0; /* Avoid double margin when nested in .reflist */
list-style-type: inherit; /* Enable custom list style types */
}
/* columns-2 and columns-3 are legacy for "2 or more" column view from when the
* template was implemented with column-count.
*/
.reflist-columns-2 {
column-width: 30em;
}
.reflist-columns-3 {
column-width: 25em;
}
/* Reset top margin for lists embedded in columns */
.reflist-columns {
margin-top: 0.3em;
}
.reflist-columns ol {
margin-top: 0;
}
/* Avoid elements breaking between columns */
.reflist-columns li {
page-break-inside: avoid; /* Removed from CSS in favor of break-inside c. 2020 */
break-inside: avoid-column;
}
.reflist-upper-alpha {
list-style-type: upper-alpha;
}
.reflist-upper-roman {
list-style-type: upper-roman;
}
.reflist-lower-alpha {
list-style-type: lower-alpha;
}
.reflist-lower-greek {
list-style-type: lower-greek;
}
.reflist-lower-roman {
list-style-type: lower-roman;
}
n1076v46ma6f6p24w7nhtc3ft3kbc4h
886369
886368
2025-06-13T17:03:25Z
KartikMistry
10383
[[:en:Template:Reflist/styles.css]] માંથી આયાત કરેલ ૧ પુનરાવર્તન
886368
sanitized-css
text/css
/* {{pp|small=yes}} */
.reflist {
margin-bottom: 0.5em;
list-style-type: decimal;
}
@media screen {
/* can we remove the font size declarations? .references gets a font-size in
* common.css that is always 90, and there is nothing else in reflist out in
* the wild. May affect column sizes.
*/
/* make smaller only on screens */
.reflist {
font-size: 90%; /* Default font-size */
}
}
.reflist .references {
font-size: 100%; /* Reset font-size when nested in .reflist */
margin-bottom: 0; /* Avoid double margin when nested in .reflist */
list-style-type: inherit; /* Enable custom list style types */
}
/* columns-2 and columns-3 are legacy for "2 or more" column view from when the
* template was implemented with column-count.
*/
.reflist-columns-2 {
column-width: 30em;
}
.reflist-columns-3 {
column-width: 25em;
}
/* Reset top margin for lists embedded in columns */
.reflist-columns {
margin-top: 0.3em;
}
.reflist-columns ol {
margin-top: 0;
}
/* Avoid elements breaking between columns */
.reflist-columns li {
page-break-inside: avoid; /* Removed from CSS in favor of break-inside c. 2020 */
break-inside: avoid-column;
}
.reflist-upper-alpha {
list-style-type: upper-alpha;
}
.reflist-upper-roman {
list-style-type: upper-roman;
}
.reflist-lower-alpha {
list-style-type: lower-alpha;
}
.reflist-lower-greek {
list-style-type: lower-greek;
}
.reflist-lower-roman {
list-style-type: lower-roman;
}
n1076v46ma6f6p24w7nhtc3ft3kbc4h
વિભાગ:Hatnote/styles.css
828
126789
886380
790067
2024-07-22T21:11:47Z
en>Izno
0
move @print none to templatestyles from [[MediaWiki:Print.css]] to support [[MediaWiki talk:Common.css/to do#Turn mobile.css/js totally off]]
886380
sanitized-css
text/css
/* {{pp|small=y}} */
.hatnote {
font-style: italic;
}
/* Limit structure CSS to divs because of [[Module:Hatnote inline]] */
div.hatnote {
/* @noflip */
padding-left: 1.6em;
margin-bottom: 0.5em;
}
.hatnote i {
font-style: normal;
}
/* The templatestyles element inserts a link element before hatnotes.
* TODO: Remove link if/when WMF resolves T200206 */
.hatnote + link + .hatnote {
margin-top: -0.5em;
}
@media print {
body.ns-0 .hatnote {
display: none !important;
}
}
jwhkjblpyf93ejffkuu68hxj9zpt08y
886381
886380
2025-06-13T17:03:26Z
KartikMistry
10383
[[:en:Module:Hatnote/styles.css]] માંથી આયાત કરેલ ૧ પુનરાવર્તન
886380
sanitized-css
text/css
/* {{pp|small=y}} */
.hatnote {
font-style: italic;
}
/* Limit structure CSS to divs because of [[Module:Hatnote inline]] */
div.hatnote {
/* @noflip */
padding-left: 1.6em;
margin-bottom: 0.5em;
}
.hatnote i {
font-style: normal;
}
/* The templatestyles element inserts a link element before hatnotes.
* TODO: Remove link if/when WMF resolves T200206 */
.hatnote + link + .hatnote {
margin-top: -0.5em;
}
@media print {
body.ns-0 .hatnote {
display: none !important;
}
}
jwhkjblpyf93ejffkuu68hxj9zpt08y
વિભાગ:Format link
828
130563
886376
810412
2022-10-04T13:37:11Z
en>Pppery
0
Avoid Lua erroring when we run out of expensive parser function calls
886376
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format link}} template.
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg
local mArguments -- lazily initialise [[Module:Arguments]]
local mError -- lazily initialise [[Module:Error]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
local function maybeItalicize(s, shouldItalicize)
-- Italicize s if s is a string and the shouldItalicize parameter is true.
if s and shouldItalicize then
return '<i>' .. s .. '</i>'
else
return s
end
end
local function parseLink(link)
-- Parse a link and return a table with the link's components.
-- These components are:
-- - link: the link, stripped of any initial colon (always present)
-- - page: the page name (always present)
-- - section: the page name (may be nil)
-- - display: the display text, if manually entered after a pipe (may be nil)
link = removeInitialColon(link)
-- Find whether a faux display value has been added with the {{!}} magic
-- word.
local prePipe, display = link:match('^(.-)|(.*)$')
link = prePipe or link
-- Find the page, if it exists.
-- For links like [[#Bar]], the page will be nil.
local preHash, postHash = link:match('^(.-)#(.*)$')
local page
if not preHash then
-- We have a link like [[Foo]].
page = link
elseif preHash ~= '' then
-- We have a link like [[Foo#Bar]].
page = preHash
end
-- Find the section, if it exists.
local section
if postHash and postHash ~= '' then
section = postHash
end
return {
link = link,
page = page,
section = section,
display = display,
}
end
local function formatDisplay(parsed, options)
-- Formats a display string based on a parsed link table (matching the
-- output of parseLink) and an options table (matching the input options for
-- _formatLink).
local page = maybeItalicize(parsed.page, options.italicizePage)
local section = maybeItalicize(parsed.section, options.italicizeSection)
if (not section) then
return page
elseif (not page) then
return mw.ustring.format('§ %s', section)
else
return mw.ustring.format('%s § %s', page, section)
end
end
local function missingArgError(target)
mError = require('Module:Error')
return mError.error{message =
'Error: no link or target specified! ([[' .. target .. '#Errors|help]])'
}
end
--------------------------------------------------------------------------------
-- Main functions
--------------------------------------------------------------------------------
function p.formatLink(frame)
-- The formatLink export function, for use in templates.
yesno = require('Module:Yesno')
local args = getArgs(frame)
local link = args[1] or args.link
local target = args[3] or args.target
if not (link or target) then
return missingArgError('Template:Format link')
end
return p._formatLink{
link = link,
display = args[2] or args.display,
target = target,
italicizePage = yesno(args.italicizepage),
italicizeSection = yesno(args.italicizesection),
categorizeMissing = args.categorizemissing
}
end
function p._formatLink(options)
-- The formatLink export function, for use in modules.
checkType('_formatLink', 1, options, 'table')
local function check(key, expectedType) --for brevity
checkTypeForNamedArg(
'_formatLink', key, options[key], expectedType or 'string', true
)
end
check('link')
check('display')
check('target')
check('italicizePage', 'boolean')
check('italicizeSection', 'boolean')
check('categorizeMissing')
-- Normalize link and target and check that at least one is present
if options.link == '' then options.link = nil end
if options.target == '' then options.target = nil end
if not (options.link or options.target) then
return missingArgError('Module:Format link')
end
local parsed = parseLink(options.link)
local display = options.display or parsed.display
local catMissing = options.categorizeMissing
local category = ''
-- Find the display text
if not display then display = formatDisplay(parsed, options) end
-- Handle the target option if present
if options.target then
local parsedTarget = parseLink(options.target)
parsed.link = parsedTarget.link
parsed.page = parsedTarget.page
end
-- Test if page exists if a diagnostic category is specified
if catMissing and (mw.ustring.len(catMissing) > 0) then
local title = nil
if parsed.page then title = mw.title.new(parsed.page) end
if title and (not title.isExternal) then
local success, exists = pcall(function() return title.exists end)
if success and not exists then
category = mw.ustring.format('[[Category:%s]]', catMissing)
end
end
end
-- Format the result as a link
if parsed.link == display then
return mw.ustring.format('[[:%s]]%s', parsed.link, category)
else
return mw.ustring.format('[[:%s|%s]]%s', parsed.link, display, category)
end
end
--------------------------------------------------------------------------------
-- Derived convenience functions
--------------------------------------------------------------------------------
function p.formatPages(options, pages)
-- Formats an array of pages using formatLink and the given options table,
-- and returns it as an array. Nil values are not allowed.
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink{
link = page,
categorizeMissing = options.categorizeMissing,
italicizePage = options.italicizePage,
italicizeSection = options.italicizeSection
}
end
return ret
end
return p
252hp8jk2qv051cngf0qjx0ljcf0bof
886377
886376
2025-06-13T17:03:26Z
KartikMistry
10383
[[:en:Module:Format_link]] માંથી આયાત કરેલ ૧ પુનરાવર્તન
886376
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format link}} template.
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg
local mArguments -- lazily initialise [[Module:Arguments]]
local mError -- lazily initialise [[Module:Error]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
local function maybeItalicize(s, shouldItalicize)
-- Italicize s if s is a string and the shouldItalicize parameter is true.
if s and shouldItalicize then
return '<i>' .. s .. '</i>'
else
return s
end
end
local function parseLink(link)
-- Parse a link and return a table with the link's components.
-- These components are:
-- - link: the link, stripped of any initial colon (always present)
-- - page: the page name (always present)
-- - section: the page name (may be nil)
-- - display: the display text, if manually entered after a pipe (may be nil)
link = removeInitialColon(link)
-- Find whether a faux display value has been added with the {{!}} magic
-- word.
local prePipe, display = link:match('^(.-)|(.*)$')
link = prePipe or link
-- Find the page, if it exists.
-- For links like [[#Bar]], the page will be nil.
local preHash, postHash = link:match('^(.-)#(.*)$')
local page
if not preHash then
-- We have a link like [[Foo]].
page = link
elseif preHash ~= '' then
-- We have a link like [[Foo#Bar]].
page = preHash
end
-- Find the section, if it exists.
local section
if postHash and postHash ~= '' then
section = postHash
end
return {
link = link,
page = page,
section = section,
display = display,
}
end
local function formatDisplay(parsed, options)
-- Formats a display string based on a parsed link table (matching the
-- output of parseLink) and an options table (matching the input options for
-- _formatLink).
local page = maybeItalicize(parsed.page, options.italicizePage)
local section = maybeItalicize(parsed.section, options.italicizeSection)
if (not section) then
return page
elseif (not page) then
return mw.ustring.format('§ %s', section)
else
return mw.ustring.format('%s § %s', page, section)
end
end
local function missingArgError(target)
mError = require('Module:Error')
return mError.error{message =
'Error: no link or target specified! ([[' .. target .. '#Errors|help]])'
}
end
--------------------------------------------------------------------------------
-- Main functions
--------------------------------------------------------------------------------
function p.formatLink(frame)
-- The formatLink export function, for use in templates.
yesno = require('Module:Yesno')
local args = getArgs(frame)
local link = args[1] or args.link
local target = args[3] or args.target
if not (link or target) then
return missingArgError('Template:Format link')
end
return p._formatLink{
link = link,
display = args[2] or args.display,
target = target,
italicizePage = yesno(args.italicizepage),
italicizeSection = yesno(args.italicizesection),
categorizeMissing = args.categorizemissing
}
end
function p._formatLink(options)
-- The formatLink export function, for use in modules.
checkType('_formatLink', 1, options, 'table')
local function check(key, expectedType) --for brevity
checkTypeForNamedArg(
'_formatLink', key, options[key], expectedType or 'string', true
)
end
check('link')
check('display')
check('target')
check('italicizePage', 'boolean')
check('italicizeSection', 'boolean')
check('categorizeMissing')
-- Normalize link and target and check that at least one is present
if options.link == '' then options.link = nil end
if options.target == '' then options.target = nil end
if not (options.link or options.target) then
return missingArgError('Module:Format link')
end
local parsed = parseLink(options.link)
local display = options.display or parsed.display
local catMissing = options.categorizeMissing
local category = ''
-- Find the display text
if not display then display = formatDisplay(parsed, options) end
-- Handle the target option if present
if options.target then
local parsedTarget = parseLink(options.target)
parsed.link = parsedTarget.link
parsed.page = parsedTarget.page
end
-- Test if page exists if a diagnostic category is specified
if catMissing and (mw.ustring.len(catMissing) > 0) then
local title = nil
if parsed.page then title = mw.title.new(parsed.page) end
if title and (not title.isExternal) then
local success, exists = pcall(function() return title.exists end)
if success and not exists then
category = mw.ustring.format('[[Category:%s]]', catMissing)
end
end
end
-- Format the result as a link
if parsed.link == display then
return mw.ustring.format('[[:%s]]%s', parsed.link, category)
else
return mw.ustring.format('[[:%s|%s]]%s', parsed.link, display, category)
end
end
--------------------------------------------------------------------------------
-- Derived convenience functions
--------------------------------------------------------------------------------
function p.formatPages(options, pages)
-- Formats an array of pages using formatLink and the given options table,
-- and returns it as an array. Nil values are not allowed.
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink{
link = page,
categorizeMissing = options.categorizeMissing,
italicizePage = options.italicizePage,
italicizeSection = options.italicizeSection
}
end
return ret
end
return p
252hp8jk2qv051cngf0qjx0ljcf0bof
વિભાગ:Transclusion count/data/O
828
130565
886412
810426
2025-06-08T05:12:50Z
en>Ahechtbot
0
[[Wikipedia:BOT|Bot]]: Updated page.
886412
Scribunto
text/plain
return {
["OCLC"] = 16000,
["OCLC_search_link"] = 2000,
["ODNBsub"] = 22000,
["OEDsub"] = 2200,
["OKA"] = 2100,
["OKATO_reference"] = 2200,
["OSM_Location_map"] = 6600,
["OW"] = 48000,
["Occupation_by_nationality_and_century_category_header"] = 16000,
["Occupation_by_nationality_and_century_category_header/continental"] = 22000,
["Occupation_by_nationality_and_century_category_header/continental/core"] = 2600,
["Occupation_by_nationality_and_century_category_header/diffusingchildren"] = 5800,
["Occupation_by_nationality_and_century_category_header/era"] = 24000,
["Occupation_by_nationality_and_century_category_header/nationality"] = 24000,
["Occupation_by_nationality_and_century_category_header/nationality/core"] = 21000,
["Occupation_by_nationality_and_century_category_header/nationality/innercore"] = 24000,
["Occupation_by_nationality_and_century_category_header/nationality/parentnationality"] = 2800,
["Occupation_by_nationality_and_century_category_header/outercore"] = 16000,
["Occupation_by_nationality_and_century_category_header/portal"] = 25000,
["Occupation_by_nationality_and_century_category_header/portal/core"] = 25000,
["Oceania_topic"] = 3000,
["Oclc"] = 2500,
["Od"] = 52000,
["Odlist"] = 4500,
["Official"] = 38000,
["Official_URL"] = 17000,
["Official_site"] = 2500,
["Official_website"] = 331000,
["Ofsted"] = 3900,
["Okina"] = 5600,
["OldStyleDate"] = 4100,
["Old_AfD_multi"] = 121000,
["Old_CfD"] = 52000,
["Old_IP_warnings_bottom"] = 2100,
["Old_IP_warnings_top"] = 2100,
["Old_MfD"] = 3600,
["Old_RfD"] = 31000,
["Old_TfD"] = 7700,
["Old_XfD_multi"] = 194000,
["Old_merge"] = 2900,
["Old_move"] = 21000,
["Old_moves"] = 24000,
["Old_peer_review"] = 5300,
["Old_prod"] = 35000,
["Old_prod_full"] = 5400,
["Old_rfd"] = 2100,
["Oldafdfull"] = 46000,
["Older_warnings"] = 51000,
["Oldffdfull"] = 3200,
["Oldid"] = 14000,
["Oldid2"] = 100000,
["Oldprodfull"] = 2800,
["Oldrfd"] = 6300,
["Oldtfdfull"] = 4100,
["Olympedia"] = 18000,
["Olympics.com"] = 20000,
["Olympics.com_profile"] = 9500,
["Ombox"] = 1280000,
["Ombox/shortcut"] = 4000,
["OnThisDay"] = 8500,
["On_hold"] = 4300,
["On_this_day"] = 12000,
["OneLegResult"] = 4200,
["OneLegStart"] = 4000,
["One_source"] = 60000,
["Onesource"] = 4300,
["Onhold"] = 2300,
["Open_access"] = 34000,
["Opentask"] = 4900,
["Opentasks"] = 4000,
["Order_of_magnitude"] = 281000,
["Ordered_list"] = 5600,
["Ordinal"] = 232000,
["Ordnance_Survey_url"] = 21000,
["Original_research"] = 15000,
["Original_research_inline"] = 3900,
["Orphan"] = 56000,
["Orphan_file"] = 49000,
["Orphan_image"] = 49000,
["Other_people"] = 34000,
["Other_places"] = 22000,
["Other_ships"] = 15000,
["Other_uses"] = 68000,
["Otheruses"] = 2000,
["Outdent"] = 77000,
["Overline"] = 2200,
["Overly_detailed"] = 3800,
["Own"] = 12000,
["Own_work"] = 12000,
["Module:OSM_Location_map"] = 6600,
["Module:Official_website"] = 331000,
["Module:Old_XfD_multi"] = 194000,
["Module:Old_moves"] = 24000,
["Module:On_this_day"] = 12000,
["Module:Ordinal"] = 251000,
["Module:Ordnance_Survey_coordinates"] = 44000,
["Module:Ordnance_Survey_coordinates/data"] = 44000,
["Module:Other_people"] = 34000,
["Module:Other_uses"] = 143000,
["Module:Outdent"] = 77000,
["Module:OutputBuffer"] = 22000,
}
fxuonqhyxhtq37hfbprwzmpmfearq4t
886413
886412
2025-06-13T17:03:31Z
KartikMistry
10383
[[:en:Module:Transclusion_count/data/O]] માંથી આયાત કરેલ ૧ પુનરાવર્તન
886412
Scribunto
text/plain
return {
["OCLC"] = 16000,
["OCLC_search_link"] = 2000,
["ODNBsub"] = 22000,
["OEDsub"] = 2200,
["OKA"] = 2100,
["OKATO_reference"] = 2200,
["OSM_Location_map"] = 6600,
["OW"] = 48000,
["Occupation_by_nationality_and_century_category_header"] = 16000,
["Occupation_by_nationality_and_century_category_header/continental"] = 22000,
["Occupation_by_nationality_and_century_category_header/continental/core"] = 2600,
["Occupation_by_nationality_and_century_category_header/diffusingchildren"] = 5800,
["Occupation_by_nationality_and_century_category_header/era"] = 24000,
["Occupation_by_nationality_and_century_category_header/nationality"] = 24000,
["Occupation_by_nationality_and_century_category_header/nationality/core"] = 21000,
["Occupation_by_nationality_and_century_category_header/nationality/innercore"] = 24000,
["Occupation_by_nationality_and_century_category_header/nationality/parentnationality"] = 2800,
["Occupation_by_nationality_and_century_category_header/outercore"] = 16000,
["Occupation_by_nationality_and_century_category_header/portal"] = 25000,
["Occupation_by_nationality_and_century_category_header/portal/core"] = 25000,
["Oceania_topic"] = 3000,
["Oclc"] = 2500,
["Od"] = 52000,
["Odlist"] = 4500,
["Official"] = 38000,
["Official_URL"] = 17000,
["Official_site"] = 2500,
["Official_website"] = 331000,
["Ofsted"] = 3900,
["Okina"] = 5600,
["OldStyleDate"] = 4100,
["Old_AfD_multi"] = 121000,
["Old_CfD"] = 52000,
["Old_IP_warnings_bottom"] = 2100,
["Old_IP_warnings_top"] = 2100,
["Old_MfD"] = 3600,
["Old_RfD"] = 31000,
["Old_TfD"] = 7700,
["Old_XfD_multi"] = 194000,
["Old_merge"] = 2900,
["Old_move"] = 21000,
["Old_moves"] = 24000,
["Old_peer_review"] = 5300,
["Old_prod"] = 35000,
["Old_prod_full"] = 5400,
["Old_rfd"] = 2100,
["Oldafdfull"] = 46000,
["Older_warnings"] = 51000,
["Oldffdfull"] = 3200,
["Oldid"] = 14000,
["Oldid2"] = 100000,
["Oldprodfull"] = 2800,
["Oldrfd"] = 6300,
["Oldtfdfull"] = 4100,
["Olympedia"] = 18000,
["Olympics.com"] = 20000,
["Olympics.com_profile"] = 9500,
["Ombox"] = 1280000,
["Ombox/shortcut"] = 4000,
["OnThisDay"] = 8500,
["On_hold"] = 4300,
["On_this_day"] = 12000,
["OneLegResult"] = 4200,
["OneLegStart"] = 4000,
["One_source"] = 60000,
["Onesource"] = 4300,
["Onhold"] = 2300,
["Open_access"] = 34000,
["Opentask"] = 4900,
["Opentasks"] = 4000,
["Order_of_magnitude"] = 281000,
["Ordered_list"] = 5600,
["Ordinal"] = 232000,
["Ordnance_Survey_url"] = 21000,
["Original_research"] = 15000,
["Original_research_inline"] = 3900,
["Orphan"] = 56000,
["Orphan_file"] = 49000,
["Orphan_image"] = 49000,
["Other_people"] = 34000,
["Other_places"] = 22000,
["Other_ships"] = 15000,
["Other_uses"] = 68000,
["Otheruses"] = 2000,
["Outdent"] = 77000,
["Overline"] = 2200,
["Overly_detailed"] = 3800,
["Own"] = 12000,
["Own_work"] = 12000,
["Module:OSM_Location_map"] = 6600,
["Module:Official_website"] = 331000,
["Module:Old_XfD_multi"] = 194000,
["Module:Old_moves"] = 24000,
["Module:On_this_day"] = 12000,
["Module:Ordinal"] = 251000,
["Module:Ordnance_Survey_coordinates"] = 44000,
["Module:Ordnance_Survey_coordinates/data"] = 44000,
["Module:Other_people"] = 34000,
["Module:Other_uses"] = 143000,
["Module:Outdent"] = 77000,
["Module:OutputBuffer"] = 22000,
}
fxuonqhyxhtq37hfbprwzmpmfearq4t
વિભાગ:GetParameters
828
131492
886374
816084
2024-11-25T21:58:30Z
en>Pppery
0
Changed protection settings for "[[Module:GetParameters]]": Used on 4.8 million pages ([Edit=Require administrator access] (indefinite) [Move=Require administrator access] (indefinite))
816083
Scribunto
text/plain
local p = {}
--[[
Helper function that populates the argument list given that user may need to use a mix of
named and unnamed parameters. This is relevant because named parameters are not
identical to unnamed parameters due to string trimming, and when dealing with strings
we sometimes want to either preserve or remove that whitespace depending on the application.
]]
function p.getParameters( frame_args, arg_list )
local new_args = {};
local index = 1;
local value;
for i,arg in ipairs( arg_list ) do
value = frame_args[arg]
if value == nil then
value = frame_args[index];
index = index + 1;
end
new_args[arg] = value;
end
return new_args;
end
--[[
Helper Function to interpret boolean strings
]]
function p.getBoolean( boolean_str )
local boolean_value;
if type( boolean_str ) == 'string' then
boolean_str = boolean_str:lower();
if boolean_str == 'false' or boolean_str == 'no' or boolean_str == '0'
or boolean_str == '' then
boolean_value = false;
else
boolean_value = true;
end
elseif type( boolean_str ) == 'boolean' then
boolean_value = boolean_str;
else
error( 'No boolean value found' );
end
return boolean_value
end
function p.defined(frame)
local arg = mw.text.trim(frame.args[1])
--if arg == tostring(tonumber(arg)) then -- undesired result for '-0'
-- arg = tonumber(arg)
--end
--if mw.ustring.find(arg, '^%s*-?[1-9][0-9]*%s*$') ~= nil or arg == '0' then
-- arg = tonumber(arg)
--end
if mw.ustring.find(arg, '^-?[1-9][0-9]*$') ~= nil then
arg = tonumber(arg)
elseif arg == '0' then
arg = 0
end
return frame:getParent().args[arg] ~= nil
end
return p
03tz719zcckyx3el16h6xmmjd2dn09v
886375
886374
2025-06-13T17:03:26Z
KartikMistry
10383
[[:en:Module:GetParameters]] માંથી આયાત કરેલ ૧ પુનરાવર્તન
816083
Scribunto
text/plain
local p = {}
--[[
Helper function that populates the argument list given that user may need to use a mix of
named and unnamed parameters. This is relevant because named parameters are not
identical to unnamed parameters due to string trimming, and when dealing with strings
we sometimes want to either preserve or remove that whitespace depending on the application.
]]
function p.getParameters( frame_args, arg_list )
local new_args = {};
local index = 1;
local value;
for i,arg in ipairs( arg_list ) do
value = frame_args[arg]
if value == nil then
value = frame_args[index];
index = index + 1;
end
new_args[arg] = value;
end
return new_args;
end
--[[
Helper Function to interpret boolean strings
]]
function p.getBoolean( boolean_str )
local boolean_value;
if type( boolean_str ) == 'string' then
boolean_str = boolean_str:lower();
if boolean_str == 'false' or boolean_str == 'no' or boolean_str == '0'
or boolean_str == '' then
boolean_value = false;
else
boolean_value = true;
end
elseif type( boolean_str ) == 'boolean' then
boolean_value = boolean_str;
else
error( 'No boolean value found' );
end
return boolean_value
end
function p.defined(frame)
local arg = mw.text.trim(frame.args[1])
--if arg == tostring(tonumber(arg)) then -- undesired result for '-0'
-- arg = tonumber(arg)
--end
--if mw.ustring.find(arg, '^%s*-?[1-9][0-9]*%s*$') ~= nil or arg == '0' then
-- arg = tonumber(arg)
--end
if mw.ustring.find(arg, '^-?[1-9][0-9]*$') ~= nil then
arg = tonumber(arg)
elseif arg == '0' then
arg = 0
end
return frame:getParent().args[arg] ~= nil
end
return p
03tz719zcckyx3el16h6xmmjd2dn09v
વિભાગ:Message box/ombox.css
828
134281
886414
869919
2024-08-03T21:24:20Z
en>Izno
0
only on screen
886414
sanitized-css
text/css
/* {{pp|small=y}} */
.ombox {
margin: 4px 0;
border-collapse: collapse;
border: 1px solid #a2a9b1; /* Default "notice" gray */
background-color: var(--background-color-neutral-subtle, #f8f9fa);
box-sizing: border-box;
color: var(--color-base, #202122);
}
/* For the "small=yes" option. */
.ombox.mbox-small {
font-size: 88%;
line-height: 1.25em;
}
.ombox-speedy {
border: 2px solid #b32424; /* Red */
background-color: #fee7e6; /* Pink */
}
.ombox-delete {
border: 2px solid #b32424; /* Red */
}
.ombox-content {
border: 1px solid #f28500; /* Orange */
}
.ombox-style {
border: 1px solid #fc3; /* Yellow */
}
.ombox-move {
border: 1px solid #9932cc; /* Purple */
}
.ombox-protection {
border: 2px solid #a2a9b1; /* Gray-gold */
}
.ombox .mbox-text {
border: none;
/* @noflip */
padding: 0.25em 0.9em;
width: 100%;
}
.ombox .mbox-image {
border: none;
/* @noflip */
padding: 2px 0 2px 0.9em;
text-align: center;
}
.ombox .mbox-imageright {
border: none;
/* @noflip */
padding: 2px 0.9em 2px 0;
text-align: center;
}
/* An empty narrow cell */
.ombox .mbox-empty-cell {
border: none;
padding: 0;
width: 1px;
}
.ombox .mbox-invalid-type {
text-align: center;
}
@media (min-width: 720px) {
.ombox {
margin: 4px 10%;
}
.ombox.mbox-small {
/* @noflip */
clear: right;
/* @noflip */
float: right;
/* @noflip */
margin: 4px 0 4px 1em;
width: 238px;
}
}
/** T367463 */
body.skin--responsive table.ombox img {
max-width: none !important;
}
@media screen {
html.skin-theme-clientpref-night .ombox-speedy {
background-color: #310402; /* Dark red, same hue/saturation as light */
}
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .ombox-speedy {
background-color: #310402; /* Dark red, same hue/saturation as light */
}
}
aknh3fw7498ygg9b1o85bvfqn56r48n
886415
870475
2025-06-13T17:03:31Z
KartikMistry
10383
[[:en:Module:Message_box/ombox.css]] માંથી આયાત કરેલ ૧ પુનરાવર્તન
870475
sanitized-css
text/css
/* {{pp|small=y}} */
.ombox {
margin: 4px 0;
border-collapse: collapse;
border: 1px solid #a2a9b1; /* Default "notice" gray */
background-color: var(--background-color-neutral-subtle, #f8f9fa);
box-sizing: border-box;
color: var(--color-base, #202122);
}
/* For the "small=yes" option. */
.ombox.mbox-small {
font-size: 88%;
line-height: 1.25em;
}
.ombox-speedy {
border: 2px solid #b32424; /* Red */
background-color: #fee7e6; /* Pink */
}
.ombox-delete {
border: 2px solid #b32424; /* Red */
}
.ombox-content {
border: 1px solid #f28500; /* Orange */
}
.ombox-style {
border: 1px solid #fc3; /* Yellow */
}
.ombox-move {
border: 1px solid #9932cc; /* Purple */
}
.ombox-protection {
border: 2px solid #a2a9b1; /* Gray-gold */
}
.ombox .mbox-text {
border: none;
/* @noflip */
padding: 0.25em 0.9em;
width: 100%;
}
.ombox .mbox-image {
border: none;
/* @noflip */
padding: 2px 0 2px 0.9em;
text-align: center;
}
.ombox .mbox-imageright {
border: none;
/* @noflip */
padding: 2px 0.9em 2px 0;
text-align: center;
}
/* An empty narrow cell */
.ombox .mbox-empty-cell {
border: none;
padding: 0;
width: 1px;
}
.ombox .mbox-invalid-type {
text-align: center;
}
@media (min-width: 720px) {
.ombox {
margin: 4px 10%;
}
.ombox.mbox-small {
/* @noflip */
clear: right;
/* @noflip */
float: right;
/* @noflip */
margin: 4px 0 4px 1em;
width: 238px;
}
}
/** T367463 */
body.skin--responsive table.ombox img {
max-width: none !important;
}
@media screen {
html.skin-theme-clientpref-night .ombox-speedy {
background-color: #310402; /* Dark red, same hue/saturation as light */
}
}
@media screen and ( prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .ombox-speedy {
background-color: #310402; /* Dark red, same hue/saturation as light */
}
}
94fgri006ezjuf8buhmwait7x4j84mj
સભ્યની ચર્ચા:ClashOfClansFTW157
3
139274
886429
853942
2025-06-14T10:50:37Z
Cabayi
24752
Cabayiએ [[સભ્યની ચર્ચા:Naren.Ayinala]]ને [[સભ્યની ચર્ચા:ClashOfClansFTW157]] પર ખસેડ્યું: Automatically moved page while renaming the user "[[Special:CentralAuth/Naren.Ayinala|Naren.Ayinala]]" to "[[Special:CentralAuth/ClashOfClansFTW157|ClashOfClansFTW157]]"
842020
wikitext
text/x-wiki
{{ઢાંચો:સ્વાગત|realName=|name=Repto79456}}
-- [[સભ્ય:Aniket|Aniket]] ([[સભ્યની ચર્ચા:Aniket|ચર્ચા]]) ૧૪:૨૬, ૨૭ માર્ચ ૨૦૨૩ (IST)
9w84z77a2zqqjkne4axuhpohbjud9ut
૨૦૨૫ અમદાવાદ વિમાન દુર્ઘટના
0
150835
886337
886288
2025-06-13T17:01:36Z
KartikMistry
10383
નકશો.
886337
wikitext
text/x-wiki
{{OSM Location map
| coord = {{Coord|23.066|N|72.624|E}}
| zoom = 13
| float = right
| width = 285
| height = 230
| caption = દુર્ઘટના સ્થળનો નકશો
| title =
| scalemark = 60
| shape1 =
| mark1 =
| mark-size1 = 14
| label1 = અમદાવાદ હવાઇમથક
| mark-coord1 = {{Coord|23.077222|N|72.634722|E}}
| label-pos1 = left
| label-size1 = 14
| label-color1 = green
| mark-title1 = અમદાવાદ આંતરરાષ્ટ્રીય હવાઇમથક
| mark-image =
| mark-description1 =
| shape2 =
| mark2 =
| mark-size2 = 16
| label2 = દુર્ઘટના સ્થળ
| mark-coord2 = {{Coord|23.055547|N|72.612406|E}}
| label-pos2 = top
| label-size2 = 14
| label-color2 = red
| mark-title2 = દુર્ઘટના સ્થળ
| mark-image2 =
| mark-description2 =
}}
૧૨ જૂન ૨૦૨૫ના રોજ અમદાવાદના [[સરદાર વલ્લભભાઈ પટેલ આંતરરાષ્ટ્રીય હવાઈમથક]]થી ઉડાણ ભરતી વખતે [[એર ઇન્ડિયા એક્સપ્રેસ]]નું બોઇંગ ૭૮૭-૮ ડ્રીમલાઇનર (ટેઇલ નંબર VT-ANB) વિમાન ધ્વસ્ત થયું હતું.<ref>{{Cite web|date=2025-06-12|title= Ahmedabad to London... ક્રેશ થયેલા વિમાનમાં 169 ભારતીય, બ્રિટનના 53, પોર્ટુગલના 7 અને કેનેડાનો એક મુસાફરો હતા|url= https://gujarati.abplive.com/news/ahmedabad/air-india-plane-crash-list-of-passengers-in-air-india-flight-crash-ahmedabad-to-london-943803#google_vignette|access-date=2025-06-12|website=ABP Live }}</ref> લંડન તરફ જતું આ વિમાન એરપોર્ટના પરિઘની બહાર [[અમદાવાદ]]ના વસાહતી વિસ્તાર મેઘાણીનગરમાં ધ્વસ્ત થયું હતું.<ref>{{Cite web|date=2025-06-12|title= પ્લેન દુર્ઘટના / ક્રેશ થયેલા વિમાનમાં 61 મુસાફરો વિદેશી હતા, એકલા બ્રિટનના જ 53 નાગરિકો સવાર હતા, જુઓ મુસાફરોની સંપૂર્ણ યાદી|url= https://www.vtvgujarati.com/news-details/61-passengers-in-the-crashed-plane-were-foreigners-see-the-list-of-passengers|access-date=2025-06-12|website=VTV ગુજરાતી}}</ref>
== સંદર્ભ ==
{{Reflist}}
{{સ્ટબ}}
h9gastztl8uw1tjomuy1jefh4wqyrvd
સભ્યની ચર્ચા:塔太师
3
150852
886327
2025-06-13T13:09:40Z
New user message
14116
નવા સભ્યનાં ચર્ચાનાં પાના પર [[ઢાંચો:સ્વાગત|સ્વાગત સંદેશ]]નો ઉમેરો
886327
wikitext
text/x-wiki
{{ઢાંચો:સ્વાગત|realName=|name=塔太师}}
-- [[સભ્ય:Aniket|Aniket]] ([[સભ્યની ચર્ચા:Aniket|ચર્ચા]]) ૧૮:૩૯, ૧૩ જૂન ૨૦૨૫ (IST)
l4v4svkxmwwb2ggbjbz7mruc9ol07xk
સભ્યની ચર્ચા:Ht7201
3
150853
886328
2025-06-13T13:46:16Z
New user message
14116
નવા સભ્યનાં ચર્ચાનાં પાના પર [[ઢાંચો:સ્વાગત|સ્વાગત સંદેશ]]નો ઉમેરો
886328
wikitext
text/x-wiki
{{ઢાંચો:સ્વાગત|realName=|name=Ht7201}}
-- [[સભ્ય:Aniket|Aniket]] ([[સભ્યની ચર્ચા:Aniket|ચર્ચા]]) ૧૯:૧૬, ૧૩ જૂન ૨૦૨૫ (IST)
qx1675k4wwlo7xzbaldqptpy58xiw21
સભ્યની ચર્ચા:Amiya224
3
150854
886329
2025-06-13T13:54:15Z
New user message
14116
નવા સભ્યનાં ચર્ચાનાં પાના પર [[ઢાંચો:સ્વાગત|સ્વાગત સંદેશ]]નો ઉમેરો
886329
wikitext
text/x-wiki
{{ઢાંચો:સ્વાગત|realName=|name=Amiya224}}
-- [[:User:Dsvyas|ધવલ સુધન્વા વ્યાસ]]<sup>[[:User_talk:Dsvyas|ચર્ચા]]/[[:Special:Contributions/Dsvyas|યોગદાન]]</sup> ૧૯:૨૪, ૧૩ જૂન ૨૦૨૫ (IST)
35bg4rvj8r00vn22p07wctfjv2qaysy
ઢાંચો:OSM Location map
10
150855
886331
2025-04-17T21:25:34Z
en>RobinLeicester
0
re-instate the strapline within a noinclude tag
886331
wikitext
text/x-wiki
{{#invoke:OSM Location map|main|useFormatStyle={{{useFormatStyle|standardstyle}}} <noinclude>
|label1=Annotated maps of anywhere in the world
|mark-coord1={{coord|0|0}}|mark-size1=10|label-pos1=top</noinclude> }}
{{Main other|[[Category:Articles containing OSM location maps]]}}<noinclude>
{{documentation}}
[[Category:OSM Location map templates]]
[[Category:Pages with disabled graphs]]
</noinclude>
l7qb604qqxvvpz96vlse07jzlwizrv6
886332
886331
2025-06-13T16:57:51Z
KartikMistry
10383
[[:en:Template:OSM_Location_map]] માંથી આયાત કરેલ ૧ પુનરાવર્તન
886331
wikitext
text/x-wiki
{{#invoke:OSM Location map|main|useFormatStyle={{{useFormatStyle|standardstyle}}} <noinclude>
|label1=Annotated maps of anywhere in the world
|mark-coord1={{coord|0|0}}|mark-size1=10|label-pos1=top</noinclude> }}
{{Main other|[[Category:Articles containing OSM location maps]]}}<noinclude>
{{documentation}}
[[Category:OSM Location map templates]]
[[Category:Pages with disabled graphs]]
</noinclude>
l7qb604qqxvvpz96vlse07jzlwizrv6
વિભાગ:OSM Location map
828
150856
886333
2025-06-12T16:31:22Z
en>Taavi
0
bypass tools.wmflabs.org redirect
886333
Scribunto
text/plain
require('strict')
local delink=require('Module:Delink').delink
local getArgs = require('Module:Arguments').getArgs
local p = {}
local maplist={}
local sgNames={}
local highlightOption=false
local highlightNum
local visibleLinks
-- This module creates framed maps of anywhere in the world, at the required scale, and enables annotations,
-- dots, shapes, lines and other ways to customise the area of the map being shown. It also provides a link
-- to an interactive fullscreen version, which has locator dots instead of annotations and shapes.
-- This is the 2025 successor module to a wiki-markup template version of 2024, which itself was a successor
-- to the 'Graph'/VEGA driven template that was begun in 2016, until the Vega version was switched off in 2023.
-- This module is called from template {{OSM Location map}}, which uses the same parameter formats as before.
-- In addition it will be possible to use a more concise parameter format using the template {{OSM Location dots}}
-- In general the css output from the two formats will be identical, but the the concise version will allow bits of
-- greater control over some of the settings.
-- see the documentation on the two template pages for details of how to use the mapping features.
-- If language customisation is needed, there are text items below that can be translated. Also see the color table
-- below with details of how to add additional color names to allow localised alternatives.
-- (Translating other language shape-types could be possible, but has not currently been contemplated.
-- Parameter name translation would be harder but likely to be possible, ideally still retaining compatibility
-- with template calls already written using English).
local negativeAnswer={no=1,'0'-1,off=1}
local fullscreenlinktext='Click for interactive fullscreen map with links to nearby articles'
local toggletext='[Hide/show caption list]'
local termsOfUse='Maps: terms of use'
local aboutOSM='About OpenStreetMaps'
local shapeList={} --This sets up the 'factoryDefault' shape group 0
shapeList["0"]={shapeType="0",
Name="initialSettings",
Parent="0",
--sga items for the shape
shape="circle",
shapeSize="12px",
shapeColor="blue",
shapeAngle="0deg",
--sgb items for border of the shape
outlineWidth="0.5px",
outlineColor="darkblue",
outlineStyle="solid",
--sgc items text settings for labels
textSZ="11px",
textCL="darkgrey",
textNG="0deg",
--sgf further text settings
textSP="0px",
textLH="120%",
textOL="0px",
textBG="transparent",
--sgd items for dotTag text settings
tagSize="10px",
tagColor="white",
tagSpacer="0px",
tagAngle="0deg",
--sge items for extension line to connect label to dot
textEW="0px",
textEC="darkgrey",
textES="solid"
}
local colorList={} -- used by colorLookup to catch unsupported colors (eg 'LimeGreen'), to convert to generic version
colorList['green']='hardgreen' -- it could also be added to to include alternative language equivelants, for a quick solution.
colorList['red']='hardred' -- colorList ['source'] = target
colorList['white']='white' -- converts any color that includes 'source' into its equivelent target
colorList['blue']='hardblue' -- note, for translation you can add to this list, rather than replace it,
colorList['brown']='brown' -- which would mean existing map definitions in english would also still work, alongside translated ones
colorList['grey']='hardgrey'
colorList['gray']='hardgrey'
colorList['purple']='hardpurple'
colorList['orange']='hardorange'
colorList['leaf']='hardleaf'
--for a more thorough translation, you can add all the variants of the colors as further CTB elements and hex values or redirects
local CTB={} -- set up a table of color names (the CTB Color table index) and html hash colorhex values.
CTB["paleblue"],CTB["softblue"],CTB["hardblue"],CTB["darkblue"]="#D6E1EC","#77A1CB","#4B77D6","#1c559e"
CTB["palered"],CTB["softred"],CTB["hardred"],CTB["darkred"] = "#FCC6C0","#EC644B","#DB3123","#AA1205"
CTB["palegreen"],CTB["softgreen"],CTB["hardgreen"],CTB["darkgreen"]= "#D2F0E5","#81AF81","#269F46","#0b7527"
CTB["paleleaf"],CTB["softleaf"],CTB["hardleaf"],CTB["darkleaf"]= "#dff5c1","#b5e376","#8cc244","#679c21"
CTB["palegrey"],CTB["softgrey"],CTB["hardgrey"],CTB["darkgrey"]= "#E8E8D6","#AAAA88","#777755","#444433"
CTB["palegray"],CTB["softgray"],CTB["hardgray"],CTB["darkgray"]=CTB["palegrey"],CTB["softgrey"],CTB["hardgrey"],CTB["darkgrey"]
CTB["palebrown"],CTB["softbrown"],CTB["hardbrown"],CTB["darkbrown"]="#FAF6ED","#CCB56C","#AD7F14","#754910"
CTB["palepurple"],CTB["softpurple"],CTB["hardpurple"],CTB["darkpurple"]="#e0d1e6","#c784e0","#a029cf","#7a05a8"
CTB["paleorange"],CTB["softorange"],CTB["hardorange"],CTB["darkorange"]="#ffedc2","#ffcf61","#EEB533","#e39f05"
CTB["black"],CTB["white"],CTB["yellow"]="#000000","#FFFFFF","#FAF039"
CTB["background"],CTB["paleground"],CTB["beigeground"]="#f9f6f2","#FEFEFA","#F5F5DC"
CTB["beige"]=CTB["beigeground"]
CTB["aqua"],CTB["teal"],CTB["fuchsia"] = "#00FFFF","#008080","#FF00FF"
CTB["maroon"],CTB["olive"],CTB["navy"] = "#800000","#808000","#000080"
CTB["lime"],CTB["limegreen"],CTB["aquamarine"] = "#00FF00","#32CD32","#7FFFD4"
CTB["silver"],CTB["yellow"],CTB["orchid"] = "#800000","#FFFF00","#DA70D6"
-- set up a table of predefined clip-paths
local pathshape={}
pathshape.squaredd = "M 19,1.25 l 0,18 -18,0 0,-18 18,0m-1,1 -16,0 0,16 16,0 0,-16m-1,1 0,14 -14,0 0,-14 14,0zm-1,1 -12,0 0,12 12,0 0,-12zm-1,1 0,10 -10,0 0,-10 10,0z"
pathshape.squared = "M 18,2.5 l 0,15 -15,0 0,-15 15,0m-1,1 -13,0 0,13 13,0 0,-13zm-1,1 0,11 -11,0 0,-11 11,0z"
pathshape.triangledd="M 0 20,20 20,10 0,0 20ZM1.5 19,10 1.7,18.5 19,1.5 19ZM3 18,17 18,10 3.8,3 18ZM4.5 17,10 5.4,15.4 17, 4.5,17ZM6 16,13.8 16,10 7.4z"
pathshape.triangled ="M1,18 l 18,0 l -9,-18 l -9,18zm1.7,-1.1 l 7.3,-14.6 l 7.3,14.6 l -14.6, 0zm1.7,-1 l 11.0,0 l -5.5,-11 l -5.5,11z"
pathshape.circledd = "M0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm0.8,0a9.2,9.2 0 1,1 18.4,0a9.2,9.2 0 1,1 -18.4,0m1,0a8.2,8.2 0 1,0 16.4,0a8.2,8.2 0 1,0 -16.4,0zm0.8,0a7.2,7.2 0 1,1 14.8,0a7.2,7.2 0 1,1 -14.8,0m1,0 a6.4,6.4 0 1,1 12.8,0a6.4,6.4 0 1,1 -12.8,0z"
pathshape.circled = "M2.5,10a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0m0.8,0 a5,5 0 1,1 11.4,0a5,5 0 1,1 -11.4,0"
pathshape.diamond = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10z"
pathshape.diamondd = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10zm1,0 l 6,8.5 l 6,-8.5 -6,-8.5 -6,8.5zm1,0 l 5,-7 5,7 -5,7 -5,-7z"
pathshape.diamonddd = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10zm0.75,0 l 6.25,9 l 6.25,-9 -6.25,-9 -6.25,9zm0.75,0 l 5.5,-8 5.5,8 -5.5,8 -5.5,-8zm0.75,0 l 4.75,7 l 4.75,-7 -4.75,-7 -4.75,7zm0.75,0 l 4,-6 4,6 -4,6 -4,-6z"
pathshape.crossd = "M3.1,12.5 l4.2,0 l0,4.2 l5,0 l0,-4 l4.2,0 l0,-5 l-4.2,0 l0,-4.2 l-5,0 l0,4.2 l-4.2,0zM2.3,10a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0z"
pathshape.cross = "M3.1,12.5 l4.2,0 l0,4.2 l5,0 l0,-4 l4.2,0 l0,-5 l-4.2,0 l0,-4.2 l-5,0 l0,4.2 l-4.2,0z"
pathshape.thincross = "M2,12 l6,0 l0,6 l4,0 l0,-6 l6,0 l0,-4 l-6,0 l0,-6 l-4,0 l0,6 l-6,0z"
pathshape.fivepointstar = "M10 0 L12.245 6.91 19.511 6.91 13.633 11.18 15.878 18.09 10 13.82 4.122 18.09 6.367 11.18 0.489 6.91 7.755 6.91Z"
pathshape.fivepointstard = "M10 1.5 L 11.90825 7.3735 18.08435 7.3735 13.08805 11.003 14.9963 16.8765 10 13.247 5.0037 16.8765 6.91195 11.003 1.91565 7.3735 8.09175 7.3735 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.sixpointstar = "M10 0 L12.323 5.977 18.66 5 14.645 10 18.66 15 12.323 14.023 10 20 7.677 14.023 1.34 15 5.355 10 1.34 5 7.677 5.977Z"
pathshape.sixpointstard = "M10 1.5 L 11.97455 6.58045 17.361 5.75 13.94825 10 17.361 14.25 11.97455 13.41955 10 18.5 8.02545 13.41955 2.639 14.25 6.05175 10 2.639 5.75 8.02545 6.58045 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.sevenpointstar = "M10 0 L12.048 5.747 17.818 3.765 14.602 8.95 19.749 12.225 13.69 12.943 14.339 19.01 10 14.72 5.661 19.01 6.31 12.943 0.251 12.225 5.398 8.95 2.182 3.765 7.952 5.747Z"
pathshape.sevenpointstard = "M10 1.5 L11.7408 6.38495 16.6453 4.70025 13.9117 9.1075 18.28665 11.89125 13.1365 12.50155 13.68815 17.6585 10 14.012 6.31185 17.6585 6.8635 12.50155 1.71335 11.89125 6.0883 9.1075 3.3547 4.70025 8.2592 6.38495 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.eightpointstar = "M10 0 L11.88 5.46 17.071 2.929 14.54 8.12 20 10 14.54 11.88 17.071 17.071 11.88 14.54 10 20 8.12 14.54 2.929 17.071 5.46 11.88 0 10 5.46 8.12 2.929 2.929 8.12 5.46Z"
pathshape.eightpointstard = "M10 0 L10 1.5 L11.598 6.141 16.01035 3.98965 13.859 8.402 18.5 10 13.859 11.598 16.01035 16.01035 11.598 13.859 10 18.5 8.402 13.859 3.98965 16.01035 6.141 11.598 1.5 10 6.141 8.402 3.98965 3.98965 8.402 6.141ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.ring="M2.6,9.5a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0z"
pathshape.boxd=pathshape.squared
pathshape.boxdd=pathshape.squaredd
pathshape.ellipsed=pathshape.circled
pathshape.ellipsedd=pathshape.circledd
local msg={}
local function debugmsg(txt)
table.insert(msg,txt)
end
local pmsg={}
local function previewMsg(txt)
table.insert(pmsg,txt)
end
local function colorLookup(color)
for c,l in pairs(colorList) do
if string.find(color,c) then return l end
end
return color
end
local function getColor (color,chk)
local c
local opacity="100"
if not color or color=='' then color='hardgrey' end
if color=="transparent" then return color end
if color=="background1" then color='background' end
if string.byte(color,1,1)==35 and (#color == 7 or #color == 9) then
c=color
elseif string.byte(color,1,1)==35 and #color == 4 then
c=string.sub(color,1,2)..'f'..string.sub(color,3,3)..'f'..string.sub(color,4,4)..'f'
else
local s=color..'1'
s= s:sub(0,s:find("%d")-1)
opacity=string.match(color,"%d+")
if not CTB[s] then s= colorList[s] end -- check for synonyms and translations
if not CTB[s] then debugmsg(mw.addWarning('color = '..color..'. The color name is not defined. Used default grey instead')) end
c=CTB[s] or CTB.hardgrey
end
if opacity and (tonumber(opacity) < 100) and string.find(c,"#")==1 and string.len(c)==7 and opacity~="" then
local hexval=string.format("%x",(math.floor(tonumber(opacity)*2.55)))
c=c..hexval
end
return c
end
function p.colorvalue(frame) -- enable external access to the CTB colorTable values. usage: {{#invoke:OSM Location map|colorvalue|color=hard blue}}
local c
if not frame.args.color or frame.args.color=='' then c='grey'
else c=string.lower(string.gsub(frame.args.color,'%s+','')) end
return string.upper(string.sub(getColor(c),2))
end
local function checkColors(color)
local c=getColor(color,'check')
local opacity =1 -- calculate colour brightness and return black or white for contrast
if c=='transparent' then return c,'#000000',0 end
if not (string.find(c,'#')==1) then return colorCap(c),'#FFFFFF',0 end
if #c>8 then opacity= tonumber('0x'..(string.sub(c,8,9)))/500 end
local r=tonumber('0x'..(string.sub(c,2,3)))/255
local g=tonumber('0x'..(string.sub(c,4,5)))/255
local b=tonumber('0x'..(string.sub(c,6,7)))/255
if 0.2126 * r + 0.7152 * g + 0.0722 * b / opacity < 0.7 then
return c,'#FFFFFF',0.2126 * r + 0.7152 * g + 0.0722 * b / opacity
else return c,'#000000',0.2126 * r + 0.7152 * g + 0.0722 * b / opacity
end
end
local function morethan(a,b)
a = tonumber(string.match(a, '%f[%d]%d[,.%d]*%f[%D]') or '0')
b = tonumber(string.match(b, '%f[%d]%d[,.%d]*%f[%D]') or '0')
return a>b
end
local function lessthan(a,b)
if tonumber(string.match(a, '%f[%d]%d[,.%d]*%f[%D]')) then
a = tonumber(string.match(a, '%f[%d]%d[,.%d]*%f[%D]'))
b = tonumber(string.match(b, '%f[%d]%d[,.%d]*%f[%D]') or '0')
end
return a<b
end
local function getsize(size)
--size1 is between 1 and 3 values, each with px, equating to width,height,corner-rounding
--eg '15px 25px 5px' (spaces are optional) or '18px'. returns three numbers
local sizeval = {}
for v in string.gmatch(size, "[^px]+") do
table.insert(sizeval,v)
end
sizeval[1] = tonumber(sizeval[1]) or 13
sizeval[2] = tonumber(sizeval[2]) or sizeval[1]
sizeval[3] = tonumber(sizeval[3]) or 0
return sizeval[1],sizeval[2],sizeval[3]
end
local function coord2text(coord) -- looks through the output from {{coord}} to find the lat and long decimal values
-- and converts compass points to minus or not-minus, return with separating comma.
local lat = string.match(coord,'[%.%d]+°[NS]')
local lon = string.match(coord,'[%.%d]+°[EW]')
local neg={N="",S="-",W="-",E=""}
return neg[string.match(lat, '[NS]')]..string.match(lat,'[%.%d]+')..","..neg[string.match(lon, '[EW]')]..string.match(lon,'[%.%d]+')
end
local function convertCoordsTrad (row)
local coords=''
if row and string.find (row,'<span class="geo">') then
local a,b=string.find(row,'<span class="geo">')
local start=b+1
a,b=string.find(row,"</span>",b)
local finish=a-1
coords=string.sub(row,start,finish)
coords=string.gsub(coords,'; ',',')
end
return coords
end
local function convertCoords (row)
local start,finish,lat,lon,coords,says
if row then
local a,b=string.find(row,"<span class=")
start=a
while a do -- find the final span>
finish=b
a,b=string.find(row,"span>",b)
end
if start then
coords= string.sub(row,start,finish)
says=""
if string.find(coords,'<span class="error">') then
error("coord error: badly formed coordinates",0)
end
coords=coord2text(coords)
coords = string.sub(row,1,start-1)..coords..string.sub(row,finish+1)
else
coords=row
end
return coords
else
return "Nothing to see here"
end
end
local function fillCommas(val,max)
local line=''
if not val then line=',' -- ensure there is some content
else line = val --string.lower(string.gsub(val,"%s+","")) -- or strip spaces
end
if string.find(line,',') == 1 then line=' '..line end -- ensure initial comma is not skipped
local _, count=string.gsub(line,",","") -- add enough subsequent commas for all entries
line=line..string.rep(',',max-count)
while(string.find(line,",,") ) do
line=string.gsub(line,",,",", ,") --ensure string.gmatch doesn't ignore any empty items by padding with spaces
end
return line
end
local function makeLinkBox(left,top,wid,label, link)
local linkBoxName='Transparent square.svg'
if visibleLinks or '' =='yes' then linkBoxName='Red hollow square.svg' end
local builder = mw.html.create('div') --display:inline-block;
builder
:cssText('position:absolute;left:'..tostring(left-1-wid/2)..'px;top:'..tostring(top-1 + math.min(wid/2-12,0) - wid/2)..'px')
:wikitext(string.format( '[[File:%s|%dpx|link=%s|%s]]', linkBoxName, wid+2, link, label ))
return tostring(builder)
end
local function extractItem(row,searchItem)
-- remove text following a searchItem or start of line, which might be in quote-marks to allow commas
local xend,xstart=1,0
if not row then return '','' end
if searchItem then xend,xstart= string.find(row or '',searchItem or 'image:') end
if not xstart then return string.gsub(string.gsub(row or '',"%b\"\"", ''),"%b\'\'", '') or '','' end
while row:byte(xstart+1) == 32 and xstart<#row do -- skip over any leading spaces
xstart=xstart+1
end
local xbyte=row:byte(xstart+1)
if xbyte == 34 or xbyte == 39 then -- are they wrapped in single or double quotes
xstart=xstart+1
xend=row:find(string.char(xbyte),xstart+1)
else
xend = row:find(',',xstart+1) -- if no quotes, we assume no commas
if not xend then xend=#row+1 end
end -- return residual row and extracted text
return row:sub(0,xstart)..row:sub(xend), row:sub(xstart+1,xend-1)
end
local function itemCheck(item,ext)
if not item then return nil end
if not ext then ext='' end
return (string.match(item,"[%.%-?%d]+") or '0')..ext
end
local function stripdivs(line)
return string.gsub(line or '',"%b<>", ' ')
end
local function splitItem(item,max) -- takes a commas-sep list and returns a table of lowercase items with no spaces, or nil
local r={}
local x=1
item=string.lower(fillCommas(item,max))
for t in string.gmatch(item,"[^,]+") do
r[x]=string.gsub(t,"%s+","")
if r[x]=='' then r[x]= nil else -- residual items might have commas
if x>max then r[max]=(r[max] or '')..', '..r[x] end
end
x=x+1
end
return r
end
local function ParseShapeTypes (result,args,sgval) -- for use with compressed, comma-separated 'sg plus dots' parameters
--[[ shape table items and default values as set at top of page
shapeType="0", Name="initialSettings", Parent="0",
--sga items for the shape shape="circle", shapeSize="12px", shapeColor="blue", shapeAngle="0deg",
--sgb items for border outlineWidth="0.5px", outlineColor="darkblue", outlineStyle="solid",
--sgc items label text textSZ="11px", textCL="white", textNG="0deg", textAT=attributes ("bold" and/or "italic")
--sgd items for dotTag tagSize="11px", tagColor="darkgrey", tagSpacer="0px", tagAngle="0deg",
--sge extension line textEW="1px", textEC="darkblue", textES="solid"
--sgf fx for text labels textSP="0px" textLH="100%" textOL="1px", textBG="paleground",
<!--| sga = shape,Size,Color,Angle|sgb= outlineWidth,color,style eg: sga1=circle,14px,blue,0deg| sgb1=0px,dark grey,solid
| sgc=textSize,color,angle,bold/italic | sgd=tagSize,Color,Spacer,Angle eg: sgc1=11px,dark grey,0deg,normal| sgd1=9px,white,0px,0deg
| sge=lineWidth,color,style |sgf=textspacing,lineHeight%,outlinepx,backgroundcolor, [bold,italic] eg: sge1=0px,black, solid| sgf1=0px,120%,0px,background
| dot=shape-group/lat/lon/title/dotTag | dotlink=link or tooltip | dotlabel=label,position,dx,dy,param1,info| dotpic=filename-->
--]]
if args["sgn"..sgval] then
local sgname=string.match(args["sgn"..sgval],"(%w+)(.*)")
sgNames[sgname]=sgval
end
local parent= args["sgp"..sgval]
if parent then
parent=string.match(parent,"(%w+)(.*)")
local pos= string.find(parent,"%d+")
if pos == 1 then
parent=string.match(parent,"%d+")
else
parent=sgNames[parent] or '1'
end
end
if sgval~='H' then
if not parent or tonumber(parent) > tonumber(sgval) then
if sgval=="1" then parent="0" else parent="1" end
end
end
local itemTab, line, filename
result[sgval]={}
result[sgval].shapeType=sgval
line,filename=extractItem(args['sga'..sgval] or '','image:')
if sgval=='1' and not args.sga1 then line='circle,12px,blue,0deg' end -- ensure there is a parent=1 sga
result[sgval].shapeFile=filename or ''
-- sga= Attributes for shape
itemTab=splitItem(line,4)
result[sgval].shape = itemTab[1] or result[parent].shape
result[sgval].shapeSize=itemTab[2] or result[parent].shapeSize
result[sgval].shapeColor=itemTab[3] or result[parent].shapeColor
result[sgval].shapeAngle=itemCheck(itemTab[4],'deg') or result[parent].shapeAngle
-- sgb= Border outline attributes for shape
itemTab=splitItem(args['sgb'..sgval],3)
result[sgval].outlineWidth=itemCheck(itemTab[1],'px') or result[parent].outlineWidth
result[sgval].outlineColor=itemTab[2] or result[parent].outlineColor
result[sgval].outlineStyle=itemTab[3] or result[parent].outlineStyle
--sgc=character attributes for label
itemTab=splitItem(args['sgc'..sgval],4)
result[sgval].textSZ=itemCheck(itemTab[1],'px') or result[parent].textSZ -- size of text in px
result[sgval].textCL=itemTab[2] or result[parent].textCL -- colour for text
result[sgval].textNG=itemCheck(itemTab[3],'deg') or result[parent].textNG -- Angle for text
result[sgval].textAT=itemTab[4] or result[parent].textAT -- attributes bold, and/or italic
--sgd=dotTag attributes
itemTab=splitItem(args['sgd'..sgval],4)
result[sgval].tagSize=itemCheck(itemTab[1],'px') or result[parent].tagSize
result[sgval].tagColor=itemTab[2] or result[parent].tagColor
result[sgval].tagSpacer=itemCheck(itemTab[3],'px') or result[parent].tagSpacer
result[sgval].tagAngle=itemCheck(itemTab[4],'deg') or result[parent].tagAngle
--sge= extension line attributes
itemTab=splitItem(args['sge'..sgval],4)
result[sgval].textEW=itemCheck(itemTab[1],'px') or result[parent].textEW -- width
result[sgval].textEC=itemTab[2] or result[parent].textEC -- colour
result[sgval].textES=itemTab[3] or result[parent].textES -- style
--sgf= fx for label text
itemTab=splitItem(args['sgf'..sgval],4)
result[sgval].textSP=itemCheck(itemTab[1],'px') or result[parent].textSP -- spacing value for letters
result[sgval].textLH=itemCheck(itemTab[2],'%') or result[parent].textLH -- Angle for text
result[sgval].textOL=itemCheck(itemTab[3],'px') or result[parent].textOL -- width of text-border line
result[sgval].textBG=itemTab[4] or result[parent].textBG -- color for text background
return result
end
local function round(x,dec)
-- x=number [, dec=integer] returns numeric value with upto dec decimals (all but first trailing zeros get truncated)
if (dec or 0)==0 then
return x>=0 and math.floor(x+0.5) or math.ceil(x-0.5) --this avoids .0 where dec=0
end
dec =10^(dec)
return x>=0 and math.floor(x*dec+0.5)/dec or math.ceil(x*dec-0.5)/dec
end
local function maptogrid(t,r)
--[[ converts mercator projection longitude and latitude coordinates to x and y pixel coordinates, within a frame of given size, centre coordinates and zoom level.
t is a table of named indices: {lon, lat, lonbase, latbase, width, height, zoom}
output is two values, x and y, rounded to r decimal places--]]
local x=t.width/2 + ( ((math.rad(t.lon)*6378137) - (math.rad(t.lonbase)* 6378137) ) / (156543.03*math.cos(t.latbase/180)/(2^t.zoom) ) )*(1-(0.055*(t.latbase/90)))
local y=t.height/2 + ( ( (math.log(math.tan(math.rad(t.latbase)/2+math.pi/4))*6378137) - (math.log(math.tan(math.rad(t.lat)/2+math.pi/4))*6378137) ) / (156543.03*math.cos(t.latbase/180) / (2^t.zoom) ) )*(1-(0.055*(t.latbase/90) ) )
return round(x,r),round(y,r)
--source: python code at https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames and https://wiki.openstreetmap.org/wiki/Mercator
--[[
width and height are the size, in pixels, of the map, which will be centerd around lonbase,latbase.
Method: Convert lon and lonbase to meter-offsets from coord(0,0), and subtract lonbase from lon,
zoom and latbase are used to scale the resulting meter-offset to pixels, and add it to width/2.
Convert lat and lat-base to meter-offsets from coord(0,0), subtract lat from latbase,
scale the resulting meter-offset to pixels, add it to height/2.
A correction factor '*(1-(0.055*(t.latbase/90) ) )' compensates for an error that seems to creep in towards
the edges of the map at higher latitudes. It was identified experimentally, and ensures a dot at the edge is
in the same place as if that location is positioned at the centre.
Original Python code for lat,lon to x,y where 0,0 is the centre of the map
print('x=',width+(((math.radians(lon1) * 6378137)-
(math.radians(lonbase) * 6378137))/
(156543.03*math.cos(latbase/180)/(2**zoom))),' y=',height+((
(math.log(math.tan(math.pi / 4 + math.radians(latbase) / 2)) * 6378137)-
(math.log(math.tan(math.pi / 4 + math.radians(lat1) / 2)) * 6378137))/
(156543.03*math.cos(latbase/180)/(2**zoom)))
) --]]
end
local function getScale(zoom, lat,magVal)
if magVal and magVal>1 and magVal <=2 then zoom=zoom+(magVal-1) end
local dist=(156543.03 * math.cos(math.rad(math.abs(lat))) / (2 ^ zoom))/17
local y
if dist < 1 then
y=(round(dist*10,1))
return tostring(y*100)..'m', tostring(round(y*109,0))..'yds'
elseif dist <18 then
y=(round(dist,0))
return tostring(y)..'km', tostring(round(y*0.621371,1))..'miles'
elseif dist <500 then
y=round(dist/10)
return tostring(y*10)..'km', tostring(round(y*6.21371,0))..'miles'
else
y=round(dist/100)
return tostring(y*100)..'km', tostring(round(y*62.1371,0))..'miles'
end
end
local function ParseData (args,dotval) -- for use with compressed, comma-separated 'sg plus dots' parameters
-- takes a structured series of comma-separated items which get parsed as the following:
-- dot(n)= (sgNumber or Name),{{coord}} or (lat,lon), (dotTag)
-- dotlink(n) = single-parameter text to give wikilink and/or title used by tootlip, fullscreen dots and autocaption list
-- dotlabel(n) = 'label text',pos(left,roght,top,bottom,centre,auto),(dx), (dy) pixel offsets, params, info
-- dotpic(n) = single parameter wikimedia filename for an image to use in photopanel and/or fullscreen dots
-- dotfeature(n)= 'mark-line' (,linewidth,style,gap) or 'photo-panel' (,image-dim,panel-width,panel-height), draws line to n-1
-- label is used if either a position and/or an x,y offset are not 0,0 ( if no label then dotTag will be put at at the x,y offset, or over the dot
-- label text can be autoaligned if x,y puts it left or right of the dot, or centered if above/below)
-- quote marks are not needed unless including commas within the label text
-- param1 is optional items separated by spaces, and can include [nolabel nolist nomap hemisphere+1 hemisphere-1]
-- info is free wikitext, to be used in the fullscreen dot box. (use dotpic to show a picture)
--<!--| dotx=shape-group,[lat,lon or {{coord}} ], dotTag | dotlink=link or tooltip | dotlabel=label,position,dx,dy,param1,info| dotpic=filename-->
local result={}
local count=1
local row = convertCoords (args["dot"..dotval]) -- swap in any {{coord}} values so they are csv lat and lon
row=fillCommas(row,4)
result.code=dotval -- store the parameter name as the id code
for item in string.gmatch(row,"[^,]+") do -- iterate through 'row', adding each csv item in turn, if present
if count==1 then --see if it is a number or a name
local pos= string.find(item,"%d+")
if pos == 1 then
result.group=string.lower(string.gsub(item,"%s+",""))
else
item=string.match(item,"(%w+)(.*)") -- ensure just a single word
result.group=sgNames[item]
end
elseif count==2 then
result.lat=tonumber(string.match(item,"[%.%-?%d]+")) or 0-- find the number, with no non-numeric stuff
elseif count==3 then
result.lon=tonumber(string.match(item,"[%.%-?%d]+")) or 0
elseif count==4 then
result.dotTag=item:match( "^%s*(.-)%s*$" ) or "" -- dotTag allows for internal spaces, but no commas
end
count=count+1
end
row, result.labelText= extractItem(args["dotlabel"..dotval])
result.labelText= string.gsub(result.labelText,"[%^]+","<br>") -- convert hats to line breaks
local item=splitItem(row,6)
result.labelPos=item[2] or 'center'
result.dx=tonumber(string.match(item[3] or '0',"[%.%-?%d]+")) or 0
result.dy=tonumber(string.match(item[4] or '0',"[%.%-?%d]+")) or 0
result.param1=string.lower(item[5] or '')
if string.find(result.param1,"hemisphere-1",1,true) then result.lon=result.lon-360
elseif string.find(result.param1,"hemisphere+1",1,true) then result.lon=result.lon+360
end
local txt = ''
if item[6] then -- ensure all info elements are included, including commas
count=1
local max=6
for t1 in string.gmatch(fillCommas(row,max),"[^,]+") do
if count>max then txt=txt..',' end
if count>=max then txt=txt..t1 end
count=count+1
end
end
result.info=txt
result.imageName = args['dotpic'..dotval]
-- Get first wikilinked item (if any) from the args.dotlink and set this plus the delinked text
local testx=args["dotlink"..dotval] or ''
result.dotLink=testx
if testx ~= '' then
testx=stripdivs(testx)
result.title=delink({ testx })
local linkstart= string.find(testx,'[[',1,true) -- use true to ensure a plain search (no pattern)
if linkstart then
result.dlink=delink( { string.sub(testx,linkstart,string.find(testx,']]',1,true)+1),wikilinks='target' } )
else result.dlink=''
end
else
result.dlink=''
result.title=''
end
if args['dotfeature'..dotval] then
local item=splitItem(args['dotfeature'..dotval],6)
if (item[1] or '') =='photo-panel' then
result.ppwidth= tonumber(string.match((item[3] or '110'),"%d+")) -- panel width
result.ppheight= tonumber(string.match((item[4] or '48'),"%d+") ) --panel height
result.photowidth=round(tonumber(string.match((item[2] or '1.3'),"[%.%-?%d]+")) * result.ppheight+1,0) -- dimension to set image size
result.photoImage=result.imageName
result.posType='photo-panel'
elseif (item[1] or '') == 'mark-line' then
local x=tonumber(dotval or '0')
result.markDest=item[5] or tostring(x-1)
result.mlWidth= tonumber(string.match((item[2]) or '',"%d+") or '1')
result.mlStyle= item[3] or 'solid'
result.mlGap=tonumber(string.match((item[4] or ''),"[%d]+") or '0')
result.posType='mark-line'
-- debugmsg('making line for '..tostring(x)..'to '..tostring(result.markDest)..' with width '..tostring(result.mlWidth))
end
--debugmsg('photo-panel, '..shapePos[2]..', 3='..shapePos[3]..', 4='..shapePos[4]..', 5='..(shapePos[5] or '(48')..'photowidth='..tostring(dotItem.photowidth))
end
maplist.lon=result.lon
maplist.lat=result.lat
result.gridx, result.gridy = maptogrid(maplist,1) -- convert geo coords to grid xy - using values from maplist table
return result
end
local function multiCheck (args, argName, argVal, defVal, alt)
if not alt then alt='nonexistant' end
if argVal=='H' and not args[argName..'H'] then argVal=highlightNum or '1' end
if argVal=='' then
return (args[argName] or args[alt] or (args[argName..'D']) or args[alt..'D'] or defVal) -- unnumbered args do not inherit from D or 1
else
return (args[argName..argVal]) or (args[alt..argVal]) or (args[argName..'D']) or (args[alt..'D']) or (args[argName..'1']) or (args[alt..'1']) or defVal
end
end
local function assignTradstyleShape(shapeResult,default,dotResult,args,nval)
local item,itemTab
local autoDotTag=''
local shapeWidth,shapeHeight=0,0
local argval=nval -- to catch the unnumbered shape series
if argval=='0' then argval='' end
if nval=='H' then shapeResult.H={} end
item=string.lower(multiCheck(args,'shape',argval,'image'))
if string.find(item,'n-',0,true)==1 or string.find(item,'l-',0,true)==1 then
autoDotTag=string.sub(item,0,1)
item=string.sub(item,3)
end
if item == 'image' then
shapeResult[nval].shape = 'image:'
shapeResult[nval].shapeFile =multiCheck(args,'mark',argval,'Red pog.svg')
shapeWidth=-1
else shapeResult[nval].shape = item
end
item= multiCheck(args,'mark-size',argval,'14px')
local a,b,c= getsize(string.gsub(string.gsub(item,',','px')..'px','pxpx','px'))
if b==a and args['mark-dim'..argval] then
b= b / tonumber(string.match(args['mark-dim'..argval],"[%.%-?%d]+"))
end
shapeHeight=b/2
item=tostring(a)..'px'..tostring(b)..'px'..tostring(c)..'px'
shapeResult[nval].shapeSize= item
itemTab=splitItem(multiCheck(args,'shape-color',argval,'hard red'),2)
shapeResult[nval].shapeColor=itemTab[1] or 'hardred'
item=itemCheck(itemTab[2],'%') -- jump through the various opacity hoops and add to color if needed
if not item then item=itemCheck(args['shape-opacity'..argval],'%') end
if item and item~='0%' and item~='100%' then shapeResult[nval].shapeColor=shapeResult[nval].shapeColor..item end
shapeResult[nval].shapeAngle=itemCheck(multiCheck(args,'shape-angle',argval,'0'),'deg') or '0deg'
--sort out the outline entry
itemTab=splitItem(multiCheck(args,'shape-outline',argval,'transparent,0,100,solid'),4)
shapeResult[nval].outlineColor=itemTab[1] or 'dark grey'
shapeResult[nval].outlineWidth=itemCheck(itemTab[2],'px') or '1px'
if itemTab[3] and itemTab[3]~='100' and itemTab[3]~='0' then
shapeResult[nval].outlineColor=shapeResult[nval].outlineColor..itemCheck(itemTab[3],'%')
end
shapeResult[nval].outlineStyle=itemTab[4] or 'solid'
-- label size, background, outline
itemTab=splitItem( multiCheck(args,'label-size',argval,'12'),3)
shapeResult[nval].textSZ=itemCheck(itemTab[1],'px') or '12px'
if itemTab[2]=='outline' then
shapeResult[nval].textBG=itemTab[3] or 'transparent'
shapeResult[nval].textOL='1px'
elseif itemTab[3]=='outline' then
shapeResult[nval].textBG=itemTab[2] or 'transparent'
shapeResult[nval].textOL='1px'
else shapeResult[nval].textOL='0px'
shapeResult[nval].textBG=itemTab[2] or 'transparent'
end
if getColor(shapeResult[nval].textBG)==CTB['hardgrey'] and shapeResult[nval].textBG~='hardgrey' then shapeResult[nval].textBG= 'transparent' end
--label color etc
itemTab=splitItem(multiCheck(args,'label-color',argval, 'darkgrey','label-colour'),2)
shapeResult[nval].textCL=itemTab[1] or 'darkgrey'
if itemTab[2] and itemTab[2]~='0%' and itemTab[2]~='100%' then shapeResult[nval].textCL=shapeResult[nval].textCL..itemTab[2] end
shapeResult[nval].textSP=itemCheck( multiCheck(args,'label-spacing',argval,'0'),'px') -- sets letter-spacing in px
shapeResult[nval].textLH=itemCheck( multiCheck(args,'label-height',argval,'120'),'%') -- sets line height, 120% default
shapeResult[nval].textNG=itemCheck(multiCheck(args,'label-angle',argval,'0'),'deg')
--sgd=dotTag attributes
shapeResult[nval].tagSize=tostring(shapeHeight*1.5)..'px'
local c1,c2=checkColors(shapeResult[nval].shapeColor)
shapeResult[nval].tagColor=c2
shapeResult[nval].tagSpacer='0px'
shapeResult[nval].tagAngle='0deg'
-- sge extension line attributes
local shapePos=splitItem(multiCheck(args,'label-pos',argval,'right'),6)
if shapePos[2]=='with-line' or shapePos[2]=='n-line' then
shapeResult[nval].textEW=(shapePos[3] or '1')..'px' -- width
shapeResult[nval].textEC=(shapePos[4] or shapeResult[nval].shapeColor or 'darkgrey')
elseif shapePos[2]=='photo-panel' then
shapeResult[nval].textEW='2px' -- width
shapeResult[nval].textEC=shapeResult[nval].textCL
else
shapeResult[nval].textEW='0px' -- width
shapeResult[nval].textEC='grey'-- colour
end
shapeResult[nval].textES='solid'
if argval=='H' then return dotResult end
--Assign dot values
local dotItem={}
dotItem.group=nval
dotItem.code=nval
dotItem.posType=shapePos[2] or 'nil'
if (shapePos[2] or '') =='photo-panel' then
dotItem.ppwidth= tonumber(string.match((shapePos[4] or '110'),"%d+"))
dotItem.ppheight= tonumber(string.match((shapePos[5] or '48'),"%d+") )
dotItem.photowidth=round(tonumber(string.match((shapePos[3] or '1.3'),"[%.%-?%d]+")) * dotItem.ppheight+1,0)
dotItem.photoImage=args['mark-image'..argval]
--debugmsg('photo-panel, '..shapePos[2]..', 3='..shapePos[3]..', 4='..shapePos[4]..', 5='..(shapePos[5] or '(48')..'photowidth='..tostring(dotItem.photowidth))
end
if (shapePos[2] or '') =='mark-line' then
local x=tonumber(argval or '1')
dotItem.markDest=shapePos[6] or tostring(x-1)
dotItem.mlWidth= tonumber(string.match((shapePos[3] or '1'),"%d+"))
dotItem.mlStyle= shapePos[4] or 'solid'
dotItem.mlGap=tonumber(string.match((shapePos[5] or '0'),"[%d]+"))
shapeResult[nval].textEC=shapeResult[nval].outlineColor or 'darkgrey'
end
if args['mark-coord'..argval] then
itemTab=splitItem(convertCoordsTrad (args['mark-coord'..argval]),2)
dotItem.lat=tonumber(string.match(itemTab[1],"[%.%-?%d]+")) or 0
dotItem.lon=tonumber(string.match(itemTab[2],"[%.%-?%d]+")) or 0
else
dotItem.lat=tonumber(string.match(args['mark-lat'..argval],"[%.%-?%d]+")) or 0
dotItem.lon=tonumber(string.match(args['mark-lon'..argval],"[%.%-?%d]+")) or 0
end
if args['dateline'..argval] and (args['dateline'..argval]=='1' or args['dateline'..argval]=='-1') then
dotItem.lon=dotItem.lon+(tonumber(args['dateline'..argval] ) *360)
end
maplist.lon=dotItem.lon
maplist.lat=dotItem.lat
dotItem.gridx, dotItem.gridy = maptogrid(maplist,1)
local item=args['mark-title'..argval] or '' -- sort out the caption, wikilink and plaintext tooltip items from dotLink
if item=='none' then dotItem.param1='nomap nolist' item='' end
dotItem.dotLink=item
if item ~= '' then
item=stripdivs(item)
dotItem.title=delink({item})
local linkstart= string.find(item,'[[',1,true) -- use true to ensure a plain search (no pattern)
if linkstart then
dotItem.dlink=delink({string.sub(item,linkstart,string.find(item,']]',1,true)+1),wikilinks='target'})
else dotItem.dlink=''
end
else
dotItem.dlink=''
dotItem.title=''
end
if autoDotTag=='n' then item=nval
elseif autoDotTag=='l' then item=string.char(64+tonumber(nval))
else item='' end
dotItem.dotTag = args['numbered'..argval] or item
if shapePos[2]=='n-line' and (args['label'..argval] or args['label'..argval]=='') then
if dotItem.dlink =='' then
item=dotItem.dotTag..' '..args['label'..argval]
else
item='[['..dotItem.dlink..'|'..dotItem.dotTag..']] '..args['label'..argval]
end
else item=(args['label'..argval] or '') end
if args['labela'..argval] then item = item..'^'..args['labela'..argval] end
if args['labelb'..argval] then item = item..'^'..args['labelb'..argval] end
local a='' for c in item:gmatch('.') do a=a..(c:gsub('%^','<br>') or c) end
dotItem.labelText = a -- convert hats to line breaks
if argval=='' then item = (args['label-offset-x']) or (args.ldx) or '0'
else item=args['label-offset-x'..argval] or args['ldx'..argval] or args['label-offset-xD'] or args.ldxD or args['label-offset-x1'] or args.ldx1 or '0'
end
dotItem.dx=tonumber(string.match(item,"[%.%-?%d]+"))
if argval=='' then item = (args['label-offset-y']) or (args.ldy) or '0'
else item=args['label-offset-y'..argval] or args['ldy'..argval] or args['label-offset-yD'] or args.ldyD or args['label-offset-y1'] or args.ldy1 or '0'
end
dotItem.dy=tonumber(string.match(item,"[%.%-?%d]+"))
dotItem.labelPos=shapePos[1]
if args['mark-image'..argval] then dotItem.imageName= args['mark-image'..argval] end
dotItem.info=args['mark-description'..argval] or ''
table.insert(dotResult,1,dotItem) -- add to start of list, so they are in reverese order for displaying
return dotResult
end
local function tradstyleParseShapes(args,dotTable,dotmax)
local sgNumbers,sgSortable={},{}
for argindex=1,dotmax do -- build a list of all the numbered coords or lat,lons that have been used
local x=tostring(argindex)
if args['mark-coord'..x] or (args['mark-lat'..x] and args['mark-lon'..x]) then
sgNumbers[x]=x -- add it to the list
end
end
for indx,sgnum in pairs(sgNumbers) do table.insert(sgSortable,sgnum) end
table.sort(sgSortable,lessthan) -- put the list in a sortable form TODO this must be possible in a single list!
if args['mark-coord'] or (args['mark-lat'] and args['mark-lon']) then table.insert(sgSortable,'0') end -- add it to the end of the list
local default={} default.D={}
local dotResult={}
for k,sgnum in pairs(sgSortable) do -- work through the sorted list, parsing each set of shapes in turn, from 1 upwards
shapeList[sgnum]={}
dotTable=assignTradstyleShape(shapeList,default,dotTable,args,sgnum)
end
dotTable=assignTradstyleShape(shapeList,default,dotTable,args,'H') -- construct an extra highlight shapeitem
return dotTable
end
local function checkfortooltip (title,dx,dy,dotlabel,dlink,nolabel) -- returns tlink if available, and dlink, if needed and tshape=true if shape needed
local tshape,tlink = false,""
if dlink~='' and not nolabel then tlink=dlink end
if (tlink=="" or nolabel) and title~="" then tshape=true end -- tshape flags True if title is wanted for shape
if (not (dx==0 or dy==0) or dotlabel=='') and title~='' then tshape=true end -- add tooltip to shape if its number has moved
return tshape,tlink
end
local function tshift(angle) -- adjustment to place text near the centre of a triangle, shifted to allow rotation of triangle shape
local x=tonumber(string.match(angle,"%-?%d+"))
if x<0 then x=360+x end -- set to a single degree direction, 0 to 360
if x>359 then return 0,0 end
-- shift the centre of the triangle based on rotation value
if x <=40 or x>=320 then return -0.17,0 -- triangle up= -shiftv
elseif x>=140 and x<=220 then return 0.17,0 --triangle down= +shiftv
elseif x >220 then return 0,-0.17 --triangle left= -shifth
elseif x >40 then return 0,0.22 --triangle right= +shifth
end
return 0,0
end
local function makeTriangle(result,row,shape,outline,tlink)
local w,h,r=getsize(shape.shapeSize)
if outline then
local p=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
w=w+p*2
h=h+p*2
end
table.insert(result,'<div ')
if tlink then
table.insert(result,' title="'..row.title..'" ')
end
table.insert(result,'style="display:inline-block; position: absolute')
if shape.shapeAngle ~= '0deg' then
table.insert(result,'; transform: rotate('..shape.shapeAngle..')')
end
local shiftv,shifth=0,0
shiftv,shifth=tshift(shape.shapeAngle)
table.insert(result,'; top: '..tostring(row.gridy-h/2+h*shiftv)..'px')
table.insert(result,'; left: '..tostring(row.gridx-w/2+w*shifth)..'px; width: 0; height: 0; outline-width: 0px')
table.insert(result,'; border-left: '..tostring(w/2)..'px solid transparent')
table.insert(result,'; border-right: '..tostring(w/2)..'px solid transparent')
if outline then -- fill with outline colour, to make a 'base layer' or shape colour
table.insert(result,'; border-bottom: '..tostring(h)..'px solid '..getColor(shape.outlineColor).. '">')
else
table.insert(result,'; border-bottom: '..tostring(h)..'px solid '..getColor(shape.shapeColor).. '">')
end
table.insert(result,'</div>')
end
local function makeSquare(result,row,shape,tshape)
local w,h,r=getsize(shape.shapeSize)
if shape.shape=='square' then h=w end -- squares are always square!. box can stretch
local div=mw.html.create ('div')
if tshape then -- Add tooltip if needed
div:attr('title',row.title)
end
div:css('position', "absolute")
if shape.outlineWidth ~= "0px" then
div:css('outline', shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
end
if shape.shapeAngle ~= "0deg" then
div:css('transform',"rotate("..shape.shapeAngle..")")
end
if r~=0 then div:css('border-radius',tostring(r).."px") end
if shape.shape=='panel' then
div:css('top', tostring(row.gridy).."px")
div:css('left', tostring(row.gridx).."px")
else
div:css('top', tostring(row.gridy-h/2).."px")
div:css('left', tostring(row.gridx-w/2).."px")
end
div:css('width', tostring(w).."px")
div:css('height', tostring(h).."px")
div:css('background-color', getColor(shape.shapeColor) )
div:css('color', 'inherit')
table.insert(result,tostring(div))
end
local function makeCircle(result,row,shape,tshape)
local w,h,r=getsize(shape.shapeSize) -- = width,height,rounding
if shape.shape=='circle' then h=w end -- circles are always round. ellipse can stretch
local div=mw.html.create ('div')
if tshape then -- Add tooltip if needed
div:attr('title',row.title)
end
div:css('position', "absolute")
if shape.outlineWidth ~= "0px" then
div:css('outline', shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
end
if shape.shapeAngle ~= "0deg" then
div:css('transform',"rotate("..shape.shapeAngle..")")
end
div
:css('top', tostring(row.gridy-h/2).."px")
:css('left', tostring(row.gridx-w/2).."px")
:css('width', tostring(w).."px")
:css('height', tostring(h).."px")
:css('border-radius', "50%")
:css('background-color', getColor(shape.shapeColor) )
:css('color', 'inherit')
table.insert(result,tostring(div))
end
local function makeRuleA(result,row,shape)
local w,h,r=getsize(shape.shapeSize) -- = width,height,rounding
local oWid=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
local lineV=0
if shape.shape=='rulea' then lineV=oWid*3+16 end
table.insert(result,"<div style=\"display:inline-block; position: absolute") -- create a square transparent container, which will rotate line and arrow together
table.insert(result,"; top:"..tostring(row.gridy - w/2).."px")
table.insert(result,"; left:"..tostring(row.gridx - w/2).."px")
table.insert(result,"; width:"..tostring(w).."px")
table.insert(result,"; height:"..tostring(w).."px; background:transparent; color:inherit;")
table.insert(result,"; transform: rotate( "..tostring(tonumber(string.match(shape.shapeAngle,"[%.%-?%d]+")) - 90).."deg);\">" )
table.insert(result,"<div style=\"display:inline-block; position: absolute") -- put the line (as a border-right) across the container
table.insert(result,"; top:"..tostring(lineV).."px")
table.insert(result,"; left:"..tostring((w - oWid )/2).."px; width: 0px")
table.insert(result,"; height: "..tostring(w -lineV).."px")
table.insert(result,"; border-right: "..shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
table.insert(result,"; background:transparent; color:inherit;\"></div>")
if shape.shape=='rulea' then
table.insert(result,"<div style=\"display:inline-block; position: absolute; top: 0px") --and add arrow head
table.insert(result,"; left:"..tostring(w/2-( oWid/2)-oWid*0.55-2).."px; width: 0; height: 0; outline-width: 0px")
table.insert(result,"; border-left: "..tostring(oWid*1.1+2).."px solid transparent")
table.insert(result,"; border-right: "..tostring(oWid*1.1+2).."px solid transparent")
table.insert(result,"; border-bottom: "..tostring(oWid*3+16).."px solid "..getColor(shape.outlineColor).."\"></div>")
end
table.insert(result,"</div>")
end
local function makeCurveA(result,row,shape) -- draw a curve with Arrow -----
local w,h=getsize(shape.shapeSize) -- = width,height
local oWid=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
local Angle=tonumber(string.match(shape.shapeAngle,"[%.%d]+"))
table.insert(result,'<div style="position: absolute;') --set up out div, which will allow the whole to rotate
table.insert(result,'top:'..tostring(row.gridy - (w + oWid*3+16)/2)..'px;')
table.insert(result,'left:'..tostring(row.gridx - ( w + oWid*3+16)/2)..'px; ')
table.insert(result,'width: '..tostring(w+oWid*3+16)..'px; ')
table.insert(result,'height: '..tostring(w+oWid*3+16)..'px; ')
if shape.shape=='curvea' then
table.insert(result,'transform: rotate( '..tostring(Angle-120)..'deg);">')
else table.insert(result,'transform: rotate( '..tostring(Angle -62)..'deg);">')
end
table.insert(result,'<div style="position: absolute;') --set up div for the rounded corner of a rectangle
table.insert(result,'border-left: '..shape.outlineWidth..' '..shape.outlineStyle..' '..getColor(shape.outlineColor)..';')
if shape.shape=='curvea' then
table.insert(result,'border-radius: 10000px 0 0 '..tostring(w)..'px; top:0px; left:'..tostring(w*0.25)..'px;')
else
table.insert(result,'border-radius: '..tostring(w)..'px 0 0 10000px;')
table.insert(result,'top:'..tostring((oWid*3+16)/2+w*0.15)..'px; left:'..tostring(w*0.25)..'px;')
end -- and add a triangular arrow head
table.insert(result,'width: '..tostring(w)..'px; height: '..tostring(w)..'px;"></div><div style="position: absolute; ')
if shape.shape=='curvea' then
table.insert(result,'transform: rotate(180deg); top:'..tostring(w-1)..'px; ')
else table.insert(result,'transform: rotate(0deg); top: '..tostring(0-( ( oWid*3+16)/2)+1+( w*0.15) )..'px;')
end -- reverse
table.insert(result,'left:'..tostring(0-( oWid*0.6)-2+(w*0.25))..'px;')
table.insert(result,'width: 0; height: 0; outline-width: 0px; border-left: '..tostring(oWid*1.1+2)..'px solid transparent;')
table.insert(result,'border-right: '..tostring(oWid*1.1+2)..'px solid transparent;')
table.insert(result,'border-bottom: '..tostring(oWid*3+16)..'px solid '..getColor(shape.outlineColor)..';"></div></div>')
end
local function makeLineTo (result,x1,y1,x2,y2,oWid, oStyle, oCol,double)
table.insert(result,"<div style=\"display:inline-block; position: absolute;")
-- draw a line between x1,y1 and x2,y2, px-coords where 0,0 is centre of frame
-- Maths calculations thanks to ES
table.insert(result,"left: "..tostring(x1+( (x2-x1)/2) - (math.sqrt( ( x2-x1)^2 + (y2-y1)^2 )/2)-1).."px;")
table.insert(result,"top: "..tostring(y1+( ( y2-y1 )/2) ).."px;")
table.insert(result,"width: "..tostring(math.sqrt( (x2-x1 )^2 + ( y2-y1 )^2) ).."px;")
table.insert(result,"height: "..tostring(double).."px; background-color:transparent; color:inherit; ")
table.insert(result,"outline-width: 0; border-bottom: "..oWid.." "..oStyle.." "..getColor(oCol)..";" )
if double>1 then table.insert(result,"border-top: "..oWid.." "..oStyle.." "..getColor(oCol)..";" ) end
if x1==x2 then table.insert(result,"transform: rotate(90deg);")
else table.insert(result,"transform: rotate("..tostring(math.atan(( y2-y1)/( x2-x1 ) )*180/math.pi).."deg);\"></div>")
end
end
local function makeClipPath(result,row,shape,outline,tshape) --tshape is a flag to show if the tooltip (title=) is wanted
-- return the text css div code to position and draw a shape occupying a specified clippath
if not pathshape[shape.shape] then
debugmsg(mw.addWarning('shape'..row.code..' = '..shape.shape..'. The shape name is not defined'))
return
end
local w,h,r=getsize(shape.shapeSize)
if string.match(shape.shape,"circle") or string.match(shape.shape,"square") then h=w end -- use ellipse and box for stretched shapes
local shifth,shiftv = 0,0
if string.match(shape.shape,"triangle") then
shiftv,shifth =tshift(shape.shapeAngle)
end
if outline then
local p=tonumber(string.match(shape.outlineWidth,"[%.%d]+")) or 0
w=w+p*2
h=h+p*2
end
table.insert(result,"<div ")
if tshape then -- Add tooltip if needed
table.insert(result," title=\""..row.title.."\" ")
end
table.insert(result,"style=\"display:inline-block; position: absolute; background-color:")
if outline then
table.insert(result,getColor(shape.outlineColor)) -- fill with outline colour, to make a 'base layer'
else
table.insert(result,getColor(shape.shapeColor))
end
table.insert(result,"; color:inherit; clip-path:path(nonzero, '"..pathshape[shape.shape].."') ")
-- adds the required clippath data from the table of pathshape string literals
table.insert(result,"; top:"..tostring(row.gridy - 10 + h*shiftv).."px")
table.insert(result,"; left:"..tostring(row.gridx - 10 + w*shifth).."px")
table.insert(result,"; width:20px") -- needs to be a path within a 20px20px box, and then rescales using size values to match other shape sizes
table.insert(result,"; height:20px; transform:scale("..tostring(w/16)..", "..tostring(h/16)..")")
if shape.shapeAngle ~= "0deg" then
table.insert(result," rotate("..shape.shapeAngle..")")
end
table.insert(result,"\"></div>")
end
local function makeImage(result,row,shape)
local w,h,r=getsize(shape.shapeSize)
local image=shape.shapeFile
if not image or image=='' then image='Red pog.svg' end
local imagediv=mw.html.create ('div')
imagediv:css('position', "absolute")
if shape.shapeAngle ~= "0deg" then
imagediv:css('transform',"rotate("..shape.shapeAngle..")")
end
imagediv
:css('top', (row.gridy-1 + math.min(h/2-12,0) - h/2).."px") --File seems to adjust pos for small images
:css('left', (row.gridx-1-w/2).."px")
:css('background-color', "transparent" )
:css('color','inherit')
:wikitext('[[file:'..image..'|'..tostring(w+2)..'px|alt='..(row.title or '')..'|link=]]')
table.insert(result,tostring(imagediv))
end
local function makePhotoPanel(result,row,shape)
local h=row.ppheight
table.insert(result, '<div style="position: absolute; top: '..tostring(row.gridy+row.dy-h/2)..'px;' )
table.insert(result, 'left: '..tostring(row.gridx+row.dx - row.ppwidth/2)..'px;')
table.insert(result, 'width: '..tostring(row.ppwidth)..'px; height: '..tostring(h)..'px; border-radius: 2px; color:inherit;')
table.insert(result, 'background-color: #E8E8D6; outline: 2px solid '..getColor(shape.textCL)..'; box-shadow: 2px 2px 4px #33203335;"></div>')
if row.photoImage and row.photowidth >0 then
table.insert(result, '<div style="position: absolute; top: '..tostring(row.gridy+row.dy-h/2)..'px;')
if row.labelPos=='left' or string.find(row.labelPos,'west') then
--debugmsg(row.labelText..', Align=right, ppwidth='..tostring(row.ppwidth)..', photowidth='..row.photowidth..', dx='..tostring(row.dx)..', dy='..tostring(row.dy))
table.insert(result, 'left: '..tostring(row.gridx+row.dx - row.ppwidth/2)..'px;')
row.dx=row.dx+row.photowidth/2
else
--debugmsg(row.labelText..', Align=left, ppwidth='..tostring(row.ppwidth)..', photowidth='..row.photowidth..', dx='..tostring(row.dx)..', dy='..tostring(row.dy))
table.insert(result, 'left: '..tostring(row.gridx+row.dx + (row.ppwidth-row.photowidth) - row.ppwidth/2)..'px;')
row.dx=row.dx-row.photowidth/2
end
table.insert(result, 'background-color:transparent; color:inherit; border-radius: 2px;">')
table.insert(result,'[[File:'..row.photoImage..'|x'..tostring(h)..'px|File:'..row.photoImage..']]</div>')
row.labelPos='center'
end
end
local function makePanelText(result, row, shape)
local w,h,r=getsize(shape.shapeSize)
local ty=tonumber(string.match(shape.textSZ or '11',"%d+") )
table.insert(result,'<div style="position:absolute; line-height: 120%; font-size: '..shape.textSZ..'; color:'..getColor(shape.textCL)..';')
if row.labelPos == 'left' or string.find(row.labelPos,'west') then
table.insert(result,'top: '..tostring(row.dy+row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+3)..'px; text-align: left; width:'..tostring(w)..'px;')
elseif row.labelPos == 'right' or string.find(row.labelPos,'east') then
table.insert(result,'top: '..tostring(row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+w-3)..'px; text-align: right; width: max-content; transform: translateX(-100%);')
elseif row.labelPos == 'top' or row.labelPos== 'north' then
table.insert(result,'top: '..tostring(row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
elseif row.labelPos == 'bottom' or row.labelPos=='south' then
local bry=(select(2, string.gsub(row.labelText,"<br>", ""))+1)*1.1
table.insert(result,'top: '..tostring(row.gridy+w-(ty*bry))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
else -- center or centre
local bry=(select(2, string.gsub(row.labelText,"<br>", ""))+1)*0.6
table.insert(result,'top: '..tostring(row.gridy+(h/2)-(ty*bry))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
end
if shape.textSP and shape.textSP ~='0px' then table.insert(result,"letter-spacing: "..shape.textSP..';') end
if shape.textLH and shape.textLH ~='120%' then table.insert(result,"line-height: "..shape.textLH..';') end
table.insert(result,"vertical-align: middle;\">"..row.labelText.."</div>")
end
local function makeTextItem(result, row, shape, align, tlink, textItem, dotItem)
local w,h,r=getsize(shape.shapeSize)
table.insert(result,"<div ")
if row.title ~= "" then table.insert(result," title=\""..row.title.."\" ") end
local ty,bry,linkoffset = 0,0,0
local compy=0
local lh=tonumber(string.match(shape.textLH or '120',"%d+"))/100
if dotItem==1 or (dotItem==2 and row.posType~='n-line') then -- if there is a dotTag in the middle of the shape then use the tag settings
ty=tonumber(string.match(shape.tagSize,"%d+")) or 0
table.insert(result,"style=\"position:absolute; line-height: 120%; top: "..tostring(row.gridy-ty*0.62))
table.insert(result,"px; left: "..tostring(row.gridx).."px; width: fit-content; ")
table.insert(result,"text-align: center; color: "..getColor(shape.tagColor).."; background-color: transparent;")
local trf=""
if shape.tagAngle ~="0deg" then trf=" rotate("..shape.tagAngle..")" end
table.insert(result, "transform: translateX(-50%)"..trf.."; font-size: "..shape.tagSize..";")
if shape.tagSpacer~='0px' then table.insert(result, "letter-spacing:"..shape.tagSpacer..";") end
else -- or add tfx settings for left, right or center align, colors, backgrounds, border-outline
table.insert(result,'style="position:absolute; ')
if dotItem==2 then -- dotTag is out at x,y so 85%
ty=tonumber(string.match(shape.tagSize or '0',"%d+"))
table.insert(result,'font-size: '..shape.tagSize..'; padding:0px 2px;line-height: 85%; top: '..tostring(row.dy+row.gridy-ty*0.52))
else -- it is labelText, so use textLH or 120%
ty=tonumber(string.match(shape.textSZ or '11',"%d+"))
if row.labelPos=='northwest' or row.labelPos=='northeast' then compy=-ty
elseif row.labelPos=='southeast' or row.labelPos=='southwest' then compy=ty/2 end
if row.labelPos and not (row.labelPos== 'auto' or row.labelPos=='') then
bry=(select(2, string.gsub(row.labelText,"<br>", ""))*lh) -- is it a multiline text? expand by line-height /120%?
if row.posType == 'with-line' then bry=0 end
if row.labelPos=='bottom' or row.labelPos == 'south' then bry = 0 -- and shift by none, all or half
elseif row.labelPos=='top' or row.labelPos == 'north' then
if shape.shape=='image:' then bry= 1 + math.min(w/2-10,0)-bry*ty
else bry= -bry*ty+2
end
else bry=-bry*(ty/2 * lh)
end
end
if row.posType == 'photo-panel' then bry=bry+3 end
table.insert(result,'font-size: '..shape.textSZ..'; padding:0px 3px;line-height: '..shape.textLH..'; ')
table.insert(result,'top: '..tostring(row.dy+row.gridy+compy+bry-ty*lh/2))
end
table.insert(result,"px; left: "..tostring(row.dx+row.gridx).."px; color: "..getColor(shape.textCL).."; ")
table.insert(result,"width: max-content; ")
local trf=""
if shape.textNG ~="0deg" then trf="rotate("..shape.textNG..")" end
if shape.textOL~="0px" then
table.insert(result,"background-color: "..getColor(shape.textBG).."; ")
table.insert(result,"border: "..shape.textOL.." solid "..getColor(shape.textCL).."; border-radius:6px;")
else table.insert(result,"background-color: transparent;")
end
if row.labelPos=="right" or string.find(row.labelPos,'east') then
table.insert(result,"text-align: left; ")
linkoffset=w
if shape.textNG ~="0px" then table.insert(result,"transform-origin: left; transform: rotate("..shape.textNG.."); ") end
elseif row.labelPos=="left" or string.find(row.labelPos,'west') then
if shape.textNG ~="0px" then table.insert(result,"transform-origin: right;") end
linkoffset=-w
table.insert(result,"text-align: right; transform: translateX(-100%) "..trf.."; ")
else
table.insert(result,"text-align: center; transform: translateX(-50%) "..trf.."; ")
end
table.insert(result,'font-weight: normal; line-height: '..shape.textLH..'; letter-spacing:'..shape.textSP..'; vertical-align: bottom;')
end
if string.find(shape.textAT or '','bold') then textItem='<b>'..textItem..'</b>' end
if string.find(shape.textAT or '','italic') then textItem='<i>'..textItem..'</i>' end
if shape.textOL=='0px' and shape.textBG~='transparent' and dotItem==0 then
table.insert(result,'\"><span style=\"background-color: '..getColor(shape.textBG)..'; color:inherit;\">'..textItem..'</span></div>')
else
table.insert(result,"\">"..textItem.."</div>")
end
if tlink~='' and not string.match(row.param1 or "","nolink") then
table.insert(result,makeLinkBox(row.gridx+row.dx+linkoffset, row.gridy+row.dy+bry, 16, row.dotTag..' '..row.title,tlink))
end
end
local function getshapetable(row,shape) -- Construct CSS divs for a dot from shape and map data
local result={}
local w,h,r=getsize(shape.shapeSize)
local tshape,tlink=checkfortooltip(row.title,row.dx,row.dy,row.dotTag,row.dlink,string.match(row.param1 or "","nolink") )
local align=row.labelPos or ''
local offsetx,offsety=0,0
local ty=tonumber(string.match((shape.tagSize or 9),"%d+" ))
if row.labelText and row.labelText~='' then ty=tonumber(string.match((shape.textSZ or 11),"%d+" ))
else align='center' end -- it is just for dotTag, so justify center
-- identify align value and extend offsets
local widthzone,heightzone = w/2+4,h/2+4
local theta,r = math.deg( math.atan2(row.dy, row.dx)) , math.sqrt(row.dx^2 + row.dy^2)
if align=='auto' or align=='' then
if (theta < -112 or theta > 112) and math.abs(row.dx)>=w/2 and math.abs(row.dy)<w/1.4 then align = "left" offsetx=1
elseif (theta > -68 and theta < 68) and math.abs(row.dx)>=w/2 and math.abs(row.dy)<w/1.4 then align = "right" offsetx=-1
elseif theta <0 then offsety=ty/2-1 -- bottom
else offsety=0-ty/2+1 -- top
end
elseif align=='left' or string.find(align,'west') then
row.dx=row.dx - w/2 widthzone=4 offsetx=-1
elseif align=='right' or string.find(align,'east') then
row.dx=row.dx + w/2 widthzone=4 offsetx=1 offsety=-ty/2 +1
elseif align=='top' or align=='north' then
if string.find(shape.shape,'curve') then
row.dy=row.dy-(h/2)-12 heightzone=4 offsety=ty/2-1
else
row.dy=row.dy - h-2 heightzone=4 offsety=ty/2-1
end
elseif align=='bottom' or align=='south' then
if string.find(shape.shape,'curve') then
row.dy=row.dy+(h/2) heightzone=4 offsety=ty/2-1
else
row.dy=row.dy + h+2 heightzone=4 offsety=0-ty/2-1
end
end
if r > widthzone and shape.textEW ~= "0px" and not string.match(row.param1 or "","noline") then
makeLineTo(result, row.gridx+1, row.gridy-1, row.gridx+row.dx+offsetx, row.gridy+row.dy+offsety, shape.textEW, shape.textES,shape.textEC,1)
end
if row.posType and row.posType=='mark-line' then
if row.gridx2 and row.gridy2 then
makeLineTo(result, row.gridx, row.gridy, row.gridx2, row.gridy2, tostring(row.mlWidth)..'px', row.mlStyle, getColor(shape.textEC),row.mlGap)
-- debugmsg('makeLineTo line drawn from '..row.code..' with width '..tostring(row.mlWidth)..'px '..row.mlStyle..' and color '..getColor(shape.textEC))
end
end
if w ~= 0 then
if shape.shape=='itriangle' then shape.shape='triangle' shape.shapeSize=tostring(w)..'px'..tostring(w/2)..'px' end
if shape.shape=="triangle" then
if shape.outlineWidth ~= "0px" then
makeTriangle(result,row,shape,true,false) -- larger triangle to give the outline, if required
end
makeTriangle(result,row,shape,false,tshape) -- smaller triangle to fit over the top
elseif shape.shape=="square" or shape.shape=="box" or shape.shape=='panel' then
makeSquare(result,row,shape,tshape)
elseif shape.shape=="circle" or shape.shape=="ellipse" then
makeCircle(result,row,shape,tshape)
elseif string.find(shape.shape,'image:')==1 then
makeImage(result,row,shape)
elseif shape.shape=="rulea" or shape.shape=='rule' then
makeRuleA(result,row,shape)
elseif shape.shape=="curvea" or shape.shape=="curvec" then
makeCurveA(result,row,shape)
else -- use a pathshape clipPath
if shape.outlineWidth ~= "0px" then
makeClipPath(result,row,shape,true,false) -- larger path-shape to give the outline, if required
end
makeClipPath(result,row,shape,false,tshape)
end
end
if row.ppwidth and row.ppwidth>0 then makePhotoPanel(result,row,shape) end
if shape.shape=='panel' and row.labelText then makePanelText(result, row, shape)
else
if row.dotTag and row.dotTag ~= "" then -- there is a dotTag
if (row.dx==0 and row.dy==0) and w>0 then -- it is on the dot so if dotsize is not 0 any label is ignored
makeTextItem(result, row, shape, align, tlink, '<b>'..row.dotTag..'</b>', 1)
else
if row.labelText and row.labelText~='' then -- tag and label both used
makeTextItem(result, row, shape, align, '', '<b>'..row.dotTag..'</b>', 1)
makeTextItem(result, row, shape, align, tlink, row.labelText, 0)
else
makeTextItem(result, row, shape, align, '', '<b>'..row.dotTag..'</b>', 1)
makeTextItem(result, row, shape, align, tlink, row.dotTag, 2) -- tag is ouside the dot
end
end
else
if (row.labelText and row.labelText~='') then -- just the label. No tag
makeTextItem(result, row, shape, align, tlink, row.labelText, 0)
end
end
end
if tlink and tlink~='' then
table.insert(result,makeLinkBox(row.gridx, row.gridy,w+3,row.dotTag..' '..row.title,tlink))
end
return table.concat(result)
end
local function getmapframecontent(args,use)
local result, comma={},''
if use=='basemap' then table.insert(result,'[') end
if args['map-data-inverse'] then
table.insert(result,'{ "type": "ExternalData", "service": "geomask", "ids": "'..args['map-data-inverse']..'", "properties": {')
table.insert(result,' "title": "Wikidata: '..args['map-data-inverse']..'", "fill": "#555555", "fill-opacity": 0.1, "stroke": "#555555", "stroke-width": 1, "stroke-opacity": 0.5 } }')
comma=', '
end
if args['map-data-heavy'] then
table.insert(result,comma..'{ "type": "ExternalData", "service": "geoline", "ids": "'..args['map-data-heavy']..'", "properties": {' )
table.insert(result,'"stroke": "#000000", "stroke-width": 9, "stroke-opacity": 0.1 } }')
comma=', '
end
if args['map-data-light'] then
table.insert(result,comma..' { "type": "ExternalData", "service": "geoline", "ids": "'..args['map-data-light']..'", "properties": {' )
table.insert(result,'"stroke": "#000000", "stroke-width": 3, "stroke-opacity": 0.1 } }' )
comma=', '
end
if args['map-data'] then
table.insert(result,comma..'{ "type": "ExternalData", "service": "geoline", "ids": "'..args['map-data']..'", "properties": {' )
table.insert(result,'"title": "'..(args['map-data-text'] or '')..'", "stroke": "#000000", "stroke-width": 6, "stroke-opacity": 0.1 } }' )
end
if use=='basemap' then table.insert(result,']') end
return table.concat(result)
end
--eg | minilocator=filename,bottom right,132px153px, 38%,60%, 22px
local function makeLocatorMap (args, result)
local miniFile,pos,itemlist,miniW,miniH, miniX,miniY,miniBox, miniBH
if args['mini-locator'] then
pos,miniFile=extractItem(args['mini-locator']) -- first item is filename, in quotes if it includes commas
itemlist=splitItem(pos,5) -- put items in a table filename removed, position,WpxHpx, x%y%,box
pos=itemlist[2] or 'right'
miniW,miniH=getsize(itemlist[3])
miniX=tonumber(string.match(itemlist[4] or '0','[%d]+'))*miniW/100
miniY=tonumber(string.match(itemlist[5] or '0','[%d]+'))*miniH/100
miniBox=tonumber(string.match(itemlist[6] or '0','[%d]+'))
miniBox=miniBox*miniW/100
miniBH=miniBox * maplist.height/maplist.width
elseif args['mini-file'] then
miniFile = args['mini-file']
pos=string.lower(args.minimap or 'right')
if pos=='file' then pos='right' end
miniW,miniH = tonumber(args['mini-width'] or 60), tonumber(args['mini-height'] or 60) -- find top left corner of locator
miniBox,miniBH=tonumber(args['minimap-boxwidth'] or '0'),0 -- firm up pos offsets for dot (with % or not) and boxsize if any,
miniX, miniY=args['minipog-gx'],args['minipog-gy']
if not miniX then miniX=tonumber(args['minipog-x'] or '0') else miniX=tonumber(miniX)*tonumber(miniW)/100 end
if not miniY then miniY=tonumber(args['minipog-y'] or '0') else miniY=tonumber(miniY)*tonumber(miniH)/100 end
if args['minipog-gx'] then miniBox=miniBox*miniW/100 end
miniBH=miniBox * maplist.height/maplist.width
else return end
local miniTop,miniLeft=0,1
if not string.find(pos,'top') then --only use top left, as link box is in top right or put bottom left or right
miniTop=maplist.height+2-miniH
if string.find(pos,'right') then
miniTop=miniTop-15 -- to avoid (c) line
miniLeft=maplist.width-1-miniW
end
end
table.insert(result,'<div style="position: absolute; outline-width: 1px; outline-style: solid; outline-color: white;')
table.insert(result,'top: '..tostring(miniTop)..'px; left:'..tostring(miniLeft)..'px; width:'..tostring(miniW)..'px;' )
table.insert(result,'background-color:transparent; color:inherit;">[[File:'..miniFile..'|'..tostring(miniW)..'px|File:'..miniFile..']]</div>')
if miniX and miniX>0 then
if args['minipog-y'] then miniY=miniY/1.04 end
if args['minipog-x'] then miniX=miniX/1.04 end
if miniBox<1 then
table.insert(result,'<div style="position: absolute; top: '..tostring(miniY+miniTop-3-6)..'px; left:'..tostring(miniX+miniLeft-3)..'px;')
table.insert(result, 'width: 6px; background-color:transparent; color:inherit;">[[File:Red pog.svg|6px|link=]]</div>')
else
table.insert(result,'<div style="position: absolute; top: '..tostring(miniY+miniTop-miniBH/2)..'px; left:'..tostring(miniX+miniLeft-miniBox/2)..'px;')
table.insert(result, 'width: '..tostring(miniBox)..'px; height:'..tostring(miniBH)..'px; outline:1px solid #AA1205; background-color:#AA120522; color:inherit;"></div>')
end
end
end
local function makeArcText(args,result,nval)
local items, itemlist='',{}
local arcText=''
if args['arc'..nval] then
items=convertCoords (args['arc'..nval])
items,arcText=extractItem(items) -- first item is text, in quotes if it includes commas
itemlist=splitItem(items,9) -- put items in a table: text, lat,lon,size,color,angle,gap,radius,ellipse
if itemlist[4]=='' then itemlist[4]='12' end
itemlist[4]=string.match((itemlist[4] or '12'),"[%.%-?%d]+")
if itemlist[5]=='' then itemlist[5]='grey' end
itemlist[6]=string.match((itemlist[6] or '0'),"[%.%-?%d]+")
itemlist[7]=string.match((itemlist[7] or '0'),"[%.%-?%d]+")
itemlist[8]=string.match((itemlist[8] or '0'),"[%.%-?%d]+")
end
if args['arc-coord'..nval] then
local itemTab=splitItem(convertCoordsTrad (args['arc-coord'..nval]),2)
maplist.lat=tonumber(string.match(itemTab[1],"[%.%-?%d]+"))
maplist.lon=tonumber(string.match(itemTab[2],"[%.%-?%d]+"))
else
maplist.lat=tonumber(string.match(args['arc-lat'..nval] or itemlist[2] or '0',"[%.%-?%d]+"))
maplist.lon=tonumber(string.match(args['arc-lon'..nval] or itemlist[3] or '0',"[%.%-?%d]+"))
end
local arcX,arcY=maptogrid(maplist,6)
arcText = args['arc-text'..nval] or arcText
local fontSize =tonumber(args['arc-text-size'..nval] or itemlist[4] or '12')
local textColor=getColor(string.gsub(args['arc-text-color'..nval] or itemlist[5] or 'grey','[%s]+','') )
local arcAngle= tonumber((args['arc-angle'..nval]) or (itemlist[6]) or '45')-90
local arcRadius =tonumber(args['arc-radius'..nval] or itemlist[8] or '0.05')
local arcGap = tonumber(args['arc-gap'..nval] or itemlist[7] or '1')* ( ( math.sin(8-math.rad(arcRadius))^8 )+0.4 )*( ( fontSize+6 )/15 )
arcRadius=450*arcRadius*0.75
local ellipseFactor=tonumber(args['ellipse-factor'..nval] or itemlist[9] or '1')
local arcRotate =arcAngle+90
if arcGap<0 then arcRotate=arcAngle-90 end
local latF=arcY - fontSize + (0-(math.sin(math.rad(arcAngle))) * arcRadius)
local lonF=arcX - fontSize +(0-(math.cos(math.rad(arcAngle))) * arcRadius)
local step=1
for codepoint in mw.ustring.gcodepoint( arcText ) do -- block step=1,#arcText do
table.insert(result,'<div style="position: absolute;')
local posY=tostring(round( (latF + (math.sin(math.rad(arcAngle+((step-1)*arcGap))) * arcRadius)) *ellipseFactor,2))..'px;'
local posX=tostring(round( (lonF + (math.cos(math.rad(arcAngle+((step-1)*arcGap))) * arcRadius))/ellipseFactor,2))..'px;'
table.insert(result,' top: '..posY..' left: '..posX..' transform: rotate( '..tostring(round(arcRotate +((step-1)*arcGap)),2)..'deg);')
table.insert(result,'width:'..tostring(fontSize*2)..'px; text-align: center; background-color:transparent; color: '..textColor..';')
table.insert(result,'vertical-align: baseline; font-size: '..tostring(fontSize)..'px;">'..mw.ustring.char(codepoint)..'</div>')
step=step+1
end
end
local function makeFullscreenItem (itemtitle,itemdescription,lat,lon,group,itemcolor)
local item={}
itemdescription=stripdivs(itemdescription or '')
local templon=lon
if lon > 180 then templon=lon-360 end --for hemisphere+ or -1 dots
if lon < -180 then templon=lon+360 end -- use 'real' coordinates for geohack label, while retaining shifted coords for plot
if itemcolor=='transparent' then itemcolor='white' end
itemcolor=getColor(itemcolor) -- ensure no opacity, which breaks maplink
if string.find(itemcolor,'#')==1 and #itemcolor>7 then itemcolor=string.sub(itemcolor,1,7) end
table.insert(item, '{ "type": "Feature", "properties": {')
table.insert(item, ' "title": "'..itemtitle..'",')
table.insert(item, ' "description": "'..itemdescription)
table.insert(item, ' ([https://geohack.toolforge.org/geohack.php?params='..tostring(lat)..';'..tostring(templon))
table.insert(item, '_dim:2000 '..tostring(lat)..','..tostring(templon)..'])",')
table.insert(item, ' "marker-symbol": "-number-'..string.gsub(group,'%W','')..'", "marker-size": "medium", "marker-color": "'..itemcolor..'" },')
table.insert(item, ' "geometry": {"type": "Point", "coordinates": ['..tostring(lon)..','..tostring(lat)..'] } }')
return table.concat(item)
end
local function makeLegendBox(result,args)
local legend ={}
local line,count, maxWidth='',1,8
local item
line,item=extractItem(args.legendBox or '')
local a='' for c in item:gmatch('.') do a=a..(c:gsub('%^','<br>') or c) end
legend.Text = a -- convert hats to line breaks
line=splitItem(line,6) -- (text, size, poition,background color, text/outline color, param options)
legend.Size=line[2] or '150px80px1px'
legend.Pos=line[3] or '10px10px'
legend.Background=line[4] or 'beigeground'
legend.Color=line[5] or 'darkbrown'
legend.Param= line[6] or ''
local argnum,legendCount,titleHeight='1',1,0
if (legend.Text and legend.Text~='') then
titleHeight=15+(13.4*(select(2, string.gsub(legend.Text,"<br>", ""))))
end
local legendLine,legendGroup,legendY={},{},{}
while args['legendItem'..argnum] do -- assign legendLine, legendGroup, legendY for each dot
line,legendLine[legendCount] = extractItem(args['legendItem'..argnum] or '')
line=splitItem(line,3)
legendGroup[legendCount]=line[2] or argnum
if shapeList[legendGroup[legendCount]] then
maxWidth=math.max(tonumber(string.match(shapeList[legendGroup[legendCount]].shapeSize or '10','[%d]+')),maxWidth)
else maxWidth=math.max(tonumber(string.match(shapeList['1'].shapeSize or '10','[%d]+')),maxWidth)
end
maxWidth=maxWidth+1
if line[3] then
legendY[legendCount]=tonumber(string.match(line[3],'[%d]+'))
else legendY[legendCount] = 3+maxWidth*(legendCount-1)+titleHeight
-- if (legend.Text and legend.Text~='') then legendY[legendCount]=legendY[legendCount]+15 end
end
legendCount=legendCount+1
argnum=tostring(legendCount)
end
local w,h,r=getsize(legend.Size or '')
local x,y=getsize(legend.Pos or '')
local div=mw.html.create ('div')
div:css('position', 'absolute')
div:css('outline', '1px solid'..getColor(legend.Color))
if r~=0 then div:css('border-radius',tostring(r).."px") end
div
:css('top', y.."px")
:css('left', x.."px")
:css('width', w.."px")
:css('height', h.."px")
:css('line-height','105%')
:css('background-color', getColor(legend.Background) )
:css('color','inherit')
if not string.find(legend.Param,'noshadow') then div:css('box-shadow', '2px 2px 4px #33203335') end
div:tag( 'div' )
:css('position', 'absolute')
:css('top','1px')
:css('left', (w/2).."px")
:css('width',(w-8)..'px')
:css('text-align', 'center')
:css('color', getColor(legend.Color))
:css('transform', 'translateX(-50%)')
:css('font-size','11px')
:wikitext(legend.Text)
:done()
for lct=1,legendCount-1 do
--local t=legendGroup[lct]
local shape=shapeList[legendGroup[lct]] or shapeList['1']
local row={}
row.gridx=3+maxWidth/2
row.gridy=(legendY[lct] or 0) + 5
if shape.shape=='image:' then row.gridy=row.gridy+6 end
row.dx=0 row.dy=0
local legendShape= getshapetable(row,shape)
div:wikitext(legendShape)
:tag( 'div' )
:css('position', 'absolute')
:css('top',(legendY[lct] or 0)..'px')
:css('left', (maxWidth+6)..'px')
:css('width', (w-maxWidth-6)..'px')
:css('text-align', 'left')
:css('line-height','103%')
:css('color', getColor(legend.Color))
:css('font-size','10px')
:wikitext(legendLine[lct])
:done()
end
div:allDone()
table.insert(result,tostring(div))
end
function p._main ( args )
local result={}
local frame=mw.getCurrentFrame()
local dotTable={}
local magVal,scaleVal = args.magnify,''
local origH,origW=maplist.height,maplist.width
if magVal then --set up the values needed to magnify the top-right portion of the map
magVal=tonumber(string.match(magVal or '0',"[%.%-?%d]+")) or 1
if magVal>1 and magVal <=2 then
maplist.height= round(maplist.height/magVal,0)
maplist.width=round(maplist.width/magVal,0)
scaleVal='transform: scale('..magVal..') translateY('..tostring((origH-maplist.height)/2)..'px);'
else magVal=1
end
--debugmsg('magVal='..magVal..', Magnify: H='..maplist.height..', ('..origH..'), W='..maplist.width..', ('..origW..'), scale='..scaleVal)
--debugmsg('top: '..tostring(((origW-maplist.width))/2)..'px;right: '..tostring(0-((origW-maplist.width))/2)..'px; (alt top: '..tostring(((origH-maplist.height))/2)..'px;)')
end
-- set up the three nested div boxes (plus an extra if centered) to put the map plus title/caption area, in an appropriate frame on the page
if args.float=='center' or args.float=='centre' then table.insert(result,'<div class="center"><div class="thumb tnone">')
elseif args.float=='left' then table.insert(result,'<div class="thumb tleft">')
else table.insert(result,'<div class="thumb tright">')
end
table.insert(result,'<div class="thumbinner" style="position: relative; top: 0px; right: 0px; width: '..(args.width or "400")..'px;">')
if args.title then table.insert(result,'<div class="center" style="font-weight:bold">'..args.title..'</div>') end
if magVal and magVal >1 and magVal<=2 then
table.insert(result,'<div class="thumbinner noresize" style="display:block; position: relative; outline:0px; border:0px; padding:0px; background-color:transparent; color:inherit;')
table.insert(result,'top: 50%; right: '..tostring(0-((origW-maplist.width))/2)..'px; ')
table.insert(result,'height: '..origH..'px; width: '..maplist.width..'px; '..scaleVal..'">')
else
magVal=1
table.insert(result,'<div class="thumbinner noresize" style="position: relative; outline:0px; border:0px; padding:0px;')
table.insert(result,'top: 0px; right: 0px; ')
table.insert(result,'height: '..maplist.height..'px; width: '..maplist.width..'px ">')
end
-- Create the basemap using mapframe
local mapframecontent=getmapframecontent(args,'basemap')
table.insert(result, frame:extensionTag{ name ='mapframe', content=mapframecontent, args={width=tostring(maplist.width), height=tostring(maplist.height),
zoom=tostring(maplist.zoom), longitude=tostring(maplist.lonbase), latitude=tostring(maplist.latbase), mapstyle=maplist.mapstyle, frameless=true } } )
--Add coverall box to block the unhelpful links from mapframe - which wouldn't include all the dots. Reinstate some links to osm and wikimedia
table.insert(result,'<div style="position: absolute;width:'..tostring(maplist.width)..'px; height:'..tostring(maplist.height)..'px;')
table.insert(result,'top:0px;left:0px;background-color:#FFFFFF00; color:inherit;"></div>')
--Add replacent hover-links for OpenStreetMap and maps terms and conditions
table.insert(result,'<div style="position: absolute; top: '..tostring(maplist.height-18)..'px; left: '..tostring(maplist.width-13)..'px; width: 12px; height: 12px">')
table.insert(result,'[[file:Transparent.svg|12px|link=https://www.openstreetmap.org/copyright|'..aboutOSM..']]</div>')
table.insert(result,'<div style="position: absolute; top: '..tostring(maplist.height-18)..'px; left: '..tostring(maplist.width-110)..'px;')
table.insert(result,'width: 12px; height: 12px background-color: transparent; color:inherit;">')
table.insert(result,'[[file:Transparent.svg|12px| link=https://foundation.wikimedia.org/wiki/Policy:Maps_Terms_of_Use|'..termsOfUse..']]</div>')
-- Add scale-line
if not args.scalemark or args.scalemark~='0' then
local top=maplist.height-42
local left=maplist.width-61-(tonumber(args.scalemark or '1'))
local minipos=string.lower(args.minimap or '') -- scalemark gets pushed left if it would be behind the minimap
if minipos~='' and not(string.find(minipos,'left') or string.find(minipos,'top') ) then
local offset=tonumber(args.scalemark or '1')-tonumber(args['mini-width'] or '60')
if offset<1 then left=maplist.width-61-tonumber(args['mini-width'] or '60') end
end
if maplist.width-left >216 then top=top+14 end -- shunt scaleline down if it is beyond the copyright stuff
local scalek,scalem=getScale(maplist.zoom, maplist.latbase, magVal)
local magReduce=''
table.insert(result,"<div style=\"display:inline-block; position: absolute; background-color: #111111")
table.insert(result,"; color:inherit; clip-path:path(nonzero, 'M0,8 l0,4 l20,0 l0,-4 l-0.3,0 l0,3.7 l-19.4,0 l0,-3.7 z') ")
table.insert(result,"; width:20px") -- path is a 20px20px box, and then rescales
if magVal==1 then
table.insert(result,"; top:"..tostring(top-1).."px")
table.insert(result,"; left:"..tostring(left+16).."px")
table.insert(result,"; height:20px; transform:scale("..tostring(2.5)..", "..tostring(1.5)..")")
else -- shrink the scalemark to compensate for magnification
table.insert(result,"; top:"..tostring(top-(magVal*0.28)).."px")
table.insert(result,"; left:"..tostring(left+(16*(magVal*1.15))).."px")
table.insert(result,"; height:20px; transform:scale("..tostring(2.5*(1/magVal))..", "..tostring(1.5*(1/magVal))..")")
magReduce= 'scale('..tostring(1/magVal)..')'
end
table.insert(result,"\"></div>")
table.insert(result,'<div style="position: absolute; top: '..tostring(top)..'px; left: '..tostring(left+47)..'px; font-size: 9.5px; line-height: 126%; width: fit-content;')
table.insert(result,'color: #444433; background-color: transparent; text-align: right; transform: '..magReduce..' translateX(-100%);">'..scalek..'<br>'..scalem..'</div>')
end
--Set up the shapeList and dotList tables, to provide data to go on the map
local sgNumbers,sgSortable={},{} --s1,s2
sgNumbers["1"]="1"
if args.useFormatStyle and args.useFormatStyle=='shortstyle' then
shapeList=ParseShapeTypes (shapeList,args,"1")
for argindex,argv in pairs(args) do -- build a list of all the numbered sg's that have been used
if string.find(argindex,"sg[a-f,n%d]+") == 1 then -- only look through the sga,sgb, sgc,sgd,sge,sgf and sgn args
local x=string.match(argindex,"[%d]+") -- find its number
if x and not sgNumbers[x] then sgNumbers[x]=x end -- only add if not already found
end
if string.find(argindex,"sg[a-f]H") == 1 and highlightNum then highlightOption=true end
end
for indx,sgnum in pairs(sgNumbers) do table.insert(sgSortable,sgnum) end
table.sort(sgSortable,lessthan) -- put the list in a sortable form
for k,v in pairs(sgSortable) do -- work through the sorted list, parsing each set of sg's in turn, from 1 upwards
shapeList=ParseShapeTypes (shapeList,args,v)
end
if highlightOption==true then shapeList=ParseShapeTypes (shapeList,args,'H') end
end
local dotList,dotresult,dotItemTable,dotGroupList={},{},{},{}
if (not args.useFormatStyle) or args.useFormatStyle=='standardstyle' then
local dotmax=0
for indx in pairs(args) do
if string.match(indx,'mark%-coord[%d]+') or string.match(indx,'lat[%d]+') then
dotmax=math.max(dotmax, tonumber(string.match(indx,"[%d]+")))
end
if (indx=='shapeH') or (indx=='shape-colorH') or (indx=='shape-outlineH') or (indx=='label-colorH') then highlightOption=true end
end
dotItemTable=tradstyleParseShapes(args,dotItemTable,dotmax)
for argindex=1,dotmax do -- build a list of all the numbered coords or lat,lons that have been used
local x=tostring(argindex)
if args['mark-coord'..x] or (args['mark-lat'..x] and args['mark-lon'..x]) then
sgNumbers[x]=x -- add it to the list
end
end
else
for indx in pairs(args) do
if string.match(indx,'dot[%d]+') then table.insert(dotList,indx) end --add the index name for dot1, dot2 etc to dotList
end
table.sort(dotList,morethan)
for indx,dotName in pairs(dotList) do
dotresult=ParseData(args,string.match(dotName,'[%d]+') ) -- using each dot number, assign the settings for each dot to a dotresult item line
table.insert(dotItemTable,dotresult) -- and store that item line within the dotItemTable
end
end
for arcVal = 65,91 do -- check through args looking for any arcs
local arcLetter=string.char(arcVal)
if args['arc-text'..arcLetter] or args['arc'..arcLetter] then
makeArcText(args,result,arcLetter)
end
end
local dotdivs=''
local ddots=0
if dotItemTable[1] then
local ddots=(dotItemTable[1].lat or 0)+(dotItemTable[1].lon or 0)
end
local fgroup='F'..tostring(maplist.latbase+maplist.lonbase+ddots )
local FullscreenList={}
local addcomma=''
for i,dotitem in pairs(dotItemTable) do -- working throug each dot item, merge the dot and shape values into a full set of css text
local dotgroup= dotitem.group or "0"
if dotitem.posType=='mark-line' and dotitem.markDest then --find destination xy values for any mark-lines
for n,v in pairs(dotItemTable) do
if v.code == dotitem.markDest then dotitem.gridx2=v.gridx dotitem.gridy2=v.gridy break end
end
end
local qtype=dotitem.group -- find which shape group each dot has been assigned
--debugmsg('dotgroup='..qtype..', sg='..(sgNumbers[qtype] or 'nil')..' , shapeList='..shapeList[qtype].shape)
if not sgNumbers[qtype] then qtype="0" end --shapeList[dotitem.group] will give access to the shape values for that dot
if highlightNum==dotitem.code and highlightOption==true and shapeList['H'] then
table.insert(result, getshapetable(dotitem,shapeList['H']))
else
table.insert(result, getshapetable(dotitem,shapeList[qtype])) -- Add the actual css instructions for each dot
end
if shapeList[dotgroup] and not string.find((dotitem.param1 or ''),'nomap') then -- only add if not excluded with 'nomap' labelText
local ftext=''
if dotitem.dotTag~='' and not string.match(dotitem.labelText or '','[%d]') then ftext=stripdivs(dotitem.dotTag or '')..' <br>' end
if (dotitem.labelText ~= ftext) and dotitem.dotLink =='' then ftext=ftext..' '..stripdivs(dotitem.labelText)..'<br>' end
if (dotitem.dotLink) and (dotitem.dotLink ~='') then ftext=ftext..dotitem.dotLink..'<br>' end
if dotitem.imageName then ftext=ftext..'[[File:'..dotitem.imageName..'|250px]]' end
table.insert(FullscreenList,1, makeFullscreenItem (string.gsub(ftext,"[\n]+"," "), dotitem.info,dotitem.lat,dotitem.lon,fgroup,shapeList[dotgroup].shapeColor)..addcomma )
addcomma=', '
end
-- makeFullscreenItem (itemtitle,itemdescription,lat,lon,group,itemcolor)
-- Always add to start of list, to reverse the sequence, and separate with commas except for first item, which is now at the end
end
if args.legendBox then makeLegendBox(result,args) end
if args.minimap or args['mini-locator'] then makeLocatorMap(args,result) end
-- add tag link and details for fullscreen version
addcomma=''
if (mapframecontent or '[]') ~= '[]' then addcomma=',' end
mapframecontent=getmapframecontent(args,'fullscreen')
local contentstart='[ '..mapframecontent..addcomma..'{ "type": "FeatureCollection", "features": [ ' --extra features after first square bracket
local contentend=' ] } ]'
table.insert(result, '<div style="position: absolute;top: 9px;left: '..tostring(maplist.width-34)..'px">')
table.insert(result, '<div style="color: white; opacity:100; font-size: 19px; font-weight:normal; text-align: left;">')
table.insert(result, frame:extensionTag{ name ='maplink', content=contentstart..table.concat(FullscreenList)..contentend, args={zoom=tostring(maplist.zoom+1), class='no-icon', frameless='1',
latitude=tostring(maplist.latbase), longitude=tostring(maplist.lonbase), --add invisble 'en-spaces' for tooltip
text='<div title="'..fullscreenlinktext..'"> </div>'} } )
table.insert(result,'</div></div>') -- end of maplink -----
-- add closing div for main map
table.insert(result,'</div>')
-- collate caption material to go in the outer div class
local autocaption=string.lower(args['auto-caption'] or 'no')
local autoOff=autocaption:match("(%w+)(.*)") -- select the first word in autocaption and see if it is a negativeAnswer)
if args.caption or not negativeAnswer[autoOff] then
table.insert(result,'<div class="thumbcaption" style="text-align:left">')
if args.caption then table.insert(result,args.caption) end
end
local columns=tonumber(autoOff:match("[%d]+") or '1')
if columns>1 then columns=round(maplist.width/(columns*17), 0) end -- convert from em to px for historical reasons
--for k in pairs(dotList) do capchk=capchk..(args["dotlink"..k] or '') end
local capchk=nil
local captionList = {}
for key, value in pairs(dotItemTable) do
-- only add an autocaption line if there is both a dotTag and a dotLink line available and it is not marked as nolist
if value.dotTag and value.dotTag~='' and (not string.find(value.param1 or '','nolist')) and string.gsub(value.dotLink or '',"%s+","")~='' then
table.insert(captionList, {key = key, value = value})
capchk=true
end
end
if capchk and (not negativeAnswer[autoOff]) then
table.sort(captionList, function (a,b) return lessthan(string.match(a.value.dotTag,'[%w]+'), string.match(b.value.dotTag,'[%w]+')) end)
local myDivision = string.gsub((args.toggletext or toggletext), "%s+", "")
if string.find(autocaption,'collaps') then
table.insert(result,'<hr><div class="mw-customtoggle-'..myDivision..'">'..(args.toggletext or toggletext)..'</div>')
if string.find(autocaption,'collapsed') then
table.insert(result,'<div class="mw-collapsible mw-collapsed" id="mw-customcollapsible-'..myDivision..'">')
else
table.insert(result,'<div class="mw-collapsible" id="mw-customcollapsible-'..myDivision..'">')
end
end
if string.find(autocaption, 'columns=') then
columns=string.match(autocaption,'[%d]+',string.find(autocaption, 'columns=') )
end
table.insert(result,'<div class="mw-collapsible-content" style="column-count:'..columns..'; column-rule:solid 1px;text-align:left;padding-top:5px">')
table.sort(dotList,lessthan)
local nval,ngrp='','0'
for k,v in pairs(captionList) do
nval=string.match(v.value.dotTag,'[%w]+') -- find the first alphanumeric item in the dotTag
ngrp=v.value.group or '0'
if v.value.dotLink and v.value.dotLink~='' and nval and nval~='' then
local c1,c2
if nval==args.highlight then
c1,c2=checkColors(shapeList['H'].shapeColor)
else
c1,c2=checkColors(shapeList[ngrp].shapeColor)
end
table.insert(result,'<div style="display:inline-block;line-height:110%;vertical-align:middle; padding:1px 4px;border-radius:8px;border: 0.5px solid black;')
table.insert(result,'background-color:'..c1..';color:'..c2..';font-size:88%;font-weight:bold">'..nval..'</div> '..v.value.dotLink..'<br>')
end
end
table.insert(result,'</div>') -- end for caption-content div
if string.find(autocaption,'collaps') then table.insert(result,'</div>') end -- end for toggle frame
end
if args.caption or not negativeAnswer[autoOff] then table.insert(result,'</div>') end -- end for whole caption frame
table.insert(result,'</div></div>') -- outer two frames
if args.float == 'center' or args.float=='centre' then table.insert(result,'</div>') end
if args['show-new-format'] == 'hints' then -- provide a 'format hint panel' in the 'Preview Box'
local w="<small>Below are some template hints for the 'sga' compressed version, "
w=w..'{{tl|OSM Location dots}}. It can use these, instead of the more verbose {{tl|OSM Location map}} parameter format. '
w=w..'Data is divided between a "ShapeGroup" and the "Dots", so that a single shapeGroup can be used for multiple dots on the map. '
previewMsg(w..'(nb. the "group" value can be the number or an assigned name of a shapeGroup)</small>')
w='<small>{{tl|OSM Location dots}}: | dot(n)=group,lat,lon,dotTag | '
w=w..'dotlink=link/tooltip | dotlabel=label,position,dx,dy,param1,info | dotpic=filename <br>'
w=w.."(nb. param1 options include 'nolink' 'nolist' 'nomap' 'hemisphere-1' 'hemisphere+1', 'noline' - quotes not required, separate with spaces).<br>"
w=w..'| sga = Shape,Sizepx,Color,Angledeg | sgb= OutlineWidth,Color,Style | sgc=TextSize,Color,Angle,bold italic <br>'
w=w..'| sgd=TagSizepx,Color,Spacer,Angledeg | sge=LineWidth,Color,Style | sgf=TextSpacingpx,LineHeight%,Outlinepx,backgroundColor<br>'
w=w..'| sgn=Name (optional, to assign a meaningful name) | sgp=Parent (can be the name or number of the parent shapeGroup. '
previewMsg(w..'Each shapegroup will inherit values from a parent, stretching back to "sga1" and its default values.)</small>')
if #pmsg then
local dbg={}
for i,x in pairs(pmsg) do
table.insert(dbg,x..'<br>')
end
dbg=mw.addWarning(table.concat(dbg))
table.insert(result, dbg)
end
end
if args.coordtest then debugmsg(mw.text.nowiki(args.coordtest)) end
for i,x in pairs(msg) do
table.insert(result, x..'<br>')
end
return table.concat(result)
end
function p.main(frame)
local args = getArgs(frame)
local itemTab={}
maplist.width=tonumber(args.width) or 400
maplist.height=tonumber(args.height) or 300
if args.coord then
itemTab=splitItem(convertCoordsTrad (args.coord),2)
maplist.latbase=itemTab[1]
maplist.lonbase=itemTab[2]
else
maplist.lonbase=tonumber(args.lon) or 5
maplist.latbase=tonumber(args.lat) or 0
end
maplist.zoom=tonumber(args.zoom) or 1
visibleLinks=args.showlinks
highlightNum=args.highlight
if args.nolabels=='1' then maplist.mapstyle='osm' else maplist.mapstyle='osm-intl' end
return p._main(args)
end
return p
9x2ku7g9uvcxaiexregj6gdhw7ij4fs
886334
886333
2025-06-13T16:58:17Z
KartikMistry
10383
[[:en:Module:OSM_Location_map]] માંથી આયાત કરેલ ૧ પુનરાવર્તન
886333
Scribunto
text/plain
require('strict')
local delink=require('Module:Delink').delink
local getArgs = require('Module:Arguments').getArgs
local p = {}
local maplist={}
local sgNames={}
local highlightOption=false
local highlightNum
local visibleLinks
-- This module creates framed maps of anywhere in the world, at the required scale, and enables annotations,
-- dots, shapes, lines and other ways to customise the area of the map being shown. It also provides a link
-- to an interactive fullscreen version, which has locator dots instead of annotations and shapes.
-- This is the 2025 successor module to a wiki-markup template version of 2024, which itself was a successor
-- to the 'Graph'/VEGA driven template that was begun in 2016, until the Vega version was switched off in 2023.
-- This module is called from template {{OSM Location map}}, which uses the same parameter formats as before.
-- In addition it will be possible to use a more concise parameter format using the template {{OSM Location dots}}
-- In general the css output from the two formats will be identical, but the the concise version will allow bits of
-- greater control over some of the settings.
-- see the documentation on the two template pages for details of how to use the mapping features.
-- If language customisation is needed, there are text items below that can be translated. Also see the color table
-- below with details of how to add additional color names to allow localised alternatives.
-- (Translating other language shape-types could be possible, but has not currently been contemplated.
-- Parameter name translation would be harder but likely to be possible, ideally still retaining compatibility
-- with template calls already written using English).
local negativeAnswer={no=1,'0'-1,off=1}
local fullscreenlinktext='Click for interactive fullscreen map with links to nearby articles'
local toggletext='[Hide/show caption list]'
local termsOfUse='Maps: terms of use'
local aboutOSM='About OpenStreetMaps'
local shapeList={} --This sets up the 'factoryDefault' shape group 0
shapeList["0"]={shapeType="0",
Name="initialSettings",
Parent="0",
--sga items for the shape
shape="circle",
shapeSize="12px",
shapeColor="blue",
shapeAngle="0deg",
--sgb items for border of the shape
outlineWidth="0.5px",
outlineColor="darkblue",
outlineStyle="solid",
--sgc items text settings for labels
textSZ="11px",
textCL="darkgrey",
textNG="0deg",
--sgf further text settings
textSP="0px",
textLH="120%",
textOL="0px",
textBG="transparent",
--sgd items for dotTag text settings
tagSize="10px",
tagColor="white",
tagSpacer="0px",
tagAngle="0deg",
--sge items for extension line to connect label to dot
textEW="0px",
textEC="darkgrey",
textES="solid"
}
local colorList={} -- used by colorLookup to catch unsupported colors (eg 'LimeGreen'), to convert to generic version
colorList['green']='hardgreen' -- it could also be added to to include alternative language equivelants, for a quick solution.
colorList['red']='hardred' -- colorList ['source'] = target
colorList['white']='white' -- converts any color that includes 'source' into its equivelent target
colorList['blue']='hardblue' -- note, for translation you can add to this list, rather than replace it,
colorList['brown']='brown' -- which would mean existing map definitions in english would also still work, alongside translated ones
colorList['grey']='hardgrey'
colorList['gray']='hardgrey'
colorList['purple']='hardpurple'
colorList['orange']='hardorange'
colorList['leaf']='hardleaf'
--for a more thorough translation, you can add all the variants of the colors as further CTB elements and hex values or redirects
local CTB={} -- set up a table of color names (the CTB Color table index) and html hash colorhex values.
CTB["paleblue"],CTB["softblue"],CTB["hardblue"],CTB["darkblue"]="#D6E1EC","#77A1CB","#4B77D6","#1c559e"
CTB["palered"],CTB["softred"],CTB["hardred"],CTB["darkred"] = "#FCC6C0","#EC644B","#DB3123","#AA1205"
CTB["palegreen"],CTB["softgreen"],CTB["hardgreen"],CTB["darkgreen"]= "#D2F0E5","#81AF81","#269F46","#0b7527"
CTB["paleleaf"],CTB["softleaf"],CTB["hardleaf"],CTB["darkleaf"]= "#dff5c1","#b5e376","#8cc244","#679c21"
CTB["palegrey"],CTB["softgrey"],CTB["hardgrey"],CTB["darkgrey"]= "#E8E8D6","#AAAA88","#777755","#444433"
CTB["palegray"],CTB["softgray"],CTB["hardgray"],CTB["darkgray"]=CTB["palegrey"],CTB["softgrey"],CTB["hardgrey"],CTB["darkgrey"]
CTB["palebrown"],CTB["softbrown"],CTB["hardbrown"],CTB["darkbrown"]="#FAF6ED","#CCB56C","#AD7F14","#754910"
CTB["palepurple"],CTB["softpurple"],CTB["hardpurple"],CTB["darkpurple"]="#e0d1e6","#c784e0","#a029cf","#7a05a8"
CTB["paleorange"],CTB["softorange"],CTB["hardorange"],CTB["darkorange"]="#ffedc2","#ffcf61","#EEB533","#e39f05"
CTB["black"],CTB["white"],CTB["yellow"]="#000000","#FFFFFF","#FAF039"
CTB["background"],CTB["paleground"],CTB["beigeground"]="#f9f6f2","#FEFEFA","#F5F5DC"
CTB["beige"]=CTB["beigeground"]
CTB["aqua"],CTB["teal"],CTB["fuchsia"] = "#00FFFF","#008080","#FF00FF"
CTB["maroon"],CTB["olive"],CTB["navy"] = "#800000","#808000","#000080"
CTB["lime"],CTB["limegreen"],CTB["aquamarine"] = "#00FF00","#32CD32","#7FFFD4"
CTB["silver"],CTB["yellow"],CTB["orchid"] = "#800000","#FFFF00","#DA70D6"
-- set up a table of predefined clip-paths
local pathshape={}
pathshape.squaredd = "M 19,1.25 l 0,18 -18,0 0,-18 18,0m-1,1 -16,0 0,16 16,0 0,-16m-1,1 0,14 -14,0 0,-14 14,0zm-1,1 -12,0 0,12 12,0 0,-12zm-1,1 0,10 -10,0 0,-10 10,0z"
pathshape.squared = "M 18,2.5 l 0,15 -15,0 0,-15 15,0m-1,1 -13,0 0,13 13,0 0,-13zm-1,1 0,11 -11,0 0,-11 11,0z"
pathshape.triangledd="M 0 20,20 20,10 0,0 20ZM1.5 19,10 1.7,18.5 19,1.5 19ZM3 18,17 18,10 3.8,3 18ZM4.5 17,10 5.4,15.4 17, 4.5,17ZM6 16,13.8 16,10 7.4z"
pathshape.triangled ="M1,18 l 18,0 l -9,-18 l -9,18zm1.7,-1.1 l 7.3,-14.6 l 7.3,14.6 l -14.6, 0zm1.7,-1 l 11.0,0 l -5.5,-11 l -5.5,11z"
pathshape.circledd = "M0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm0.8,0a9.2,9.2 0 1,1 18.4,0a9.2,9.2 0 1,1 -18.4,0m1,0a8.2,8.2 0 1,0 16.4,0a8.2,8.2 0 1,0 -16.4,0zm0.8,0a7.2,7.2 0 1,1 14.8,0a7.2,7.2 0 1,1 -14.8,0m1,0 a6.4,6.4 0 1,1 12.8,0a6.4,6.4 0 1,1 -12.8,0z"
pathshape.circled = "M2.5,10a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0m0.8,0 a5,5 0 1,1 11.4,0a5,5 0 1,1 -11.4,0"
pathshape.diamond = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10z"
pathshape.diamondd = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10zm1,0 l 6,8.5 l 6,-8.5 -6,-8.5 -6,8.5zm1,0 l 5,-7 5,7 -5,7 -5,-7z"
pathshape.diamonddd = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10zm0.75,0 l 6.25,9 l 6.25,-9 -6.25,-9 -6.25,9zm0.75,0 l 5.5,-8 5.5,8 -5.5,8 -5.5,-8zm0.75,0 l 4.75,7 l 4.75,-7 -4.75,-7 -4.75,7zm0.75,0 l 4,-6 4,6 -4,6 -4,-6z"
pathshape.crossd = "M3.1,12.5 l4.2,0 l0,4.2 l5,0 l0,-4 l4.2,0 l0,-5 l-4.2,0 l0,-4.2 l-5,0 l0,4.2 l-4.2,0zM2.3,10a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0z"
pathshape.cross = "M3.1,12.5 l4.2,0 l0,4.2 l5,0 l0,-4 l4.2,0 l0,-5 l-4.2,0 l0,-4.2 l-5,0 l0,4.2 l-4.2,0z"
pathshape.thincross = "M2,12 l6,0 l0,6 l4,0 l0,-6 l6,0 l0,-4 l-6,0 l0,-6 l-4,0 l0,6 l-6,0z"
pathshape.fivepointstar = "M10 0 L12.245 6.91 19.511 6.91 13.633 11.18 15.878 18.09 10 13.82 4.122 18.09 6.367 11.18 0.489 6.91 7.755 6.91Z"
pathshape.fivepointstard = "M10 1.5 L 11.90825 7.3735 18.08435 7.3735 13.08805 11.003 14.9963 16.8765 10 13.247 5.0037 16.8765 6.91195 11.003 1.91565 7.3735 8.09175 7.3735 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.sixpointstar = "M10 0 L12.323 5.977 18.66 5 14.645 10 18.66 15 12.323 14.023 10 20 7.677 14.023 1.34 15 5.355 10 1.34 5 7.677 5.977Z"
pathshape.sixpointstard = "M10 1.5 L 11.97455 6.58045 17.361 5.75 13.94825 10 17.361 14.25 11.97455 13.41955 10 18.5 8.02545 13.41955 2.639 14.25 6.05175 10 2.639 5.75 8.02545 6.58045 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.sevenpointstar = "M10 0 L12.048 5.747 17.818 3.765 14.602 8.95 19.749 12.225 13.69 12.943 14.339 19.01 10 14.72 5.661 19.01 6.31 12.943 0.251 12.225 5.398 8.95 2.182 3.765 7.952 5.747Z"
pathshape.sevenpointstard = "M10 1.5 L11.7408 6.38495 16.6453 4.70025 13.9117 9.1075 18.28665 11.89125 13.1365 12.50155 13.68815 17.6585 10 14.012 6.31185 17.6585 6.8635 12.50155 1.71335 11.89125 6.0883 9.1075 3.3547 4.70025 8.2592 6.38495 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.eightpointstar = "M10 0 L11.88 5.46 17.071 2.929 14.54 8.12 20 10 14.54 11.88 17.071 17.071 11.88 14.54 10 20 8.12 14.54 2.929 17.071 5.46 11.88 0 10 5.46 8.12 2.929 2.929 8.12 5.46Z"
pathshape.eightpointstard = "M10 0 L10 1.5 L11.598 6.141 16.01035 3.98965 13.859 8.402 18.5 10 13.859 11.598 16.01035 16.01035 11.598 13.859 10 18.5 8.402 13.859 3.98965 16.01035 6.141 11.598 1.5 10 6.141 8.402 3.98965 3.98965 8.402 6.141ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.ring="M2.6,9.5a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0z"
pathshape.boxd=pathshape.squared
pathshape.boxdd=pathshape.squaredd
pathshape.ellipsed=pathshape.circled
pathshape.ellipsedd=pathshape.circledd
local msg={}
local function debugmsg(txt)
table.insert(msg,txt)
end
local pmsg={}
local function previewMsg(txt)
table.insert(pmsg,txt)
end
local function colorLookup(color)
for c,l in pairs(colorList) do
if string.find(color,c) then return l end
end
return color
end
local function getColor (color,chk)
local c
local opacity="100"
if not color or color=='' then color='hardgrey' end
if color=="transparent" then return color end
if color=="background1" then color='background' end
if string.byte(color,1,1)==35 and (#color == 7 or #color == 9) then
c=color
elseif string.byte(color,1,1)==35 and #color == 4 then
c=string.sub(color,1,2)..'f'..string.sub(color,3,3)..'f'..string.sub(color,4,4)..'f'
else
local s=color..'1'
s= s:sub(0,s:find("%d")-1)
opacity=string.match(color,"%d+")
if not CTB[s] then s= colorList[s] end -- check for synonyms and translations
if not CTB[s] then debugmsg(mw.addWarning('color = '..color..'. The color name is not defined. Used default grey instead')) end
c=CTB[s] or CTB.hardgrey
end
if opacity and (tonumber(opacity) < 100) and string.find(c,"#")==1 and string.len(c)==7 and opacity~="" then
local hexval=string.format("%x",(math.floor(tonumber(opacity)*2.55)))
c=c..hexval
end
return c
end
function p.colorvalue(frame) -- enable external access to the CTB colorTable values. usage: {{#invoke:OSM Location map|colorvalue|color=hard blue}}
local c
if not frame.args.color or frame.args.color=='' then c='grey'
else c=string.lower(string.gsub(frame.args.color,'%s+','')) end
return string.upper(string.sub(getColor(c),2))
end
local function checkColors(color)
local c=getColor(color,'check')
local opacity =1 -- calculate colour brightness and return black or white for contrast
if c=='transparent' then return c,'#000000',0 end
if not (string.find(c,'#')==1) then return colorCap(c),'#FFFFFF',0 end
if #c>8 then opacity= tonumber('0x'..(string.sub(c,8,9)))/500 end
local r=tonumber('0x'..(string.sub(c,2,3)))/255
local g=tonumber('0x'..(string.sub(c,4,5)))/255
local b=tonumber('0x'..(string.sub(c,6,7)))/255
if 0.2126 * r + 0.7152 * g + 0.0722 * b / opacity < 0.7 then
return c,'#FFFFFF',0.2126 * r + 0.7152 * g + 0.0722 * b / opacity
else return c,'#000000',0.2126 * r + 0.7152 * g + 0.0722 * b / opacity
end
end
local function morethan(a,b)
a = tonumber(string.match(a, '%f[%d]%d[,.%d]*%f[%D]') or '0')
b = tonumber(string.match(b, '%f[%d]%d[,.%d]*%f[%D]') or '0')
return a>b
end
local function lessthan(a,b)
if tonumber(string.match(a, '%f[%d]%d[,.%d]*%f[%D]')) then
a = tonumber(string.match(a, '%f[%d]%d[,.%d]*%f[%D]'))
b = tonumber(string.match(b, '%f[%d]%d[,.%d]*%f[%D]') or '0')
end
return a<b
end
local function getsize(size)
--size1 is between 1 and 3 values, each with px, equating to width,height,corner-rounding
--eg '15px 25px 5px' (spaces are optional) or '18px'. returns three numbers
local sizeval = {}
for v in string.gmatch(size, "[^px]+") do
table.insert(sizeval,v)
end
sizeval[1] = tonumber(sizeval[1]) or 13
sizeval[2] = tonumber(sizeval[2]) or sizeval[1]
sizeval[3] = tonumber(sizeval[3]) or 0
return sizeval[1],sizeval[2],sizeval[3]
end
local function coord2text(coord) -- looks through the output from {{coord}} to find the lat and long decimal values
-- and converts compass points to minus or not-minus, return with separating comma.
local lat = string.match(coord,'[%.%d]+°[NS]')
local lon = string.match(coord,'[%.%d]+°[EW]')
local neg={N="",S="-",W="-",E=""}
return neg[string.match(lat, '[NS]')]..string.match(lat,'[%.%d]+')..","..neg[string.match(lon, '[EW]')]..string.match(lon,'[%.%d]+')
end
local function convertCoordsTrad (row)
local coords=''
if row and string.find (row,'<span class="geo">') then
local a,b=string.find(row,'<span class="geo">')
local start=b+1
a,b=string.find(row,"</span>",b)
local finish=a-1
coords=string.sub(row,start,finish)
coords=string.gsub(coords,'; ',',')
end
return coords
end
local function convertCoords (row)
local start,finish,lat,lon,coords,says
if row then
local a,b=string.find(row,"<span class=")
start=a
while a do -- find the final span>
finish=b
a,b=string.find(row,"span>",b)
end
if start then
coords= string.sub(row,start,finish)
says=""
if string.find(coords,'<span class="error">') then
error("coord error: badly formed coordinates",0)
end
coords=coord2text(coords)
coords = string.sub(row,1,start-1)..coords..string.sub(row,finish+1)
else
coords=row
end
return coords
else
return "Nothing to see here"
end
end
local function fillCommas(val,max)
local line=''
if not val then line=',' -- ensure there is some content
else line = val --string.lower(string.gsub(val,"%s+","")) -- or strip spaces
end
if string.find(line,',') == 1 then line=' '..line end -- ensure initial comma is not skipped
local _, count=string.gsub(line,",","") -- add enough subsequent commas for all entries
line=line..string.rep(',',max-count)
while(string.find(line,",,") ) do
line=string.gsub(line,",,",", ,") --ensure string.gmatch doesn't ignore any empty items by padding with spaces
end
return line
end
local function makeLinkBox(left,top,wid,label, link)
local linkBoxName='Transparent square.svg'
if visibleLinks or '' =='yes' then linkBoxName='Red hollow square.svg' end
local builder = mw.html.create('div') --display:inline-block;
builder
:cssText('position:absolute;left:'..tostring(left-1-wid/2)..'px;top:'..tostring(top-1 + math.min(wid/2-12,0) - wid/2)..'px')
:wikitext(string.format( '[[File:%s|%dpx|link=%s|%s]]', linkBoxName, wid+2, link, label ))
return tostring(builder)
end
local function extractItem(row,searchItem)
-- remove text following a searchItem or start of line, which might be in quote-marks to allow commas
local xend,xstart=1,0
if not row then return '','' end
if searchItem then xend,xstart= string.find(row or '',searchItem or 'image:') end
if not xstart then return string.gsub(string.gsub(row or '',"%b\"\"", ''),"%b\'\'", '') or '','' end
while row:byte(xstart+1) == 32 and xstart<#row do -- skip over any leading spaces
xstart=xstart+1
end
local xbyte=row:byte(xstart+1)
if xbyte == 34 or xbyte == 39 then -- are they wrapped in single or double quotes
xstart=xstart+1
xend=row:find(string.char(xbyte),xstart+1)
else
xend = row:find(',',xstart+1) -- if no quotes, we assume no commas
if not xend then xend=#row+1 end
end -- return residual row and extracted text
return row:sub(0,xstart)..row:sub(xend), row:sub(xstart+1,xend-1)
end
local function itemCheck(item,ext)
if not item then return nil end
if not ext then ext='' end
return (string.match(item,"[%.%-?%d]+") or '0')..ext
end
local function stripdivs(line)
return string.gsub(line or '',"%b<>", ' ')
end
local function splitItem(item,max) -- takes a commas-sep list and returns a table of lowercase items with no spaces, or nil
local r={}
local x=1
item=string.lower(fillCommas(item,max))
for t in string.gmatch(item,"[^,]+") do
r[x]=string.gsub(t,"%s+","")
if r[x]=='' then r[x]= nil else -- residual items might have commas
if x>max then r[max]=(r[max] or '')..', '..r[x] end
end
x=x+1
end
return r
end
local function ParseShapeTypes (result,args,sgval) -- for use with compressed, comma-separated 'sg plus dots' parameters
--[[ shape table items and default values as set at top of page
shapeType="0", Name="initialSettings", Parent="0",
--sga items for the shape shape="circle", shapeSize="12px", shapeColor="blue", shapeAngle="0deg",
--sgb items for border outlineWidth="0.5px", outlineColor="darkblue", outlineStyle="solid",
--sgc items label text textSZ="11px", textCL="white", textNG="0deg", textAT=attributes ("bold" and/or "italic")
--sgd items for dotTag tagSize="11px", tagColor="darkgrey", tagSpacer="0px", tagAngle="0deg",
--sge extension line textEW="1px", textEC="darkblue", textES="solid"
--sgf fx for text labels textSP="0px" textLH="100%" textOL="1px", textBG="paleground",
<!--| sga = shape,Size,Color,Angle|sgb= outlineWidth,color,style eg: sga1=circle,14px,blue,0deg| sgb1=0px,dark grey,solid
| sgc=textSize,color,angle,bold/italic | sgd=tagSize,Color,Spacer,Angle eg: sgc1=11px,dark grey,0deg,normal| sgd1=9px,white,0px,0deg
| sge=lineWidth,color,style |sgf=textspacing,lineHeight%,outlinepx,backgroundcolor, [bold,italic] eg: sge1=0px,black, solid| sgf1=0px,120%,0px,background
| dot=shape-group/lat/lon/title/dotTag | dotlink=link or tooltip | dotlabel=label,position,dx,dy,param1,info| dotpic=filename-->
--]]
if args["sgn"..sgval] then
local sgname=string.match(args["sgn"..sgval],"(%w+)(.*)")
sgNames[sgname]=sgval
end
local parent= args["sgp"..sgval]
if parent then
parent=string.match(parent,"(%w+)(.*)")
local pos= string.find(parent,"%d+")
if pos == 1 then
parent=string.match(parent,"%d+")
else
parent=sgNames[parent] or '1'
end
end
if sgval~='H' then
if not parent or tonumber(parent) > tonumber(sgval) then
if sgval=="1" then parent="0" else parent="1" end
end
end
local itemTab, line, filename
result[sgval]={}
result[sgval].shapeType=sgval
line,filename=extractItem(args['sga'..sgval] or '','image:')
if sgval=='1' and not args.sga1 then line='circle,12px,blue,0deg' end -- ensure there is a parent=1 sga
result[sgval].shapeFile=filename or ''
-- sga= Attributes for shape
itemTab=splitItem(line,4)
result[sgval].shape = itemTab[1] or result[parent].shape
result[sgval].shapeSize=itemTab[2] or result[parent].shapeSize
result[sgval].shapeColor=itemTab[3] or result[parent].shapeColor
result[sgval].shapeAngle=itemCheck(itemTab[4],'deg') or result[parent].shapeAngle
-- sgb= Border outline attributes for shape
itemTab=splitItem(args['sgb'..sgval],3)
result[sgval].outlineWidth=itemCheck(itemTab[1],'px') or result[parent].outlineWidth
result[sgval].outlineColor=itemTab[2] or result[parent].outlineColor
result[sgval].outlineStyle=itemTab[3] or result[parent].outlineStyle
--sgc=character attributes for label
itemTab=splitItem(args['sgc'..sgval],4)
result[sgval].textSZ=itemCheck(itemTab[1],'px') or result[parent].textSZ -- size of text in px
result[sgval].textCL=itemTab[2] or result[parent].textCL -- colour for text
result[sgval].textNG=itemCheck(itemTab[3],'deg') or result[parent].textNG -- Angle for text
result[sgval].textAT=itemTab[4] or result[parent].textAT -- attributes bold, and/or italic
--sgd=dotTag attributes
itemTab=splitItem(args['sgd'..sgval],4)
result[sgval].tagSize=itemCheck(itemTab[1],'px') or result[parent].tagSize
result[sgval].tagColor=itemTab[2] or result[parent].tagColor
result[sgval].tagSpacer=itemCheck(itemTab[3],'px') or result[parent].tagSpacer
result[sgval].tagAngle=itemCheck(itemTab[4],'deg') or result[parent].tagAngle
--sge= extension line attributes
itemTab=splitItem(args['sge'..sgval],4)
result[sgval].textEW=itemCheck(itemTab[1],'px') or result[parent].textEW -- width
result[sgval].textEC=itemTab[2] or result[parent].textEC -- colour
result[sgval].textES=itemTab[3] or result[parent].textES -- style
--sgf= fx for label text
itemTab=splitItem(args['sgf'..sgval],4)
result[sgval].textSP=itemCheck(itemTab[1],'px') or result[parent].textSP -- spacing value for letters
result[sgval].textLH=itemCheck(itemTab[2],'%') or result[parent].textLH -- Angle for text
result[sgval].textOL=itemCheck(itemTab[3],'px') or result[parent].textOL -- width of text-border line
result[sgval].textBG=itemTab[4] or result[parent].textBG -- color for text background
return result
end
local function round(x,dec)
-- x=number [, dec=integer] returns numeric value with upto dec decimals (all but first trailing zeros get truncated)
if (dec or 0)==0 then
return x>=0 and math.floor(x+0.5) or math.ceil(x-0.5) --this avoids .0 where dec=0
end
dec =10^(dec)
return x>=0 and math.floor(x*dec+0.5)/dec or math.ceil(x*dec-0.5)/dec
end
local function maptogrid(t,r)
--[[ converts mercator projection longitude and latitude coordinates to x and y pixel coordinates, within a frame of given size, centre coordinates and zoom level.
t is a table of named indices: {lon, lat, lonbase, latbase, width, height, zoom}
output is two values, x and y, rounded to r decimal places--]]
local x=t.width/2 + ( ((math.rad(t.lon)*6378137) - (math.rad(t.lonbase)* 6378137) ) / (156543.03*math.cos(t.latbase/180)/(2^t.zoom) ) )*(1-(0.055*(t.latbase/90)))
local y=t.height/2 + ( ( (math.log(math.tan(math.rad(t.latbase)/2+math.pi/4))*6378137) - (math.log(math.tan(math.rad(t.lat)/2+math.pi/4))*6378137) ) / (156543.03*math.cos(t.latbase/180) / (2^t.zoom) ) )*(1-(0.055*(t.latbase/90) ) )
return round(x,r),round(y,r)
--source: python code at https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames and https://wiki.openstreetmap.org/wiki/Mercator
--[[
width and height are the size, in pixels, of the map, which will be centerd around lonbase,latbase.
Method: Convert lon and lonbase to meter-offsets from coord(0,0), and subtract lonbase from lon,
zoom and latbase are used to scale the resulting meter-offset to pixels, and add it to width/2.
Convert lat and lat-base to meter-offsets from coord(0,0), subtract lat from latbase,
scale the resulting meter-offset to pixels, add it to height/2.
A correction factor '*(1-(0.055*(t.latbase/90) ) )' compensates for an error that seems to creep in towards
the edges of the map at higher latitudes. It was identified experimentally, and ensures a dot at the edge is
in the same place as if that location is positioned at the centre.
Original Python code for lat,lon to x,y where 0,0 is the centre of the map
print('x=',width+(((math.radians(lon1) * 6378137)-
(math.radians(lonbase) * 6378137))/
(156543.03*math.cos(latbase/180)/(2**zoom))),' y=',height+((
(math.log(math.tan(math.pi / 4 + math.radians(latbase) / 2)) * 6378137)-
(math.log(math.tan(math.pi / 4 + math.radians(lat1) / 2)) * 6378137))/
(156543.03*math.cos(latbase/180)/(2**zoom)))
) --]]
end
local function getScale(zoom, lat,magVal)
if magVal and magVal>1 and magVal <=2 then zoom=zoom+(magVal-1) end
local dist=(156543.03 * math.cos(math.rad(math.abs(lat))) / (2 ^ zoom))/17
local y
if dist < 1 then
y=(round(dist*10,1))
return tostring(y*100)..'m', tostring(round(y*109,0))..'yds'
elseif dist <18 then
y=(round(dist,0))
return tostring(y)..'km', tostring(round(y*0.621371,1))..'miles'
elseif dist <500 then
y=round(dist/10)
return tostring(y*10)..'km', tostring(round(y*6.21371,0))..'miles'
else
y=round(dist/100)
return tostring(y*100)..'km', tostring(round(y*62.1371,0))..'miles'
end
end
local function ParseData (args,dotval) -- for use with compressed, comma-separated 'sg plus dots' parameters
-- takes a structured series of comma-separated items which get parsed as the following:
-- dot(n)= (sgNumber or Name),{{coord}} or (lat,lon), (dotTag)
-- dotlink(n) = single-parameter text to give wikilink and/or title used by tootlip, fullscreen dots and autocaption list
-- dotlabel(n) = 'label text',pos(left,roght,top,bottom,centre,auto),(dx), (dy) pixel offsets, params, info
-- dotpic(n) = single parameter wikimedia filename for an image to use in photopanel and/or fullscreen dots
-- dotfeature(n)= 'mark-line' (,linewidth,style,gap) or 'photo-panel' (,image-dim,panel-width,panel-height), draws line to n-1
-- label is used if either a position and/or an x,y offset are not 0,0 ( if no label then dotTag will be put at at the x,y offset, or over the dot
-- label text can be autoaligned if x,y puts it left or right of the dot, or centered if above/below)
-- quote marks are not needed unless including commas within the label text
-- param1 is optional items separated by spaces, and can include [nolabel nolist nomap hemisphere+1 hemisphere-1]
-- info is free wikitext, to be used in the fullscreen dot box. (use dotpic to show a picture)
--<!--| dotx=shape-group,[lat,lon or {{coord}} ], dotTag | dotlink=link or tooltip | dotlabel=label,position,dx,dy,param1,info| dotpic=filename-->
local result={}
local count=1
local row = convertCoords (args["dot"..dotval]) -- swap in any {{coord}} values so they are csv lat and lon
row=fillCommas(row,4)
result.code=dotval -- store the parameter name as the id code
for item in string.gmatch(row,"[^,]+") do -- iterate through 'row', adding each csv item in turn, if present
if count==1 then --see if it is a number or a name
local pos= string.find(item,"%d+")
if pos == 1 then
result.group=string.lower(string.gsub(item,"%s+",""))
else
item=string.match(item,"(%w+)(.*)") -- ensure just a single word
result.group=sgNames[item]
end
elseif count==2 then
result.lat=tonumber(string.match(item,"[%.%-?%d]+")) or 0-- find the number, with no non-numeric stuff
elseif count==3 then
result.lon=tonumber(string.match(item,"[%.%-?%d]+")) or 0
elseif count==4 then
result.dotTag=item:match( "^%s*(.-)%s*$" ) or "" -- dotTag allows for internal spaces, but no commas
end
count=count+1
end
row, result.labelText= extractItem(args["dotlabel"..dotval])
result.labelText= string.gsub(result.labelText,"[%^]+","<br>") -- convert hats to line breaks
local item=splitItem(row,6)
result.labelPos=item[2] or 'center'
result.dx=tonumber(string.match(item[3] or '0',"[%.%-?%d]+")) or 0
result.dy=tonumber(string.match(item[4] or '0',"[%.%-?%d]+")) or 0
result.param1=string.lower(item[5] or '')
if string.find(result.param1,"hemisphere-1",1,true) then result.lon=result.lon-360
elseif string.find(result.param1,"hemisphere+1",1,true) then result.lon=result.lon+360
end
local txt = ''
if item[6] then -- ensure all info elements are included, including commas
count=1
local max=6
for t1 in string.gmatch(fillCommas(row,max),"[^,]+") do
if count>max then txt=txt..',' end
if count>=max then txt=txt..t1 end
count=count+1
end
end
result.info=txt
result.imageName = args['dotpic'..dotval]
-- Get first wikilinked item (if any) from the args.dotlink and set this plus the delinked text
local testx=args["dotlink"..dotval] or ''
result.dotLink=testx
if testx ~= '' then
testx=stripdivs(testx)
result.title=delink({ testx })
local linkstart= string.find(testx,'[[',1,true) -- use true to ensure a plain search (no pattern)
if linkstart then
result.dlink=delink( { string.sub(testx,linkstart,string.find(testx,']]',1,true)+1),wikilinks='target' } )
else result.dlink=''
end
else
result.dlink=''
result.title=''
end
if args['dotfeature'..dotval] then
local item=splitItem(args['dotfeature'..dotval],6)
if (item[1] or '') =='photo-panel' then
result.ppwidth= tonumber(string.match((item[3] or '110'),"%d+")) -- panel width
result.ppheight= tonumber(string.match((item[4] or '48'),"%d+") ) --panel height
result.photowidth=round(tonumber(string.match((item[2] or '1.3'),"[%.%-?%d]+")) * result.ppheight+1,0) -- dimension to set image size
result.photoImage=result.imageName
result.posType='photo-panel'
elseif (item[1] or '') == 'mark-line' then
local x=tonumber(dotval or '0')
result.markDest=item[5] or tostring(x-1)
result.mlWidth= tonumber(string.match((item[2]) or '',"%d+") or '1')
result.mlStyle= item[3] or 'solid'
result.mlGap=tonumber(string.match((item[4] or ''),"[%d]+") or '0')
result.posType='mark-line'
-- debugmsg('making line for '..tostring(x)..'to '..tostring(result.markDest)..' with width '..tostring(result.mlWidth))
end
--debugmsg('photo-panel, '..shapePos[2]..', 3='..shapePos[3]..', 4='..shapePos[4]..', 5='..(shapePos[5] or '(48')..'photowidth='..tostring(dotItem.photowidth))
end
maplist.lon=result.lon
maplist.lat=result.lat
result.gridx, result.gridy = maptogrid(maplist,1) -- convert geo coords to grid xy - using values from maplist table
return result
end
local function multiCheck (args, argName, argVal, defVal, alt)
if not alt then alt='nonexistant' end
if argVal=='H' and not args[argName..'H'] then argVal=highlightNum or '1' end
if argVal=='' then
return (args[argName] or args[alt] or (args[argName..'D']) or args[alt..'D'] or defVal) -- unnumbered args do not inherit from D or 1
else
return (args[argName..argVal]) or (args[alt..argVal]) or (args[argName..'D']) or (args[alt..'D']) or (args[argName..'1']) or (args[alt..'1']) or defVal
end
end
local function assignTradstyleShape(shapeResult,default,dotResult,args,nval)
local item,itemTab
local autoDotTag=''
local shapeWidth,shapeHeight=0,0
local argval=nval -- to catch the unnumbered shape series
if argval=='0' then argval='' end
if nval=='H' then shapeResult.H={} end
item=string.lower(multiCheck(args,'shape',argval,'image'))
if string.find(item,'n-',0,true)==1 or string.find(item,'l-',0,true)==1 then
autoDotTag=string.sub(item,0,1)
item=string.sub(item,3)
end
if item == 'image' then
shapeResult[nval].shape = 'image:'
shapeResult[nval].shapeFile =multiCheck(args,'mark',argval,'Red pog.svg')
shapeWidth=-1
else shapeResult[nval].shape = item
end
item= multiCheck(args,'mark-size',argval,'14px')
local a,b,c= getsize(string.gsub(string.gsub(item,',','px')..'px','pxpx','px'))
if b==a and args['mark-dim'..argval] then
b= b / tonumber(string.match(args['mark-dim'..argval],"[%.%-?%d]+"))
end
shapeHeight=b/2
item=tostring(a)..'px'..tostring(b)..'px'..tostring(c)..'px'
shapeResult[nval].shapeSize= item
itemTab=splitItem(multiCheck(args,'shape-color',argval,'hard red'),2)
shapeResult[nval].shapeColor=itemTab[1] or 'hardred'
item=itemCheck(itemTab[2],'%') -- jump through the various opacity hoops and add to color if needed
if not item then item=itemCheck(args['shape-opacity'..argval],'%') end
if item and item~='0%' and item~='100%' then shapeResult[nval].shapeColor=shapeResult[nval].shapeColor..item end
shapeResult[nval].shapeAngle=itemCheck(multiCheck(args,'shape-angle',argval,'0'),'deg') or '0deg'
--sort out the outline entry
itemTab=splitItem(multiCheck(args,'shape-outline',argval,'transparent,0,100,solid'),4)
shapeResult[nval].outlineColor=itemTab[1] or 'dark grey'
shapeResult[nval].outlineWidth=itemCheck(itemTab[2],'px') or '1px'
if itemTab[3] and itemTab[3]~='100' and itemTab[3]~='0' then
shapeResult[nval].outlineColor=shapeResult[nval].outlineColor..itemCheck(itemTab[3],'%')
end
shapeResult[nval].outlineStyle=itemTab[4] or 'solid'
-- label size, background, outline
itemTab=splitItem( multiCheck(args,'label-size',argval,'12'),3)
shapeResult[nval].textSZ=itemCheck(itemTab[1],'px') or '12px'
if itemTab[2]=='outline' then
shapeResult[nval].textBG=itemTab[3] or 'transparent'
shapeResult[nval].textOL='1px'
elseif itemTab[3]=='outline' then
shapeResult[nval].textBG=itemTab[2] or 'transparent'
shapeResult[nval].textOL='1px'
else shapeResult[nval].textOL='0px'
shapeResult[nval].textBG=itemTab[2] or 'transparent'
end
if getColor(shapeResult[nval].textBG)==CTB['hardgrey'] and shapeResult[nval].textBG~='hardgrey' then shapeResult[nval].textBG= 'transparent' end
--label color etc
itemTab=splitItem(multiCheck(args,'label-color',argval, 'darkgrey','label-colour'),2)
shapeResult[nval].textCL=itemTab[1] or 'darkgrey'
if itemTab[2] and itemTab[2]~='0%' and itemTab[2]~='100%' then shapeResult[nval].textCL=shapeResult[nval].textCL..itemTab[2] end
shapeResult[nval].textSP=itemCheck( multiCheck(args,'label-spacing',argval,'0'),'px') -- sets letter-spacing in px
shapeResult[nval].textLH=itemCheck( multiCheck(args,'label-height',argval,'120'),'%') -- sets line height, 120% default
shapeResult[nval].textNG=itemCheck(multiCheck(args,'label-angle',argval,'0'),'deg')
--sgd=dotTag attributes
shapeResult[nval].tagSize=tostring(shapeHeight*1.5)..'px'
local c1,c2=checkColors(shapeResult[nval].shapeColor)
shapeResult[nval].tagColor=c2
shapeResult[nval].tagSpacer='0px'
shapeResult[nval].tagAngle='0deg'
-- sge extension line attributes
local shapePos=splitItem(multiCheck(args,'label-pos',argval,'right'),6)
if shapePos[2]=='with-line' or shapePos[2]=='n-line' then
shapeResult[nval].textEW=(shapePos[3] or '1')..'px' -- width
shapeResult[nval].textEC=(shapePos[4] or shapeResult[nval].shapeColor or 'darkgrey')
elseif shapePos[2]=='photo-panel' then
shapeResult[nval].textEW='2px' -- width
shapeResult[nval].textEC=shapeResult[nval].textCL
else
shapeResult[nval].textEW='0px' -- width
shapeResult[nval].textEC='grey'-- colour
end
shapeResult[nval].textES='solid'
if argval=='H' then return dotResult end
--Assign dot values
local dotItem={}
dotItem.group=nval
dotItem.code=nval
dotItem.posType=shapePos[2] or 'nil'
if (shapePos[2] or '') =='photo-panel' then
dotItem.ppwidth= tonumber(string.match((shapePos[4] or '110'),"%d+"))
dotItem.ppheight= tonumber(string.match((shapePos[5] or '48'),"%d+") )
dotItem.photowidth=round(tonumber(string.match((shapePos[3] or '1.3'),"[%.%-?%d]+")) * dotItem.ppheight+1,0)
dotItem.photoImage=args['mark-image'..argval]
--debugmsg('photo-panel, '..shapePos[2]..', 3='..shapePos[3]..', 4='..shapePos[4]..', 5='..(shapePos[5] or '(48')..'photowidth='..tostring(dotItem.photowidth))
end
if (shapePos[2] or '') =='mark-line' then
local x=tonumber(argval or '1')
dotItem.markDest=shapePos[6] or tostring(x-1)
dotItem.mlWidth= tonumber(string.match((shapePos[3] or '1'),"%d+"))
dotItem.mlStyle= shapePos[4] or 'solid'
dotItem.mlGap=tonumber(string.match((shapePos[5] or '0'),"[%d]+"))
shapeResult[nval].textEC=shapeResult[nval].outlineColor or 'darkgrey'
end
if args['mark-coord'..argval] then
itemTab=splitItem(convertCoordsTrad (args['mark-coord'..argval]),2)
dotItem.lat=tonumber(string.match(itemTab[1],"[%.%-?%d]+")) or 0
dotItem.lon=tonumber(string.match(itemTab[2],"[%.%-?%d]+")) or 0
else
dotItem.lat=tonumber(string.match(args['mark-lat'..argval],"[%.%-?%d]+")) or 0
dotItem.lon=tonumber(string.match(args['mark-lon'..argval],"[%.%-?%d]+")) or 0
end
if args['dateline'..argval] and (args['dateline'..argval]=='1' or args['dateline'..argval]=='-1') then
dotItem.lon=dotItem.lon+(tonumber(args['dateline'..argval] ) *360)
end
maplist.lon=dotItem.lon
maplist.lat=dotItem.lat
dotItem.gridx, dotItem.gridy = maptogrid(maplist,1)
local item=args['mark-title'..argval] or '' -- sort out the caption, wikilink and plaintext tooltip items from dotLink
if item=='none' then dotItem.param1='nomap nolist' item='' end
dotItem.dotLink=item
if item ~= '' then
item=stripdivs(item)
dotItem.title=delink({item})
local linkstart= string.find(item,'[[',1,true) -- use true to ensure a plain search (no pattern)
if linkstart then
dotItem.dlink=delink({string.sub(item,linkstart,string.find(item,']]',1,true)+1),wikilinks='target'})
else dotItem.dlink=''
end
else
dotItem.dlink=''
dotItem.title=''
end
if autoDotTag=='n' then item=nval
elseif autoDotTag=='l' then item=string.char(64+tonumber(nval))
else item='' end
dotItem.dotTag = args['numbered'..argval] or item
if shapePos[2]=='n-line' and (args['label'..argval] or args['label'..argval]=='') then
if dotItem.dlink =='' then
item=dotItem.dotTag..' '..args['label'..argval]
else
item='[['..dotItem.dlink..'|'..dotItem.dotTag..']] '..args['label'..argval]
end
else item=(args['label'..argval] or '') end
if args['labela'..argval] then item = item..'^'..args['labela'..argval] end
if args['labelb'..argval] then item = item..'^'..args['labelb'..argval] end
local a='' for c in item:gmatch('.') do a=a..(c:gsub('%^','<br>') or c) end
dotItem.labelText = a -- convert hats to line breaks
if argval=='' then item = (args['label-offset-x']) or (args.ldx) or '0'
else item=args['label-offset-x'..argval] or args['ldx'..argval] or args['label-offset-xD'] or args.ldxD or args['label-offset-x1'] or args.ldx1 or '0'
end
dotItem.dx=tonumber(string.match(item,"[%.%-?%d]+"))
if argval=='' then item = (args['label-offset-y']) or (args.ldy) or '0'
else item=args['label-offset-y'..argval] or args['ldy'..argval] or args['label-offset-yD'] or args.ldyD or args['label-offset-y1'] or args.ldy1 or '0'
end
dotItem.dy=tonumber(string.match(item,"[%.%-?%d]+"))
dotItem.labelPos=shapePos[1]
if args['mark-image'..argval] then dotItem.imageName= args['mark-image'..argval] end
dotItem.info=args['mark-description'..argval] or ''
table.insert(dotResult,1,dotItem) -- add to start of list, so they are in reverese order for displaying
return dotResult
end
local function tradstyleParseShapes(args,dotTable,dotmax)
local sgNumbers,sgSortable={},{}
for argindex=1,dotmax do -- build a list of all the numbered coords or lat,lons that have been used
local x=tostring(argindex)
if args['mark-coord'..x] or (args['mark-lat'..x] and args['mark-lon'..x]) then
sgNumbers[x]=x -- add it to the list
end
end
for indx,sgnum in pairs(sgNumbers) do table.insert(sgSortable,sgnum) end
table.sort(sgSortable,lessthan) -- put the list in a sortable form TODO this must be possible in a single list!
if args['mark-coord'] or (args['mark-lat'] and args['mark-lon']) then table.insert(sgSortable,'0') end -- add it to the end of the list
local default={} default.D={}
local dotResult={}
for k,sgnum in pairs(sgSortable) do -- work through the sorted list, parsing each set of shapes in turn, from 1 upwards
shapeList[sgnum]={}
dotTable=assignTradstyleShape(shapeList,default,dotTable,args,sgnum)
end
dotTable=assignTradstyleShape(shapeList,default,dotTable,args,'H') -- construct an extra highlight shapeitem
return dotTable
end
local function checkfortooltip (title,dx,dy,dotlabel,dlink,nolabel) -- returns tlink if available, and dlink, if needed and tshape=true if shape needed
local tshape,tlink = false,""
if dlink~='' and not nolabel then tlink=dlink end
if (tlink=="" or nolabel) and title~="" then tshape=true end -- tshape flags True if title is wanted for shape
if (not (dx==0 or dy==0) or dotlabel=='') and title~='' then tshape=true end -- add tooltip to shape if its number has moved
return tshape,tlink
end
local function tshift(angle) -- adjustment to place text near the centre of a triangle, shifted to allow rotation of triangle shape
local x=tonumber(string.match(angle,"%-?%d+"))
if x<0 then x=360+x end -- set to a single degree direction, 0 to 360
if x>359 then return 0,0 end
-- shift the centre of the triangle based on rotation value
if x <=40 or x>=320 then return -0.17,0 -- triangle up= -shiftv
elseif x>=140 and x<=220 then return 0.17,0 --triangle down= +shiftv
elseif x >220 then return 0,-0.17 --triangle left= -shifth
elseif x >40 then return 0,0.22 --triangle right= +shifth
end
return 0,0
end
local function makeTriangle(result,row,shape,outline,tlink)
local w,h,r=getsize(shape.shapeSize)
if outline then
local p=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
w=w+p*2
h=h+p*2
end
table.insert(result,'<div ')
if tlink then
table.insert(result,' title="'..row.title..'" ')
end
table.insert(result,'style="display:inline-block; position: absolute')
if shape.shapeAngle ~= '0deg' then
table.insert(result,'; transform: rotate('..shape.shapeAngle..')')
end
local shiftv,shifth=0,0
shiftv,shifth=tshift(shape.shapeAngle)
table.insert(result,'; top: '..tostring(row.gridy-h/2+h*shiftv)..'px')
table.insert(result,'; left: '..tostring(row.gridx-w/2+w*shifth)..'px; width: 0; height: 0; outline-width: 0px')
table.insert(result,'; border-left: '..tostring(w/2)..'px solid transparent')
table.insert(result,'; border-right: '..tostring(w/2)..'px solid transparent')
if outline then -- fill with outline colour, to make a 'base layer' or shape colour
table.insert(result,'; border-bottom: '..tostring(h)..'px solid '..getColor(shape.outlineColor).. '">')
else
table.insert(result,'; border-bottom: '..tostring(h)..'px solid '..getColor(shape.shapeColor).. '">')
end
table.insert(result,'</div>')
end
local function makeSquare(result,row,shape,tshape)
local w,h,r=getsize(shape.shapeSize)
if shape.shape=='square' then h=w end -- squares are always square!. box can stretch
local div=mw.html.create ('div')
if tshape then -- Add tooltip if needed
div:attr('title',row.title)
end
div:css('position', "absolute")
if shape.outlineWidth ~= "0px" then
div:css('outline', shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
end
if shape.shapeAngle ~= "0deg" then
div:css('transform',"rotate("..shape.shapeAngle..")")
end
if r~=0 then div:css('border-radius',tostring(r).."px") end
if shape.shape=='panel' then
div:css('top', tostring(row.gridy).."px")
div:css('left', tostring(row.gridx).."px")
else
div:css('top', tostring(row.gridy-h/2).."px")
div:css('left', tostring(row.gridx-w/2).."px")
end
div:css('width', tostring(w).."px")
div:css('height', tostring(h).."px")
div:css('background-color', getColor(shape.shapeColor) )
div:css('color', 'inherit')
table.insert(result,tostring(div))
end
local function makeCircle(result,row,shape,tshape)
local w,h,r=getsize(shape.shapeSize) -- = width,height,rounding
if shape.shape=='circle' then h=w end -- circles are always round. ellipse can stretch
local div=mw.html.create ('div')
if tshape then -- Add tooltip if needed
div:attr('title',row.title)
end
div:css('position', "absolute")
if shape.outlineWidth ~= "0px" then
div:css('outline', shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
end
if shape.shapeAngle ~= "0deg" then
div:css('transform',"rotate("..shape.shapeAngle..")")
end
div
:css('top', tostring(row.gridy-h/2).."px")
:css('left', tostring(row.gridx-w/2).."px")
:css('width', tostring(w).."px")
:css('height', tostring(h).."px")
:css('border-radius', "50%")
:css('background-color', getColor(shape.shapeColor) )
:css('color', 'inherit')
table.insert(result,tostring(div))
end
local function makeRuleA(result,row,shape)
local w,h,r=getsize(shape.shapeSize) -- = width,height,rounding
local oWid=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
local lineV=0
if shape.shape=='rulea' then lineV=oWid*3+16 end
table.insert(result,"<div style=\"display:inline-block; position: absolute") -- create a square transparent container, which will rotate line and arrow together
table.insert(result,"; top:"..tostring(row.gridy - w/2).."px")
table.insert(result,"; left:"..tostring(row.gridx - w/2).."px")
table.insert(result,"; width:"..tostring(w).."px")
table.insert(result,"; height:"..tostring(w).."px; background:transparent; color:inherit;")
table.insert(result,"; transform: rotate( "..tostring(tonumber(string.match(shape.shapeAngle,"[%.%-?%d]+")) - 90).."deg);\">" )
table.insert(result,"<div style=\"display:inline-block; position: absolute") -- put the line (as a border-right) across the container
table.insert(result,"; top:"..tostring(lineV).."px")
table.insert(result,"; left:"..tostring((w - oWid )/2).."px; width: 0px")
table.insert(result,"; height: "..tostring(w -lineV).."px")
table.insert(result,"; border-right: "..shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
table.insert(result,"; background:transparent; color:inherit;\"></div>")
if shape.shape=='rulea' then
table.insert(result,"<div style=\"display:inline-block; position: absolute; top: 0px") --and add arrow head
table.insert(result,"; left:"..tostring(w/2-( oWid/2)-oWid*0.55-2).."px; width: 0; height: 0; outline-width: 0px")
table.insert(result,"; border-left: "..tostring(oWid*1.1+2).."px solid transparent")
table.insert(result,"; border-right: "..tostring(oWid*1.1+2).."px solid transparent")
table.insert(result,"; border-bottom: "..tostring(oWid*3+16).."px solid "..getColor(shape.outlineColor).."\"></div>")
end
table.insert(result,"</div>")
end
local function makeCurveA(result,row,shape) -- draw a curve with Arrow -----
local w,h=getsize(shape.shapeSize) -- = width,height
local oWid=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
local Angle=tonumber(string.match(shape.shapeAngle,"[%.%d]+"))
table.insert(result,'<div style="position: absolute;') --set up out div, which will allow the whole to rotate
table.insert(result,'top:'..tostring(row.gridy - (w + oWid*3+16)/2)..'px;')
table.insert(result,'left:'..tostring(row.gridx - ( w + oWid*3+16)/2)..'px; ')
table.insert(result,'width: '..tostring(w+oWid*3+16)..'px; ')
table.insert(result,'height: '..tostring(w+oWid*3+16)..'px; ')
if shape.shape=='curvea' then
table.insert(result,'transform: rotate( '..tostring(Angle-120)..'deg);">')
else table.insert(result,'transform: rotate( '..tostring(Angle -62)..'deg);">')
end
table.insert(result,'<div style="position: absolute;') --set up div for the rounded corner of a rectangle
table.insert(result,'border-left: '..shape.outlineWidth..' '..shape.outlineStyle..' '..getColor(shape.outlineColor)..';')
if shape.shape=='curvea' then
table.insert(result,'border-radius: 10000px 0 0 '..tostring(w)..'px; top:0px; left:'..tostring(w*0.25)..'px;')
else
table.insert(result,'border-radius: '..tostring(w)..'px 0 0 10000px;')
table.insert(result,'top:'..tostring((oWid*3+16)/2+w*0.15)..'px; left:'..tostring(w*0.25)..'px;')
end -- and add a triangular arrow head
table.insert(result,'width: '..tostring(w)..'px; height: '..tostring(w)..'px;"></div><div style="position: absolute; ')
if shape.shape=='curvea' then
table.insert(result,'transform: rotate(180deg); top:'..tostring(w-1)..'px; ')
else table.insert(result,'transform: rotate(0deg); top: '..tostring(0-( ( oWid*3+16)/2)+1+( w*0.15) )..'px;')
end -- reverse
table.insert(result,'left:'..tostring(0-( oWid*0.6)-2+(w*0.25))..'px;')
table.insert(result,'width: 0; height: 0; outline-width: 0px; border-left: '..tostring(oWid*1.1+2)..'px solid transparent;')
table.insert(result,'border-right: '..tostring(oWid*1.1+2)..'px solid transparent;')
table.insert(result,'border-bottom: '..tostring(oWid*3+16)..'px solid '..getColor(shape.outlineColor)..';"></div></div>')
end
local function makeLineTo (result,x1,y1,x2,y2,oWid, oStyle, oCol,double)
table.insert(result,"<div style=\"display:inline-block; position: absolute;")
-- draw a line between x1,y1 and x2,y2, px-coords where 0,0 is centre of frame
-- Maths calculations thanks to ES
table.insert(result,"left: "..tostring(x1+( (x2-x1)/2) - (math.sqrt( ( x2-x1)^2 + (y2-y1)^2 )/2)-1).."px;")
table.insert(result,"top: "..tostring(y1+( ( y2-y1 )/2) ).."px;")
table.insert(result,"width: "..tostring(math.sqrt( (x2-x1 )^2 + ( y2-y1 )^2) ).."px;")
table.insert(result,"height: "..tostring(double).."px; background-color:transparent; color:inherit; ")
table.insert(result,"outline-width: 0; border-bottom: "..oWid.." "..oStyle.." "..getColor(oCol)..";" )
if double>1 then table.insert(result,"border-top: "..oWid.." "..oStyle.." "..getColor(oCol)..";" ) end
if x1==x2 then table.insert(result,"transform: rotate(90deg);")
else table.insert(result,"transform: rotate("..tostring(math.atan(( y2-y1)/( x2-x1 ) )*180/math.pi).."deg);\"></div>")
end
end
local function makeClipPath(result,row,shape,outline,tshape) --tshape is a flag to show if the tooltip (title=) is wanted
-- return the text css div code to position and draw a shape occupying a specified clippath
if not pathshape[shape.shape] then
debugmsg(mw.addWarning('shape'..row.code..' = '..shape.shape..'. The shape name is not defined'))
return
end
local w,h,r=getsize(shape.shapeSize)
if string.match(shape.shape,"circle") or string.match(shape.shape,"square") then h=w end -- use ellipse and box for stretched shapes
local shifth,shiftv = 0,0
if string.match(shape.shape,"triangle") then
shiftv,shifth =tshift(shape.shapeAngle)
end
if outline then
local p=tonumber(string.match(shape.outlineWidth,"[%.%d]+")) or 0
w=w+p*2
h=h+p*2
end
table.insert(result,"<div ")
if tshape then -- Add tooltip if needed
table.insert(result," title=\""..row.title.."\" ")
end
table.insert(result,"style=\"display:inline-block; position: absolute; background-color:")
if outline then
table.insert(result,getColor(shape.outlineColor)) -- fill with outline colour, to make a 'base layer'
else
table.insert(result,getColor(shape.shapeColor))
end
table.insert(result,"; color:inherit; clip-path:path(nonzero, '"..pathshape[shape.shape].."') ")
-- adds the required clippath data from the table of pathshape string literals
table.insert(result,"; top:"..tostring(row.gridy - 10 + h*shiftv).."px")
table.insert(result,"; left:"..tostring(row.gridx - 10 + w*shifth).."px")
table.insert(result,"; width:20px") -- needs to be a path within a 20px20px box, and then rescales using size values to match other shape sizes
table.insert(result,"; height:20px; transform:scale("..tostring(w/16)..", "..tostring(h/16)..")")
if shape.shapeAngle ~= "0deg" then
table.insert(result," rotate("..shape.shapeAngle..")")
end
table.insert(result,"\"></div>")
end
local function makeImage(result,row,shape)
local w,h,r=getsize(shape.shapeSize)
local image=shape.shapeFile
if not image or image=='' then image='Red pog.svg' end
local imagediv=mw.html.create ('div')
imagediv:css('position', "absolute")
if shape.shapeAngle ~= "0deg" then
imagediv:css('transform',"rotate("..shape.shapeAngle..")")
end
imagediv
:css('top', (row.gridy-1 + math.min(h/2-12,0) - h/2).."px") --File seems to adjust pos for small images
:css('left', (row.gridx-1-w/2).."px")
:css('background-color', "transparent" )
:css('color','inherit')
:wikitext('[[file:'..image..'|'..tostring(w+2)..'px|alt='..(row.title or '')..'|link=]]')
table.insert(result,tostring(imagediv))
end
local function makePhotoPanel(result,row,shape)
local h=row.ppheight
table.insert(result, '<div style="position: absolute; top: '..tostring(row.gridy+row.dy-h/2)..'px;' )
table.insert(result, 'left: '..tostring(row.gridx+row.dx - row.ppwidth/2)..'px;')
table.insert(result, 'width: '..tostring(row.ppwidth)..'px; height: '..tostring(h)..'px; border-radius: 2px; color:inherit;')
table.insert(result, 'background-color: #E8E8D6; outline: 2px solid '..getColor(shape.textCL)..'; box-shadow: 2px 2px 4px #33203335;"></div>')
if row.photoImage and row.photowidth >0 then
table.insert(result, '<div style="position: absolute; top: '..tostring(row.gridy+row.dy-h/2)..'px;')
if row.labelPos=='left' or string.find(row.labelPos,'west') then
--debugmsg(row.labelText..', Align=right, ppwidth='..tostring(row.ppwidth)..', photowidth='..row.photowidth..', dx='..tostring(row.dx)..', dy='..tostring(row.dy))
table.insert(result, 'left: '..tostring(row.gridx+row.dx - row.ppwidth/2)..'px;')
row.dx=row.dx+row.photowidth/2
else
--debugmsg(row.labelText..', Align=left, ppwidth='..tostring(row.ppwidth)..', photowidth='..row.photowidth..', dx='..tostring(row.dx)..', dy='..tostring(row.dy))
table.insert(result, 'left: '..tostring(row.gridx+row.dx + (row.ppwidth-row.photowidth) - row.ppwidth/2)..'px;')
row.dx=row.dx-row.photowidth/2
end
table.insert(result, 'background-color:transparent; color:inherit; border-radius: 2px;">')
table.insert(result,'[[File:'..row.photoImage..'|x'..tostring(h)..'px|File:'..row.photoImage..']]</div>')
row.labelPos='center'
end
end
local function makePanelText(result, row, shape)
local w,h,r=getsize(shape.shapeSize)
local ty=tonumber(string.match(shape.textSZ or '11',"%d+") )
table.insert(result,'<div style="position:absolute; line-height: 120%; font-size: '..shape.textSZ..'; color:'..getColor(shape.textCL)..';')
if row.labelPos == 'left' or string.find(row.labelPos,'west') then
table.insert(result,'top: '..tostring(row.dy+row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+3)..'px; text-align: left; width:'..tostring(w)..'px;')
elseif row.labelPos == 'right' or string.find(row.labelPos,'east') then
table.insert(result,'top: '..tostring(row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+w-3)..'px; text-align: right; width: max-content; transform: translateX(-100%);')
elseif row.labelPos == 'top' or row.labelPos== 'north' then
table.insert(result,'top: '..tostring(row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
elseif row.labelPos == 'bottom' or row.labelPos=='south' then
local bry=(select(2, string.gsub(row.labelText,"<br>", ""))+1)*1.1
table.insert(result,'top: '..tostring(row.gridy+w-(ty*bry))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
else -- center or centre
local bry=(select(2, string.gsub(row.labelText,"<br>", ""))+1)*0.6
table.insert(result,'top: '..tostring(row.gridy+(h/2)-(ty*bry))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
end
if shape.textSP and shape.textSP ~='0px' then table.insert(result,"letter-spacing: "..shape.textSP..';') end
if shape.textLH and shape.textLH ~='120%' then table.insert(result,"line-height: "..shape.textLH..';') end
table.insert(result,"vertical-align: middle;\">"..row.labelText.."</div>")
end
local function makeTextItem(result, row, shape, align, tlink, textItem, dotItem)
local w,h,r=getsize(shape.shapeSize)
table.insert(result,"<div ")
if row.title ~= "" then table.insert(result," title=\""..row.title.."\" ") end
local ty,bry,linkoffset = 0,0,0
local compy=0
local lh=tonumber(string.match(shape.textLH or '120',"%d+"))/100
if dotItem==1 or (dotItem==2 and row.posType~='n-line') then -- if there is a dotTag in the middle of the shape then use the tag settings
ty=tonumber(string.match(shape.tagSize,"%d+")) or 0
table.insert(result,"style=\"position:absolute; line-height: 120%; top: "..tostring(row.gridy-ty*0.62))
table.insert(result,"px; left: "..tostring(row.gridx).."px; width: fit-content; ")
table.insert(result,"text-align: center; color: "..getColor(shape.tagColor).."; background-color: transparent;")
local trf=""
if shape.tagAngle ~="0deg" then trf=" rotate("..shape.tagAngle..")" end
table.insert(result, "transform: translateX(-50%)"..trf.."; font-size: "..shape.tagSize..";")
if shape.tagSpacer~='0px' then table.insert(result, "letter-spacing:"..shape.tagSpacer..";") end
else -- or add tfx settings for left, right or center align, colors, backgrounds, border-outline
table.insert(result,'style="position:absolute; ')
if dotItem==2 then -- dotTag is out at x,y so 85%
ty=tonumber(string.match(shape.tagSize or '0',"%d+"))
table.insert(result,'font-size: '..shape.tagSize..'; padding:0px 2px;line-height: 85%; top: '..tostring(row.dy+row.gridy-ty*0.52))
else -- it is labelText, so use textLH or 120%
ty=tonumber(string.match(shape.textSZ or '11',"%d+"))
if row.labelPos=='northwest' or row.labelPos=='northeast' then compy=-ty
elseif row.labelPos=='southeast' or row.labelPos=='southwest' then compy=ty/2 end
if row.labelPos and not (row.labelPos== 'auto' or row.labelPos=='') then
bry=(select(2, string.gsub(row.labelText,"<br>", ""))*lh) -- is it a multiline text? expand by line-height /120%?
if row.posType == 'with-line' then bry=0 end
if row.labelPos=='bottom' or row.labelPos == 'south' then bry = 0 -- and shift by none, all or half
elseif row.labelPos=='top' or row.labelPos == 'north' then
if shape.shape=='image:' then bry= 1 + math.min(w/2-10,0)-bry*ty
else bry= -bry*ty+2
end
else bry=-bry*(ty/2 * lh)
end
end
if row.posType == 'photo-panel' then bry=bry+3 end
table.insert(result,'font-size: '..shape.textSZ..'; padding:0px 3px;line-height: '..shape.textLH..'; ')
table.insert(result,'top: '..tostring(row.dy+row.gridy+compy+bry-ty*lh/2))
end
table.insert(result,"px; left: "..tostring(row.dx+row.gridx).."px; color: "..getColor(shape.textCL).."; ")
table.insert(result,"width: max-content; ")
local trf=""
if shape.textNG ~="0deg" then trf="rotate("..shape.textNG..")" end
if shape.textOL~="0px" then
table.insert(result,"background-color: "..getColor(shape.textBG).."; ")
table.insert(result,"border: "..shape.textOL.." solid "..getColor(shape.textCL).."; border-radius:6px;")
else table.insert(result,"background-color: transparent;")
end
if row.labelPos=="right" or string.find(row.labelPos,'east') then
table.insert(result,"text-align: left; ")
linkoffset=w
if shape.textNG ~="0px" then table.insert(result,"transform-origin: left; transform: rotate("..shape.textNG.."); ") end
elseif row.labelPos=="left" or string.find(row.labelPos,'west') then
if shape.textNG ~="0px" then table.insert(result,"transform-origin: right;") end
linkoffset=-w
table.insert(result,"text-align: right; transform: translateX(-100%) "..trf.."; ")
else
table.insert(result,"text-align: center; transform: translateX(-50%) "..trf.."; ")
end
table.insert(result,'font-weight: normal; line-height: '..shape.textLH..'; letter-spacing:'..shape.textSP..'; vertical-align: bottom;')
end
if string.find(shape.textAT or '','bold') then textItem='<b>'..textItem..'</b>' end
if string.find(shape.textAT or '','italic') then textItem='<i>'..textItem..'</i>' end
if shape.textOL=='0px' and shape.textBG~='transparent' and dotItem==0 then
table.insert(result,'\"><span style=\"background-color: '..getColor(shape.textBG)..'; color:inherit;\">'..textItem..'</span></div>')
else
table.insert(result,"\">"..textItem.."</div>")
end
if tlink~='' and not string.match(row.param1 or "","nolink") then
table.insert(result,makeLinkBox(row.gridx+row.dx+linkoffset, row.gridy+row.dy+bry, 16, row.dotTag..' '..row.title,tlink))
end
end
local function getshapetable(row,shape) -- Construct CSS divs for a dot from shape and map data
local result={}
local w,h,r=getsize(shape.shapeSize)
local tshape,tlink=checkfortooltip(row.title,row.dx,row.dy,row.dotTag,row.dlink,string.match(row.param1 or "","nolink") )
local align=row.labelPos or ''
local offsetx,offsety=0,0
local ty=tonumber(string.match((shape.tagSize or 9),"%d+" ))
if row.labelText and row.labelText~='' then ty=tonumber(string.match((shape.textSZ or 11),"%d+" ))
else align='center' end -- it is just for dotTag, so justify center
-- identify align value and extend offsets
local widthzone,heightzone = w/2+4,h/2+4
local theta,r = math.deg( math.atan2(row.dy, row.dx)) , math.sqrt(row.dx^2 + row.dy^2)
if align=='auto' or align=='' then
if (theta < -112 or theta > 112) and math.abs(row.dx)>=w/2 and math.abs(row.dy)<w/1.4 then align = "left" offsetx=1
elseif (theta > -68 and theta < 68) and math.abs(row.dx)>=w/2 and math.abs(row.dy)<w/1.4 then align = "right" offsetx=-1
elseif theta <0 then offsety=ty/2-1 -- bottom
else offsety=0-ty/2+1 -- top
end
elseif align=='left' or string.find(align,'west') then
row.dx=row.dx - w/2 widthzone=4 offsetx=-1
elseif align=='right' or string.find(align,'east') then
row.dx=row.dx + w/2 widthzone=4 offsetx=1 offsety=-ty/2 +1
elseif align=='top' or align=='north' then
if string.find(shape.shape,'curve') then
row.dy=row.dy-(h/2)-12 heightzone=4 offsety=ty/2-1
else
row.dy=row.dy - h-2 heightzone=4 offsety=ty/2-1
end
elseif align=='bottom' or align=='south' then
if string.find(shape.shape,'curve') then
row.dy=row.dy+(h/2) heightzone=4 offsety=ty/2-1
else
row.dy=row.dy + h+2 heightzone=4 offsety=0-ty/2-1
end
end
if r > widthzone and shape.textEW ~= "0px" and not string.match(row.param1 or "","noline") then
makeLineTo(result, row.gridx+1, row.gridy-1, row.gridx+row.dx+offsetx, row.gridy+row.dy+offsety, shape.textEW, shape.textES,shape.textEC,1)
end
if row.posType and row.posType=='mark-line' then
if row.gridx2 and row.gridy2 then
makeLineTo(result, row.gridx, row.gridy, row.gridx2, row.gridy2, tostring(row.mlWidth)..'px', row.mlStyle, getColor(shape.textEC),row.mlGap)
-- debugmsg('makeLineTo line drawn from '..row.code..' with width '..tostring(row.mlWidth)..'px '..row.mlStyle..' and color '..getColor(shape.textEC))
end
end
if w ~= 0 then
if shape.shape=='itriangle' then shape.shape='triangle' shape.shapeSize=tostring(w)..'px'..tostring(w/2)..'px' end
if shape.shape=="triangle" then
if shape.outlineWidth ~= "0px" then
makeTriangle(result,row,shape,true,false) -- larger triangle to give the outline, if required
end
makeTriangle(result,row,shape,false,tshape) -- smaller triangle to fit over the top
elseif shape.shape=="square" or shape.shape=="box" or shape.shape=='panel' then
makeSquare(result,row,shape,tshape)
elseif shape.shape=="circle" or shape.shape=="ellipse" then
makeCircle(result,row,shape,tshape)
elseif string.find(shape.shape,'image:')==1 then
makeImage(result,row,shape)
elseif shape.shape=="rulea" or shape.shape=='rule' then
makeRuleA(result,row,shape)
elseif shape.shape=="curvea" or shape.shape=="curvec" then
makeCurveA(result,row,shape)
else -- use a pathshape clipPath
if shape.outlineWidth ~= "0px" then
makeClipPath(result,row,shape,true,false) -- larger path-shape to give the outline, if required
end
makeClipPath(result,row,shape,false,tshape)
end
end
if row.ppwidth and row.ppwidth>0 then makePhotoPanel(result,row,shape) end
if shape.shape=='panel' and row.labelText then makePanelText(result, row, shape)
else
if row.dotTag and row.dotTag ~= "" then -- there is a dotTag
if (row.dx==0 and row.dy==0) and w>0 then -- it is on the dot so if dotsize is not 0 any label is ignored
makeTextItem(result, row, shape, align, tlink, '<b>'..row.dotTag..'</b>', 1)
else
if row.labelText and row.labelText~='' then -- tag and label both used
makeTextItem(result, row, shape, align, '', '<b>'..row.dotTag..'</b>', 1)
makeTextItem(result, row, shape, align, tlink, row.labelText, 0)
else
makeTextItem(result, row, shape, align, '', '<b>'..row.dotTag..'</b>', 1)
makeTextItem(result, row, shape, align, tlink, row.dotTag, 2) -- tag is ouside the dot
end
end
else
if (row.labelText and row.labelText~='') then -- just the label. No tag
makeTextItem(result, row, shape, align, tlink, row.labelText, 0)
end
end
end
if tlink and tlink~='' then
table.insert(result,makeLinkBox(row.gridx, row.gridy,w+3,row.dotTag..' '..row.title,tlink))
end
return table.concat(result)
end
local function getmapframecontent(args,use)
local result, comma={},''
if use=='basemap' then table.insert(result,'[') end
if args['map-data-inverse'] then
table.insert(result,'{ "type": "ExternalData", "service": "geomask", "ids": "'..args['map-data-inverse']..'", "properties": {')
table.insert(result,' "title": "Wikidata: '..args['map-data-inverse']..'", "fill": "#555555", "fill-opacity": 0.1, "stroke": "#555555", "stroke-width": 1, "stroke-opacity": 0.5 } }')
comma=', '
end
if args['map-data-heavy'] then
table.insert(result,comma..'{ "type": "ExternalData", "service": "geoline", "ids": "'..args['map-data-heavy']..'", "properties": {' )
table.insert(result,'"stroke": "#000000", "stroke-width": 9, "stroke-opacity": 0.1 } }')
comma=', '
end
if args['map-data-light'] then
table.insert(result,comma..' { "type": "ExternalData", "service": "geoline", "ids": "'..args['map-data-light']..'", "properties": {' )
table.insert(result,'"stroke": "#000000", "stroke-width": 3, "stroke-opacity": 0.1 } }' )
comma=', '
end
if args['map-data'] then
table.insert(result,comma..'{ "type": "ExternalData", "service": "geoline", "ids": "'..args['map-data']..'", "properties": {' )
table.insert(result,'"title": "'..(args['map-data-text'] or '')..'", "stroke": "#000000", "stroke-width": 6, "stroke-opacity": 0.1 } }' )
end
if use=='basemap' then table.insert(result,']') end
return table.concat(result)
end
--eg | minilocator=filename,bottom right,132px153px, 38%,60%, 22px
local function makeLocatorMap (args, result)
local miniFile,pos,itemlist,miniW,miniH, miniX,miniY,miniBox, miniBH
if args['mini-locator'] then
pos,miniFile=extractItem(args['mini-locator']) -- first item is filename, in quotes if it includes commas
itemlist=splitItem(pos,5) -- put items in a table filename removed, position,WpxHpx, x%y%,box
pos=itemlist[2] or 'right'
miniW,miniH=getsize(itemlist[3])
miniX=tonumber(string.match(itemlist[4] or '0','[%d]+'))*miniW/100
miniY=tonumber(string.match(itemlist[5] or '0','[%d]+'))*miniH/100
miniBox=tonumber(string.match(itemlist[6] or '0','[%d]+'))
miniBox=miniBox*miniW/100
miniBH=miniBox * maplist.height/maplist.width
elseif args['mini-file'] then
miniFile = args['mini-file']
pos=string.lower(args.minimap or 'right')
if pos=='file' then pos='right' end
miniW,miniH = tonumber(args['mini-width'] or 60), tonumber(args['mini-height'] or 60) -- find top left corner of locator
miniBox,miniBH=tonumber(args['minimap-boxwidth'] or '0'),0 -- firm up pos offsets for dot (with % or not) and boxsize if any,
miniX, miniY=args['minipog-gx'],args['minipog-gy']
if not miniX then miniX=tonumber(args['minipog-x'] or '0') else miniX=tonumber(miniX)*tonumber(miniW)/100 end
if not miniY then miniY=tonumber(args['minipog-y'] or '0') else miniY=tonumber(miniY)*tonumber(miniH)/100 end
if args['minipog-gx'] then miniBox=miniBox*miniW/100 end
miniBH=miniBox * maplist.height/maplist.width
else return end
local miniTop,miniLeft=0,1
if not string.find(pos,'top') then --only use top left, as link box is in top right or put bottom left or right
miniTop=maplist.height+2-miniH
if string.find(pos,'right') then
miniTop=miniTop-15 -- to avoid (c) line
miniLeft=maplist.width-1-miniW
end
end
table.insert(result,'<div style="position: absolute; outline-width: 1px; outline-style: solid; outline-color: white;')
table.insert(result,'top: '..tostring(miniTop)..'px; left:'..tostring(miniLeft)..'px; width:'..tostring(miniW)..'px;' )
table.insert(result,'background-color:transparent; color:inherit;">[[File:'..miniFile..'|'..tostring(miniW)..'px|File:'..miniFile..']]</div>')
if miniX and miniX>0 then
if args['minipog-y'] then miniY=miniY/1.04 end
if args['minipog-x'] then miniX=miniX/1.04 end
if miniBox<1 then
table.insert(result,'<div style="position: absolute; top: '..tostring(miniY+miniTop-3-6)..'px; left:'..tostring(miniX+miniLeft-3)..'px;')
table.insert(result, 'width: 6px; background-color:transparent; color:inherit;">[[File:Red pog.svg|6px|link=]]</div>')
else
table.insert(result,'<div style="position: absolute; top: '..tostring(miniY+miniTop-miniBH/2)..'px; left:'..tostring(miniX+miniLeft-miniBox/2)..'px;')
table.insert(result, 'width: '..tostring(miniBox)..'px; height:'..tostring(miniBH)..'px; outline:1px solid #AA1205; background-color:#AA120522; color:inherit;"></div>')
end
end
end
local function makeArcText(args,result,nval)
local items, itemlist='',{}
local arcText=''
if args['arc'..nval] then
items=convertCoords (args['arc'..nval])
items,arcText=extractItem(items) -- first item is text, in quotes if it includes commas
itemlist=splitItem(items,9) -- put items in a table: text, lat,lon,size,color,angle,gap,radius,ellipse
if itemlist[4]=='' then itemlist[4]='12' end
itemlist[4]=string.match((itemlist[4] or '12'),"[%.%-?%d]+")
if itemlist[5]=='' then itemlist[5]='grey' end
itemlist[6]=string.match((itemlist[6] or '0'),"[%.%-?%d]+")
itemlist[7]=string.match((itemlist[7] or '0'),"[%.%-?%d]+")
itemlist[8]=string.match((itemlist[8] or '0'),"[%.%-?%d]+")
end
if args['arc-coord'..nval] then
local itemTab=splitItem(convertCoordsTrad (args['arc-coord'..nval]),2)
maplist.lat=tonumber(string.match(itemTab[1],"[%.%-?%d]+"))
maplist.lon=tonumber(string.match(itemTab[2],"[%.%-?%d]+"))
else
maplist.lat=tonumber(string.match(args['arc-lat'..nval] or itemlist[2] or '0',"[%.%-?%d]+"))
maplist.lon=tonumber(string.match(args['arc-lon'..nval] or itemlist[3] or '0',"[%.%-?%d]+"))
end
local arcX,arcY=maptogrid(maplist,6)
arcText = args['arc-text'..nval] or arcText
local fontSize =tonumber(args['arc-text-size'..nval] or itemlist[4] or '12')
local textColor=getColor(string.gsub(args['arc-text-color'..nval] or itemlist[5] or 'grey','[%s]+','') )
local arcAngle= tonumber((args['arc-angle'..nval]) or (itemlist[6]) or '45')-90
local arcRadius =tonumber(args['arc-radius'..nval] or itemlist[8] or '0.05')
local arcGap = tonumber(args['arc-gap'..nval] or itemlist[7] or '1')* ( ( math.sin(8-math.rad(arcRadius))^8 )+0.4 )*( ( fontSize+6 )/15 )
arcRadius=450*arcRadius*0.75
local ellipseFactor=tonumber(args['ellipse-factor'..nval] or itemlist[9] or '1')
local arcRotate =arcAngle+90
if arcGap<0 then arcRotate=arcAngle-90 end
local latF=arcY - fontSize + (0-(math.sin(math.rad(arcAngle))) * arcRadius)
local lonF=arcX - fontSize +(0-(math.cos(math.rad(arcAngle))) * arcRadius)
local step=1
for codepoint in mw.ustring.gcodepoint( arcText ) do -- block step=1,#arcText do
table.insert(result,'<div style="position: absolute;')
local posY=tostring(round( (latF + (math.sin(math.rad(arcAngle+((step-1)*arcGap))) * arcRadius)) *ellipseFactor,2))..'px;'
local posX=tostring(round( (lonF + (math.cos(math.rad(arcAngle+((step-1)*arcGap))) * arcRadius))/ellipseFactor,2))..'px;'
table.insert(result,' top: '..posY..' left: '..posX..' transform: rotate( '..tostring(round(arcRotate +((step-1)*arcGap)),2)..'deg);')
table.insert(result,'width:'..tostring(fontSize*2)..'px; text-align: center; background-color:transparent; color: '..textColor..';')
table.insert(result,'vertical-align: baseline; font-size: '..tostring(fontSize)..'px;">'..mw.ustring.char(codepoint)..'</div>')
step=step+1
end
end
local function makeFullscreenItem (itemtitle,itemdescription,lat,lon,group,itemcolor)
local item={}
itemdescription=stripdivs(itemdescription or '')
local templon=lon
if lon > 180 then templon=lon-360 end --for hemisphere+ or -1 dots
if lon < -180 then templon=lon+360 end -- use 'real' coordinates for geohack label, while retaining shifted coords for plot
if itemcolor=='transparent' then itemcolor='white' end
itemcolor=getColor(itemcolor) -- ensure no opacity, which breaks maplink
if string.find(itemcolor,'#')==1 and #itemcolor>7 then itemcolor=string.sub(itemcolor,1,7) end
table.insert(item, '{ "type": "Feature", "properties": {')
table.insert(item, ' "title": "'..itemtitle..'",')
table.insert(item, ' "description": "'..itemdescription)
table.insert(item, ' ([https://geohack.toolforge.org/geohack.php?params='..tostring(lat)..';'..tostring(templon))
table.insert(item, '_dim:2000 '..tostring(lat)..','..tostring(templon)..'])",')
table.insert(item, ' "marker-symbol": "-number-'..string.gsub(group,'%W','')..'", "marker-size": "medium", "marker-color": "'..itemcolor..'" },')
table.insert(item, ' "geometry": {"type": "Point", "coordinates": ['..tostring(lon)..','..tostring(lat)..'] } }')
return table.concat(item)
end
local function makeLegendBox(result,args)
local legend ={}
local line,count, maxWidth='',1,8
local item
line,item=extractItem(args.legendBox or '')
local a='' for c in item:gmatch('.') do a=a..(c:gsub('%^','<br>') or c) end
legend.Text = a -- convert hats to line breaks
line=splitItem(line,6) -- (text, size, poition,background color, text/outline color, param options)
legend.Size=line[2] or '150px80px1px'
legend.Pos=line[3] or '10px10px'
legend.Background=line[4] or 'beigeground'
legend.Color=line[5] or 'darkbrown'
legend.Param= line[6] or ''
local argnum,legendCount,titleHeight='1',1,0
if (legend.Text and legend.Text~='') then
titleHeight=15+(13.4*(select(2, string.gsub(legend.Text,"<br>", ""))))
end
local legendLine,legendGroup,legendY={},{},{}
while args['legendItem'..argnum] do -- assign legendLine, legendGroup, legendY for each dot
line,legendLine[legendCount] = extractItem(args['legendItem'..argnum] or '')
line=splitItem(line,3)
legendGroup[legendCount]=line[2] or argnum
if shapeList[legendGroup[legendCount]] then
maxWidth=math.max(tonumber(string.match(shapeList[legendGroup[legendCount]].shapeSize or '10','[%d]+')),maxWidth)
else maxWidth=math.max(tonumber(string.match(shapeList['1'].shapeSize or '10','[%d]+')),maxWidth)
end
maxWidth=maxWidth+1
if line[3] then
legendY[legendCount]=tonumber(string.match(line[3],'[%d]+'))
else legendY[legendCount] = 3+maxWidth*(legendCount-1)+titleHeight
-- if (legend.Text and legend.Text~='') then legendY[legendCount]=legendY[legendCount]+15 end
end
legendCount=legendCount+1
argnum=tostring(legendCount)
end
local w,h,r=getsize(legend.Size or '')
local x,y=getsize(legend.Pos or '')
local div=mw.html.create ('div')
div:css('position', 'absolute')
div:css('outline', '1px solid'..getColor(legend.Color))
if r~=0 then div:css('border-radius',tostring(r).."px") end
div
:css('top', y.."px")
:css('left', x.."px")
:css('width', w.."px")
:css('height', h.."px")
:css('line-height','105%')
:css('background-color', getColor(legend.Background) )
:css('color','inherit')
if not string.find(legend.Param,'noshadow') then div:css('box-shadow', '2px 2px 4px #33203335') end
div:tag( 'div' )
:css('position', 'absolute')
:css('top','1px')
:css('left', (w/2).."px")
:css('width',(w-8)..'px')
:css('text-align', 'center')
:css('color', getColor(legend.Color))
:css('transform', 'translateX(-50%)')
:css('font-size','11px')
:wikitext(legend.Text)
:done()
for lct=1,legendCount-1 do
--local t=legendGroup[lct]
local shape=shapeList[legendGroup[lct]] or shapeList['1']
local row={}
row.gridx=3+maxWidth/2
row.gridy=(legendY[lct] or 0) + 5
if shape.shape=='image:' then row.gridy=row.gridy+6 end
row.dx=0 row.dy=0
local legendShape= getshapetable(row,shape)
div:wikitext(legendShape)
:tag( 'div' )
:css('position', 'absolute')
:css('top',(legendY[lct] or 0)..'px')
:css('left', (maxWidth+6)..'px')
:css('width', (w-maxWidth-6)..'px')
:css('text-align', 'left')
:css('line-height','103%')
:css('color', getColor(legend.Color))
:css('font-size','10px')
:wikitext(legendLine[lct])
:done()
end
div:allDone()
table.insert(result,tostring(div))
end
function p._main ( args )
local result={}
local frame=mw.getCurrentFrame()
local dotTable={}
local magVal,scaleVal = args.magnify,''
local origH,origW=maplist.height,maplist.width
if magVal then --set up the values needed to magnify the top-right portion of the map
magVal=tonumber(string.match(magVal or '0',"[%.%-?%d]+")) or 1
if magVal>1 and magVal <=2 then
maplist.height= round(maplist.height/magVal,0)
maplist.width=round(maplist.width/magVal,0)
scaleVal='transform: scale('..magVal..') translateY('..tostring((origH-maplist.height)/2)..'px);'
else magVal=1
end
--debugmsg('magVal='..magVal..', Magnify: H='..maplist.height..', ('..origH..'), W='..maplist.width..', ('..origW..'), scale='..scaleVal)
--debugmsg('top: '..tostring(((origW-maplist.width))/2)..'px;right: '..tostring(0-((origW-maplist.width))/2)..'px; (alt top: '..tostring(((origH-maplist.height))/2)..'px;)')
end
-- set up the three nested div boxes (plus an extra if centered) to put the map plus title/caption area, in an appropriate frame on the page
if args.float=='center' or args.float=='centre' then table.insert(result,'<div class="center"><div class="thumb tnone">')
elseif args.float=='left' then table.insert(result,'<div class="thumb tleft">')
else table.insert(result,'<div class="thumb tright">')
end
table.insert(result,'<div class="thumbinner" style="position: relative; top: 0px; right: 0px; width: '..(args.width or "400")..'px;">')
if args.title then table.insert(result,'<div class="center" style="font-weight:bold">'..args.title..'</div>') end
if magVal and magVal >1 and magVal<=2 then
table.insert(result,'<div class="thumbinner noresize" style="display:block; position: relative; outline:0px; border:0px; padding:0px; background-color:transparent; color:inherit;')
table.insert(result,'top: 50%; right: '..tostring(0-((origW-maplist.width))/2)..'px; ')
table.insert(result,'height: '..origH..'px; width: '..maplist.width..'px; '..scaleVal..'">')
else
magVal=1
table.insert(result,'<div class="thumbinner noresize" style="position: relative; outline:0px; border:0px; padding:0px;')
table.insert(result,'top: 0px; right: 0px; ')
table.insert(result,'height: '..maplist.height..'px; width: '..maplist.width..'px ">')
end
-- Create the basemap using mapframe
local mapframecontent=getmapframecontent(args,'basemap')
table.insert(result, frame:extensionTag{ name ='mapframe', content=mapframecontent, args={width=tostring(maplist.width), height=tostring(maplist.height),
zoom=tostring(maplist.zoom), longitude=tostring(maplist.lonbase), latitude=tostring(maplist.latbase), mapstyle=maplist.mapstyle, frameless=true } } )
--Add coverall box to block the unhelpful links from mapframe - which wouldn't include all the dots. Reinstate some links to osm and wikimedia
table.insert(result,'<div style="position: absolute;width:'..tostring(maplist.width)..'px; height:'..tostring(maplist.height)..'px;')
table.insert(result,'top:0px;left:0px;background-color:#FFFFFF00; color:inherit;"></div>')
--Add replacent hover-links for OpenStreetMap and maps terms and conditions
table.insert(result,'<div style="position: absolute; top: '..tostring(maplist.height-18)..'px; left: '..tostring(maplist.width-13)..'px; width: 12px; height: 12px">')
table.insert(result,'[[file:Transparent.svg|12px|link=https://www.openstreetmap.org/copyright|'..aboutOSM..']]</div>')
table.insert(result,'<div style="position: absolute; top: '..tostring(maplist.height-18)..'px; left: '..tostring(maplist.width-110)..'px;')
table.insert(result,'width: 12px; height: 12px background-color: transparent; color:inherit;">')
table.insert(result,'[[file:Transparent.svg|12px| link=https://foundation.wikimedia.org/wiki/Policy:Maps_Terms_of_Use|'..termsOfUse..']]</div>')
-- Add scale-line
if not args.scalemark or args.scalemark~='0' then
local top=maplist.height-42
local left=maplist.width-61-(tonumber(args.scalemark or '1'))
local minipos=string.lower(args.minimap or '') -- scalemark gets pushed left if it would be behind the minimap
if minipos~='' and not(string.find(minipos,'left') or string.find(minipos,'top') ) then
local offset=tonumber(args.scalemark or '1')-tonumber(args['mini-width'] or '60')
if offset<1 then left=maplist.width-61-tonumber(args['mini-width'] or '60') end
end
if maplist.width-left >216 then top=top+14 end -- shunt scaleline down if it is beyond the copyright stuff
local scalek,scalem=getScale(maplist.zoom, maplist.latbase, magVal)
local magReduce=''
table.insert(result,"<div style=\"display:inline-block; position: absolute; background-color: #111111")
table.insert(result,"; color:inherit; clip-path:path(nonzero, 'M0,8 l0,4 l20,0 l0,-4 l-0.3,0 l0,3.7 l-19.4,0 l0,-3.7 z') ")
table.insert(result,"; width:20px") -- path is a 20px20px box, and then rescales
if magVal==1 then
table.insert(result,"; top:"..tostring(top-1).."px")
table.insert(result,"; left:"..tostring(left+16).."px")
table.insert(result,"; height:20px; transform:scale("..tostring(2.5)..", "..tostring(1.5)..")")
else -- shrink the scalemark to compensate for magnification
table.insert(result,"; top:"..tostring(top-(magVal*0.28)).."px")
table.insert(result,"; left:"..tostring(left+(16*(magVal*1.15))).."px")
table.insert(result,"; height:20px; transform:scale("..tostring(2.5*(1/magVal))..", "..tostring(1.5*(1/magVal))..")")
magReduce= 'scale('..tostring(1/magVal)..')'
end
table.insert(result,"\"></div>")
table.insert(result,'<div style="position: absolute; top: '..tostring(top)..'px; left: '..tostring(left+47)..'px; font-size: 9.5px; line-height: 126%; width: fit-content;')
table.insert(result,'color: #444433; background-color: transparent; text-align: right; transform: '..magReduce..' translateX(-100%);">'..scalek..'<br>'..scalem..'</div>')
end
--Set up the shapeList and dotList tables, to provide data to go on the map
local sgNumbers,sgSortable={},{} --s1,s2
sgNumbers["1"]="1"
if args.useFormatStyle and args.useFormatStyle=='shortstyle' then
shapeList=ParseShapeTypes (shapeList,args,"1")
for argindex,argv in pairs(args) do -- build a list of all the numbered sg's that have been used
if string.find(argindex,"sg[a-f,n%d]+") == 1 then -- only look through the sga,sgb, sgc,sgd,sge,sgf and sgn args
local x=string.match(argindex,"[%d]+") -- find its number
if x and not sgNumbers[x] then sgNumbers[x]=x end -- only add if not already found
end
if string.find(argindex,"sg[a-f]H") == 1 and highlightNum then highlightOption=true end
end
for indx,sgnum in pairs(sgNumbers) do table.insert(sgSortable,sgnum) end
table.sort(sgSortable,lessthan) -- put the list in a sortable form
for k,v in pairs(sgSortable) do -- work through the sorted list, parsing each set of sg's in turn, from 1 upwards
shapeList=ParseShapeTypes (shapeList,args,v)
end
if highlightOption==true then shapeList=ParseShapeTypes (shapeList,args,'H') end
end
local dotList,dotresult,dotItemTable,dotGroupList={},{},{},{}
if (not args.useFormatStyle) or args.useFormatStyle=='standardstyle' then
local dotmax=0
for indx in pairs(args) do
if string.match(indx,'mark%-coord[%d]+') or string.match(indx,'lat[%d]+') then
dotmax=math.max(dotmax, tonumber(string.match(indx,"[%d]+")))
end
if (indx=='shapeH') or (indx=='shape-colorH') or (indx=='shape-outlineH') or (indx=='label-colorH') then highlightOption=true end
end
dotItemTable=tradstyleParseShapes(args,dotItemTable,dotmax)
for argindex=1,dotmax do -- build a list of all the numbered coords or lat,lons that have been used
local x=tostring(argindex)
if args['mark-coord'..x] or (args['mark-lat'..x] and args['mark-lon'..x]) then
sgNumbers[x]=x -- add it to the list
end
end
else
for indx in pairs(args) do
if string.match(indx,'dot[%d]+') then table.insert(dotList,indx) end --add the index name for dot1, dot2 etc to dotList
end
table.sort(dotList,morethan)
for indx,dotName in pairs(dotList) do
dotresult=ParseData(args,string.match(dotName,'[%d]+') ) -- using each dot number, assign the settings for each dot to a dotresult item line
table.insert(dotItemTable,dotresult) -- and store that item line within the dotItemTable
end
end
for arcVal = 65,91 do -- check through args looking for any arcs
local arcLetter=string.char(arcVal)
if args['arc-text'..arcLetter] or args['arc'..arcLetter] then
makeArcText(args,result,arcLetter)
end
end
local dotdivs=''
local ddots=0
if dotItemTable[1] then
local ddots=(dotItemTable[1].lat or 0)+(dotItemTable[1].lon or 0)
end
local fgroup='F'..tostring(maplist.latbase+maplist.lonbase+ddots )
local FullscreenList={}
local addcomma=''
for i,dotitem in pairs(dotItemTable) do -- working throug each dot item, merge the dot and shape values into a full set of css text
local dotgroup= dotitem.group or "0"
if dotitem.posType=='mark-line' and dotitem.markDest then --find destination xy values for any mark-lines
for n,v in pairs(dotItemTable) do
if v.code == dotitem.markDest then dotitem.gridx2=v.gridx dotitem.gridy2=v.gridy break end
end
end
local qtype=dotitem.group -- find which shape group each dot has been assigned
--debugmsg('dotgroup='..qtype..', sg='..(sgNumbers[qtype] or 'nil')..' , shapeList='..shapeList[qtype].shape)
if not sgNumbers[qtype] then qtype="0" end --shapeList[dotitem.group] will give access to the shape values for that dot
if highlightNum==dotitem.code and highlightOption==true and shapeList['H'] then
table.insert(result, getshapetable(dotitem,shapeList['H']))
else
table.insert(result, getshapetable(dotitem,shapeList[qtype])) -- Add the actual css instructions for each dot
end
if shapeList[dotgroup] and not string.find((dotitem.param1 or ''),'nomap') then -- only add if not excluded with 'nomap' labelText
local ftext=''
if dotitem.dotTag~='' and not string.match(dotitem.labelText or '','[%d]') then ftext=stripdivs(dotitem.dotTag or '')..' <br>' end
if (dotitem.labelText ~= ftext) and dotitem.dotLink =='' then ftext=ftext..' '..stripdivs(dotitem.labelText)..'<br>' end
if (dotitem.dotLink) and (dotitem.dotLink ~='') then ftext=ftext..dotitem.dotLink..'<br>' end
if dotitem.imageName then ftext=ftext..'[[File:'..dotitem.imageName..'|250px]]' end
table.insert(FullscreenList,1, makeFullscreenItem (string.gsub(ftext,"[\n]+"," "), dotitem.info,dotitem.lat,dotitem.lon,fgroup,shapeList[dotgroup].shapeColor)..addcomma )
addcomma=', '
end
-- makeFullscreenItem (itemtitle,itemdescription,lat,lon,group,itemcolor)
-- Always add to start of list, to reverse the sequence, and separate with commas except for first item, which is now at the end
end
if args.legendBox then makeLegendBox(result,args) end
if args.minimap or args['mini-locator'] then makeLocatorMap(args,result) end
-- add tag link and details for fullscreen version
addcomma=''
if (mapframecontent or '[]') ~= '[]' then addcomma=',' end
mapframecontent=getmapframecontent(args,'fullscreen')
local contentstart='[ '..mapframecontent..addcomma..'{ "type": "FeatureCollection", "features": [ ' --extra features after first square bracket
local contentend=' ] } ]'
table.insert(result, '<div style="position: absolute;top: 9px;left: '..tostring(maplist.width-34)..'px">')
table.insert(result, '<div style="color: white; opacity:100; font-size: 19px; font-weight:normal; text-align: left;">')
table.insert(result, frame:extensionTag{ name ='maplink', content=contentstart..table.concat(FullscreenList)..contentend, args={zoom=tostring(maplist.zoom+1), class='no-icon', frameless='1',
latitude=tostring(maplist.latbase), longitude=tostring(maplist.lonbase), --add invisble 'en-spaces' for tooltip
text='<div title="'..fullscreenlinktext..'"> </div>'} } )
table.insert(result,'</div></div>') -- end of maplink -----
-- add closing div for main map
table.insert(result,'</div>')
-- collate caption material to go in the outer div class
local autocaption=string.lower(args['auto-caption'] or 'no')
local autoOff=autocaption:match("(%w+)(.*)") -- select the first word in autocaption and see if it is a negativeAnswer)
if args.caption or not negativeAnswer[autoOff] then
table.insert(result,'<div class="thumbcaption" style="text-align:left">')
if args.caption then table.insert(result,args.caption) end
end
local columns=tonumber(autoOff:match("[%d]+") or '1')
if columns>1 then columns=round(maplist.width/(columns*17), 0) end -- convert from em to px for historical reasons
--for k in pairs(dotList) do capchk=capchk..(args["dotlink"..k] or '') end
local capchk=nil
local captionList = {}
for key, value in pairs(dotItemTable) do
-- only add an autocaption line if there is both a dotTag and a dotLink line available and it is not marked as nolist
if value.dotTag and value.dotTag~='' and (not string.find(value.param1 or '','nolist')) and string.gsub(value.dotLink or '',"%s+","")~='' then
table.insert(captionList, {key = key, value = value})
capchk=true
end
end
if capchk and (not negativeAnswer[autoOff]) then
table.sort(captionList, function (a,b) return lessthan(string.match(a.value.dotTag,'[%w]+'), string.match(b.value.dotTag,'[%w]+')) end)
local myDivision = string.gsub((args.toggletext or toggletext), "%s+", "")
if string.find(autocaption,'collaps') then
table.insert(result,'<hr><div class="mw-customtoggle-'..myDivision..'">'..(args.toggletext or toggletext)..'</div>')
if string.find(autocaption,'collapsed') then
table.insert(result,'<div class="mw-collapsible mw-collapsed" id="mw-customcollapsible-'..myDivision..'">')
else
table.insert(result,'<div class="mw-collapsible" id="mw-customcollapsible-'..myDivision..'">')
end
end
if string.find(autocaption, 'columns=') then
columns=string.match(autocaption,'[%d]+',string.find(autocaption, 'columns=') )
end
table.insert(result,'<div class="mw-collapsible-content" style="column-count:'..columns..'; column-rule:solid 1px;text-align:left;padding-top:5px">')
table.sort(dotList,lessthan)
local nval,ngrp='','0'
for k,v in pairs(captionList) do
nval=string.match(v.value.dotTag,'[%w]+') -- find the first alphanumeric item in the dotTag
ngrp=v.value.group or '0'
if v.value.dotLink and v.value.dotLink~='' and nval and nval~='' then
local c1,c2
if nval==args.highlight then
c1,c2=checkColors(shapeList['H'].shapeColor)
else
c1,c2=checkColors(shapeList[ngrp].shapeColor)
end
table.insert(result,'<div style="display:inline-block;line-height:110%;vertical-align:middle; padding:1px 4px;border-radius:8px;border: 0.5px solid black;')
table.insert(result,'background-color:'..c1..';color:'..c2..';font-size:88%;font-weight:bold">'..nval..'</div> '..v.value.dotLink..'<br>')
end
end
table.insert(result,'</div>') -- end for caption-content div
if string.find(autocaption,'collaps') then table.insert(result,'</div>') end -- end for toggle frame
end
if args.caption or not negativeAnswer[autoOff] then table.insert(result,'</div>') end -- end for whole caption frame
table.insert(result,'</div></div>') -- outer two frames
if args.float == 'center' or args.float=='centre' then table.insert(result,'</div>') end
if args['show-new-format'] == 'hints' then -- provide a 'format hint panel' in the 'Preview Box'
local w="<small>Below are some template hints for the 'sga' compressed version, "
w=w..'{{tl|OSM Location dots}}. It can use these, instead of the more verbose {{tl|OSM Location map}} parameter format. '
w=w..'Data is divided between a "ShapeGroup" and the "Dots", so that a single shapeGroup can be used for multiple dots on the map. '
previewMsg(w..'(nb. the "group" value can be the number or an assigned name of a shapeGroup)</small>')
w='<small>{{tl|OSM Location dots}}: | dot(n)=group,lat,lon,dotTag | '
w=w..'dotlink=link/tooltip | dotlabel=label,position,dx,dy,param1,info | dotpic=filename <br>'
w=w.."(nb. param1 options include 'nolink' 'nolist' 'nomap' 'hemisphere-1' 'hemisphere+1', 'noline' - quotes not required, separate with spaces).<br>"
w=w..'| sga = Shape,Sizepx,Color,Angledeg | sgb= OutlineWidth,Color,Style | sgc=TextSize,Color,Angle,bold italic <br>'
w=w..'| sgd=TagSizepx,Color,Spacer,Angledeg | sge=LineWidth,Color,Style | sgf=TextSpacingpx,LineHeight%,Outlinepx,backgroundColor<br>'
w=w..'| sgn=Name (optional, to assign a meaningful name) | sgp=Parent (can be the name or number of the parent shapeGroup. '
previewMsg(w..'Each shapegroup will inherit values from a parent, stretching back to "sga1" and its default values.)</small>')
if #pmsg then
local dbg={}
for i,x in pairs(pmsg) do
table.insert(dbg,x..'<br>')
end
dbg=mw.addWarning(table.concat(dbg))
table.insert(result, dbg)
end
end
if args.coordtest then debugmsg(mw.text.nowiki(args.coordtest)) end
for i,x in pairs(msg) do
table.insert(result, x..'<br>')
end
return table.concat(result)
end
function p.main(frame)
local args = getArgs(frame)
local itemTab={}
maplist.width=tonumber(args.width) or 400
maplist.height=tonumber(args.height) or 300
if args.coord then
itemTab=splitItem(convertCoordsTrad (args.coord),2)
maplist.latbase=itemTab[1]
maplist.lonbase=itemTab[2]
else
maplist.lonbase=tonumber(args.lon) or 5
maplist.latbase=tonumber(args.lat) or 0
end
maplist.zoom=tonumber(args.zoom) or 1
visibleLinks=args.showlinks
highlightNum=args.highlight
if args.nolabels=='1' then maplist.mapstyle='osm' else maplist.mapstyle='osm-intl' end
return p._main(args)
end
return p
9x2ku7g9uvcxaiexregj6gdhw7ij4fs
ઢાંચો:OSM Location map/doc
10
150857
886335
2025-06-10T08:10:34Z
en>John of Reading
0
Typo fixing, replaced: acheived → achieved, fron → from
886335
wikitext
text/x-wiki
{{Documentation subpage}}
<!-- Please place categories where indicated at the bottom of this page and interwikis at Wikidata (see [[Wikipedia:Wikidata]]) -->
{{High-use}}
{{Lua|Module:OSM Location map}}
==Technical issue==
{{tracked|T334940}}
{{warning|heading=Even Newer version now live, as of 21 March 2025!| Not only does this continue and extend the range of graphical elements, but now allows unlimited numbers of dots/marks, and much faster load times with lower resource demands! See the [[Template:OSM Location map/Return to service|Return to service]] article for details on both the 2024 and 2025 features.}}
==Major rewrite for 2025==
In 2024 the template was finally able to return to something close to full functionality, following the 2023 withdrawal of the 'graph'/VEGA capability that was previously relied on. The use of inline CSS meant maps were once again possible, but the wiki-template solution was very slow and resource-heavy, as well as limiting the number of dots and marks. A 2025 overhaul has resulted in a template now entirely delivered via a Lua/Scribunto module, significantly improving the loading times and enabling additional features and 'unlimited' dots and graphical elements, whilst aiming to retain full compatibility with existing maps. Full details of the 2025 additions are at [[Template:OSM Location map/Return to service#Additional features, new to 2025]].
==Documentation==
{{OSM Location map
| coord = {{Coord|52.6909|-1.2163}}
| zoom = 13
| width = 300
| height = 300
| caption = Old John Tower in [[Bradgate Park]], Leicestershire
| minimap = file
| mini-file = Leicestershire UK location map.svg
| mini-width = 80
| mini-height = 68
| minipog-gx = 46
| minipog-gy = 44
| scalemark = 90
| mark-coord = {{Coord|52.69632|-1.22391}}
| mark-size = 11
| label = Old John^Tower
| label-size = 13
|label-color = dark grey
| label-pos = right,photo-panel,1.3,120
| ldx=80| ldy=-60
| mark-title = [[Old John]]
| mark-image = Old John (Leicestershire).jpg <!-- used within photobox and the full screen linked page -->
| mark-description = Hill top tower of Old John, in [[Bradgate Park]]
| label1 = War memorial
| label-pos1 = left
| label-size1 = 10
|label-color1 = hard grey
| mark-coord1 = {{Coord|52.694944|-1.2254584}}
| mark-size1 = 7
| mark-title1 = War memorial at Bradgate
| mark-image1 = Leicestershire Yeomanry Memorial, Bradgate Park - geograph.org.uk - 885190.jpg
| mark-description1 = Leicestershire Yeomanry Memorial, [[Bradgate Park]]
| label2 = B R A D G A T E P A R K
| label-pos2 = top
| mark-coord2 = {{Coord|52.6909|-1.2163}}
| mark-size2 = 0| label-size2=11| label-color2=soft green
| mark-title2 = Bradgate Park
| mark-image2 = Bradgate Park - geograph.org.uk - 885175.jpg
| mark-description2 = With its origins as a medieval deep park, [[Bradgate Park]] is now a popular visitor destination.
| label3 = Bradgate
| labela3 = House
| label-pos3 = top
| mark-coord3 = {{Coord|52.686698|-1.2111676}}
| mark-size3 = 7
| mark-title3 = [[Bradgate House, Bradgate Park|Bradgate House ruins]]
| mark-image3 = Bradgate House - geograph.org.uk - 883366.jpg
| mark-description3 = Ruins of the 16th century [[Bradgate House (16th century)|Bradgate House]], home of the Grey family
| arc-textA = Cropston Reservoir
|arc-text-colorA = soft blue
| arc-angleA = -70
| arc-gapA = 2.2
| arc-radiusA = 0.3
| arc-coordA = {{Coord|52.6888|-1.2010}}
|arc-text-sizeA = 9
| mark-coord4 = {{Coord|52.6934|-1.1965}}
| mark-size4 = 0
| mark-title4 = [[Cropston reservoir]]
| mark-image4 = Boathouse and Cropston Reservoir - geograph.org.uk - 48496.jpg
| mark-description4 = It was opened in May 1871 in a corner of Bradgate Park, to supply water for the growing town of Leicester.
| label5 = Old John ^Car Park^(Hunt's Hill)
| label-pos5 = left
| mark5 = BSicon PARKING.svg
| mark-coord5 = {{Coord|52.70037|-1.22784}}
| mark-size5 = 11
| mark-title5 = Old John Car Park
| label6 = Hallgates
| label-pos6 = bottom
| mark6 = BSicon PARKING.svg
| mark-coord6 = {{Coord|52.69727|-1.1991620}}
| mark-size6 = 11
| mark-title6 = Hallgates Car Park
| label-pos7 = bottom
| mark7 = BSicon PARKING.svg
| mark-coord7 = {{Coord|52.68356|-1.2277007}}
| mark-size7 = 11
| mark-title7 = Newtown Linford Car Park
}}
This template provides
* A map (from [[OpenStreetMap]]) in a frame, for any location, anywhere in the world, at scale from global to an individual building.
* Optional multiple markers, text labels, numbered dots, live wikilinks and other graphical elements.
* A link (top right corner) to a fullscreen interactive version, which can have 'dots and details' from the article/map.
* A mini-locator map can be shown to provide context for the map.
==Purpose==
'''OSM Location map''' allows an editor to include a map in a frame with a zoom level appropriate to the topic. A scale marker is provided in the bottom right corner. This is at best a rough guide to the distances on the map, as the map projection results in scale changes depending on the latitude. Allowance has been made for this, but it is useful only as a guide to the maps scale, not a reliable measuring tool.
The multiple marks, images and labels can be a shape or image, include a text label, and potentially 'pointer-lines' and other graphical elements. The fullscreen map on the other hand will show pointer marks, which can be given popup thumbnail images and captions, as well as providing links to access other maps and satellite images, and an option to show locations of other articles nearby.
A selection of example maps, along with links to an even wider range if use-types, is at [[Template:OSM Location map/examples]].
== Usage Examples==
===Scenario 1: Minimal version===
An unadorned map centred on a latitude and longitiude coordinates, via a {{tl|coord}} value. Set the zoom to give a scale that fits the subject (0=whole world, 18=a street). With just these options set, all other parameters use the defaults, or are left unused. It also gives a link to the interactive fullscreen version.
{|
|-
| <syntaxhighlight lang="wikitext">{{OSM Location map
| coord = {{coord|53.4146|-4.3341}} <!--latitude/longitude coordinates for map's center -->
| zoom = 15 <!--zoom 0=whole world, 18=a street.-->
}} </syntaxhighlight>
|}
{{clear}}
===Scenario 2: Single marker with text label===
A default 'Red pog' marker and accompanying label. Additional items (the last three parameters) don't show up on the page, but give extra information when hovering/clicking on a dot in the fullscreen version.
{{OSM Location map
| coord = {{coord|53.394|-4.450}}
| zoom = 13
| width = 300
| height = 150
| caption = The 'Llanfechell Triangle' standing stones are north-west of [[Llanfechell]].
| label = [[Llanfechell#Llanfechell Triangle|Llanfechell Triangle]]
| mark-coord = {{coord|53.3966|-4.46204}}
| label-pos = right
| mark-title = [[Llanfechell#Llanfechell Triangle|Llanfechell Triangle]]
| mark-image = The Llanfechell Triangle - geograph.org.uk - 1260817.jpg
| mark-description=Group of three prehistoric standing stones thought to date to the Bronze Age, at [[Llanfechell]], [[Anglesey]]
}}
{|
|-
|<syntaxhighlight lang="wikitext">
| mark-coord = {{coord|53.3966|-4.46204}}
| label = [[Llanfechell#Llanfechell Triangle|Llanfechell Triangle]] <!--text label/wikilink -->
| label-pos = right <!--right, left, top, bottom - or use compass point: south, southeast, east etc -->
| mark-title = [[Llanfechell#Llanfechell Triangle|Llanfechell Triangle]] <!--clickable link &/or tooltip -->
| mark-image = The Llanfechell Triangle - geograph.org.uk - 1260817.jpg
| mark-description=Prehistoric standing stones at [[Llanfechell]] <!--fullscreen marker content -->
</syntaxhighlight>
|}
The mark-title can optionally be wiki-linked, and the dot on the map becomes a live link, even if the page doesn't exist.
The label can also be wiki-linked, but as in the case here will display the page that contains a subsection as the live link rather than a picture of the feature as is shown on full screen. It can work well when there is a page on the specific subject.
{{clear}}
{| class="wikitable mw-collapsible mw-collapsed"
|-
! Blank code for a single marker map
! Blank code with comments
|-
|<syntaxhighlight lang="wikitext">{{OSM Location map
| coord = {{coord| | }}
| zoom =
| width =
| height =
| caption =
| label =
| mark-coord = {{coord| | }}
| label-pos =
| mark-title =
| mark-image =
| mark-description =
}}</syntaxhighlight>
|<syntaxhighlight lang="wikitext">{{OSM Location map
| coord = {{coord| | }} <!-- {{coord}} has various formats for latitude and longitude -->
| zoom = <!-- (0=whole world, 18=a street)-->
| width = <!-- width and height of the map, in pixels. Do not add px -->
| height = <!-- default is width=350, height=250 -->
| caption = <!-- Text below the map. Can include [[wikilinks]] -->
<!-- Parameters for 1st mark -->
| label = <!-- text alongside the mark. (Can include wikilink and other markup)-->
| mark-coord = {{coord| | }} <!-- lat and lon location for the marker -->
| label-pos = <!-- default position is left. It can also be right, top, bottom. -->
| mark-title = <!-- tooltip and/or clickable link on the map and in fullscreen -->
| mark-image = <!-- image for fullscreen map content. Use a filename from Commons, without File: -->
| mark-description = <!-- text shown on fullscreen map when clicking on mark -->
}}</syntaxhighlight>
|}
{{-}}
==Multiple markers, labels and images==
In addition to the un-numbered mark parameter set, there are 60 numbered ones. These are otherwise identical to the one above, but the name terminates in a number (1-60). Each mark and label has its own set of parameters ({{para|mark1}}, {{para|mark-coord1}}, {{para|label1}}, {{para|label-pos1}} etc...{{para|mark2}}, {{para|mark-coord2}}, {{para|label2}}, {{para|label-pos2}} etc.) Values can be inherited either from the 'mark1 master parameter set', or from a special 'markD' Default set that provides override defaults. When set, these values are inherited by the other numbered sets to avoid having to repeat for each, whilst they can still be set individually where required.
===Scenario 3: Numbered dots with labels and auto-caption===
{{OSM Location map
| coord = {{coord|45.438|12.3285}}
| zoom = 13
| float = right
| nolabels = 1
| width = 380
| height = 240
| title = Venice
| caption = Some key locations:
| auto-caption = 14
| shapeD = n-circle
| shape-colorD = green
| label-colorD = dark blue
| shape-outlineD = white
| label-sizeD = 12
| label-posD = left
| mark-sizeD = 17
| ldxD = 0
| ldyD = 0
| label1 = Rialto Bridge
| mark-coord1 = {{coord|45.438|12.336}}
| mark-title1 = [[Rialto Bridge]]
| label-offset-y1 = -6
| label2 = Piazza^San Marco
| mark-coord2 = {{coord|45.433889|12.338056}}
| mark-title2 = [[Piazza San Marco]]<br />[[St Mark's Basilica]]<br />[[St Mark's Campanile]]<br />[[Horses of Saint Mark]]<br />[[Doge's Palace]]
| ldy2 = -3
| label3 = Grand Canal
| mark-coord3 = {{coord|45.4315|12.336}}
| mark-title3 = [[Grand Canal (Venice)]]
| label-pos3 = right
| label4 = Bridge of ^Sighs
| mark-coord4 = {{coord|45.434056|12.340861}}
| mark-title4 = [[Bridge of Sighs]]
| label-pos4 = top,with-line
| ldx4 = 8 | ldy4 = -37
| label5 = Santa Maria ^della Salute
| mark-coord5 = {{coord|45.430833|12.334444}}
| mark-title5 = [[Santa Maria della Salute]]
| label-pos5 = bottom
| ldx5 = -14
| label6 = Cannaregio
| mark-coord6 = {{coord|45.446|12.327}}
| mark-title6 = [[Cannaregio]]
| label-pos6 = northwest
| label7 = Isola di
| labela7 = San Michele
| mark-coord7 = {{coord|45.447|12.347}}
| mark-title7 = [[Isola di San Michele]]
| label8 = Castello
| mark-coord8 = {{coord|45.4357|12.3485}}
| mark-title8 = [[Castello, Venice]]
| label-pos8 = top
| label9 = Dorsoduro
| mark-coord9 = {{coord|45.431|12.326}}
| mark-title9 = [[Dorsoduro]]
| label-pos9 = left
| label10 = Santa Croce
| mark-coord10 = {{coord|45.437661|12.319208}}
| mark-title10 = [[Santa Croce (Venice)]]
| label11 = Santa Lucia
| labela11 = railway station
| mark-coord11 = {{coord|45.440833|12.320833}}
| mark-title11 = [[Venezia Santa Lucia railway station]]
| label-pos11 = top | ldx11 = -10
| label12 = Tronchetto
| mark-coord12 = {{coord|45.440556|12.305}}
| mark-title12 = [[Tronchetto]]
| label-pos12 = right
}}
Source-code for the map can be seen by clicking the 'Edit' link. Some noteworthy points about the numbered-dot map example:
*The dots are given numbers when <code>shape = n-circle</code> (square, triangle and diamond also available)
*Depending on use cases, and on the number and density of dots, you might choose not to set some (or any) labels, relying on captions/links/main text to explain which feature is which.
*To avoid over-writing, a label position can be adjusted in relation to its dot using ldx and ldy parameters to set + or - pixel offsets horizontally and vertically. (Down and right are +ve, up and left -ve).
*To avoid a crowded area of the map, shape4 uses <code>| label-pos4 = top,with-line | ldx4 = 8 | ldy4 = -37 </code> to move the label much further away, and add a line linking the label to its shape.
*Line breaks can be added to any label by adding a ^ symbol wherever needed, to split a long label.
*By setting <code>auto-caption=1</code>, numbered shapes are all listed within the caption, using the 'mark-title' values, which as with shape2 here, might include links or explanations of multiple different items.
*<code>auto-caption = 14 </code> can be used to specify a column width of (in this case) at least 14 [[Em (typography)|ems]]. The template then adds as many columns as that width permits. Alternatively, the number of columns can be specied directly, as can making it collapsible or collapsed (to hide/show the list). eg <code>auto-caption = collapsed columns=2 </code>
*By default, each dot will be given the same number as its 'mark-number'. If each dot is used in turn the numbers will go in sequence, and will match the numbers on the fullscreen version. Fullscreen numbers always run in sequence from 1 upwards, so if you don't use some mark-numbers, or over-ride them with 'numbered=', the fullscreen dots won't match. But they are self-documenting via their hover/click links, so that is not a problematic outcome, if that is desired.
{{tl|Flushing Meadows-Corona Park map}} is a useful real life example in template form.
====Inherited values====
Each mark and label can be given a unique set of attributes (size, colour, outline, angle, relative position, etc.) To minimise repetition of code, there is a sliding scale of inheritance that applies to each value in each parameter set. For example, if <code>label-size4=16</code> is set, that will always take precedence. If label-size4 has not been set, it will inherit the value from the special Default setting (defined using <code>label-sizeD= </code>). If no Default has been set, it will inherit the value set by the 'master parameter set', <code>label-size1=</code>. If that is also undefined it will fall back to the underlying default, which in the case of label-size is 13. The same is true of all the variables relating to marks and labels, (although not to the coordinates, labels themselves, or mark-titles, which are always unique to the particular mark they relate to.)
==Scenario 4: Using marks to add graphical elements to the map==
{{OSM Location map
| coord = {{coord|51.7802|-3.6525}}
| zoom = 14
| width = 280
| height = 280
| caption = Map of the area around [[Banwen]], South Wales, showing the Roman roads and relocation from camp to a more permanent fort.<ref>{{cite book|title=Glamorgan Inventory, Vol 1, Part 2: The Iron Age and Roman Occupation| year=1976| author=Royal Commission on the Ancient and Historical Monuments of Wales | url=https://shop.rcahmw.gov.uk/products/glamorgan-inventory-vol-1-part-2-the-iron-age-and-roman-occupation-ebook?variant=29576736997441| page=100}}</ref>
|minimap = file bottom right
|mini-file = Wales relief location map.jpg
|mini-width = 75
|mini-height = 94
|minipog-gx = 60
|minipog-gy = 78
|scalemark = 87
| map-data-inverse=Q748078 <!-- adds Neath Port Talbert boundary to interactive map -->
| map-data-text=Neath Port Talbot
| map-data-width=3
| map-data-color=#CC3333
| label-sizeD = 11
| mark-coord1 = {{coord|51.78365|-3.6553}}
| shape1 = box
| mark-size1 = 17,17,3 <!-- width, height, corner radius -->
| shape-outline1=hard grey,1,70 <!--color,line-width,opacity% -->
| shape-color1= white
| shape-angle1= -9
| label1 = Roman Fort
| mark-title1 = [[Roman auxiliaries in Britain|Roman Auxiliary Fort]]
| label-pos1 = left
| label-angle1= 0
| label-size1 = 12
| mark-coord2 = {{coord|51.7802|-3.6515}}
| shape2 = box
| mark-size2 = 44,62,4 <!-- width, height, corner radius -->
| label-pos2 = center
| shape-outline2=hard grey,4,40,dashed
| shape-color2= transparent
| shape-angle2= -20
| label-color2= soft grey
| label2 = Roman^Marching^Camp
| ldx2=-2|ldy2 = -2
| mark-title2 = Roman Marching Camp
| label-angle2= -20
| mark-coord3 = {{coord|51.7811|-3.6425}}
| label3 = Blast ^Furnaces
| shape3 = image
| mark-size3=8
| label-pos3=bottom
| shape-outline3=transparent,0
| mark-title3 = [[Blast Furnace]]s at Banwen
| mark-coord4 = {{coord|51.7820|-3.6520}}
| shape4 = curveA
| mark-size4 = 44
| shape-angle4=229
| shape-outline4=dark grey,4,50
| mark-title4=none
| mark-coord6 = {{coord|51.7861|-3.6466}}
| label6 = Roman road
| label-pos6=center
| ldy6=-7
| shape6=rule
| mark-title6 = none
| mark-size6=145
| shape-outline6=hard grey,2,60,dashed
| shape-angle6=-32
| label-angle6=-32
| mark-coord7 = {{coord|51.7838|-3.6534}}
| shape7=rule
| mark-size7=30
| shape-outline7=hard grey,2,60,dashed
| mark-title7 = none
| shape-angle7=-9
| mark-coord8 = {{coord|51.78492|-3.6596}}
| mark-size8=0
| mark-title8 = none
| label8=Powys^Neath Port Talbot
| label-size8=8
| label-angle8=-1
| label-pos8=center
| label-color8=black,40
}}
Noteworthy Points illustrated by the map of Banwen area:
* Each of these marks has a very different appearance, and some are sized to be 'map features' rather than markers.
* Mark-size can be used not just to set width, but also height, by adding a second, comma-separated value. This allows a 'box' to be given a rectangular shape. In the case of 'box' a third value sets a radius for the corners, which gives the rounded corners of the two Roman forts. eg <code>| mark-size2 = 44,62,4</code>
* The Marching fort has a transparent colour, so only the outline shows up, set to give a wide, dashed outline.
* Label-pos2 is set to 'center', so the words appear within the shape, rather than alongside it.
* There are now various ways to add a line to the map. The Roman road on this map is included by using <code>shape6=rule</code>. A rule has 'shape-outline' attributes, which are all set by <code>| shape-outline6=hard grey,2,60,dashed </code>color and line-width are required. Opacity% and 'style' are optional. The style options are solid, dashed, dotted or double.
* If you look at the fullscreen version, you will only see the first three marks. An item can be excluded by setting <code>mark-title6=none</code>, for example.
* All three of the fullscreen marks here show as white, but for different reasons. Mark-color1 is set to white. Mark-color3 is not defined, (Red pog.svg, being an image item, brings its own color), so the fullscreen dot 'inherits' white from Mark1. Mark-color2 is set to transparent, which shows as white for the fullscreen dot. Adding more diverse items to the map can affect the fullscreen colors, intended or otherwise.
* This map also shows the county boundary. See the [[Template:OSM Location map#Parameters| Parameters Section]] for details of how Qvalues can be used to show 'map-data' lines from OpenStreetMap.
* 'curveA', an arrow-curve has been added to this (contrived) example. curveC (clockwise version) is also available.
* Note also that citation refs can be included in the caption. Non-obvious location data should be backed up by sources, just like any other wikipedia information, either within the main body text or, as here, in the caption.
{{Section reflist}}
====Adding a Minimap in the left or right corner====
Many pages with info-boxes already include a locator map showing where the topic or place is located. For those that don't it might be helpful to include one within your map. It is possible to incorporate an existing 'Location map' (from Wikimedia Commons) in a corner of this map.
The width and height of the Location map both have to be decided upon, specified (and calculated so as to not shift the map away from the corner). Some location maps may already highlight the feature, but if not, an optional locator 'pog' can be placed by calculating and specifying the minipog-gx and -gy values, using a 100x100 grid across the minimap. (so gx and gy values of 25 and 50 would place a dot a quarter of the way across and half way down the minimap. It does not pick up or use latitude and longitude values. The origin (0,0) is in the top left of the minimap and the map itself defaults to the bottom right corner, but can also use <code>minimap=file bottom left</code>, <code>minimap=file top left</code> and <code>minimap=file top right</code>.
The dot will remain in the right place even if you alter minimap size. (Note, the now redundant minipog-x and minipog-y are retained for compatibility. These used the same pixel dimensions as the width and height of the map, which made them harder to calculate/guess and needed to be re-done if the size was changed. Much better to use gx and gy from here on.)
If the area of the actual map is a large portion of the mini-map, an open red box can be included instead of a dot, to show the bounds of the main map. To use this feature, simply specify the width of the required box: <code>minimap-boxwidth=xx</code> where xx = the percentage of the minimap width for the box. In general anything much below xx=15 will be better served by a dot. The box will be centered at the coordinates minipog-gx and minipog-gy. The required width will require some trial and error to pin down. The box height is then matched in proportion to the actual map and will scale if the minimap size changes.
==={{anchor |arc}}Text on an arc===
{{OSM Location map
| coord = {{coord|52.4138|-4.0855}}
| zoom = 15
| float = right
| width = 310
| height = 290
<!--text, lat,lon,size,color,angle,gap,radius,ellipse-factor-->
| arcA=Afon Rheidol,52.4109,-4.0875,11,blue,-31,4.5,0.24,0.98
| arcB=CARDIGAN BAY,52.4155,-4.0913,10,blue,150,-1,1.2
| shapeD = image
| shape-colorD = red
| shape-outlineD =
|shape-angleD =0
| mark-sizeD = 9
|ldxD=0
|label-posD=east
| mark-coord1 = {{coord|52.4133|-4.0899}}
| shape-color1 = pale grey
| shape1 = diamondD
| shape-outline1 = dark grey,3,90
| shape-angle1=3 | mark-size1=30,34
| label1=Castle ^ruins|label-pos1=southwest|ldx1=2
| mark-title1 = [[Aberystwyth Castle]]
| mark-image1=Aberystwyth Castle - geograph.org.uk - 5610614.jpg
| mark-coord3 = {{coord|52.4139|-4.0816}}
| mark-title3 = [[Aberystwyth railway station]]
|label3=Railway ^Station
|label-pos3=south
| mark-coord4 = {{coord|52.4157|-4.0875}}
| mark-title4 = [[Royal Pier, Aberystwyth]]
| label4=Royal Pier
| label-pos4=west
| mark-coord6 = {{coord|52.4148|-4.0884}}
| label6='''Old College'''
| label-pos6=west
| mark-title6 = [[Old College, Aberystwyth]]
| mark-image6=Aberystwyth - Yr Hen Goleg 20180704-04.jpg
| caption = Location of Old College and other Aberystwyth Seafront buildings
}}
Text for broader geographic features can be placed around an arc, to convey a sense of a broader area, or to follow the curve of a river, mountain range, coastline, etc. This works entirely separately from the other labels. It does not attach itself to any mark or dot, and does not create any fullscreen markers. It requires coordinates for the first letter, parameters for the starting angle, radius of arc and gap between letters.
As of 2025 there are two alternative ways of defining text on an arc: using the standard parameters is clearer but more verbose, so there is also now a condensed format, where all the values are listed as a comma separated single parameter. The end result on the map is identical.
For example, from the Aberystwyth map, the river name could be defined by either
<pre>
| arc-textA=Afon Rheidol
| arc-coordA={{coord|52.4109|-4.0875}}
| arc-text-sizeA = 11
|arc-text-colorA = blue
| arc-angleA = -31
| arc-gapA = 4.5
| arc-radiusA = 0.24
|ellipse-factorA = 0.98
</pre>
or the same item can now be defined by
<pre>
<!--text, lat,lon, size, color, angle, gap, radius [,ellipse-factor]-->
| arcA = Afon Rheidol, 52.4109,-4.0875, 11, blue, -31, 4.5, 0.24, 0.98
</pre>
{{see|Template:OSM Location map/ArcText/doc|topic= arc-text, with sample patterns and more example code}}
==Alternative marks==
If <code>|shape=image</code> the default is to show a standard 'Red pog', which is useful to mark points of interest on the map. However other images can be used, selected from Wikimedia Commons: particularly useful for the large library of map icons and symbols. Note: if a particular image file is specified in <code>mark1=</code> or <code>markD=</code>, all subsequent marks will use it as well unless they name their own image file, or set shape= to a built-in shape. If using an image that is not square, a height setting (or alternatively a dimension value) should be set to ensure the image is vertically centered. (nb, there is no longer any ability to adjust the actual dimension of any images, which will display at the proportions supplied by wikimedia commons)
===Transparent overlay===
{{OSM Location map
| lat =52.6332
| lon =-1.138
| zoom =14
| width = 300 <!-- width and height of the frame. numeric input - do not add px -->
| height = 300
| caption = Roman and Medieval Leicester: some key sites.
| scalemark =156 <!-- shifts the scalemark further to the left -->
| legendBox=Legend,115px65px1px, 175px205px <!--text, size, position,background color, text/outline color, param options)-->
| legendItem1=Main Roman archaeological sites,7
| legendItem2=Selected Medieval sites,1,42 <!--Item text, shape number [,pixels from top] -->
| mark = Leicester Town Walls map overlay.svg
| mark-coord = {{coord|52.6324 |-1.1380}} <!-- lat and lon location for the overlay -->
| mark-size = 250,312 <!--height of the overlay image, in a tracing matches the frame-->
| mark-dim = 0.8 <!--dimension (scale factor) for if the image is not square, deprecated. Using a second size value is generally easier-->
| mark-title=none <!--no marker within the linked full screen-->
| mark-coord1 = {{coord|52.632333|-1.141194}} <!-- lat and lon location for the marker -->
| mark-size1=10
| shape1=square
| shape-color1=hard red
| mark-title1 = [[Leicester Castle]]
| mark-image1 = The Great Hall Leicester Castle.jpg <!-- for use within the full screen linked page -->
| mark-description1 = The Great Hall, now with a Queen Anne frontage, is the main standing remains of Leicester's medieval castle.
| mark-coord2 = {{coord|52.6312|-1.1405}}
| mark-title2= [[Trinity Hospital, Leicester]]
| mark-coord3 = {{coord|52.6317028|-1.1379222}}
| mark-title3 = [[Magazine Gateway]]
| mark-image3 = Leicester Magazine Gateway west.jpg
| mark-coord4 = {{coord|52.63401|-1.13624}}
| mark-title4= [[Greyfriars, Leicester]], Richard III's original burial site
| mark-coord5 = {{coord|52.634644|-1.137086}}
| mark-title5 = [[Leicester Cathedral]]
| mark-coord6 = {{coord|52.64041|-1.13617}}
| mark-title6 = [[St Margaret's Church, Leicester]]
| mark-coord7 = {{coord|52.63487|-1.14136}}
| mark-title7 = [[Jewry Wall and Baths]]
|shape7=diamond
|mark-size7=12
|shape-color7=hard green
| mark-coord8 = {{coord|52.63512|-1.14072}}
| mark-title8 = [[St Nicholas Church, Leicester]] (Saxon, built from Roman material)
|shape8=diamond
|mark-size8=12
|shape-color8=hard green
| mark-coord9 = {{coord|52.6353|-1.1387}}
| mark-title9 = Roman Forum an Bassilica (site of)
|shape9=diamond
|mark-size9=12
|shape-color9=hard green
| mark-coord10 = {{coord|52.6362|-1.1388}}
| mark-title10 = Mosaic floors and Roman theatre (site of)
|shape10=diamond
|mark-size10=12
|shape-color10=hard green
| mark-coord11 = {{coord|52.6316|-1.1366}}
| mark-title11 = Roman cemetery site
|shape11=diamond
|mark-size11=12
|shape-color11=hard green
| mark-coord22 = {{coord|52.630|-1.1458}}
| mark-size22=0
| label22 = River Soar| label-size22=9
| label-pos22=top| label-angle22=-55
| label-color22=soft blue| mark-title22 = none
}}
A marker image does not have to be small and opaque. A larger overlay image (with a transparent background) can be used to show particular features not included in the base map, such as a town's former walls (see the adjacent map, which is using <nowiki>[[File:Leicester Town Walls map overlay.svg]]</nowiki> ). Such images can be created in several ways (such as tracing over a copy of the base map); they are invoked like any other marker image file. (For a detailed guide to creating and deploying an overlay for these maps see [[User:J. Johnson/OSM overlay how-to]]). (See below for some direct ways to show linear graphics.)
===Shapes===
Instead of using a wikimedia image, it is possible to show a wide range of built in shapes. Until recently this was a fairly limited list, mainly being circles, squares and triangles. With the 2025 rewrite a new selection of shapes has been implemented, using the CSS 'clip-path' capability. There are now diamond, cross, various stars and an open ring, most having variants with single or double outer lines (indicated by D or DD). Unlike circles and squares, these are only solid shapes, which carry limitations. When an outline is set for these, it is actually a larger version of the shape 'underneath', so you cannot use opacity on the 'top' version, nor can you use css styles on the outline. On the other hand all of these shapes can be stretched by setting different width and height values, rotated to any desired angle, as well as given whatever color values are desired. The full list of clip-path shapes, as of March 2025, is:
<blockquote>squareD, squareDD, triangleD, triangleDD, circleD, circleDD, diamond, diamondD, diamondDD, thincross, cross, crossD, fivepointstar, fivepointstarD, sixpointstar, sixpointstarD, sevenpointstar, sevenpointstarD, eightpointstar, eightpointstarD, ring. (box, boxD, boxDD, ellipse, ellipseD, and ellipseDD are all synonyms for their square and circle equivalents)
</blockquote>
A demo map is at [[Template:OSM Location map/Return to service]] which shows some of these shapes. Use them with care! The aim of the map is to elucidate its content, not overload with excess complexity.
===Legend/Key/Information panel within the map===
This is a new and improved (for 2025) feature, using <code>| legendBox=</code> and <code>| legendItem1=</code>. It uses the pixel x,y values (rather than coordinates), to place the top-left corner of the legendBox on a fixed point relative to the frame top-left corner, and centres an optional heading-text. The multiple <code>legendItem</code> lines nominate a particular existing shape number, and places the dot/shape and the given text as the next line in the legendBox.
<pre>| legendBox=Legend,115px65px1px, 175px205px <!-- text, size, position [,background, text color, options] -->
| legendItem1=Main Roman archaeological sites,7 <!-- this will use the shape info set for mark7, for example -->
| legendItem2=Selected Medieval sites,1,42 <!--Item text, shape number [,pixels from top] -->
</pre>
It is possible to get similar results using <code>shape=panel</code>, which allows more layout and control options, but will all use coordinate values, and specially defined shapes/dots. The legendBox alternative gives a much more concise and convenient version, where that is sufficient.
With no numbers or labels, the use-case here is showing the scatter of different types of sites. The individual dots can still be 'interrogated' for further information, particularly using the fullscreen version, but only if 'looked for' rather than presented up-front. Different page content will have different needs. A map should aim to show the most relevant information, and editors can make choices on how to best make the map a part of the article's narrative.
A note on order of drawing: The un-numbered mark is drawn first. Numbered marks are drawn starting with highest first and drawing shape1 last, which is therefore 'on top'. The overlay uses the un-numbered mark, to go below everything else. if using a 'panel' you will want a higher number to put it below its contents. In this example the legendBox is also drawn underneath any other marks. (Note: a legendBox with no items can create quick and easy map title in a box).
===Text color===
Color of label text can be specified using <code>label-color = </code>. Standard html colors can be specified by name, and any color can be specified using the hex triplets coding #xxyyzz (see [[Web colors]]). However, to create a consistent look and feel across the wikipedia maps, there are some OSM Location map specific colors, with a more muted tone range that fit well with the color scheme of the base maps. In general it is best to make use of this range, unless there are good reasons for using other particular shades for specific features.
Under normal usage, the following label color scheme can be followed:-
{| class="wikitable" style="text-align: left;"
|-
| style="background-color:#{{#invoke:OSM Location map|colorvalue|color=background}};" | {{font color|#{{#invoke:OSM Location map|colorvalue|color=soft grey}} |soft grey}}
|Settlements = soft grey (Subject of the map can be hard grey and larger label-size)
Map areas with darker or busier backgrounds may need to move a shade darker to hard grey and dark gray respectively.
|-
| style="background-color:#{{#invoke:OSM Location map|colorvalue|color=pale blue}};" | {{font color|#{{#invoke:OSM Location map|colorvalue|color=soft blue}}|soft blue}}
|Rivers, lakes, sea areas etc = soft blue (Works well on top of OSM blue areas)
|-
| style="background-color:#{{#invoke:OSM Location map|colorvalue|color=pale green}};" | {{font color|#{{#invoke:OSM Location map|colorvalue|color=soft green}}|soft green}}
|Parkland, national/regional parks, gardens, forests etc = soft green works well on top of OSM green areas. (hard green may be desirable in forests or for the subject of the map)
|-
| style="background-color:#ffffff;" | {{font color|#{{#invoke:OSM Location map|colorvalue|color=hard red}} |hard red}}
| Use with care, or not at all. Any red text can look like a wikilink redlink. Since wikilink text is now possible on the map, this would be an unwanted confusion.
|-
| style="background-color:#{{#invoke:OSM Location map|colorvalue|color=paleground}};" | {{font color|#{{#invoke:OSM Location map|colorvalue|color=dark grey}} |dark grey}}
|Individual sites = dark grey (no need to specify if the site has a single label to accompany a red pog - it will be the default)
|-
| style="background-color:#{{#invoke:OSM Location map|colorvalue|color=pale brown}};" | {{font color|#{{#invoke:OSM Location map|colorvalue|color=dark brown}} |dark brown}}
|Information panel, or legend = dark brown (works nicely on a 'pale brown' panel, for example. It is the default for legendBox)
|}
===Full table of color options===
{| class="wikitable" style="width: 70%; text-align: left;"
! colspan="4" style="background-color:lightblue" | Colors recommended for OSM Maps.
These have a more pastel shade than the standard colors, so blend well the map backgrounds (pale variants are only for panel-backgrounds, etc)
|-
|style="background-color:#{{#invoke:OSM Location map|colorvalue|color=pale red}}" |pale red #{{#invoke:OSM Location map|colorvalue|color=pale red}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=soft red}}|soft red #{{#invoke:OSM Location map|colorvalue|color=soft red}} }}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=hard red}} |hard red or Red #{{#invoke:OSM Location map|colorvalue|color=hard red}}}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=dark red}} |dark red #{{#invoke:OSM Location map|colorvalue|color=dark red}}}}
|-
|style="background-color:#{{#invoke:OSM Location map|colorvalue|color=pale green}}" |pale green #{{#invoke:OSM Location map|colorvalue|color=pale green}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=soft green}} |soft green #{{#invoke:OSM Location map|colorvalue|color=soft green}}}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=hard green}} |hard green or Green #{{#invoke:OSM Location map|colorvalue|color=hard green}}}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=dark green}} |dark green #{{#invoke:OSM Location map|colorvalue|color=dark green}}}}
|-
|style="background-color:#{{#invoke:OSM Location map|colorvalue|color=pale blue}}" |pale blue #{{#invoke:OSM Location map|colorvalue|color=pale blue}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=soft blue}} |soft blue #{{#invoke:OSM Location map|colorvalue|color=soft blue}}}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=hard blue}} |hard blue or Blue #{{#invoke:OSM Location map|colorvalue|color=hard blue}}}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=dark blue}} |dark blue #{{#invoke:OSM Location map|colorvalue|color=hard blue}}}}
|-
|style="background-color:#{{#invoke:OSM Location map|colorvalue|color=pale grey}}" |pale grey #{{#invoke:OSM Location map|colorvalue|color=pale grey}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=soft grey}} |soft grey #{{#invoke:OSM Location map|colorvalue|color=soft grey}}}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=hard grey}} |hard grey or Grey #{{#invoke:OSM Location map|colorvalue|color=hard grey}}}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=dark grey}} |dark grey #{{#invoke:OSM Location map|colorvalue|color=dark grey}}}}
|-
|style="background-color:#{{#invoke:OSM Location map|colorvalue|color=pale brown}}" |pale brown #{{#invoke:OSM Location map|colorvalue|color=pale brown}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=soft brown}}|soft brown #{{#invoke:OSM Location map|colorvalue|color=soft brown}}}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=hard brown}}|hard brown or Brown #{{#invoke:OSM Location map|colorvalue|color=hard brown}}}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=dark brown}}|dark brown #{{#invoke:OSM Location map|colorvalue|color=dark brown}}}}
|-
! colspan="4" style="background-color:lightblue" |Other named colors.
Standard HTML colors tend to look rather harsh on the OSM maps so some of them are given more subtle shades. If you need the standard HTML version, it can be set by using an appropriate '#' 6-figure hex value.
|-
| White #{{#invoke:OSM Location map|colorvalue|color=White}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=Silver}}|Silver #{{#invoke:OSM Location map|colorvalue|color=Silver}}}}
|style="background-color:#{{#invoke:OSM Location map|colorvalue|color=Beige}}; color: inherit"|Beige or beigeground #{{#invoke:OSM Location map|colorvalue|color=Beige}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=Orchid}}|Orchid #{{#invoke:OSM Location map|colorvalue|color=Orchid}}}}
|-
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=Maroon}}|Maroon #{{#invoke:OSM Location map|colorvalue|color=Maroon}}}}
| style="background-color:#{{#invoke:OSM Location map|colorvalue|color=hard grey}}; color: #{{#invoke:OSM Location map|colorvalue|color=Yellow}}|Yellow #{{#invoke:OSM Location map|colorvalue|color=Yellow}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=Olive}}|Olive #{{#invoke:OSM Location map|colorvalue|color=Olive}}}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=Limegreen}}|Limegreen #{{#invoke:OSM Location map|colorvalue|color=Limegreen}}}}
|-
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=hard Leaf}}|Leaf #{{#invoke:OSM Location map|colorvalue|color=Leaf}}}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=Aqua}}|Aqua #{{#invoke:OSM Location map|colorvalue|color=Aqua}}}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=Teal}}|Teal #{{#invoke:OSM Location map|colorvalue|color=Teal}}}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=Aquamarine}}|Aquamarine #{{#invoke:OSM Location map|colorvalue|color=Aquamarine}}}}
|-
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=Navy}}|Navy #{{#invoke:OSM Location map|colorvalue|color=Navy}}}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=Fuchsia}}|Fuchsia #{{#invoke:OSM Location map|colorvalue|color=Fuchsia}}}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=Purple}}|Purple #{{#invoke:OSM Location map|colorvalue|color=Purple}}}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=Black}}|Black #{{#invoke:OSM Location map|colorvalue|color=Black}}}}
|-
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=Orange}}|Orange #{{#invoke:OSM Location map|colorvalue|color=Orange}}}}
|transparent #transparent
|style="background-color:#{{#invoke:OSM Location map|colorvalue|color=background}}; color: inherit" |background #{{#invoke:OSM Location map|colorvalue|color=background}}
|style="background-color:#{{#invoke:OSM Location map|colorvalue|color=paleground}}; color: inherit" |paleground #{{#invoke:OSM Location map|colorvalue|color=paleground}}
|}
New to 2024: All colors can have an opacity value as well as the color name. eg <code>label-color3=dark blue,30</code> will set a 30% opacity. (The '50%' and 'opacity' parameters are now deprecated. ie they still work, but are not needed anymore)
{{font color|{{#invoke:OSM Location map|colorvalue|color=#AAAAAA}} | It is also possible to specify any HTML Hex color using the six-figure hex-code, eg #AAAAAA, but sticking to defaults allows a consistency between pages}}
{{font color|#{{#invoke:OSM Location map|colorvalue|color=}} |If no valid color is specified the color will be set to a default of 'hard grey'}}
===Text, shape and outline opacity===
{{OSM Location map
| coord={{coord|57.087|-4.702}}
| float=right
| zoom =6
| width = 200
| height = 200
| mark-coord={{coord|57.087|-4.702}}
|mark-size=0
|label=GREAT GLEN
|label-pos=top
|label-size=16
|label-angle=-55
|label-offset-y=6
| label-color=#1659165A
| mark-coord1={{coord|56.417|-6.102}}
|mark-size1=0
|label1=SCOTLAND
|label-pos1=right
|label-size1=26
|label-color1=soft grey,50
| mark-coord2={{coord|57.391|-5.890}}
| mark-size2=30
| shape2=circle
|shape-color2=orange,75
|shape-outline2=orange,11,35,dashed
}}
With the 2024 changes it is now possible to easily set the opacity value of any specified color. For 'label-color' and 'shape-color', this is a second, comma-separated value. For 'shape-outline' it is the third item, (it takes color name,line-width,opacity,style). eg <code>shape-outline4=hard red,8,30,solid</code> would add a transparent red outline 8px thick around the shape. (Note, the opacity is given as a %. For historical reasons, an opacity of 0 and 100 both give fully opaque colours. To set as entirely transparent, use a value of 1 - or the colour name 'transparent'.)
*Alternatively: <code>| label-color=#1659165A</code>. It is possible to set an opacity value when using #hexvalues instead of just named colours from the HTML color-space. The opacity is an additional two-digit hex value to make an 8 digit #value, with the opacity digits ranging from 00 (opaque) to FF (transparent).
Note: whilst the shape-color is also used by the markers in the fullscreen map, these cannot use any opacity. This 'feature' can be utilised as a way of setting the marker color for any transparent shape. Using the 'transparent' color setting will give a white marker, bus using <code>dark red,1</code> will look transparent on the map, but still give a red fullscreen marker.
There was previously an option to specify 'Halo' features around shapes. These were rarely used (it at all) and would have made considerable resource demands through the use of multiple additional shapes. With the template size risking difficulties when many dots are used, the halo parameter has been removed for now. Various halo-like effects can be obtained using the shape-outline option.
===Text effects===
;multi-line label: Where label text is too long to fit on a single line, using <code>label = </code>, any line can now be split as many times as desired using the ^ hat symbol. This supersedes the now deprecated <code>labela = </code> and <code>labelb = </code>. To put an actual ^ in your label, use the <code>{{Tt|&Hat;}}</code> entity.
;Label with no mark: If <code>mark-size=0</code> this has the effect of a free-floating label with no marker
;Angled label: It is possible to specify a <code>label-angle = </code>, which will pivot the label text around the centre of the marker point by the specified angle. Using an angled label which also has no marker is particularly good for labelling various geographic and linear features. A more characterful alternative is to set the text on an arc, using the [[Template:OSM Location map/ArcText/doc|ArcText options]]. For stylistic consistency settlement and building names should not normally be given an angle.
;Wiki markup: New to 2024, all text is shown as standard wiki text. This means you can use '''<nowiki>'''bold'''</nowiki>''' and ''<nowiki>''italic''</nowiki>'' as normal. Wikilinks are also possible, but note that they will then take standard wikilink colors, including red-link for non-existent links.
*And one for the geeks: if you are desperate to use other fonts or font-effects, you can include elements such as <nowiki><span style="...">your text here</span></nowiki> as part of your label. But use with caution and care. the aim is not to overwhelm the map with fancy effects. However, used appropriately it could be just the solution you are looking for - provided you know enough html/css/etc.
===Use in infoboxes===
{{t|OSM Location map}} can be shown in many infoboxes. This may depend upon if they allow a <code>map_image</code> parameter (preferable) or the <code>module</code> and/or <code>embedded</code> parameters, as their <code>image</code> parameter is usually used and best reserved for images. See for instance {{t|infobox school}} by using the instruction <code>|module=<nowiki>{{OSM Location map| ...}}</nowiki></code>. For an example, see [[:St John Fisher Catholic School]] which uses the map to show its two sites within an infobox map. Some infoboxes also allow it to be incorporated within the caption below an image. However, this only works if an image and some caption text are also present. (see [[Inishmore Lighthouse]]). A dual use template with controlling logic is possible as with {{t|Karakoram OSM}} and its different display in [[:K2]] using {{t|infobox mountain}}.
===Using label text to draw graphic features===
With the arrival of various outline and line-drawing features, there is less need to resort to text labels as a way of showing simple graphics. However, for certain cases, some of the many unicode items in the [[List_of_Unicode_characters#Box_Drawing| Box Drawing]] list may be of use.
==List of parameters==
{| class="wikitable mw-collapsible mw-collapsed"
|-
! Code blank - OSM Location map template, listing all the parameters
|-
|-
| <syntaxhighlight lang="wikitext" >{{OSM Location map
| coord = {{coord| | }}
| zoom=
| float =
| width =
| height =
| fullscreen-option =
| caption =
| title =
| minimap =
| mini-file =
| mini-width =
| mini-height =
| minipog-gx =
| minipog-gy =
| minipog-boxwidth =
| scalemark =
<!-- optional default settings. These 'D' parameters only create override values for subsequent marks. They make no marks of their own -->
| shapeD =
| shape-colorD =
| shape-outlineD =
| shape-angleD =
| markD =
| mark-sizeD =
| mark-dimD =
| label-sizeD =
| label-colorD =
| label-angleD =
| label-posD =
| ldxD = <!-- short-forms of label-offset-x and -y are now available for all sets-->
| ldyD =
<!-- unumbered parameter set creates mark and/or label on the map -->
| mark-coord = {{coord| | }}
| mark =
| shape =
| shape-color =
|shape-outline =
| shape-angle =
| mark-size =
| mark-dim =
| label =
| label-size =
| label-color =
| label-angle =
| label-pos =
| ldx =
| ldy =
| mark-title =
| mark-image =
| mark-description=
<!-- Arc text (A, B and C) no shape, mark or fullscreen effect, just text on an arc. Coords are for the first letter (max 20).-->
| arc-coordA = {{coord| | }}
| arc-textA =
| arc-angleA =
| arc-gapA =
| arc-radiusA =
| arc-text-sizeA = <!-- defaults to 12 -->
|arc-text-colorA =
|ellipse-factorA = <!-- defaults to 1.0 -->
<!-- numbered markers (1 to 60). Values set in mark1 will be inherited by all other numbered markers, unless overridden by a 'D' value-->
| mark-coord1 = {{coord| | }}
| mark1 =
| shape1 = <!--image/circle/square/triangle/diamond/rule/box/ellipse/etriangle/panel -->
| shape-color1 =
|shape-outline1 = <!--color,width,opacity,style-->
| shape-angle1 =
| mark-size1 = <!--width,height,corner-->
| mark-dim1 =
| label1 = <!--use ^ to add a newline, this makes labela1 and labelb1 redundant -->
| label-size1 = <!--includes extra parameter options: ,outline,background for text effects-->
| label-color1 =
| label-angle1 =
| label-pos1 = <!--position,[line option][,further options] -->
| ldx1 =
| ldy1 =
| mark-title1 =
| mark-image1 =
| mark-description1=
| mark-coord2 ={{coord| | }}
| mark2 =
| shape2 =
| shape-color2 = <!-- ... and so on for all the parameters above, for all numbers up to 60 -->
}}</syntaxhighlight>
|}
== Parameters ==
{| class="wikitable" style="width:100%;"
! colspan="2" style="background-color:lightblue; color: inherit" | Map display parameters
|-
! Parameter
! Description
|-
| style="width:170px;"|<code>coord</code>
| ''Latitude'' and ''longitude'' coordinates of the centre point of the map.
Use <code>coord={{tlf|Coord|''latitude'' |''longitude''}}</code>. {{tlx|Coord}} can deal with a wide range of formats. e.g.: <code>coord={{tlf|Coord|57|18|22|N|4|27|32|W}}, coord={{tlf|Coord|44.112|N|87.913|W}}, coord={{tlf|Coord|44.112|-87.913}}.</code> This may not be the 'coord' points of the page, as an appropriate framing area for the map may place the subject off-centre. (The marker point(s) are set separately, see below).
HINT: [http://www.gridreferencefinder.com/ gridreferencefinder.com], is a helpful place to find coordinates if you have other grid references. In Preview mode, a right-click while using the 'Interactive fullscreen map' will also show a coord point.
|-
| <code>zoom</code>
| Sets the '''scale''' of the map, from 0 to 19, to the levels defined by OpenStreetMap. (details [https://wiki.openstreetmap.org/wiki/Zoom_levels here]).
NOTE: The actual distances represented will vary depending on the latitude, as the scale defines different fractions of a degree. The apparent scale will also vary depending on the screen device, browser magnification, etc. The on-screen 'scalemark' gives a rough indication of the scale of the map.
|-
| <code>magnify</code>
| Enlarges the central area of the map and its contents. Main use case is where a feature shows as too small at one zoom level, and too large at the next. It can be given a decimal value between 1 and 2. (1 is the default, ie no effect, 2 doubles the size). It is a 'new for 2025' option. For more details and an example see [[Template:OSM Location map/Return to service#Magnify|OSM Location map/Return to service#Magnify]].
|-
| <code>float</code>
| Positions the frame to the left, centre (or center) or right. (default is right). If centered, the text will be forced above and below, otherwise text will wrap to the side.
HINT: For left or right align, if you want to ensure some following text sits alongside the map, use {{tl|clear}} before the map template.
|-
| <code>width</code><br /><code>height</code>
| Sets the width and height of the map in pixels. Only the number is required (i.e. no px). Default is 350 by 250 pixels.
|-
| <code>fullscreen-option</code>
| From 2024, the 'link-box' symbol at top left is hard-coded onto the map, so will always include the link to an Interactive Full screen,with links to other mapping options and can show locations of other nearby articles. It is always 'on' so the parameter has no effect.
|-
| <code>nolabels</code>
| Optionally use a map-set without settlement/country names.
By default the base map uses mapstyle='osm-intl'. Progressively higher zoom levels bring more place names onto the map, in the language/script of the wiki/user preference, where that is available. For some contexts it may be preferred to 'turn off' the place name labels, and use the mark/label options to provide such names as are wanted. Use <code>nolabels = 1</code> (nb. At higher zooms road names still occur).
|-
| <code>caption</code><br><code>auto-caption</code><br><code>toggletext</code>
| Optionally, a text caption can be included, below the map. Unless overridden by tags etc., this is left-justified plain text. It can include any wiki-item that can be inserted into a table cell, including images, formatting, citation references, etc.
<code>auto-caption=1</code> a numbered list will be automatically generated to follow any caption to show items that use numbered shapes.
Where there are a lot of short items, a column width can be specified. One way to do this is to specify a minimum width (in '[[Em (typography)|ems]]'. eg <code>auto-caption=15</code> will give a minimum width of 15em, and include a vertical rule for each column. Instead of the number, include the phrase 'columns=2' to directly set the number of columns to use.
NEW for 2025: The numbered list can be set to be collapsed (hidden) like a [[Help:Collapsing tables and more|wiki-table]]. Add the word 'collapsed' or 'collapsible' to set either to start collapsed, or start visible, but with the ability to hide it. eg. <code>auto-caption=collapsed, columns=3</code> will have a 3-column list, but starting with only the main caption plus the 'toggletext' phrase showing.
<code>toggletext=</code> allows you to set the phrase that turns on/off the caption list. It should be in single square brackets and include the words 'Hide/show'. The default is '[Hide/show caption list]'
|-
| <code>title</code>
| Optionally a title or other text can be placed in a cell above the map. By default this is centred and bold, but as with the caption, any wiki-markup can be included.
|-
|<code>legendBox<br>legendItem1</code>
|A Legend or Key panel can be added within the map, to explain what some of the shapes or dots represent. The size of the panel and its position relative to the top left corner of the map are both given in pixels, so the box remains in a fixed position even if the map coords are changed. A centred title line (optional) is followed by as many 'legendItem(n)=' lines as wanted. This identifies the explanation text, the dot-number it relates to, and an optional distance (px) down the box for that line. (If no distance is given it uses the shape height to find the next line distance.) Example: <code><nowiki>| legendBox=Map Key,110px80px1px,290px45px <!--title-line, size, position [,background colour, text/outline colour])--> | legendItem1=Defended site,1 <!--explanation text,dot-number [, distance from top of the box]--></nowiki></code>
It attempts to automate as much as possible to save editing time and space. It does this by re-using the shape values from already-defined dots, and giving very few points of control. If greater control/flexibility is wanted, it is possible to use a <code>panel</code> shape and place specially defined dots on it using the same process as the rest of the map. (As with other comma-separated values, if the text items need to use commas or inverted commas the text element should be put within "double quotes").
|-
|
<code>map-data</code><br />
<code>map-data-text</code><br>
<code>map-data-color</code><br>
<code>map-data-width</code><br>
<code>map-data-heavy</code><br>
<code>map-data-light</code><br>
<code>map-data-inverse</code>
| Allows 'OSM ExternalData elements' to be added to the map. This can be an administrative boundary, highway or other linear map element that has been assigned a wikidata Q value. (e.g.: <code>map-data=Q83065</code> will add the city boundary of Leicester.) The map item needs to be on the same place as the map itself. It can be linear features or inverse areas (not filled areas/shapes). Multiple elements can be added, separating each Q value by a comma. New to 2024 is that the element will be on the framed map as well as in fullscreen, and can apply an inverse mask.
[https://nominatim.openstreetmap.org/ nominatim.openstreetmap.org/] has a search engine to identify data elements for which Q values have been assigned, or to add (or repair) wikidata Q values to map elements. The relevant [[wikidata]] page also gives its Qvalue.
All lines on the 'framed' map are for now a 10% opacity grey line, 6, 9 and 3 pixels wide for <code>map-data</code>, <code>map-data-heavy</code> and <code>map-data-light</code>, respectively. The fullscreen version will continue to draw orange lines of thickness 9 and 3 pixels for light and heavy options. <code>map-data</code> is more flexible within fullscreen. It can default to an orange line of 6 pixels, but color (using format #XXXXXX) and width in pixels can be set, as can a text element which appears when the line is clicked on. This can include wikilinks.
Links are still only visible in fullscreen. The framed version lines have no links, so it may be helpful to use a discrete label alongside a boundary (for example) to indicate what it is (See Banwen example, above).
<code>map-data-inverse</code> will only work correctly with a boundary line that forms a complete circuit, and will layer a pale grey mask over the area outside of the designated boundary, with a 1px grey boundary line. This is the equivalent to the 'type=shape-inverse' option in {{tl|maplink}}, but with paler colours, and works for both the framed map and the fullscreen version.
|-
| <code>minimap</code>
|Set to <code>file</code> to add an external locator-style map from wikimedia commons, plus optional locator dot. A map can be placed in any of the four corners. <code>minimap=file bottom left</code>, <code>minimap=file bottom right</code>, <code>minimap=file top left</code>.
If just <code>minimap=file</code> is used it defaults to 'bottom right', but it is probably better to spell it out. (nb. With the arrival of the fullscreen link in the top-right corner, that is not now useable for a mini-map.).
(nb. Automated world map is no longer available.)
|-
|<code>mini-file</code><br>
<code>mini-width</code><br>
<code>mini-height</code><br>
|Takes the file name of a standard location map from Commons (without 'File:'), and displays it as a minimap in a corner.
mini-width and mini-height are the desired dimensions of this minimap, in pixels. Unlike pre-2024, all images can only be displayed at their given proportions. The width/height ratios are still need to correctly position the file into its corner.
|-
|
<code>minipog-gx</code><br>
<code>minipog-gy</code><br>
<code>minimap-boxwidth</code>
| Within the custom minimap this can place an optional small Red pog. Nb. The x and y are not lat and lon values. They relate to a 100x100 grid of the minimap at whatever size and ratio it is set to. The origin is top,left of the mini map, so if minipog-gx=25, it will be a quarter of the way across. (Some location maps have a highlighted location already, so leaving these two parameters undefined gives the map without a dot.)
minimap-boxwidth=xx can optionally use an open red box instead of a Red pog. xx = the width of the box, in percent of the width of the minimap. The height is then matched in proportion to the actual map. The box is centered at spot corresponding to (minipog-gx,minipog-gy). In general anything much below xx=15 will be better served by a dot. The required width will need some trial and error to pin down. If xx=0 or minimap-boxwidth is undefined, a Red pog is used.
|-
|or (shortform instead)
<code>mini-locator</code>
|
Alternative, shortform to replace all the above minimap parameters, they can all be rolled into a single line of comma-separated values. (optionally put the ''filename'' element in "double quotes" if it has commas or inverted commas within the filename). Example:<br>
<code><nowiki><!--</nowiki> mini-locator=[filename], [position], [width]px[height]px, [dotposx]%, [dotposy]%, [boxwidth]--><br>
mini-locator=Leicester UK ward map 2015 (blank).svg,top left,132px153px, 38%,60%,16</code>
|-
|<code>scalemark</code>
|By default or with scalemark=1, a scale line with guide to the scale of the map is supplied near the bottom right corner of the map. If this is not required - e.g. if it interfers with a map element at that point - it can be turned off by setting this to '0'. To shift it further left, e.g. to avoid a minimap, or to avoid a 'busy' bit of the map, enter the number of pixels to shift left. If not set by hand it will automatically shift to the left of any bottom-right minimap.
|-
! colspan="2" style="background-color:lightblue; color: inherit" | Label and mark parameters.
|-
| colspan="2" style="background-color:lightblue; color: inherit" |Multiple marks can be set on the map, being an un-numbered version, and a potentially 'limitless' series running from 1 upwards. The first numbered one, (mark1, label1, etc.) is a 'master marker' and its values are inherited by the other numbered markers unless set individually. (nb: label1= and mark-title1 etc are not inherited). All label, mark and full-screen parameters are available for each numbered marker.
'''Default set''': For added flexibility a parameter set with a 'D' value instead of a number will override the master values with a more general Default. The 'cascade of inheritance' (eg for 'shape3=') runs as follows: is there a setting for shape3? if not then is 'shapeD' set? If not, then is 'shape1' set? if not then use the underlying default, which in this case would be shape=image.
On the Fullscreen map, the dots always number from 1, and will always run in sequence. If using auto-numbered dots on the framed version you can start with shape1 and provided all the dots are numbered, the sequence will match.
|-
! Parameter
! Description
|-
| <code>shape</code>
| OSM Location map can use either an external image as a location marker, or one of various shapes. The <code>mark-size</code> setting will require different numbers of items for different types of shape.
*Shapes that just need a width: '''circle, square, i-triangle'''.
*Shapes that can specify both width and height: '''box, ellipse, diamond, triangle, panel''' and all the new 'clip-path' shapes.
*NEW for 2025, it is possible to use clip-paths to define solid shapes, of which the following are now available (A 'D' indicates a surrounding outer line, and a 'DD' is a double outer line.): '''squareD, squareDD, circleD, circleDD, boxD, boxDD, ellipseD, ellipseDD, triangleD, triangleDD, diamond, diamondD, diamondDD, thincross, cross, crossD, fivepointstar, fivepointstarD, sixpointstar, sixpointstarD, sevenpointstar, sevenpointstarD, eightpointstar, eightpointstarD, ring'''
*'''rule''' provides a line centred at its coord point. It requires a length (and optionally a gap, which makes a doube line)
*'''curveA''' and '''curveC''' both add an arc-line ending with an arrow, which will point either Clockwise or Anticlockwise. They draw a 90deg arc above/below an approximate mid-point, having a length set by mark-size, and using shape-outline attributes.
*An '''image''', which is the default, will be 'Red pog.svg' unless a different file is specified using <code>mark = </code>. A width is required, and if the image is not square, a height value will ensure more accurate positioning.
* If '''numbered''' dots are required, use <code>shape1=n-circle</code> Subsequent markers will use the same setting, and automatically add its dot number. <code>shape1=l-circle</code> will do the same but with letters, and any of the built in shapes can be used with n- or l- prefixes.
|-
| <code>shape-color</code><br /><code>shape-outline</code><br /><code>shape-angle</code><br /><code>numbered</code>
|*shape-color sets the shape infill to a color, and optionally opacity. eg <code>shape-color=hard blue,60</code> will give a 60% opacity. (This replaces <code>shape-opacity</code> which is no longer needed but still works for now). As with all the opacity settings here, 0 and 100 are opaque, 1 is transparent. All the color parameters can either use the color names in the table above, or use hex triplets (e.g. #FF0000), as described at [[Web colors]]. If using hex, a further two digits can optionally set opacity. Default = dark red.
*shape-outline sets several different outline attributes: '''color,line-thickness,opacity,css-style'''. (line-thickness is in px, with 0 meaning no outline. Opacity works the same as for shape-color. css style has various options of which the most useful are solid, dotted, dashed or double. The 'style' allows some shapes (circle/square and rule) to be given attributes to the outline. For example <code>shape-outline=hard red,8,30,dotted</code> would give a 'dotted halo effect'.
<code>shape-outline=hard blue,5,100,double</code> would potentially add a second ring around a blue circle, for example.
(Note - CSS limitations mean that while triangles, clip-path shapes and arrows can be assigned an outline, they are made by drawing a larger solid shape beneath. So these cannot be 'hollow' shapes, and cannot be given line styles).
*shape-angle rotates a shape or image by the specified number of degrees (minus for anti-clockwise). This is useful to change the direction that a triangle points, fit a box to a feature on the map, or to control where rules and arrows point to, for example.
The <code>numbered = </code> parameter is used with numbered shapes, to override the automatically allocated number, and can be any text.
|-
| <code>mark</code>
| The name of a Wikimedia commons file, which is used as the marker. Default is Red pog.svg. Other pog colours are available (see [[Commons: Map pointers, dotset 1]]), and a large range of map markers can be found at [[Commons:Location markers]] and [[Commons:Category:Map icons]]. Nb, at present if a default shape-outline is set, this appears around images as well. Use shape-outline=transparent to remove it.
|-
| <code>mark-size</code> <br />(<code>mark-dim</code>)
| Size and dimensions of a marker. <code>mark-size</code> is used by both <code>shape</code> and <code>mark</code> and can define the width, height and (where relevant) corner radius of the marker symbol. A single value sets both the width and height of the mark or shape in pixels (no 'px' required, default is 12). A second, comma-separated value will set height, and a third will be used by square, box or panel to define the corner-radius. If only a text label is wanted with no marker, set <code>mark-size=0</code>.
<code>mark-dim</code> is no longer needed, but retained for compatibility, and is used by non-square marks. default is 1, i.e. equal width and height. A value of 0.7 will give a typical landscape rectangle. 1.4 will give a typical portrait rectangle. It is ignored if a height value is set.
NOTE: Of particular use with n-circles and n-square, the text-size for the number is based on the shape's 'height' value. If only one value is supplied, that is both width and height. By supplying a second value, (eg: mark-size=13,11) the text-size of the numbers can be adjusted in relation to the size of the shape.
|-
| <code>mark-coord</code>
| Latitude and longitude coordinates of the marker point. Use the format <code><nowiki>mark-coord={{Coord|lat value|lon value}}</nowiki></code>. Used by <code>shape</code> and <code>mark</code> as well as the related <code>label</code>. If the location is outside the area of the map, it will not appear. (for backwards compatibility mark-lat and mark-lon still work, but are not the preferred method.)
|-
| <code>label</code>
| Text to appear alongside a mark or shape. If left blank then a mark will show without a label. If only a text label is wanted with no marker, set <code>mark-size=0</code> If there is no label and a mark-size=0 this is an invisible marker, but will still feature on the Fullscreen option.
New for 2024: Where label text is too long to fit on a single line, using <code>label = </code>, any line can now be split as many times as desired using the ^ hat symbol. This supersedes the now deprecated <code>labela = </code> and <code>labelb = </code>. To put an actual ^ in your label, use the <code>{{Tt|&Hat;}}</code> entity.
Text that strays too close to the right edge of the frame may now 'automatically wrap' to the next line. However this will not be 'balanced' in the way the inserted linebreaks are, so in most instance a manual break will be preferable.
All label text is now standard wiki text. This means you can use '''<nowiki>'''bold'''</nowiki>''' and ''<nowiki>''italic''</nowiki>'' or even your own <nowiki><span...</span></nowiki> items, if you really need to. Wikilinks are also possible, and the label will become a live link, but note that they will then take standard wikilink colors, including red-link for non-existent links.
|-
| <code>label-size</code>
| Sets the text size for the label, in points. default = 12
New for 2024, label-size can have additions to place a background fill and/or an outline around a label. text-size=12,background will use the standard background map-color as a solid fill under the area of the text - handy if the label is being confused by other map details. Adding 'outline' will place a 1px wide black line around the text to make it more noticeable - although it will not cope well with any line breaks. They can be used separately or in combination: eg <code>text-size=12,background,outline</code>.
Two further colours can be used for the fill: If 'paleground' or 'beigeground' is used instead of 'background' it will give a fill that is respectively paler or darker.
|-
| <code>label-color</code>
| Sets the text colour for the label. The standard colour labels (red, black, grey, white, blue, green etc...) all work as described, but can be rather strident on the OSM map. Each of the colors below have three standard shades - soft, hard and dark. Default=dark grey. (Actually defaults to #222211, slightly darker than 'dark grey', so it is prominent when someone just puts a single pog and label. A comma-separated number after the color will set the opacity (%). eg dark-blue,40 will give 40% opacity.
Under normal usage, the following label color scheme should be followed:-
* {{font color|#{{OSM Location map/color|soft grey}} |soft grey}}: Settlements = soft grey (Subject of the map can be hard grey and larger label-size). Map areas with darker or busier backgrounds may need to move a shade darker to hard grey and dark gray respectively.
*{{font color|#{{OSM Location map/color|soft blue}}|soft blue}}: Rivers, lakes, sea areas etc = soft blue (Works well on top of OSM blue areas)
*{{font color|#{{OSM Location map/color|soft green}}|soft green}}: Parkland, national/regional parks, gardens, forests etc = soft green works well on top of OSM green areas. (hard green may be desirable in forests or for the subject of the map)
*{{font color|#{{OSM Location map/color|hard red}} |hard red}}: Use with care = dark red goes nicely with a red pog but can look like a redlink, and since text can now include live links, this and other 'link-indicative' colours are best avoided)
*{{font color|#{{OSM Location map/color|dark grey}} |dark grey}}: Individual sites = dark grey (no need to specify if the site has a single label to accompany a red pog)
*{{font color|#{{OSM Location map/color|dark brown}} |dark brown}}: Information panel = dark brown (works nicely on a 'pale brown' panel, for example)
{{See|Template:OSM_Location_map/doc#Full_table_of_color_options}}
It is also possible to specify any HTML Hex color using the six-figure hex-code, eg #AAAAAA, but sticking to defaults maintains consistency between pages. If no color is specified the text will have a default of 'dark grey'. (nb: Any value other than one of the named colors above or a hex code will return as 'dark grey')
|-
| <code>label-pos</code><br /><br /><code>ldx</code><br />(or <code>label-offset-x</code>)<br /><br /><code>ldy</code><br />(or <code>label-offset-y</code>)<br /><br />(in pixels,<br />- = up and left,<br />+ = down and right)
| At its simplest, label-pos sets the position of the label, relative to the marker: left, right, top bottom or centre/center. Default=left. Top, bottom and center text is center-justified, whereas left and right align against the marker. The label aims to be an appropriate distance from the edge of the marker, but irregular shapes and larger sizes may need further adjustment using the label-offsets - now in a shortened form of ldx and ldy. These 'offset' values can also move the label much further distances, and make use of the line-drawing additions shown below.
NEW for 2025: As an alternative to the 'top, bottom, left, right' terminology, you can instead use commpass-points: 'north, south, east, west'. In addition to these [[Cardinal direction]]s, the four ordinal points (northeast, southwest etc) can also be used, to further control the label position.
Center (or centre) text is placed over the middle of any shape, point, rule, box or panel. Line breaks will 'spread' the lines above and below the center-point.
Nb: in the case where the shape is a '''panel''', the text is placed 'within' rather than to the side of the shape, and left and right become 'justified' instructions for text that will wrap within the panel, rather than the 'positional' instruction it normally is.
'''Line-drawing options:''' By adding additional values to the <code>label-pos</code> parameter, and used with any of the above position indicators, a range of line-drawing options can be accessed.
*'''with-line''': Use ldx and ldy to indicate a distance away from the shape and provide a suitable line beltween the two. For example <code>label-pos2=left,with-line| ldx2=-15| ldy2=-3</code> would draw a line from the mark to that offset, and position the label to the ''left'' of that new point.
*'''n-line''': Works very much like 'with-line', but is for use with numbered shapes. If several numbered dots overlap this 'extracts' the number from its dot, and puts it on the end of the connecting line. If there is also a text label, the number and the label are both printed.
*'''mark-line''': This one does not actually use the re-positioning features of ldx or ldy, and does not engage with the label. Instead it draws a connecting line from this shape to the previous mark. Thus <code>label-pos2=right, mark-line,2</code> will deal with the label as normal, and also draw a line between mark2 and mark1. It will set a line-thickness of 2px, using the color set for shape-outline2. Two further comma-separated values can be optionally added: a CSS line-style (solid, dashed, or dotted) and a gap size, which has the effect of drawing two parallel lines of the given thickness and style, with the specified gap between. eg <code>label-pos=top,mark-line,3,dotted,6</code>. A gap of 1 or undefined draws a single line.<br />Summary:label-pos=''position'',mark-line,''line-width,style,gap-width''.
*'''photo-panel''': This uses ldx & ldy, and creates a panel (default height of 50px) to display both the label text and an optional photo, with a connecting line back to the mark/shape. As well as using the value in 'mark-image' (to find a picture to show), it requires two further comma-separated parameters: a dimension value for the photo and the required total width of the panel. eg <code>label-pos=left,photo-panel,0.8,110</code>. The position of the centre of the panel is set by ldx= and ldy=. The left or right pos will indicate the position of the photo within the panel. The label will then be centered in the remaining portion of the panel. An optional final parameter can set the height - especially useful if not using a photo. eg <code>label-pos=left,photo-panel,0,90,24</code> gives a panel for a single line of text and no photo.<br>Summary:label-pos=''position'',photo-panel,''photo-dimension,panel-width,panel-height''.
|-
|<code>label-angle</code>
| It is possible to specify a <code>label-angle = </code>, which will pivot the label text around the centre of the marker point by the specified number of degrees. (+ve angle rotates clockwise, -ve anticlockwise) If mark-size is set to zero, this has the effect of a free-floating label with no marker, useful for various geographic and linear features. For stylistic consistency all settlement names should not be given an angle.
|-
|<code> label-spacing</code>
|Extra space can be added between each letter in a label. For example <code> label-spacing12 = 4 </code> adds 4 pixels between each letter in label12. (default =0) (A controlled wide-spaced text can be achieved, without having to put real spaces in between the letters)
|-
|<code>label-height</code>
|Where there is a multi-line label, this increases or decreases the gap between lines as a % in relation to the label-size, eg <code>label-height12=90</code> would give very closely spaced lines. (default = 120)
|-
! colspan="2" style="background-color:lightblue; color: inherit" | Additional content for Full screen link
|-
| colspan="2" style="background-color:lightblue; color: inherit" |The 'full screen' map uses the same OSM base map, in a different map environment, including the option for users to scale in and out, to pan across the map, and to find (via the 'More details' button) other maps and satellite imagery for the location. It also includes numbered markers, for which tooltip-style titles, and image thumbnails with captions can be brought up. This makes most sense where there are several markers on the map. The content for this facility is set with the following three parameters - which need to be numbered for each mark as for the other mark attibutes:
|-
! Parameter
! Description
|-
| <code>mark-title</code>
| This title appears as a tooltip and also a thumbnail title, accessed by clicking the marker. if <code>mark-title=none</code> that will exclude that marker from the full screen map. (it will still show as normal on the main map).<br />New for 2024, if a wikilink item is included within mark-title, it will now make the mark a live-link on the framed map. Only the first link is used as a link, but the whole text is shown as a tooltip, when hovering over the mark/dot.
|-
| <code>mark-image</code>
| This provides a pop-up thumbnail image when the marker is clicked. Include only the image name from Wikimedia commons etc. (i.e. no brackets, or 'File:').
|-
| <code>mark-description</code>
| Caption text, which will either accompany a pop-up photo, or if no photo then as a text box, when the marker is clicked. This can include wikilinks etc., to link on to additional relevant articles.
|-
! colspan="2" style="background-color:lightblue; color: inherit" | Arc Text: label placed on an arc
|-
| colspan="2" style="background-color:lightblue; color: inherit" | Arc-text parameters allow multiple lines of text to be placed along on arc, with parameters to control the tightness of the circle (radius) and the looseness of the text (gap). Changes in radius will also have an effect on the gap size, with a larger radius opening out the gap. Trial and correction will be needed to get the shape and effect desired. As a general rule, identify the coordinates of the required start-point, then choose a starter-pattern from the selection at [[template: OSM Location map#Text on an arc|OSM Location map#Text on an arc]], and then adjust parameters to fit the requirement of the map. As an alternative to the 8 separate parameters, there is an alternative comma-separated shortform version that can be used instead. The parameters below are for set A. Any capital letter afrom A to Z is available. (Note: excessive use may overload your map with too many text arcs.)
|-
! Parameter
! Description
|-
| <code>arc-coordA</code>|| coordinates of first letter, using =<nowiki>{{coord|xxx.xx|yyy.yy}}</nowiki>
|-
| <code>arc-textA=</code>|| Add your text here
|-
| <code>arc-angleA</code> ||in degrees, -180 to 180. 0 will start as horizontal, -90 straight up, 90 straight down
|-
| <code>arc-gapA</code> ||1= a notional 'standard'. 0.2 is very tight, 10 is very wide. Applying a negative gap will invert the letters and run the other way around the circle
|-
| <code>arc-radiusA</code> ||1= a notional 'standard' 0.5 is quite a tight circle, 8 is so wide as to be almost flat
|-| <code>arc-text-sizeA</code> ||standard font sizes. If undefined, default is 12
|-
| <code>arc-text-colorA</code> ||sets text color. #000000 color hexes and standardised OSM Location map colors are accepted
|-
| <code>ellipse-factorA</code>||(optional) will squash or stretch the circle. 1= notionally circular, 0.5 to 1.0 will flattern top and bottom, above 1.0 flattens the sides.
|-
|<code>arcA</code>|| A Comma-separated multi-value parameter to replace all of the above (Note you can use lat and lon decimal values or the {{tl|coord}} template call). (optionally put the ''text'' element in "double quotes" if you wish to include commas or inverted commas within the text). Example:<br>
<code><nowiki><!--</nowiki>arcA=text, lat, lon, [size]px, color, [angle]deg, gap, radius, ellipse--><br>
arcA="River Soar, canal",52.6208,-1.1469,10px, hard blue, 128deg, -1.0, 0.55, 1</code>
|}
===Comparison with {{tl|Maplink}}===
Since May 2018 it has also been possible to create a map in a frame via {{tl|Maplink}}, which in some respects does a similar job to OSM Location map. In both cases a static map image can be added to an article, for anywhere in the world, pulling in the map from OpenStreetMap data. The differences are in what they can and can't add to the base map. Maplink, in both its framed and fullscreen versions, can add points (numbered or icon-style pointy dots), and various, lines and areas generally imported from OpenStreetMap via wikidata Q values. A large part of its role is in providing an 'automated' map, using wikidata to give quick results, often within an infobox. With OSM Location map the process is much more hand-built, drawing together elements from the article. The framed map can show a much richer selection of dots, shapes, graphics, overlays, images and especially text to convey specific details relevant to a particular article. It has now also been enabled to show Q value (at present limited to boundaries, and roads). The fullscreen equivalent is much closer to a Maplink fullscreen item, re-using as point-items the details supplied for the framed map.
==Underlying technology==
OSM Location map now makes use of the <nowiki><mapframe></nowiki> module, which in turn displays the requested portion of the globe as drawn from the [[Open Street Map]] global maps. This template then generates the overlay text and graphics on top of the base map. Until 2023 everything within the frame was produced through the template {{tl|Graph:Street map with marks}}, created by [[User:Yurik]] which itself made use of the [[Vega and Vega-Lite visualisation grammars|Vega visualisation package]] which displayed both the base map and a range of text and graphic features. In its original (2016) incarnation the result was a rendered bitmap image, making the viewing overhead very low compared to the more complex preview-mode rendering, but with a low-resolution output. In 2020, the bitmap creation process was withdrawn, so from then on rendering has been carried out for read as well as preview modes, resulting in a much clearer page view, but adding considerably to the resource overhead.
In April 2023 the Vega package was withdrawn, along with all of its 'graph' dependencies, amid security concerns. After a protracted period of uncertainty (and with the Graph replacement still under development in April 2025) a way was found to replicate the map outcomes previously handled by 'graph'. By making use of [https://www.mediawiki.org/wiki/Help:Extension:Kartographer Extension:Kartographer], through its <nowiki><mapframe></nowiki> tag, the base map can be displayed within an HTML <nowiki><div></nowiki> element. Inline [[CSS]] coding could then be used to overlay graphics and text within the resulting frame. The Mercator 'coords' are converted to (x, y) pixel values to match the map location and zoom level. This switch not only got the existing stock of 5,600 templates back in a working state, but also allowed the implementation of additional graphical items. A further gain was that standard use of wikimarkup and wikilinks became possible, allowing the maps to be a 'live' component of the page, rather than simply an illustration.
In 2025 another hurdle was overcome by rewriting the template as a [[Lua]]/[https://www.mediawiki.org/wiki/Extension:Scribunto Scribunto] module, to replace the much more cumbersome wiki-markup. The outcome was that pages could load in about 20% of the time, and use far less memory and other resources. It also allowed unlimited numbers of marks, should they be needed. The page load-times/display demands in particular had been a potential constraint to further adoption, which has at least been alleviated by the rewrite, as well as facilitating a few extra shapes, controls and features.
The full screen option, which can be clicked through from the framed map, provides an entirely different mapping approach, although also using Kartographer to access the OSM base-map data. This uses <nowiki><maplink></nowiki> to provide a fullscreen interactive map that can be panned and zoomed. It also replicates (as numbered markers) the various marks and colors from the framed map. These can then be given extra content, by way of a title, image and description, along with displaying the coordinate values. Since 2024 maplink can also show the locations of other 'nearby' wikipedia pages. The result is a map-based page that offers another way of engaging with the article content, and navigating to related material.
Further development of mapping technology is currently not a [[Wikimedia Foundation]] priority area. Having established {{tl|maplink}}, which initially just created a text link to a full-screen map, to the point where it provides a framed image with data directly from wikidata, there are no active developments for different mapping solutions. (Please tell us if you know differently!). The processor overheads are reduced by showing a static image in the frame, which can be clicked to become interactive in fullscreen. Maplink is now an effective and widespread solution within info-boxes, where the map is automatically generated from already available data.
This template is geared rather to producing a hand-editable map, in which the area displayed and the selection of items and labels included are selected, edited, and added to, to suit the specifics of the subject in hand. A third approach, which is not currently supported by any mapping template, would be to draw the data from external live data, such as using a SPARQL query, to generate, for example large-area scatter-plots to show different distributions across a map. The security and maintainability aspects of such tasks, as with other graph visualisations of live data, are a major barrier to implementation.
The technology used here (Kartographer plus inline-css) is probably best described as 'reasonably well developed'. As of late April 2025 it would seem to be in a mature, stable and sustainable state, so should not see the kind of hiatus experienced by 'Graph' in 2023. It is highly likely that this or a comparable solution will still be available into the future. Of the possible evolutions over time one may be through SVG-based graphics, allowing a richer graphical content and maybe even moving towards a proper visually-based editing environment. In the opposite direction, maps that need to show large numbers of dots might be better served by drawing from an external (wikimedia-based) data file. But these should most likely be seen as alternatives rather than replacements for this template.
== See also ==
* [[Wikipedia:Maps for Wikipedia]] - A list of mapping tools available on Wikipedia
* {{tl|Location map}} - Placing one marker/label by latitude/longitude.
* {{tl|Location map+}} - Placing unlimited list of markers/labels.
* {{tl|Location map many}} - For displaying multiple marks using latitude and longitude.
* [[:Commons:Category:Map pointers]] - List of the many Wikimedia Commons pointer symbols.
* {{tl|Overlay}} - Allows image numbered, textual number, or color tag overlays to be positioned over an image to indicate particular features in the image.
<includeonly>{{sandbox other||
<!-- Categories below this line, please; interwikis at Wikidata -->
[[Category:Location map templates|Location map templates]]
[[Category:Map formatting and function templates]]
}}</includeonly>
msv3nzv9ied6162zoux069q7mr8s7jd
886336
886335
2025-06-13T16:59:22Z
KartikMistry
10383
[[:en:Template:OSM_Location_map/doc]] માંથી આયાત કરેલ ૧ પુનરાવર્તન
886335
wikitext
text/x-wiki
{{Documentation subpage}}
<!-- Please place categories where indicated at the bottom of this page and interwikis at Wikidata (see [[Wikipedia:Wikidata]]) -->
{{High-use}}
{{Lua|Module:OSM Location map}}
==Technical issue==
{{tracked|T334940}}
{{warning|heading=Even Newer version now live, as of 21 March 2025!| Not only does this continue and extend the range of graphical elements, but now allows unlimited numbers of dots/marks, and much faster load times with lower resource demands! See the [[Template:OSM Location map/Return to service|Return to service]] article for details on both the 2024 and 2025 features.}}
==Major rewrite for 2025==
In 2024 the template was finally able to return to something close to full functionality, following the 2023 withdrawal of the 'graph'/VEGA capability that was previously relied on. The use of inline CSS meant maps were once again possible, but the wiki-template solution was very slow and resource-heavy, as well as limiting the number of dots and marks. A 2025 overhaul has resulted in a template now entirely delivered via a Lua/Scribunto module, significantly improving the loading times and enabling additional features and 'unlimited' dots and graphical elements, whilst aiming to retain full compatibility with existing maps. Full details of the 2025 additions are at [[Template:OSM Location map/Return to service#Additional features, new to 2025]].
==Documentation==
{{OSM Location map
| coord = {{Coord|52.6909|-1.2163}}
| zoom = 13
| width = 300
| height = 300
| caption = Old John Tower in [[Bradgate Park]], Leicestershire
| minimap = file
| mini-file = Leicestershire UK location map.svg
| mini-width = 80
| mini-height = 68
| minipog-gx = 46
| minipog-gy = 44
| scalemark = 90
| mark-coord = {{Coord|52.69632|-1.22391}}
| mark-size = 11
| label = Old John^Tower
| label-size = 13
|label-color = dark grey
| label-pos = right,photo-panel,1.3,120
| ldx=80| ldy=-60
| mark-title = [[Old John]]
| mark-image = Old John (Leicestershire).jpg <!-- used within photobox and the full screen linked page -->
| mark-description = Hill top tower of Old John, in [[Bradgate Park]]
| label1 = War memorial
| label-pos1 = left
| label-size1 = 10
|label-color1 = hard grey
| mark-coord1 = {{Coord|52.694944|-1.2254584}}
| mark-size1 = 7
| mark-title1 = War memorial at Bradgate
| mark-image1 = Leicestershire Yeomanry Memorial, Bradgate Park - geograph.org.uk - 885190.jpg
| mark-description1 = Leicestershire Yeomanry Memorial, [[Bradgate Park]]
| label2 = B R A D G A T E P A R K
| label-pos2 = top
| mark-coord2 = {{Coord|52.6909|-1.2163}}
| mark-size2 = 0| label-size2=11| label-color2=soft green
| mark-title2 = Bradgate Park
| mark-image2 = Bradgate Park - geograph.org.uk - 885175.jpg
| mark-description2 = With its origins as a medieval deep park, [[Bradgate Park]] is now a popular visitor destination.
| label3 = Bradgate
| labela3 = House
| label-pos3 = top
| mark-coord3 = {{Coord|52.686698|-1.2111676}}
| mark-size3 = 7
| mark-title3 = [[Bradgate House, Bradgate Park|Bradgate House ruins]]
| mark-image3 = Bradgate House - geograph.org.uk - 883366.jpg
| mark-description3 = Ruins of the 16th century [[Bradgate House (16th century)|Bradgate House]], home of the Grey family
| arc-textA = Cropston Reservoir
|arc-text-colorA = soft blue
| arc-angleA = -70
| arc-gapA = 2.2
| arc-radiusA = 0.3
| arc-coordA = {{Coord|52.6888|-1.2010}}
|arc-text-sizeA = 9
| mark-coord4 = {{Coord|52.6934|-1.1965}}
| mark-size4 = 0
| mark-title4 = [[Cropston reservoir]]
| mark-image4 = Boathouse and Cropston Reservoir - geograph.org.uk - 48496.jpg
| mark-description4 = It was opened in May 1871 in a corner of Bradgate Park, to supply water for the growing town of Leicester.
| label5 = Old John ^Car Park^(Hunt's Hill)
| label-pos5 = left
| mark5 = BSicon PARKING.svg
| mark-coord5 = {{Coord|52.70037|-1.22784}}
| mark-size5 = 11
| mark-title5 = Old John Car Park
| label6 = Hallgates
| label-pos6 = bottom
| mark6 = BSicon PARKING.svg
| mark-coord6 = {{Coord|52.69727|-1.1991620}}
| mark-size6 = 11
| mark-title6 = Hallgates Car Park
| label-pos7 = bottom
| mark7 = BSicon PARKING.svg
| mark-coord7 = {{Coord|52.68356|-1.2277007}}
| mark-size7 = 11
| mark-title7 = Newtown Linford Car Park
}}
This template provides
* A map (from [[OpenStreetMap]]) in a frame, for any location, anywhere in the world, at scale from global to an individual building.
* Optional multiple markers, text labels, numbered dots, live wikilinks and other graphical elements.
* A link (top right corner) to a fullscreen interactive version, which can have 'dots and details' from the article/map.
* A mini-locator map can be shown to provide context for the map.
==Purpose==
'''OSM Location map''' allows an editor to include a map in a frame with a zoom level appropriate to the topic. A scale marker is provided in the bottom right corner. This is at best a rough guide to the distances on the map, as the map projection results in scale changes depending on the latitude. Allowance has been made for this, but it is useful only as a guide to the maps scale, not a reliable measuring tool.
The multiple marks, images and labels can be a shape or image, include a text label, and potentially 'pointer-lines' and other graphical elements. The fullscreen map on the other hand will show pointer marks, which can be given popup thumbnail images and captions, as well as providing links to access other maps and satellite images, and an option to show locations of other articles nearby.
A selection of example maps, along with links to an even wider range if use-types, is at [[Template:OSM Location map/examples]].
== Usage Examples==
===Scenario 1: Minimal version===
An unadorned map centred on a latitude and longitiude coordinates, via a {{tl|coord}} value. Set the zoom to give a scale that fits the subject (0=whole world, 18=a street). With just these options set, all other parameters use the defaults, or are left unused. It also gives a link to the interactive fullscreen version.
{|
|-
| <syntaxhighlight lang="wikitext">{{OSM Location map
| coord = {{coord|53.4146|-4.3341}} <!--latitude/longitude coordinates for map's center -->
| zoom = 15 <!--zoom 0=whole world, 18=a street.-->
}} </syntaxhighlight>
|}
{{clear}}
===Scenario 2: Single marker with text label===
A default 'Red pog' marker and accompanying label. Additional items (the last three parameters) don't show up on the page, but give extra information when hovering/clicking on a dot in the fullscreen version.
{{OSM Location map
| coord = {{coord|53.394|-4.450}}
| zoom = 13
| width = 300
| height = 150
| caption = The 'Llanfechell Triangle' standing stones are north-west of [[Llanfechell]].
| label = [[Llanfechell#Llanfechell Triangle|Llanfechell Triangle]]
| mark-coord = {{coord|53.3966|-4.46204}}
| label-pos = right
| mark-title = [[Llanfechell#Llanfechell Triangle|Llanfechell Triangle]]
| mark-image = The Llanfechell Triangle - geograph.org.uk - 1260817.jpg
| mark-description=Group of three prehistoric standing stones thought to date to the Bronze Age, at [[Llanfechell]], [[Anglesey]]
}}
{|
|-
|<syntaxhighlight lang="wikitext">
| mark-coord = {{coord|53.3966|-4.46204}}
| label = [[Llanfechell#Llanfechell Triangle|Llanfechell Triangle]] <!--text label/wikilink -->
| label-pos = right <!--right, left, top, bottom - or use compass point: south, southeast, east etc -->
| mark-title = [[Llanfechell#Llanfechell Triangle|Llanfechell Triangle]] <!--clickable link &/or tooltip -->
| mark-image = The Llanfechell Triangle - geograph.org.uk - 1260817.jpg
| mark-description=Prehistoric standing stones at [[Llanfechell]] <!--fullscreen marker content -->
</syntaxhighlight>
|}
The mark-title can optionally be wiki-linked, and the dot on the map becomes a live link, even if the page doesn't exist.
The label can also be wiki-linked, but as in the case here will display the page that contains a subsection as the live link rather than a picture of the feature as is shown on full screen. It can work well when there is a page on the specific subject.
{{clear}}
{| class="wikitable mw-collapsible mw-collapsed"
|-
! Blank code for a single marker map
! Blank code with comments
|-
|<syntaxhighlight lang="wikitext">{{OSM Location map
| coord = {{coord| | }}
| zoom =
| width =
| height =
| caption =
| label =
| mark-coord = {{coord| | }}
| label-pos =
| mark-title =
| mark-image =
| mark-description =
}}</syntaxhighlight>
|<syntaxhighlight lang="wikitext">{{OSM Location map
| coord = {{coord| | }} <!-- {{coord}} has various formats for latitude and longitude -->
| zoom = <!-- (0=whole world, 18=a street)-->
| width = <!-- width and height of the map, in pixels. Do not add px -->
| height = <!-- default is width=350, height=250 -->
| caption = <!-- Text below the map. Can include [[wikilinks]] -->
<!-- Parameters for 1st mark -->
| label = <!-- text alongside the mark. (Can include wikilink and other markup)-->
| mark-coord = {{coord| | }} <!-- lat and lon location for the marker -->
| label-pos = <!-- default position is left. It can also be right, top, bottom. -->
| mark-title = <!-- tooltip and/or clickable link on the map and in fullscreen -->
| mark-image = <!-- image for fullscreen map content. Use a filename from Commons, without File: -->
| mark-description = <!-- text shown on fullscreen map when clicking on mark -->
}}</syntaxhighlight>
|}
{{-}}
==Multiple markers, labels and images==
In addition to the un-numbered mark parameter set, there are 60 numbered ones. These are otherwise identical to the one above, but the name terminates in a number (1-60). Each mark and label has its own set of parameters ({{para|mark1}}, {{para|mark-coord1}}, {{para|label1}}, {{para|label-pos1}} etc...{{para|mark2}}, {{para|mark-coord2}}, {{para|label2}}, {{para|label-pos2}} etc.) Values can be inherited either from the 'mark1 master parameter set', or from a special 'markD' Default set that provides override defaults. When set, these values are inherited by the other numbered sets to avoid having to repeat for each, whilst they can still be set individually where required.
===Scenario 3: Numbered dots with labels and auto-caption===
{{OSM Location map
| coord = {{coord|45.438|12.3285}}
| zoom = 13
| float = right
| nolabels = 1
| width = 380
| height = 240
| title = Venice
| caption = Some key locations:
| auto-caption = 14
| shapeD = n-circle
| shape-colorD = green
| label-colorD = dark blue
| shape-outlineD = white
| label-sizeD = 12
| label-posD = left
| mark-sizeD = 17
| ldxD = 0
| ldyD = 0
| label1 = Rialto Bridge
| mark-coord1 = {{coord|45.438|12.336}}
| mark-title1 = [[Rialto Bridge]]
| label-offset-y1 = -6
| label2 = Piazza^San Marco
| mark-coord2 = {{coord|45.433889|12.338056}}
| mark-title2 = [[Piazza San Marco]]<br />[[St Mark's Basilica]]<br />[[St Mark's Campanile]]<br />[[Horses of Saint Mark]]<br />[[Doge's Palace]]
| ldy2 = -3
| label3 = Grand Canal
| mark-coord3 = {{coord|45.4315|12.336}}
| mark-title3 = [[Grand Canal (Venice)]]
| label-pos3 = right
| label4 = Bridge of ^Sighs
| mark-coord4 = {{coord|45.434056|12.340861}}
| mark-title4 = [[Bridge of Sighs]]
| label-pos4 = top,with-line
| ldx4 = 8 | ldy4 = -37
| label5 = Santa Maria ^della Salute
| mark-coord5 = {{coord|45.430833|12.334444}}
| mark-title5 = [[Santa Maria della Salute]]
| label-pos5 = bottom
| ldx5 = -14
| label6 = Cannaregio
| mark-coord6 = {{coord|45.446|12.327}}
| mark-title6 = [[Cannaregio]]
| label-pos6 = northwest
| label7 = Isola di
| labela7 = San Michele
| mark-coord7 = {{coord|45.447|12.347}}
| mark-title7 = [[Isola di San Michele]]
| label8 = Castello
| mark-coord8 = {{coord|45.4357|12.3485}}
| mark-title8 = [[Castello, Venice]]
| label-pos8 = top
| label9 = Dorsoduro
| mark-coord9 = {{coord|45.431|12.326}}
| mark-title9 = [[Dorsoduro]]
| label-pos9 = left
| label10 = Santa Croce
| mark-coord10 = {{coord|45.437661|12.319208}}
| mark-title10 = [[Santa Croce (Venice)]]
| label11 = Santa Lucia
| labela11 = railway station
| mark-coord11 = {{coord|45.440833|12.320833}}
| mark-title11 = [[Venezia Santa Lucia railway station]]
| label-pos11 = top | ldx11 = -10
| label12 = Tronchetto
| mark-coord12 = {{coord|45.440556|12.305}}
| mark-title12 = [[Tronchetto]]
| label-pos12 = right
}}
Source-code for the map can be seen by clicking the 'Edit' link. Some noteworthy points about the numbered-dot map example:
*The dots are given numbers when <code>shape = n-circle</code> (square, triangle and diamond also available)
*Depending on use cases, and on the number and density of dots, you might choose not to set some (or any) labels, relying on captions/links/main text to explain which feature is which.
*To avoid over-writing, a label position can be adjusted in relation to its dot using ldx and ldy parameters to set + or - pixel offsets horizontally and vertically. (Down and right are +ve, up and left -ve).
*To avoid a crowded area of the map, shape4 uses <code>| label-pos4 = top,with-line | ldx4 = 8 | ldy4 = -37 </code> to move the label much further away, and add a line linking the label to its shape.
*Line breaks can be added to any label by adding a ^ symbol wherever needed, to split a long label.
*By setting <code>auto-caption=1</code>, numbered shapes are all listed within the caption, using the 'mark-title' values, which as with shape2 here, might include links or explanations of multiple different items.
*<code>auto-caption = 14 </code> can be used to specify a column width of (in this case) at least 14 [[Em (typography)|ems]]. The template then adds as many columns as that width permits. Alternatively, the number of columns can be specied directly, as can making it collapsible or collapsed (to hide/show the list). eg <code>auto-caption = collapsed columns=2 </code>
*By default, each dot will be given the same number as its 'mark-number'. If each dot is used in turn the numbers will go in sequence, and will match the numbers on the fullscreen version. Fullscreen numbers always run in sequence from 1 upwards, so if you don't use some mark-numbers, or over-ride them with 'numbered=', the fullscreen dots won't match. But they are self-documenting via their hover/click links, so that is not a problematic outcome, if that is desired.
{{tl|Flushing Meadows-Corona Park map}} is a useful real life example in template form.
====Inherited values====
Each mark and label can be given a unique set of attributes (size, colour, outline, angle, relative position, etc.) To minimise repetition of code, there is a sliding scale of inheritance that applies to each value in each parameter set. For example, if <code>label-size4=16</code> is set, that will always take precedence. If label-size4 has not been set, it will inherit the value from the special Default setting (defined using <code>label-sizeD= </code>). If no Default has been set, it will inherit the value set by the 'master parameter set', <code>label-size1=</code>. If that is also undefined it will fall back to the underlying default, which in the case of label-size is 13. The same is true of all the variables relating to marks and labels, (although not to the coordinates, labels themselves, or mark-titles, which are always unique to the particular mark they relate to.)
==Scenario 4: Using marks to add graphical elements to the map==
{{OSM Location map
| coord = {{coord|51.7802|-3.6525}}
| zoom = 14
| width = 280
| height = 280
| caption = Map of the area around [[Banwen]], South Wales, showing the Roman roads and relocation from camp to a more permanent fort.<ref>{{cite book|title=Glamorgan Inventory, Vol 1, Part 2: The Iron Age and Roman Occupation| year=1976| author=Royal Commission on the Ancient and Historical Monuments of Wales | url=https://shop.rcahmw.gov.uk/products/glamorgan-inventory-vol-1-part-2-the-iron-age-and-roman-occupation-ebook?variant=29576736997441| page=100}}</ref>
|minimap = file bottom right
|mini-file = Wales relief location map.jpg
|mini-width = 75
|mini-height = 94
|minipog-gx = 60
|minipog-gy = 78
|scalemark = 87
| map-data-inverse=Q748078 <!-- adds Neath Port Talbert boundary to interactive map -->
| map-data-text=Neath Port Talbot
| map-data-width=3
| map-data-color=#CC3333
| label-sizeD = 11
| mark-coord1 = {{coord|51.78365|-3.6553}}
| shape1 = box
| mark-size1 = 17,17,3 <!-- width, height, corner radius -->
| shape-outline1=hard grey,1,70 <!--color,line-width,opacity% -->
| shape-color1= white
| shape-angle1= -9
| label1 = Roman Fort
| mark-title1 = [[Roman auxiliaries in Britain|Roman Auxiliary Fort]]
| label-pos1 = left
| label-angle1= 0
| label-size1 = 12
| mark-coord2 = {{coord|51.7802|-3.6515}}
| shape2 = box
| mark-size2 = 44,62,4 <!-- width, height, corner radius -->
| label-pos2 = center
| shape-outline2=hard grey,4,40,dashed
| shape-color2= transparent
| shape-angle2= -20
| label-color2= soft grey
| label2 = Roman^Marching^Camp
| ldx2=-2|ldy2 = -2
| mark-title2 = Roman Marching Camp
| label-angle2= -20
| mark-coord3 = {{coord|51.7811|-3.6425}}
| label3 = Blast ^Furnaces
| shape3 = image
| mark-size3=8
| label-pos3=bottom
| shape-outline3=transparent,0
| mark-title3 = [[Blast Furnace]]s at Banwen
| mark-coord4 = {{coord|51.7820|-3.6520}}
| shape4 = curveA
| mark-size4 = 44
| shape-angle4=229
| shape-outline4=dark grey,4,50
| mark-title4=none
| mark-coord6 = {{coord|51.7861|-3.6466}}
| label6 = Roman road
| label-pos6=center
| ldy6=-7
| shape6=rule
| mark-title6 = none
| mark-size6=145
| shape-outline6=hard grey,2,60,dashed
| shape-angle6=-32
| label-angle6=-32
| mark-coord7 = {{coord|51.7838|-3.6534}}
| shape7=rule
| mark-size7=30
| shape-outline7=hard grey,2,60,dashed
| mark-title7 = none
| shape-angle7=-9
| mark-coord8 = {{coord|51.78492|-3.6596}}
| mark-size8=0
| mark-title8 = none
| label8=Powys^Neath Port Talbot
| label-size8=8
| label-angle8=-1
| label-pos8=center
| label-color8=black,40
}}
Noteworthy Points illustrated by the map of Banwen area:
* Each of these marks has a very different appearance, and some are sized to be 'map features' rather than markers.
* Mark-size can be used not just to set width, but also height, by adding a second, comma-separated value. This allows a 'box' to be given a rectangular shape. In the case of 'box' a third value sets a radius for the corners, which gives the rounded corners of the two Roman forts. eg <code>| mark-size2 = 44,62,4</code>
* The Marching fort has a transparent colour, so only the outline shows up, set to give a wide, dashed outline.
* Label-pos2 is set to 'center', so the words appear within the shape, rather than alongside it.
* There are now various ways to add a line to the map. The Roman road on this map is included by using <code>shape6=rule</code>. A rule has 'shape-outline' attributes, which are all set by <code>| shape-outline6=hard grey,2,60,dashed </code>color and line-width are required. Opacity% and 'style' are optional. The style options are solid, dashed, dotted or double.
* If you look at the fullscreen version, you will only see the first three marks. An item can be excluded by setting <code>mark-title6=none</code>, for example.
* All three of the fullscreen marks here show as white, but for different reasons. Mark-color1 is set to white. Mark-color3 is not defined, (Red pog.svg, being an image item, brings its own color), so the fullscreen dot 'inherits' white from Mark1. Mark-color2 is set to transparent, which shows as white for the fullscreen dot. Adding more diverse items to the map can affect the fullscreen colors, intended or otherwise.
* This map also shows the county boundary. See the [[Template:OSM Location map#Parameters| Parameters Section]] for details of how Qvalues can be used to show 'map-data' lines from OpenStreetMap.
* 'curveA', an arrow-curve has been added to this (contrived) example. curveC (clockwise version) is also available.
* Note also that citation refs can be included in the caption. Non-obvious location data should be backed up by sources, just like any other wikipedia information, either within the main body text or, as here, in the caption.
{{Section reflist}}
====Adding a Minimap in the left or right corner====
Many pages with info-boxes already include a locator map showing where the topic or place is located. For those that don't it might be helpful to include one within your map. It is possible to incorporate an existing 'Location map' (from Wikimedia Commons) in a corner of this map.
The width and height of the Location map both have to be decided upon, specified (and calculated so as to not shift the map away from the corner). Some location maps may already highlight the feature, but if not, an optional locator 'pog' can be placed by calculating and specifying the minipog-gx and -gy values, using a 100x100 grid across the minimap. (so gx and gy values of 25 and 50 would place a dot a quarter of the way across and half way down the minimap. It does not pick up or use latitude and longitude values. The origin (0,0) is in the top left of the minimap and the map itself defaults to the bottom right corner, but can also use <code>minimap=file bottom left</code>, <code>minimap=file top left</code> and <code>minimap=file top right</code>.
The dot will remain in the right place even if you alter minimap size. (Note, the now redundant minipog-x and minipog-y are retained for compatibility. These used the same pixel dimensions as the width and height of the map, which made them harder to calculate/guess and needed to be re-done if the size was changed. Much better to use gx and gy from here on.)
If the area of the actual map is a large portion of the mini-map, an open red box can be included instead of a dot, to show the bounds of the main map. To use this feature, simply specify the width of the required box: <code>minimap-boxwidth=xx</code> where xx = the percentage of the minimap width for the box. In general anything much below xx=15 will be better served by a dot. The box will be centered at the coordinates minipog-gx and minipog-gy. The required width will require some trial and error to pin down. The box height is then matched in proportion to the actual map and will scale if the minimap size changes.
==={{anchor |arc}}Text on an arc===
{{OSM Location map
| coord = {{coord|52.4138|-4.0855}}
| zoom = 15
| float = right
| width = 310
| height = 290
<!--text, lat,lon,size,color,angle,gap,radius,ellipse-factor-->
| arcA=Afon Rheidol,52.4109,-4.0875,11,blue,-31,4.5,0.24,0.98
| arcB=CARDIGAN BAY,52.4155,-4.0913,10,blue,150,-1,1.2
| shapeD = image
| shape-colorD = red
| shape-outlineD =
|shape-angleD =0
| mark-sizeD = 9
|ldxD=0
|label-posD=east
| mark-coord1 = {{coord|52.4133|-4.0899}}
| shape-color1 = pale grey
| shape1 = diamondD
| shape-outline1 = dark grey,3,90
| shape-angle1=3 | mark-size1=30,34
| label1=Castle ^ruins|label-pos1=southwest|ldx1=2
| mark-title1 = [[Aberystwyth Castle]]
| mark-image1=Aberystwyth Castle - geograph.org.uk - 5610614.jpg
| mark-coord3 = {{coord|52.4139|-4.0816}}
| mark-title3 = [[Aberystwyth railway station]]
|label3=Railway ^Station
|label-pos3=south
| mark-coord4 = {{coord|52.4157|-4.0875}}
| mark-title4 = [[Royal Pier, Aberystwyth]]
| label4=Royal Pier
| label-pos4=west
| mark-coord6 = {{coord|52.4148|-4.0884}}
| label6='''Old College'''
| label-pos6=west
| mark-title6 = [[Old College, Aberystwyth]]
| mark-image6=Aberystwyth - Yr Hen Goleg 20180704-04.jpg
| caption = Location of Old College and other Aberystwyth Seafront buildings
}}
Text for broader geographic features can be placed around an arc, to convey a sense of a broader area, or to follow the curve of a river, mountain range, coastline, etc. This works entirely separately from the other labels. It does not attach itself to any mark or dot, and does not create any fullscreen markers. It requires coordinates for the first letter, parameters for the starting angle, radius of arc and gap between letters.
As of 2025 there are two alternative ways of defining text on an arc: using the standard parameters is clearer but more verbose, so there is also now a condensed format, where all the values are listed as a comma separated single parameter. The end result on the map is identical.
For example, from the Aberystwyth map, the river name could be defined by either
<pre>
| arc-textA=Afon Rheidol
| arc-coordA={{coord|52.4109|-4.0875}}
| arc-text-sizeA = 11
|arc-text-colorA = blue
| arc-angleA = -31
| arc-gapA = 4.5
| arc-radiusA = 0.24
|ellipse-factorA = 0.98
</pre>
or the same item can now be defined by
<pre>
<!--text, lat,lon, size, color, angle, gap, radius [,ellipse-factor]-->
| arcA = Afon Rheidol, 52.4109,-4.0875, 11, blue, -31, 4.5, 0.24, 0.98
</pre>
{{see|Template:OSM Location map/ArcText/doc|topic= arc-text, with sample patterns and more example code}}
==Alternative marks==
If <code>|shape=image</code> the default is to show a standard 'Red pog', which is useful to mark points of interest on the map. However other images can be used, selected from Wikimedia Commons: particularly useful for the large library of map icons and symbols. Note: if a particular image file is specified in <code>mark1=</code> or <code>markD=</code>, all subsequent marks will use it as well unless they name their own image file, or set shape= to a built-in shape. If using an image that is not square, a height setting (or alternatively a dimension value) should be set to ensure the image is vertically centered. (nb, there is no longer any ability to adjust the actual dimension of any images, which will display at the proportions supplied by wikimedia commons)
===Transparent overlay===
{{OSM Location map
| lat =52.6332
| lon =-1.138
| zoom =14
| width = 300 <!-- width and height of the frame. numeric input - do not add px -->
| height = 300
| caption = Roman and Medieval Leicester: some key sites.
| scalemark =156 <!-- shifts the scalemark further to the left -->
| legendBox=Legend,115px65px1px, 175px205px <!--text, size, position,background color, text/outline color, param options)-->
| legendItem1=Main Roman archaeological sites,7
| legendItem2=Selected Medieval sites,1,42 <!--Item text, shape number [,pixels from top] -->
| mark = Leicester Town Walls map overlay.svg
| mark-coord = {{coord|52.6324 |-1.1380}} <!-- lat and lon location for the overlay -->
| mark-size = 250,312 <!--height of the overlay image, in a tracing matches the frame-->
| mark-dim = 0.8 <!--dimension (scale factor) for if the image is not square, deprecated. Using a second size value is generally easier-->
| mark-title=none <!--no marker within the linked full screen-->
| mark-coord1 = {{coord|52.632333|-1.141194}} <!-- lat and lon location for the marker -->
| mark-size1=10
| shape1=square
| shape-color1=hard red
| mark-title1 = [[Leicester Castle]]
| mark-image1 = The Great Hall Leicester Castle.jpg <!-- for use within the full screen linked page -->
| mark-description1 = The Great Hall, now with a Queen Anne frontage, is the main standing remains of Leicester's medieval castle.
| mark-coord2 = {{coord|52.6312|-1.1405}}
| mark-title2= [[Trinity Hospital, Leicester]]
| mark-coord3 = {{coord|52.6317028|-1.1379222}}
| mark-title3 = [[Magazine Gateway]]
| mark-image3 = Leicester Magazine Gateway west.jpg
| mark-coord4 = {{coord|52.63401|-1.13624}}
| mark-title4= [[Greyfriars, Leicester]], Richard III's original burial site
| mark-coord5 = {{coord|52.634644|-1.137086}}
| mark-title5 = [[Leicester Cathedral]]
| mark-coord6 = {{coord|52.64041|-1.13617}}
| mark-title6 = [[St Margaret's Church, Leicester]]
| mark-coord7 = {{coord|52.63487|-1.14136}}
| mark-title7 = [[Jewry Wall and Baths]]
|shape7=diamond
|mark-size7=12
|shape-color7=hard green
| mark-coord8 = {{coord|52.63512|-1.14072}}
| mark-title8 = [[St Nicholas Church, Leicester]] (Saxon, built from Roman material)
|shape8=diamond
|mark-size8=12
|shape-color8=hard green
| mark-coord9 = {{coord|52.6353|-1.1387}}
| mark-title9 = Roman Forum an Bassilica (site of)
|shape9=diamond
|mark-size9=12
|shape-color9=hard green
| mark-coord10 = {{coord|52.6362|-1.1388}}
| mark-title10 = Mosaic floors and Roman theatre (site of)
|shape10=diamond
|mark-size10=12
|shape-color10=hard green
| mark-coord11 = {{coord|52.6316|-1.1366}}
| mark-title11 = Roman cemetery site
|shape11=diamond
|mark-size11=12
|shape-color11=hard green
| mark-coord22 = {{coord|52.630|-1.1458}}
| mark-size22=0
| label22 = River Soar| label-size22=9
| label-pos22=top| label-angle22=-55
| label-color22=soft blue| mark-title22 = none
}}
A marker image does not have to be small and opaque. A larger overlay image (with a transparent background) can be used to show particular features not included in the base map, such as a town's former walls (see the adjacent map, which is using <nowiki>[[File:Leicester Town Walls map overlay.svg]]</nowiki> ). Such images can be created in several ways (such as tracing over a copy of the base map); they are invoked like any other marker image file. (For a detailed guide to creating and deploying an overlay for these maps see [[User:J. Johnson/OSM overlay how-to]]). (See below for some direct ways to show linear graphics.)
===Shapes===
Instead of using a wikimedia image, it is possible to show a wide range of built in shapes. Until recently this was a fairly limited list, mainly being circles, squares and triangles. With the 2025 rewrite a new selection of shapes has been implemented, using the CSS 'clip-path' capability. There are now diamond, cross, various stars and an open ring, most having variants with single or double outer lines (indicated by D or DD). Unlike circles and squares, these are only solid shapes, which carry limitations. When an outline is set for these, it is actually a larger version of the shape 'underneath', so you cannot use opacity on the 'top' version, nor can you use css styles on the outline. On the other hand all of these shapes can be stretched by setting different width and height values, rotated to any desired angle, as well as given whatever color values are desired. The full list of clip-path shapes, as of March 2025, is:
<blockquote>squareD, squareDD, triangleD, triangleDD, circleD, circleDD, diamond, diamondD, diamondDD, thincross, cross, crossD, fivepointstar, fivepointstarD, sixpointstar, sixpointstarD, sevenpointstar, sevenpointstarD, eightpointstar, eightpointstarD, ring. (box, boxD, boxDD, ellipse, ellipseD, and ellipseDD are all synonyms for their square and circle equivalents)
</blockquote>
A demo map is at [[Template:OSM Location map/Return to service]] which shows some of these shapes. Use them with care! The aim of the map is to elucidate its content, not overload with excess complexity.
===Legend/Key/Information panel within the map===
This is a new and improved (for 2025) feature, using <code>| legendBox=</code> and <code>| legendItem1=</code>. It uses the pixel x,y values (rather than coordinates), to place the top-left corner of the legendBox on a fixed point relative to the frame top-left corner, and centres an optional heading-text. The multiple <code>legendItem</code> lines nominate a particular existing shape number, and places the dot/shape and the given text as the next line in the legendBox.
<pre>| legendBox=Legend,115px65px1px, 175px205px <!-- text, size, position [,background, text color, options] -->
| legendItem1=Main Roman archaeological sites,7 <!-- this will use the shape info set for mark7, for example -->
| legendItem2=Selected Medieval sites,1,42 <!--Item text, shape number [,pixels from top] -->
</pre>
It is possible to get similar results using <code>shape=panel</code>, which allows more layout and control options, but will all use coordinate values, and specially defined shapes/dots. The legendBox alternative gives a much more concise and convenient version, where that is sufficient.
With no numbers or labels, the use-case here is showing the scatter of different types of sites. The individual dots can still be 'interrogated' for further information, particularly using the fullscreen version, but only if 'looked for' rather than presented up-front. Different page content will have different needs. A map should aim to show the most relevant information, and editors can make choices on how to best make the map a part of the article's narrative.
A note on order of drawing: The un-numbered mark is drawn first. Numbered marks are drawn starting with highest first and drawing shape1 last, which is therefore 'on top'. The overlay uses the un-numbered mark, to go below everything else. if using a 'panel' you will want a higher number to put it below its contents. In this example the legendBox is also drawn underneath any other marks. (Note: a legendBox with no items can create quick and easy map title in a box).
===Text color===
Color of label text can be specified using <code>label-color = </code>. Standard html colors can be specified by name, and any color can be specified using the hex triplets coding #xxyyzz (see [[Web colors]]). However, to create a consistent look and feel across the wikipedia maps, there are some OSM Location map specific colors, with a more muted tone range that fit well with the color scheme of the base maps. In general it is best to make use of this range, unless there are good reasons for using other particular shades for specific features.
Under normal usage, the following label color scheme can be followed:-
{| class="wikitable" style="text-align: left;"
|-
| style="background-color:#{{#invoke:OSM Location map|colorvalue|color=background}};" | {{font color|#{{#invoke:OSM Location map|colorvalue|color=soft grey}} |soft grey}}
|Settlements = soft grey (Subject of the map can be hard grey and larger label-size)
Map areas with darker or busier backgrounds may need to move a shade darker to hard grey and dark gray respectively.
|-
| style="background-color:#{{#invoke:OSM Location map|colorvalue|color=pale blue}};" | {{font color|#{{#invoke:OSM Location map|colorvalue|color=soft blue}}|soft blue}}
|Rivers, lakes, sea areas etc = soft blue (Works well on top of OSM blue areas)
|-
| style="background-color:#{{#invoke:OSM Location map|colorvalue|color=pale green}};" | {{font color|#{{#invoke:OSM Location map|colorvalue|color=soft green}}|soft green}}
|Parkland, national/regional parks, gardens, forests etc = soft green works well on top of OSM green areas. (hard green may be desirable in forests or for the subject of the map)
|-
| style="background-color:#ffffff;" | {{font color|#{{#invoke:OSM Location map|colorvalue|color=hard red}} |hard red}}
| Use with care, or not at all. Any red text can look like a wikilink redlink. Since wikilink text is now possible on the map, this would be an unwanted confusion.
|-
| style="background-color:#{{#invoke:OSM Location map|colorvalue|color=paleground}};" | {{font color|#{{#invoke:OSM Location map|colorvalue|color=dark grey}} |dark grey}}
|Individual sites = dark grey (no need to specify if the site has a single label to accompany a red pog - it will be the default)
|-
| style="background-color:#{{#invoke:OSM Location map|colorvalue|color=pale brown}};" | {{font color|#{{#invoke:OSM Location map|colorvalue|color=dark brown}} |dark brown}}
|Information panel, or legend = dark brown (works nicely on a 'pale brown' panel, for example. It is the default for legendBox)
|}
===Full table of color options===
{| class="wikitable" style="width: 70%; text-align: left;"
! colspan="4" style="background-color:lightblue" | Colors recommended for OSM Maps.
These have a more pastel shade than the standard colors, so blend well the map backgrounds (pale variants are only for panel-backgrounds, etc)
|-
|style="background-color:#{{#invoke:OSM Location map|colorvalue|color=pale red}}" |pale red #{{#invoke:OSM Location map|colorvalue|color=pale red}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=soft red}}|soft red #{{#invoke:OSM Location map|colorvalue|color=soft red}} }}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=hard red}} |hard red or Red #{{#invoke:OSM Location map|colorvalue|color=hard red}}}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=dark red}} |dark red #{{#invoke:OSM Location map|colorvalue|color=dark red}}}}
|-
|style="background-color:#{{#invoke:OSM Location map|colorvalue|color=pale green}}" |pale green #{{#invoke:OSM Location map|colorvalue|color=pale green}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=soft green}} |soft green #{{#invoke:OSM Location map|colorvalue|color=soft green}}}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=hard green}} |hard green or Green #{{#invoke:OSM Location map|colorvalue|color=hard green}}}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=dark green}} |dark green #{{#invoke:OSM Location map|colorvalue|color=dark green}}}}
|-
|style="background-color:#{{#invoke:OSM Location map|colorvalue|color=pale blue}}" |pale blue #{{#invoke:OSM Location map|colorvalue|color=pale blue}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=soft blue}} |soft blue #{{#invoke:OSM Location map|colorvalue|color=soft blue}}}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=hard blue}} |hard blue or Blue #{{#invoke:OSM Location map|colorvalue|color=hard blue}}}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=dark blue}} |dark blue #{{#invoke:OSM Location map|colorvalue|color=hard blue}}}}
|-
|style="background-color:#{{#invoke:OSM Location map|colorvalue|color=pale grey}}" |pale grey #{{#invoke:OSM Location map|colorvalue|color=pale grey}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=soft grey}} |soft grey #{{#invoke:OSM Location map|colorvalue|color=soft grey}}}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=hard grey}} |hard grey or Grey #{{#invoke:OSM Location map|colorvalue|color=hard grey}}}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=dark grey}} |dark grey #{{#invoke:OSM Location map|colorvalue|color=dark grey}}}}
|-
|style="background-color:#{{#invoke:OSM Location map|colorvalue|color=pale brown}}" |pale brown #{{#invoke:OSM Location map|colorvalue|color=pale brown}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=soft brown}}|soft brown #{{#invoke:OSM Location map|colorvalue|color=soft brown}}}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=hard brown}}|hard brown or Brown #{{#invoke:OSM Location map|colorvalue|color=hard brown}}}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=dark brown}}|dark brown #{{#invoke:OSM Location map|colorvalue|color=dark brown}}}}
|-
! colspan="4" style="background-color:lightblue" |Other named colors.
Standard HTML colors tend to look rather harsh on the OSM maps so some of them are given more subtle shades. If you need the standard HTML version, it can be set by using an appropriate '#' 6-figure hex value.
|-
| White #{{#invoke:OSM Location map|colorvalue|color=White}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=Silver}}|Silver #{{#invoke:OSM Location map|colorvalue|color=Silver}}}}
|style="background-color:#{{#invoke:OSM Location map|colorvalue|color=Beige}}; color: inherit"|Beige or beigeground #{{#invoke:OSM Location map|colorvalue|color=Beige}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=Orchid}}|Orchid #{{#invoke:OSM Location map|colorvalue|color=Orchid}}}}
|-
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=Maroon}}|Maroon #{{#invoke:OSM Location map|colorvalue|color=Maroon}}}}
| style="background-color:#{{#invoke:OSM Location map|colorvalue|color=hard grey}}; color: #{{#invoke:OSM Location map|colorvalue|color=Yellow}}|Yellow #{{#invoke:OSM Location map|colorvalue|color=Yellow}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=Olive}}|Olive #{{#invoke:OSM Location map|colorvalue|color=Olive}}}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=Limegreen}}|Limegreen #{{#invoke:OSM Location map|colorvalue|color=Limegreen}}}}
|-
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=hard Leaf}}|Leaf #{{#invoke:OSM Location map|colorvalue|color=Leaf}}}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=Aqua}}|Aqua #{{#invoke:OSM Location map|colorvalue|color=Aqua}}}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=Teal}}|Teal #{{#invoke:OSM Location map|colorvalue|color=Teal}}}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=Aquamarine}}|Aquamarine #{{#invoke:OSM Location map|colorvalue|color=Aquamarine}}}}
|-
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=Navy}}|Navy #{{#invoke:OSM Location map|colorvalue|color=Navy}}}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=Fuchsia}}|Fuchsia #{{#invoke:OSM Location map|colorvalue|color=Fuchsia}}}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=Purple}}|Purple #{{#invoke:OSM Location map|colorvalue|color=Purple}}}}
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=Black}}|Black #{{#invoke:OSM Location map|colorvalue|color=Black}}}}
|-
| {{font color|#{{#invoke:OSM Location map|colorvalue|color=Orange}}|Orange #{{#invoke:OSM Location map|colorvalue|color=Orange}}}}
|transparent #transparent
|style="background-color:#{{#invoke:OSM Location map|colorvalue|color=background}}; color: inherit" |background #{{#invoke:OSM Location map|colorvalue|color=background}}
|style="background-color:#{{#invoke:OSM Location map|colorvalue|color=paleground}}; color: inherit" |paleground #{{#invoke:OSM Location map|colorvalue|color=paleground}}
|}
New to 2024: All colors can have an opacity value as well as the color name. eg <code>label-color3=dark blue,30</code> will set a 30% opacity. (The '50%' and 'opacity' parameters are now deprecated. ie they still work, but are not needed anymore)
{{font color|{{#invoke:OSM Location map|colorvalue|color=#AAAAAA}} | It is also possible to specify any HTML Hex color using the six-figure hex-code, eg #AAAAAA, but sticking to defaults allows a consistency between pages}}
{{font color|#{{#invoke:OSM Location map|colorvalue|color=}} |If no valid color is specified the color will be set to a default of 'hard grey'}}
===Text, shape and outline opacity===
{{OSM Location map
| coord={{coord|57.087|-4.702}}
| float=right
| zoom =6
| width = 200
| height = 200
| mark-coord={{coord|57.087|-4.702}}
|mark-size=0
|label=GREAT GLEN
|label-pos=top
|label-size=16
|label-angle=-55
|label-offset-y=6
| label-color=#1659165A
| mark-coord1={{coord|56.417|-6.102}}
|mark-size1=0
|label1=SCOTLAND
|label-pos1=right
|label-size1=26
|label-color1=soft grey,50
| mark-coord2={{coord|57.391|-5.890}}
| mark-size2=30
| shape2=circle
|shape-color2=orange,75
|shape-outline2=orange,11,35,dashed
}}
With the 2024 changes it is now possible to easily set the opacity value of any specified color. For 'label-color' and 'shape-color', this is a second, comma-separated value. For 'shape-outline' it is the third item, (it takes color name,line-width,opacity,style). eg <code>shape-outline4=hard red,8,30,solid</code> would add a transparent red outline 8px thick around the shape. (Note, the opacity is given as a %. For historical reasons, an opacity of 0 and 100 both give fully opaque colours. To set as entirely transparent, use a value of 1 - or the colour name 'transparent'.)
*Alternatively: <code>| label-color=#1659165A</code>. It is possible to set an opacity value when using #hexvalues instead of just named colours from the HTML color-space. The opacity is an additional two-digit hex value to make an 8 digit #value, with the opacity digits ranging from 00 (opaque) to FF (transparent).
Note: whilst the shape-color is also used by the markers in the fullscreen map, these cannot use any opacity. This 'feature' can be utilised as a way of setting the marker color for any transparent shape. Using the 'transparent' color setting will give a white marker, bus using <code>dark red,1</code> will look transparent on the map, but still give a red fullscreen marker.
There was previously an option to specify 'Halo' features around shapes. These were rarely used (it at all) and would have made considerable resource demands through the use of multiple additional shapes. With the template size risking difficulties when many dots are used, the halo parameter has been removed for now. Various halo-like effects can be obtained using the shape-outline option.
===Text effects===
;multi-line label: Where label text is too long to fit on a single line, using <code>label = </code>, any line can now be split as many times as desired using the ^ hat symbol. This supersedes the now deprecated <code>labela = </code> and <code>labelb = </code>. To put an actual ^ in your label, use the <code>{{Tt|&Hat;}}</code> entity.
;Label with no mark: If <code>mark-size=0</code> this has the effect of a free-floating label with no marker
;Angled label: It is possible to specify a <code>label-angle = </code>, which will pivot the label text around the centre of the marker point by the specified angle. Using an angled label which also has no marker is particularly good for labelling various geographic and linear features. A more characterful alternative is to set the text on an arc, using the [[Template:OSM Location map/ArcText/doc|ArcText options]]. For stylistic consistency settlement and building names should not normally be given an angle.
;Wiki markup: New to 2024, all text is shown as standard wiki text. This means you can use '''<nowiki>'''bold'''</nowiki>''' and ''<nowiki>''italic''</nowiki>'' as normal. Wikilinks are also possible, but note that they will then take standard wikilink colors, including red-link for non-existent links.
*And one for the geeks: if you are desperate to use other fonts or font-effects, you can include elements such as <nowiki><span style="...">your text here</span></nowiki> as part of your label. But use with caution and care. the aim is not to overwhelm the map with fancy effects. However, used appropriately it could be just the solution you are looking for - provided you know enough html/css/etc.
===Use in infoboxes===
{{t|OSM Location map}} can be shown in many infoboxes. This may depend upon if they allow a <code>map_image</code> parameter (preferable) or the <code>module</code> and/or <code>embedded</code> parameters, as their <code>image</code> parameter is usually used and best reserved for images. See for instance {{t|infobox school}} by using the instruction <code>|module=<nowiki>{{OSM Location map| ...}}</nowiki></code>. For an example, see [[:St John Fisher Catholic School]] which uses the map to show its two sites within an infobox map. Some infoboxes also allow it to be incorporated within the caption below an image. However, this only works if an image and some caption text are also present. (see [[Inishmore Lighthouse]]). A dual use template with controlling logic is possible as with {{t|Karakoram OSM}} and its different display in [[:K2]] using {{t|infobox mountain}}.
===Using label text to draw graphic features===
With the arrival of various outline and line-drawing features, there is less need to resort to text labels as a way of showing simple graphics. However, for certain cases, some of the many unicode items in the [[List_of_Unicode_characters#Box_Drawing| Box Drawing]] list may be of use.
==List of parameters==
{| class="wikitable mw-collapsible mw-collapsed"
|-
! Code blank - OSM Location map template, listing all the parameters
|-
|-
| <syntaxhighlight lang="wikitext" >{{OSM Location map
| coord = {{coord| | }}
| zoom=
| float =
| width =
| height =
| fullscreen-option =
| caption =
| title =
| minimap =
| mini-file =
| mini-width =
| mini-height =
| minipog-gx =
| minipog-gy =
| minipog-boxwidth =
| scalemark =
<!-- optional default settings. These 'D' parameters only create override values for subsequent marks. They make no marks of their own -->
| shapeD =
| shape-colorD =
| shape-outlineD =
| shape-angleD =
| markD =
| mark-sizeD =
| mark-dimD =
| label-sizeD =
| label-colorD =
| label-angleD =
| label-posD =
| ldxD = <!-- short-forms of label-offset-x and -y are now available for all sets-->
| ldyD =
<!-- unumbered parameter set creates mark and/or label on the map -->
| mark-coord = {{coord| | }}
| mark =
| shape =
| shape-color =
|shape-outline =
| shape-angle =
| mark-size =
| mark-dim =
| label =
| label-size =
| label-color =
| label-angle =
| label-pos =
| ldx =
| ldy =
| mark-title =
| mark-image =
| mark-description=
<!-- Arc text (A, B and C) no shape, mark or fullscreen effect, just text on an arc. Coords are for the first letter (max 20).-->
| arc-coordA = {{coord| | }}
| arc-textA =
| arc-angleA =
| arc-gapA =
| arc-radiusA =
| arc-text-sizeA = <!-- defaults to 12 -->
|arc-text-colorA =
|ellipse-factorA = <!-- defaults to 1.0 -->
<!-- numbered markers (1 to 60). Values set in mark1 will be inherited by all other numbered markers, unless overridden by a 'D' value-->
| mark-coord1 = {{coord| | }}
| mark1 =
| shape1 = <!--image/circle/square/triangle/diamond/rule/box/ellipse/etriangle/panel -->
| shape-color1 =
|shape-outline1 = <!--color,width,opacity,style-->
| shape-angle1 =
| mark-size1 = <!--width,height,corner-->
| mark-dim1 =
| label1 = <!--use ^ to add a newline, this makes labela1 and labelb1 redundant -->
| label-size1 = <!--includes extra parameter options: ,outline,background for text effects-->
| label-color1 =
| label-angle1 =
| label-pos1 = <!--position,[line option][,further options] -->
| ldx1 =
| ldy1 =
| mark-title1 =
| mark-image1 =
| mark-description1=
| mark-coord2 ={{coord| | }}
| mark2 =
| shape2 =
| shape-color2 = <!-- ... and so on for all the parameters above, for all numbers up to 60 -->
}}</syntaxhighlight>
|}
== Parameters ==
{| class="wikitable" style="width:100%;"
! colspan="2" style="background-color:lightblue; color: inherit" | Map display parameters
|-
! Parameter
! Description
|-
| style="width:170px;"|<code>coord</code>
| ''Latitude'' and ''longitude'' coordinates of the centre point of the map.
Use <code>coord={{tlf|Coord|''latitude'' |''longitude''}}</code>. {{tlx|Coord}} can deal with a wide range of formats. e.g.: <code>coord={{tlf|Coord|57|18|22|N|4|27|32|W}}, coord={{tlf|Coord|44.112|N|87.913|W}}, coord={{tlf|Coord|44.112|-87.913}}.</code> This may not be the 'coord' points of the page, as an appropriate framing area for the map may place the subject off-centre. (The marker point(s) are set separately, see below).
HINT: [http://www.gridreferencefinder.com/ gridreferencefinder.com], is a helpful place to find coordinates if you have other grid references. In Preview mode, a right-click while using the 'Interactive fullscreen map' will also show a coord point.
|-
| <code>zoom</code>
| Sets the '''scale''' of the map, from 0 to 19, to the levels defined by OpenStreetMap. (details [https://wiki.openstreetmap.org/wiki/Zoom_levels here]).
NOTE: The actual distances represented will vary depending on the latitude, as the scale defines different fractions of a degree. The apparent scale will also vary depending on the screen device, browser magnification, etc. The on-screen 'scalemark' gives a rough indication of the scale of the map.
|-
| <code>magnify</code>
| Enlarges the central area of the map and its contents. Main use case is where a feature shows as too small at one zoom level, and too large at the next. It can be given a decimal value between 1 and 2. (1 is the default, ie no effect, 2 doubles the size). It is a 'new for 2025' option. For more details and an example see [[Template:OSM Location map/Return to service#Magnify|OSM Location map/Return to service#Magnify]].
|-
| <code>float</code>
| Positions the frame to the left, centre (or center) or right. (default is right). If centered, the text will be forced above and below, otherwise text will wrap to the side.
HINT: For left or right align, if you want to ensure some following text sits alongside the map, use {{tl|clear}} before the map template.
|-
| <code>width</code><br /><code>height</code>
| Sets the width and height of the map in pixels. Only the number is required (i.e. no px). Default is 350 by 250 pixels.
|-
| <code>fullscreen-option</code>
| From 2024, the 'link-box' symbol at top left is hard-coded onto the map, so will always include the link to an Interactive Full screen,with links to other mapping options and can show locations of other nearby articles. It is always 'on' so the parameter has no effect.
|-
| <code>nolabels</code>
| Optionally use a map-set without settlement/country names.
By default the base map uses mapstyle='osm-intl'. Progressively higher zoom levels bring more place names onto the map, in the language/script of the wiki/user preference, where that is available. For some contexts it may be preferred to 'turn off' the place name labels, and use the mark/label options to provide such names as are wanted. Use <code>nolabels = 1</code> (nb. At higher zooms road names still occur).
|-
| <code>caption</code><br><code>auto-caption</code><br><code>toggletext</code>
| Optionally, a text caption can be included, below the map. Unless overridden by tags etc., this is left-justified plain text. It can include any wiki-item that can be inserted into a table cell, including images, formatting, citation references, etc.
<code>auto-caption=1</code> a numbered list will be automatically generated to follow any caption to show items that use numbered shapes.
Where there are a lot of short items, a column width can be specified. One way to do this is to specify a minimum width (in '[[Em (typography)|ems]]'. eg <code>auto-caption=15</code> will give a minimum width of 15em, and include a vertical rule for each column. Instead of the number, include the phrase 'columns=2' to directly set the number of columns to use.
NEW for 2025: The numbered list can be set to be collapsed (hidden) like a [[Help:Collapsing tables and more|wiki-table]]. Add the word 'collapsed' or 'collapsible' to set either to start collapsed, or start visible, but with the ability to hide it. eg. <code>auto-caption=collapsed, columns=3</code> will have a 3-column list, but starting with only the main caption plus the 'toggletext' phrase showing.
<code>toggletext=</code> allows you to set the phrase that turns on/off the caption list. It should be in single square brackets and include the words 'Hide/show'. The default is '[Hide/show caption list]'
|-
| <code>title</code>
| Optionally a title or other text can be placed in a cell above the map. By default this is centred and bold, but as with the caption, any wiki-markup can be included.
|-
|<code>legendBox<br>legendItem1</code>
|A Legend or Key panel can be added within the map, to explain what some of the shapes or dots represent. The size of the panel and its position relative to the top left corner of the map are both given in pixels, so the box remains in a fixed position even if the map coords are changed. A centred title line (optional) is followed by as many 'legendItem(n)=' lines as wanted. This identifies the explanation text, the dot-number it relates to, and an optional distance (px) down the box for that line. (If no distance is given it uses the shape height to find the next line distance.) Example: <code><nowiki>| legendBox=Map Key,110px80px1px,290px45px <!--title-line, size, position [,background colour, text/outline colour])--> | legendItem1=Defended site,1 <!--explanation text,dot-number [, distance from top of the box]--></nowiki></code>
It attempts to automate as much as possible to save editing time and space. It does this by re-using the shape values from already-defined dots, and giving very few points of control. If greater control/flexibility is wanted, it is possible to use a <code>panel</code> shape and place specially defined dots on it using the same process as the rest of the map. (As with other comma-separated values, if the text items need to use commas or inverted commas the text element should be put within "double quotes").
|-
|
<code>map-data</code><br />
<code>map-data-text</code><br>
<code>map-data-color</code><br>
<code>map-data-width</code><br>
<code>map-data-heavy</code><br>
<code>map-data-light</code><br>
<code>map-data-inverse</code>
| Allows 'OSM ExternalData elements' to be added to the map. This can be an administrative boundary, highway or other linear map element that has been assigned a wikidata Q value. (e.g.: <code>map-data=Q83065</code> will add the city boundary of Leicester.) The map item needs to be on the same place as the map itself. It can be linear features or inverse areas (not filled areas/shapes). Multiple elements can be added, separating each Q value by a comma. New to 2024 is that the element will be on the framed map as well as in fullscreen, and can apply an inverse mask.
[https://nominatim.openstreetmap.org/ nominatim.openstreetmap.org/] has a search engine to identify data elements for which Q values have been assigned, or to add (or repair) wikidata Q values to map elements. The relevant [[wikidata]] page also gives its Qvalue.
All lines on the 'framed' map are for now a 10% opacity grey line, 6, 9 and 3 pixels wide for <code>map-data</code>, <code>map-data-heavy</code> and <code>map-data-light</code>, respectively. The fullscreen version will continue to draw orange lines of thickness 9 and 3 pixels for light and heavy options. <code>map-data</code> is more flexible within fullscreen. It can default to an orange line of 6 pixels, but color (using format #XXXXXX) and width in pixels can be set, as can a text element which appears when the line is clicked on. This can include wikilinks.
Links are still only visible in fullscreen. The framed version lines have no links, so it may be helpful to use a discrete label alongside a boundary (for example) to indicate what it is (See Banwen example, above).
<code>map-data-inverse</code> will only work correctly with a boundary line that forms a complete circuit, and will layer a pale grey mask over the area outside of the designated boundary, with a 1px grey boundary line. This is the equivalent to the 'type=shape-inverse' option in {{tl|maplink}}, but with paler colours, and works for both the framed map and the fullscreen version.
|-
| <code>minimap</code>
|Set to <code>file</code> to add an external locator-style map from wikimedia commons, plus optional locator dot. A map can be placed in any of the four corners. <code>minimap=file bottom left</code>, <code>minimap=file bottom right</code>, <code>minimap=file top left</code>.
If just <code>minimap=file</code> is used it defaults to 'bottom right', but it is probably better to spell it out. (nb. With the arrival of the fullscreen link in the top-right corner, that is not now useable for a mini-map.).
(nb. Automated world map is no longer available.)
|-
|<code>mini-file</code><br>
<code>mini-width</code><br>
<code>mini-height</code><br>
|Takes the file name of a standard location map from Commons (without 'File:'), and displays it as a minimap in a corner.
mini-width and mini-height are the desired dimensions of this minimap, in pixels. Unlike pre-2024, all images can only be displayed at their given proportions. The width/height ratios are still need to correctly position the file into its corner.
|-
|
<code>minipog-gx</code><br>
<code>minipog-gy</code><br>
<code>minimap-boxwidth</code>
| Within the custom minimap this can place an optional small Red pog. Nb. The x and y are not lat and lon values. They relate to a 100x100 grid of the minimap at whatever size and ratio it is set to. The origin is top,left of the mini map, so if minipog-gx=25, it will be a quarter of the way across. (Some location maps have a highlighted location already, so leaving these two parameters undefined gives the map without a dot.)
minimap-boxwidth=xx can optionally use an open red box instead of a Red pog. xx = the width of the box, in percent of the width of the minimap. The height is then matched in proportion to the actual map. The box is centered at spot corresponding to (minipog-gx,minipog-gy). In general anything much below xx=15 will be better served by a dot. The required width will need some trial and error to pin down. If xx=0 or minimap-boxwidth is undefined, a Red pog is used.
|-
|or (shortform instead)
<code>mini-locator</code>
|
Alternative, shortform to replace all the above minimap parameters, they can all be rolled into a single line of comma-separated values. (optionally put the ''filename'' element in "double quotes" if it has commas or inverted commas within the filename). Example:<br>
<code><nowiki><!--</nowiki> mini-locator=[filename], [position], [width]px[height]px, [dotposx]%, [dotposy]%, [boxwidth]--><br>
mini-locator=Leicester UK ward map 2015 (blank).svg,top left,132px153px, 38%,60%,16</code>
|-
|<code>scalemark</code>
|By default or with scalemark=1, a scale line with guide to the scale of the map is supplied near the bottom right corner of the map. If this is not required - e.g. if it interfers with a map element at that point - it can be turned off by setting this to '0'. To shift it further left, e.g. to avoid a minimap, or to avoid a 'busy' bit of the map, enter the number of pixels to shift left. If not set by hand it will automatically shift to the left of any bottom-right minimap.
|-
! colspan="2" style="background-color:lightblue; color: inherit" | Label and mark parameters.
|-
| colspan="2" style="background-color:lightblue; color: inherit" |Multiple marks can be set on the map, being an un-numbered version, and a potentially 'limitless' series running from 1 upwards. The first numbered one, (mark1, label1, etc.) is a 'master marker' and its values are inherited by the other numbered markers unless set individually. (nb: label1= and mark-title1 etc are not inherited). All label, mark and full-screen parameters are available for each numbered marker.
'''Default set''': For added flexibility a parameter set with a 'D' value instead of a number will override the master values with a more general Default. The 'cascade of inheritance' (eg for 'shape3=') runs as follows: is there a setting for shape3? if not then is 'shapeD' set? If not, then is 'shape1' set? if not then use the underlying default, which in this case would be shape=image.
On the Fullscreen map, the dots always number from 1, and will always run in sequence. If using auto-numbered dots on the framed version you can start with shape1 and provided all the dots are numbered, the sequence will match.
|-
! Parameter
! Description
|-
| <code>shape</code>
| OSM Location map can use either an external image as a location marker, or one of various shapes. The <code>mark-size</code> setting will require different numbers of items for different types of shape.
*Shapes that just need a width: '''circle, square, i-triangle'''.
*Shapes that can specify both width and height: '''box, ellipse, diamond, triangle, panel''' and all the new 'clip-path' shapes.
*NEW for 2025, it is possible to use clip-paths to define solid shapes, of which the following are now available (A 'D' indicates a surrounding outer line, and a 'DD' is a double outer line.): '''squareD, squareDD, circleD, circleDD, boxD, boxDD, ellipseD, ellipseDD, triangleD, triangleDD, diamond, diamondD, diamondDD, thincross, cross, crossD, fivepointstar, fivepointstarD, sixpointstar, sixpointstarD, sevenpointstar, sevenpointstarD, eightpointstar, eightpointstarD, ring'''
*'''rule''' provides a line centred at its coord point. It requires a length (and optionally a gap, which makes a doube line)
*'''curveA''' and '''curveC''' both add an arc-line ending with an arrow, which will point either Clockwise or Anticlockwise. They draw a 90deg arc above/below an approximate mid-point, having a length set by mark-size, and using shape-outline attributes.
*An '''image''', which is the default, will be 'Red pog.svg' unless a different file is specified using <code>mark = </code>. A width is required, and if the image is not square, a height value will ensure more accurate positioning.
* If '''numbered''' dots are required, use <code>shape1=n-circle</code> Subsequent markers will use the same setting, and automatically add its dot number. <code>shape1=l-circle</code> will do the same but with letters, and any of the built in shapes can be used with n- or l- prefixes.
|-
| <code>shape-color</code><br /><code>shape-outline</code><br /><code>shape-angle</code><br /><code>numbered</code>
|*shape-color sets the shape infill to a color, and optionally opacity. eg <code>shape-color=hard blue,60</code> will give a 60% opacity. (This replaces <code>shape-opacity</code> which is no longer needed but still works for now). As with all the opacity settings here, 0 and 100 are opaque, 1 is transparent. All the color parameters can either use the color names in the table above, or use hex triplets (e.g. #FF0000), as described at [[Web colors]]. If using hex, a further two digits can optionally set opacity. Default = dark red.
*shape-outline sets several different outline attributes: '''color,line-thickness,opacity,css-style'''. (line-thickness is in px, with 0 meaning no outline. Opacity works the same as for shape-color. css style has various options of which the most useful are solid, dotted, dashed or double. The 'style' allows some shapes (circle/square and rule) to be given attributes to the outline. For example <code>shape-outline=hard red,8,30,dotted</code> would give a 'dotted halo effect'.
<code>shape-outline=hard blue,5,100,double</code> would potentially add a second ring around a blue circle, for example.
(Note - CSS limitations mean that while triangles, clip-path shapes and arrows can be assigned an outline, they are made by drawing a larger solid shape beneath. So these cannot be 'hollow' shapes, and cannot be given line styles).
*shape-angle rotates a shape or image by the specified number of degrees (minus for anti-clockwise). This is useful to change the direction that a triangle points, fit a box to a feature on the map, or to control where rules and arrows point to, for example.
The <code>numbered = </code> parameter is used with numbered shapes, to override the automatically allocated number, and can be any text.
|-
| <code>mark</code>
| The name of a Wikimedia commons file, which is used as the marker. Default is Red pog.svg. Other pog colours are available (see [[Commons: Map pointers, dotset 1]]), and a large range of map markers can be found at [[Commons:Location markers]] and [[Commons:Category:Map icons]]. Nb, at present if a default shape-outline is set, this appears around images as well. Use shape-outline=transparent to remove it.
|-
| <code>mark-size</code> <br />(<code>mark-dim</code>)
| Size and dimensions of a marker. <code>mark-size</code> is used by both <code>shape</code> and <code>mark</code> and can define the width, height and (where relevant) corner radius of the marker symbol. A single value sets both the width and height of the mark or shape in pixels (no 'px' required, default is 12). A second, comma-separated value will set height, and a third will be used by square, box or panel to define the corner-radius. If only a text label is wanted with no marker, set <code>mark-size=0</code>.
<code>mark-dim</code> is no longer needed, but retained for compatibility, and is used by non-square marks. default is 1, i.e. equal width and height. A value of 0.7 will give a typical landscape rectangle. 1.4 will give a typical portrait rectangle. It is ignored if a height value is set.
NOTE: Of particular use with n-circles and n-square, the text-size for the number is based on the shape's 'height' value. If only one value is supplied, that is both width and height. By supplying a second value, (eg: mark-size=13,11) the text-size of the numbers can be adjusted in relation to the size of the shape.
|-
| <code>mark-coord</code>
| Latitude and longitude coordinates of the marker point. Use the format <code><nowiki>mark-coord={{Coord|lat value|lon value}}</nowiki></code>. Used by <code>shape</code> and <code>mark</code> as well as the related <code>label</code>. If the location is outside the area of the map, it will not appear. (for backwards compatibility mark-lat and mark-lon still work, but are not the preferred method.)
|-
| <code>label</code>
| Text to appear alongside a mark or shape. If left blank then a mark will show without a label. If only a text label is wanted with no marker, set <code>mark-size=0</code> If there is no label and a mark-size=0 this is an invisible marker, but will still feature on the Fullscreen option.
New for 2024: Where label text is too long to fit on a single line, using <code>label = </code>, any line can now be split as many times as desired using the ^ hat symbol. This supersedes the now deprecated <code>labela = </code> and <code>labelb = </code>. To put an actual ^ in your label, use the <code>{{Tt|&Hat;}}</code> entity.
Text that strays too close to the right edge of the frame may now 'automatically wrap' to the next line. However this will not be 'balanced' in the way the inserted linebreaks are, so in most instance a manual break will be preferable.
All label text is now standard wiki text. This means you can use '''<nowiki>'''bold'''</nowiki>''' and ''<nowiki>''italic''</nowiki>'' or even your own <nowiki><span...</span></nowiki> items, if you really need to. Wikilinks are also possible, and the label will become a live link, but note that they will then take standard wikilink colors, including red-link for non-existent links.
|-
| <code>label-size</code>
| Sets the text size for the label, in points. default = 12
New for 2024, label-size can have additions to place a background fill and/or an outline around a label. text-size=12,background will use the standard background map-color as a solid fill under the area of the text - handy if the label is being confused by other map details. Adding 'outline' will place a 1px wide black line around the text to make it more noticeable - although it will not cope well with any line breaks. They can be used separately or in combination: eg <code>text-size=12,background,outline</code>.
Two further colours can be used for the fill: If 'paleground' or 'beigeground' is used instead of 'background' it will give a fill that is respectively paler or darker.
|-
| <code>label-color</code>
| Sets the text colour for the label. The standard colour labels (red, black, grey, white, blue, green etc...) all work as described, but can be rather strident on the OSM map. Each of the colors below have three standard shades - soft, hard and dark. Default=dark grey. (Actually defaults to #222211, slightly darker than 'dark grey', so it is prominent when someone just puts a single pog and label. A comma-separated number after the color will set the opacity (%). eg dark-blue,40 will give 40% opacity.
Under normal usage, the following label color scheme should be followed:-
* {{font color|#{{OSM Location map/color|soft grey}} |soft grey}}: Settlements = soft grey (Subject of the map can be hard grey and larger label-size). Map areas with darker or busier backgrounds may need to move a shade darker to hard grey and dark gray respectively.
*{{font color|#{{OSM Location map/color|soft blue}}|soft blue}}: Rivers, lakes, sea areas etc = soft blue (Works well on top of OSM blue areas)
*{{font color|#{{OSM Location map/color|soft green}}|soft green}}: Parkland, national/regional parks, gardens, forests etc = soft green works well on top of OSM green areas. (hard green may be desirable in forests or for the subject of the map)
*{{font color|#{{OSM Location map/color|hard red}} |hard red}}: Use with care = dark red goes nicely with a red pog but can look like a redlink, and since text can now include live links, this and other 'link-indicative' colours are best avoided)
*{{font color|#{{OSM Location map/color|dark grey}} |dark grey}}: Individual sites = dark grey (no need to specify if the site has a single label to accompany a red pog)
*{{font color|#{{OSM Location map/color|dark brown}} |dark brown}}: Information panel = dark brown (works nicely on a 'pale brown' panel, for example)
{{See|Template:OSM_Location_map/doc#Full_table_of_color_options}}
It is also possible to specify any HTML Hex color using the six-figure hex-code, eg #AAAAAA, but sticking to defaults maintains consistency between pages. If no color is specified the text will have a default of 'dark grey'. (nb: Any value other than one of the named colors above or a hex code will return as 'dark grey')
|-
| <code>label-pos</code><br /><br /><code>ldx</code><br />(or <code>label-offset-x</code>)<br /><br /><code>ldy</code><br />(or <code>label-offset-y</code>)<br /><br />(in pixels,<br />- = up and left,<br />+ = down and right)
| At its simplest, label-pos sets the position of the label, relative to the marker: left, right, top bottom or centre/center. Default=left. Top, bottom and center text is center-justified, whereas left and right align against the marker. The label aims to be an appropriate distance from the edge of the marker, but irregular shapes and larger sizes may need further adjustment using the label-offsets - now in a shortened form of ldx and ldy. These 'offset' values can also move the label much further distances, and make use of the line-drawing additions shown below.
NEW for 2025: As an alternative to the 'top, bottom, left, right' terminology, you can instead use commpass-points: 'north, south, east, west'. In addition to these [[Cardinal direction]]s, the four ordinal points (northeast, southwest etc) can also be used, to further control the label position.
Center (or centre) text is placed over the middle of any shape, point, rule, box or panel. Line breaks will 'spread' the lines above and below the center-point.
Nb: in the case where the shape is a '''panel''', the text is placed 'within' rather than to the side of the shape, and left and right become 'justified' instructions for text that will wrap within the panel, rather than the 'positional' instruction it normally is.
'''Line-drawing options:''' By adding additional values to the <code>label-pos</code> parameter, and used with any of the above position indicators, a range of line-drawing options can be accessed.
*'''with-line''': Use ldx and ldy to indicate a distance away from the shape and provide a suitable line beltween the two. For example <code>label-pos2=left,with-line| ldx2=-15| ldy2=-3</code> would draw a line from the mark to that offset, and position the label to the ''left'' of that new point.
*'''n-line''': Works very much like 'with-line', but is for use with numbered shapes. If several numbered dots overlap this 'extracts' the number from its dot, and puts it on the end of the connecting line. If there is also a text label, the number and the label are both printed.
*'''mark-line''': This one does not actually use the re-positioning features of ldx or ldy, and does not engage with the label. Instead it draws a connecting line from this shape to the previous mark. Thus <code>label-pos2=right, mark-line,2</code> will deal with the label as normal, and also draw a line between mark2 and mark1. It will set a line-thickness of 2px, using the color set for shape-outline2. Two further comma-separated values can be optionally added: a CSS line-style (solid, dashed, or dotted) and a gap size, which has the effect of drawing two parallel lines of the given thickness and style, with the specified gap between. eg <code>label-pos=top,mark-line,3,dotted,6</code>. A gap of 1 or undefined draws a single line.<br />Summary:label-pos=''position'',mark-line,''line-width,style,gap-width''.
*'''photo-panel''': This uses ldx & ldy, and creates a panel (default height of 50px) to display both the label text and an optional photo, with a connecting line back to the mark/shape. As well as using the value in 'mark-image' (to find a picture to show), it requires two further comma-separated parameters: a dimension value for the photo and the required total width of the panel. eg <code>label-pos=left,photo-panel,0.8,110</code>. The position of the centre of the panel is set by ldx= and ldy=. The left or right pos will indicate the position of the photo within the panel. The label will then be centered in the remaining portion of the panel. An optional final parameter can set the height - especially useful if not using a photo. eg <code>label-pos=left,photo-panel,0,90,24</code> gives a panel for a single line of text and no photo.<br>Summary:label-pos=''position'',photo-panel,''photo-dimension,panel-width,panel-height''.
|-
|<code>label-angle</code>
| It is possible to specify a <code>label-angle = </code>, which will pivot the label text around the centre of the marker point by the specified number of degrees. (+ve angle rotates clockwise, -ve anticlockwise) If mark-size is set to zero, this has the effect of a free-floating label with no marker, useful for various geographic and linear features. For stylistic consistency all settlement names should not be given an angle.
|-
|<code> label-spacing</code>
|Extra space can be added between each letter in a label. For example <code> label-spacing12 = 4 </code> adds 4 pixels between each letter in label12. (default =0) (A controlled wide-spaced text can be achieved, without having to put real spaces in between the letters)
|-
|<code>label-height</code>
|Where there is a multi-line label, this increases or decreases the gap between lines as a % in relation to the label-size, eg <code>label-height12=90</code> would give very closely spaced lines. (default = 120)
|-
! colspan="2" style="background-color:lightblue; color: inherit" | Additional content for Full screen link
|-
| colspan="2" style="background-color:lightblue; color: inherit" |The 'full screen' map uses the same OSM base map, in a different map environment, including the option for users to scale in and out, to pan across the map, and to find (via the 'More details' button) other maps and satellite imagery for the location. It also includes numbered markers, for which tooltip-style titles, and image thumbnails with captions can be brought up. This makes most sense where there are several markers on the map. The content for this facility is set with the following three parameters - which need to be numbered for each mark as for the other mark attibutes:
|-
! Parameter
! Description
|-
| <code>mark-title</code>
| This title appears as a tooltip and also a thumbnail title, accessed by clicking the marker. if <code>mark-title=none</code> that will exclude that marker from the full screen map. (it will still show as normal on the main map).<br />New for 2024, if a wikilink item is included within mark-title, it will now make the mark a live-link on the framed map. Only the first link is used as a link, but the whole text is shown as a tooltip, when hovering over the mark/dot.
|-
| <code>mark-image</code>
| This provides a pop-up thumbnail image when the marker is clicked. Include only the image name from Wikimedia commons etc. (i.e. no brackets, or 'File:').
|-
| <code>mark-description</code>
| Caption text, which will either accompany a pop-up photo, or if no photo then as a text box, when the marker is clicked. This can include wikilinks etc., to link on to additional relevant articles.
|-
! colspan="2" style="background-color:lightblue; color: inherit" | Arc Text: label placed on an arc
|-
| colspan="2" style="background-color:lightblue; color: inherit" | Arc-text parameters allow multiple lines of text to be placed along on arc, with parameters to control the tightness of the circle (radius) and the looseness of the text (gap). Changes in radius will also have an effect on the gap size, with a larger radius opening out the gap. Trial and correction will be needed to get the shape and effect desired. As a general rule, identify the coordinates of the required start-point, then choose a starter-pattern from the selection at [[template: OSM Location map#Text on an arc|OSM Location map#Text on an arc]], and then adjust parameters to fit the requirement of the map. As an alternative to the 8 separate parameters, there is an alternative comma-separated shortform version that can be used instead. The parameters below are for set A. Any capital letter afrom A to Z is available. (Note: excessive use may overload your map with too many text arcs.)
|-
! Parameter
! Description
|-
| <code>arc-coordA</code>|| coordinates of first letter, using =<nowiki>{{coord|xxx.xx|yyy.yy}}</nowiki>
|-
| <code>arc-textA=</code>|| Add your text here
|-
| <code>arc-angleA</code> ||in degrees, -180 to 180. 0 will start as horizontal, -90 straight up, 90 straight down
|-
| <code>arc-gapA</code> ||1= a notional 'standard'. 0.2 is very tight, 10 is very wide. Applying a negative gap will invert the letters and run the other way around the circle
|-
| <code>arc-radiusA</code> ||1= a notional 'standard' 0.5 is quite a tight circle, 8 is so wide as to be almost flat
|-| <code>arc-text-sizeA</code> ||standard font sizes. If undefined, default is 12
|-
| <code>arc-text-colorA</code> ||sets text color. #000000 color hexes and standardised OSM Location map colors are accepted
|-
| <code>ellipse-factorA</code>||(optional) will squash or stretch the circle. 1= notionally circular, 0.5 to 1.0 will flattern top and bottom, above 1.0 flattens the sides.
|-
|<code>arcA</code>|| A Comma-separated multi-value parameter to replace all of the above (Note you can use lat and lon decimal values or the {{tl|coord}} template call). (optionally put the ''text'' element in "double quotes" if you wish to include commas or inverted commas within the text). Example:<br>
<code><nowiki><!--</nowiki>arcA=text, lat, lon, [size]px, color, [angle]deg, gap, radius, ellipse--><br>
arcA="River Soar, canal",52.6208,-1.1469,10px, hard blue, 128deg, -1.0, 0.55, 1</code>
|}
===Comparison with {{tl|Maplink}}===
Since May 2018 it has also been possible to create a map in a frame via {{tl|Maplink}}, which in some respects does a similar job to OSM Location map. In both cases a static map image can be added to an article, for anywhere in the world, pulling in the map from OpenStreetMap data. The differences are in what they can and can't add to the base map. Maplink, in both its framed and fullscreen versions, can add points (numbered or icon-style pointy dots), and various, lines and areas generally imported from OpenStreetMap via wikidata Q values. A large part of its role is in providing an 'automated' map, using wikidata to give quick results, often within an infobox. With OSM Location map the process is much more hand-built, drawing together elements from the article. The framed map can show a much richer selection of dots, shapes, graphics, overlays, images and especially text to convey specific details relevant to a particular article. It has now also been enabled to show Q value (at present limited to boundaries, and roads). The fullscreen equivalent is much closer to a Maplink fullscreen item, re-using as point-items the details supplied for the framed map.
==Underlying technology==
OSM Location map now makes use of the <nowiki><mapframe></nowiki> module, which in turn displays the requested portion of the globe as drawn from the [[Open Street Map]] global maps. This template then generates the overlay text and graphics on top of the base map. Until 2023 everything within the frame was produced through the template {{tl|Graph:Street map with marks}}, created by [[User:Yurik]] which itself made use of the [[Vega and Vega-Lite visualisation grammars|Vega visualisation package]] which displayed both the base map and a range of text and graphic features. In its original (2016) incarnation the result was a rendered bitmap image, making the viewing overhead very low compared to the more complex preview-mode rendering, but with a low-resolution output. In 2020, the bitmap creation process was withdrawn, so from then on rendering has been carried out for read as well as preview modes, resulting in a much clearer page view, but adding considerably to the resource overhead.
In April 2023 the Vega package was withdrawn, along with all of its 'graph' dependencies, amid security concerns. After a protracted period of uncertainty (and with the Graph replacement still under development in April 2025) a way was found to replicate the map outcomes previously handled by 'graph'. By making use of [https://www.mediawiki.org/wiki/Help:Extension:Kartographer Extension:Kartographer], through its <nowiki><mapframe></nowiki> tag, the base map can be displayed within an HTML <nowiki><div></nowiki> element. Inline [[CSS]] coding could then be used to overlay graphics and text within the resulting frame. The Mercator 'coords' are converted to (x, y) pixel values to match the map location and zoom level. This switch not only got the existing stock of 5,600 templates back in a working state, but also allowed the implementation of additional graphical items. A further gain was that standard use of wikimarkup and wikilinks became possible, allowing the maps to be a 'live' component of the page, rather than simply an illustration.
In 2025 another hurdle was overcome by rewriting the template as a [[Lua]]/[https://www.mediawiki.org/wiki/Extension:Scribunto Scribunto] module, to replace the much more cumbersome wiki-markup. The outcome was that pages could load in about 20% of the time, and use far less memory and other resources. It also allowed unlimited numbers of marks, should they be needed. The page load-times/display demands in particular had been a potential constraint to further adoption, which has at least been alleviated by the rewrite, as well as facilitating a few extra shapes, controls and features.
The full screen option, which can be clicked through from the framed map, provides an entirely different mapping approach, although also using Kartographer to access the OSM base-map data. This uses <nowiki><maplink></nowiki> to provide a fullscreen interactive map that can be panned and zoomed. It also replicates (as numbered markers) the various marks and colors from the framed map. These can then be given extra content, by way of a title, image and description, along with displaying the coordinate values. Since 2024 maplink can also show the locations of other 'nearby' wikipedia pages. The result is a map-based page that offers another way of engaging with the article content, and navigating to related material.
Further development of mapping technology is currently not a [[Wikimedia Foundation]] priority area. Having established {{tl|maplink}}, which initially just created a text link to a full-screen map, to the point where it provides a framed image with data directly from wikidata, there are no active developments for different mapping solutions. (Please tell us if you know differently!). The processor overheads are reduced by showing a static image in the frame, which can be clicked to become interactive in fullscreen. Maplink is now an effective and widespread solution within info-boxes, where the map is automatically generated from already available data.
This template is geared rather to producing a hand-editable map, in which the area displayed and the selection of items and labels included are selected, edited, and added to, to suit the specifics of the subject in hand. A third approach, which is not currently supported by any mapping template, would be to draw the data from external live data, such as using a SPARQL query, to generate, for example large-area scatter-plots to show different distributions across a map. The security and maintainability aspects of such tasks, as with other graph visualisations of live data, are a major barrier to implementation.
The technology used here (Kartographer plus inline-css) is probably best described as 'reasonably well developed'. As of late April 2025 it would seem to be in a mature, stable and sustainable state, so should not see the kind of hiatus experienced by 'Graph' in 2023. It is highly likely that this or a comparable solution will still be available into the future. Of the possible evolutions over time one may be through SVG-based graphics, allowing a richer graphical content and maybe even moving towards a proper visually-based editing environment. In the opposite direction, maps that need to show large numbers of dots might be better served by drawing from an external (wikimedia-based) data file. But these should most likely be seen as alternatives rather than replacements for this template.
== See also ==
* [[Wikipedia:Maps for Wikipedia]] - A list of mapping tools available on Wikipedia
* {{tl|Location map}} - Placing one marker/label by latitude/longitude.
* {{tl|Location map+}} - Placing unlimited list of markers/labels.
* {{tl|Location map many}} - For displaying multiple marks using latitude and longitude.
* [[:Commons:Category:Map pointers]] - List of the many Wikimedia Commons pointer symbols.
* {{tl|Overlay}} - Allows image numbered, textual number, or color tag overlays to be positioned over an image to indicate particular features in the image.
<includeonly>{{sandbox other||
<!-- Categories below this line, please; interwikis at Wikidata -->
[[Category:Location map templates|Location map templates]]
[[Category:Map formatting and function templates]]
}}</includeonly>
msv3nzv9ied6162zoux069q7mr8s7jd
ઢાંચો:Reflist-talk/styles.css
10
150858
886400
2024-07-04T04:47:36Z
en>JWBTH
0
support dark mode
886400
sanitized-css
text/css
/* {{pp|small=y}} */
.reflist-talk {
margin: auto 0;
border: 1px dashed var( --border-color-base, #a2a9b1 );
padding: 4px;
padding-left: 1em;
}
.reflist-talk-title {
font-weight: bold;
}
iew0l9llvr9tc0u6j45mjanyuz2kvvu
886401
886400
2025-06-13T17:03:30Z
KartikMistry
10383
[[:en:Template:Reflist-talk/styles.css]] માંથી આયાત કરેલ ૧ પુનરાવર્તન
886400
sanitized-css
text/css
/* {{pp|small=y}} */
.reflist-talk {
margin: auto 0;
border: 1px dashed var( --border-color-base, #a2a9b1 );
padding: 4px;
padding-left: 1em;
}
.reflist-talk-title {
font-weight: bold;
}
iew0l9llvr9tc0u6j45mjanyuz2kvvu
ઢાંચો:OSM Location map/color
10
150859
886402
2024-04-29T09:34:48Z
en>RobinLeicester
0
transparency needs to overide any opacity settings
886402
wikitext
text/x-wiki
{{#if:{{#invoke:string|match|s={{{1|}}}|pattern=^#...$|ignore_errors=1}}
|{{#invoke:string|replace|source={{{1|}}}|pattern=#(.)(.)(.)|replace=%1%1%2%2%3%3|plain=0}} <!-- expand #ABC to AABBCC hex -->
|{{#ifeq:{{#invoke:String|pos| ignore_errors=true| target={{{1|null}}}| pos=1}}|#|{{#invoke:String|sub|{{{1|}}}|2|}}|<!-- returns everything except # -->
{{#switch:{{#invoke:String2 |split |{{{1|}}}|sep=,|idx=1}}
| pale blue=D6E1EC|soft blue=77A1CB| hard blue=5581A9| dark blue=105396|
soft blue 50%=77A1CB77| hard blue 50%=5581A977| dark blue 50%=10539677|
pale gray|pale grey=E8E8D6|soft gray|soft grey=AAAA88| hard gray|hard grey=777755| dark gray|dark grey=333322|
soft gray 50%|soft grey 50%=AAAA8877| hard gray 50%|hard grey 50%=77775577| dark gray 50%|dark grey 50%=33332277|
pale green=D2F0E5|soft green=81AF81| hard green=538253| dark green=165916|
soft green 50%=81AF8177| hard green 50%=53825377| dark green 50%=16591677|
pale brown=FAF6ED|soft brown=CCB56C| hard brown=AD7F14| dark brown=8E5913|
soft brown 50%=CCB56C77| hard brown 50%=AD7F1477| dark brown 50%=8E591377|
pale red=FCC6C0|soft red=DB3123| hard red=AA1205| dark red=7A0101|
soft red 50%=DB312377| hard red 50%=AA120577| dark red 50%=7A010177|
background1=F9F5E7|
background1 50%=F9F5E777|
White|white|transparent=FFFFFF|
Silver|silver =C0C0C0|
Gray|gray|Grey|grey =808080|
Red|red =FF0000|
Maroon|maroon =800000|
Yellow|yellow =FFFF00|
Olive|olive =808000|
Lime|lime =00FF00|
Green|green =008000|
Aqua|aqua =00FFFF|
Teal|teal =008080|
Blue|blue =0000FF|
Navy|navy =000080|
Fuchsia|fuschia =FF00FF|
Purple|purple =800080|
Black|black =000000|
Orange|orange=FFA500|
White 50%|white 50% =FFFFFF77|
Silver 50%|silver 50%=C0C0C077|
Gray 50%|gray 50%|Grey 50%|grey 50% =80808077|
Red 50%|red 50% =FF000077|
Maroon 50%|maroon 50% =80000077|
Yellow 50%|yellow 50% =FFFF0077|
Olive 50%|olive 50% =80800077|
Lime 50%|lime 50% =00FF0077|
Green 50%|green 50% =00800077|
Aqua 50%|aqua 50% =00FFFF77|
Teal 50%|teal 50% =00808077|
Blue 50%|blue 50% =0000FF77|
Navy 50%|navy 50% =00008077|
Fuchsia 50%|fuschia 50% =FF00FF77|
Purple 50%|purple 50% =80008077|
Black 50%|black 50% =00000077|
222211 }} }} }}{{#ifeq:{{{1|}}}|transparent|00|{{#if:{{#invoke:String2 |split |{{{1|}}}|sep=,|idx=2}}
| {{Hexadecimal|{{#expr:{{#invoke:String2 |split |{{{1|}}}|sep=,|idx=2}}*2.55}}|no}}
| {{#ifexpr:{{{opacity|}}}|{{Hexadecimal|{{#expr:{{{opacity|100}}}*2.55}}|no}} }} }} }}<noinclude>
{{documentation}}
[[Category:OSM Location map templates]]
</noinclude>
7z8x1xu1nxki9fq9xou57n4cn5ky1f7
886403
886402
2025-06-13T17:03:30Z
KartikMistry
10383
[[:en:Template:OSM_Location_map/color]] માંથી આયાત કરેલ ૧ પુનરાવર્તન
886402
wikitext
text/x-wiki
{{#if:{{#invoke:string|match|s={{{1|}}}|pattern=^#...$|ignore_errors=1}}
|{{#invoke:string|replace|source={{{1|}}}|pattern=#(.)(.)(.)|replace=%1%1%2%2%3%3|plain=0}} <!-- expand #ABC to AABBCC hex -->
|{{#ifeq:{{#invoke:String|pos| ignore_errors=true| target={{{1|null}}}| pos=1}}|#|{{#invoke:String|sub|{{{1|}}}|2|}}|<!-- returns everything except # -->
{{#switch:{{#invoke:String2 |split |{{{1|}}}|sep=,|idx=1}}
| pale blue=D6E1EC|soft blue=77A1CB| hard blue=5581A9| dark blue=105396|
soft blue 50%=77A1CB77| hard blue 50%=5581A977| dark blue 50%=10539677|
pale gray|pale grey=E8E8D6|soft gray|soft grey=AAAA88| hard gray|hard grey=777755| dark gray|dark grey=333322|
soft gray 50%|soft grey 50%=AAAA8877| hard gray 50%|hard grey 50%=77775577| dark gray 50%|dark grey 50%=33332277|
pale green=D2F0E5|soft green=81AF81| hard green=538253| dark green=165916|
soft green 50%=81AF8177| hard green 50%=53825377| dark green 50%=16591677|
pale brown=FAF6ED|soft brown=CCB56C| hard brown=AD7F14| dark brown=8E5913|
soft brown 50%=CCB56C77| hard brown 50%=AD7F1477| dark brown 50%=8E591377|
pale red=FCC6C0|soft red=DB3123| hard red=AA1205| dark red=7A0101|
soft red 50%=DB312377| hard red 50%=AA120577| dark red 50%=7A010177|
background1=F9F5E7|
background1 50%=F9F5E777|
White|white|transparent=FFFFFF|
Silver|silver =C0C0C0|
Gray|gray|Grey|grey =808080|
Red|red =FF0000|
Maroon|maroon =800000|
Yellow|yellow =FFFF00|
Olive|olive =808000|
Lime|lime =00FF00|
Green|green =008000|
Aqua|aqua =00FFFF|
Teal|teal =008080|
Blue|blue =0000FF|
Navy|navy =000080|
Fuchsia|fuschia =FF00FF|
Purple|purple =800080|
Black|black =000000|
Orange|orange=FFA500|
White 50%|white 50% =FFFFFF77|
Silver 50%|silver 50%=C0C0C077|
Gray 50%|gray 50%|Grey 50%|grey 50% =80808077|
Red 50%|red 50% =FF000077|
Maroon 50%|maroon 50% =80000077|
Yellow 50%|yellow 50% =FFFF0077|
Olive 50%|olive 50% =80800077|
Lime 50%|lime 50% =00FF0077|
Green 50%|green 50% =00800077|
Aqua 50%|aqua 50% =00FFFF77|
Teal 50%|teal 50% =00808077|
Blue 50%|blue 50% =0000FF77|
Navy 50%|navy 50% =00008077|
Fuchsia 50%|fuschia 50% =FF00FF77|
Purple 50%|purple 50% =80008077|
Black 50%|black 50% =00000077|
222211 }} }} }}{{#ifeq:{{{1|}}}|transparent|00|{{#if:{{#invoke:String2 |split |{{{1|}}}|sep=,|idx=2}}
| {{Hexadecimal|{{#expr:{{#invoke:String2 |split |{{{1|}}}|sep=,|idx=2}}*2.55}}|no}}
| {{#ifexpr:{{{opacity|}}}|{{Hexadecimal|{{#expr:{{{opacity|100}}}*2.55}}|no}} }} }} }}<noinclude>
{{documentation}}
[[Category:OSM Location map templates]]
</noinclude>
7z8x1xu1nxki9fq9xou57n4cn5ky1f7
ઢાંચો:Tracked
10
150860
886404
2021-02-19T16:11:39Z
en>Xaosflux
0
update from sandbox
886404
wikitext
text/x-wiki
<templatestyles src="Tracked/styles.css" /><div role="note" class="tracked plainlinks {{#if:{{{1|}}}|mw-trackedTemplate}}">Tracked in [[phabricator:|Phabricator]]<br />{{#if:{{{1|}}}|<span class="tracked-url">[[phabricator:{{ #ifeq: {{padleft: | 1 | {{ uc: {{{1}}} }} }} | T | {{ uc: {{{1}}} }} | T{{ #expr: {{{1}}} + 2000 }} }}|<span class="trakfab-{{ #ifeq: {{padleft: | 1 | {{ uc: {{{1}}} }} }} | T | {{ uc: {{{1}}} }} | T{{ #expr: {{{1}}} + 2000 }} }}"> Task {{ #ifeq: {{padleft: | 1 | {{ uc: {{{1}}} }} }} | T | {{ uc: {{{1}}} }} | T{{ #expr: {{{1}}} + 2000 }} }}</span>]]</span>}}<br>{{#switch:{{lc:{{{2|}}}}}
|resolved|fixed=<span class="tracked-closure tracked-resolved">Resolved</span>
|invalid=<span class="tracked-closure">Invalid</span>
|duplicate=<span class="tracked-closure">Duplicate</span>
|declined|wontfix=<span class="tracked-closure">Declined</span>
|stalled|later=<span class="tracked-closure">Stalled</span>
|open=<span class="tracked-closure">Open</span>
}}</div><noinclude>
{{documentation}}
</noinclude>
stnepu2u43klg2k81vusf9u6gxo5qrm
886405
886404
2025-06-13T17:03:31Z
KartikMistry
10383
[[:en:Template:Tracked]] માંથી આયાત કરેલ ૧ પુનરાવર્તન
886404
wikitext
text/x-wiki
<templatestyles src="Tracked/styles.css" /><div role="note" class="tracked plainlinks {{#if:{{{1|}}}|mw-trackedTemplate}}">Tracked in [[phabricator:|Phabricator]]<br />{{#if:{{{1|}}}|<span class="tracked-url">[[phabricator:{{ #ifeq: {{padleft: | 1 | {{ uc: {{{1}}} }} }} | T | {{ uc: {{{1}}} }} | T{{ #expr: {{{1}}} + 2000 }} }}|<span class="trakfab-{{ #ifeq: {{padleft: | 1 | {{ uc: {{{1}}} }} }} | T | {{ uc: {{{1}}} }} | T{{ #expr: {{{1}}} + 2000 }} }}"> Task {{ #ifeq: {{padleft: | 1 | {{ uc: {{{1}}} }} }} | T | {{ uc: {{{1}}} }} | T{{ #expr: {{{1}}} + 2000 }} }}</span>]]</span>}}<br>{{#switch:{{lc:{{{2|}}}}}
|resolved|fixed=<span class="tracked-closure tracked-resolved">Resolved</span>
|invalid=<span class="tracked-closure">Invalid</span>
|duplicate=<span class="tracked-closure">Duplicate</span>
|declined|wontfix=<span class="tracked-closure">Declined</span>
|stalled|later=<span class="tracked-closure">Stalled</span>
|open=<span class="tracked-closure">Open</span>
}}</div><noinclude>
{{documentation}}
</noinclude>
stnepu2u43klg2k81vusf9u6gxo5qrm
ઢાંચો:Tracked/styles.css
10
150861
886406
2024-12-23T13:12:52Z
en>Taavi
0
better dark mode support
886406
sanitized-css
text/css
/* {{pp|small=y}} */
.tracked {
float: right;
clear: right;
margin: 0 0 1em 1em;
width: 12em;
border: 1px solid var( --border-color-base, #a2a9b1 );
border-radius: 2px;
background-color: var( --background-color-interactive, #EAECF0 );
color: var( --color-base, #202122 );
font-size: 85%;
text-align: center;
padding: 0.5em;
}
.tracked-url {
font-weight: bold;
}
.tracked-closure {
color: var( --color-emphasized, #000 );
font-weight: bold;
text-transform: uppercase;
}
.tracked-resolved {
color: var( --color-success, #14866d );
}
nejphk45hqr2f36r3m4kwk8h09oqk9h
886407
886406
2025-06-13T17:03:31Z
KartikMistry
10383
[[:en:Template:Tracked/styles.css]] માંથી આયાત કરેલ ૧ પુનરાવર્તન
886406
sanitized-css
text/css
/* {{pp|small=y}} */
.tracked {
float: right;
clear: right;
margin: 0 0 1em 1em;
width: 12em;
border: 1px solid var( --border-color-base, #a2a9b1 );
border-radius: 2px;
background-color: var( --background-color-interactive, #EAECF0 );
color: var( --color-base, #202122 );
font-size: 85%;
text-align: center;
padding: 0.5em;
}
.tracked-url {
font-weight: bold;
}
.tracked-closure {
color: var( --color-emphasized, #000 );
font-weight: bold;
text-transform: uppercase;
}
.tracked-resolved {
color: var( --color-success, #14866d );
}
nejphk45hqr2f36r3m4kwk8h09oqk9h
ઢાંચો:Section reflist
10
150862
886416
2024-12-06T00:45:33Z
en>Paine Ellsworth
0
add [[WP:RCAT|rcat template]]s
886416
wikitext
text/x-wiki
#REDIRECT [[Template:Reflist-talk]]
{{Rcat shell|
{{R from alternative name}}
{{R from template shortcut}}
}}
sdllgmmkg8o8w3rw1tdqga6vamv2z7n
886417
886416
2025-06-13T17:03:32Z
KartikMistry
10383
[[:en:Template:Section_reflist]] માંથી આયાત કરેલ ૧ પુનરાવર્તન
886416
wikitext
text/x-wiki
#REDIRECT [[Template:Reflist-talk]]
{{Rcat shell|
{{R from alternative name}}
{{R from template shortcut}}
}}
sdllgmmkg8o8w3rw1tdqga6vamv2z7n
સભ્યની ચર્ચા:Ahmed elnazer9
3
150863
886419
2025-06-14T00:17:11Z
New user message
14116
નવા સભ્યનાં ચર્ચાનાં પાના પર [[ઢાંચો:સ્વાગત|સ્વાગત સંદેશ]]નો ઉમેરો
886419
wikitext
text/x-wiki
{{ઢાંચો:સ્વાગત|realName=|name=Ahmed elnazer9}}
-- [[સભ્ય:Aniket|Aniket]] ([[સભ્યની ચર્ચા:Aniket|ચર્ચા]]) ૦૫:૪૭, ૧૪ જૂન ૨૦૨૫ (IST)
13ceho4o8ycvpe210cby3sygzwrp9th
સભ્યની ચર્ચા:Gajera M
3
150864
886420
2025-06-14T01:54:02Z
New user message
14116
નવા સભ્યનાં ચર્ચાનાં પાના પર [[ઢાંચો:સ્વાગત|સ્વાગત સંદેશ]]નો ઉમેરો
886420
wikitext
text/x-wiki
{{ઢાંચો:સ્વાગત|realName=|name=Gajera M}}
-- [[સભ્ય:Aniket|Aniket]] ([[સભ્યની ચર્ચા:Aniket|ચર્ચા]]) ૦૭:૨૪, ૧૪ જૂન ૨૦૨૫ (IST)
o7ve2dm7vyxoenmc3c5923am9o8qrf5
સભ્યની ચર્ચા:Shravanbhai chaudhary
3
150865
886421
2025-06-14T04:19:12Z
New user message
14116
નવા સભ્યનાં ચર્ચાનાં પાના પર [[ઢાંચો:સ્વાગત|સ્વાગત સંદેશ]]નો ઉમેરો
886421
wikitext
text/x-wiki
{{ઢાંચો:સ્વાગત|realName=|name=Shravanbhai chaudhary}}
-- [[:User:Dsvyas|ધવલ સુધન્વા વ્યાસ]]<sup>[[:User_talk:Dsvyas|ચર્ચા]]/[[:Special:Contributions/Dsvyas|યોગદાન]]</sup> ૦૯:૪૯, ૧૪ જૂન ૨૦૨૫ (IST)
gbzfczi6k6g4u38qjzorpyrhv8d0xbh
સભ્યની ચર્ચા:Shan chaudhary
3
150866
886422
2025-06-14T04:22:17Z
New user message
14116
નવા સભ્યનાં ચર્ચાનાં પાના પર [[ઢાંચો:સ્વાગત|સ્વાગત સંદેશ]]નો ઉમેરો
886422
wikitext
text/x-wiki
{{ઢાંચો:સ્વાગત|realName=|name=Shan chaudhary}}
-- [[:User:Dsvyas|ધવલ સુધન્વા વ્યાસ]]<sup>[[:User_talk:Dsvyas|ચર્ચા]]/[[:Special:Contributions/Dsvyas|યોગદાન]]</sup> ૦૯:૫૨, ૧૪ જૂન ૨૦૨૫ (IST)
de1120q6fl1oj7nis8fpnqvfwxvw72m
સભ્યની ચર્ચા:Nororp
3
150867
886423
2025-06-14T04:53:57Z
New user message
14116
નવા સભ્યનાં ચર્ચાનાં પાના પર [[ઢાંચો:સ્વાગત|સ્વાગત સંદેશ]]નો ઉમેરો
886423
wikitext
text/x-wiki
{{ઢાંચો:સ્વાગત|realName=|name=Nororp}}
-- [[:User:Dsvyas|ધવલ સુધન્વા વ્યાસ]]<sup>[[:User_talk:Dsvyas|ચર્ચા]]/[[:Special:Contributions/Dsvyas|યોગદાન]]</sup> ૧૦:૨૩, ૧૪ જૂન ૨૦૨૫ (IST)
343lm0ll6tutvw681rxzup503k35t2g
સભ્યની ચર્ચા:ADkatara
3
150868
886427
2025-06-14T07:21:44Z
New user message
14116
નવા સભ્યનાં ચર્ચાનાં પાના પર [[ઢાંચો:સ્વાગત|સ્વાગત સંદેશ]]નો ઉમેરો
886427
wikitext
text/x-wiki
{{ઢાંચો:સ્વાગત|realName=|name=ADkatara}}
-- [[સભ્ય:Aniket|Aniket]] ([[સભ્યની ચર્ચા:Aniket|ચર્ચા]]) ૧૨:૫૧, ૧૪ જૂન ૨૦૨૫ (IST)
ebdploag834w3rlnjpaaozbfw6sy3iv
સભ્યની ચર્ચા:Naren.Ayinala
3
150869
886430
2025-06-14T10:50:37Z
Cabayi
24752
Cabayiએ [[સભ્યની ચર્ચા:Naren.Ayinala]]ને [[સભ્યની ચર્ચા:ClashOfClansFTW157]] પર ખસેડ્યું: Automatically moved page while renaming the user "[[Special:CentralAuth/Naren.Ayinala|Naren.Ayinala]]" to "[[Special:CentralAuth/ClashOfClansFTW157|ClashOfClansFTW157]]"
886430
wikitext
text/x-wiki
#REDIRECT [[સભ્યની ચર્ચા:ClashOfClansFTW157]]
imhs36z39g9w1i1fqpsooo5b321hgfp
સભ્યની ચર્ચા:Beytullahyuzel
3
150870
886431
2025-06-14T11:05:52Z
New user message
14116
નવા સભ્યનાં ચર્ચાનાં પાના પર [[ઢાંચો:સ્વાગત|સ્વાગત સંદેશ]]નો ઉમેરો
886431
wikitext
text/x-wiki
{{ઢાંચો:સ્વાગત|realName=|name=Beytullahyuzel}}
-- [[સભ્ય:Aniket|Aniket]] ([[સભ્યની ચર્ચા:Aniket|ચર્ચા]]) ૧૬:૩૫, ૧૪ જૂન ૨૦૨૫ (IST)
msyrd6dw4itqv4r59n05p59x92413nf
સભ્યની ચર્ચા:Roman Ghodasara
3
150871
886432
2025-06-14T11:12:39Z
New user message
14116
નવા સભ્યનાં ચર્ચાનાં પાના પર [[ઢાંચો:સ્વાગત|સ્વાગત સંદેશ]]નો ઉમેરો
886432
wikitext
text/x-wiki
{{ઢાંચો:સ્વાગત|realName=|name=Roman Ghodasara}}
-- [[:User:Dsvyas|ધવલ સુધન્વા વ્યાસ]]<sup>[[:User_talk:Dsvyas|ચર્ચા]]/[[:Special:Contributions/Dsvyas|યોગદાન]]</sup> ૧૬:૪૨, ૧૪ જૂન ૨૦૨૫ (IST)
8y26b8kymirassolmt770dyxbvll79n
સભ્યની ચર્ચા:Ayushm47
3
150872
886433
2025-06-14T11:23:35Z
New user message
14116
નવા સભ્યનાં ચર્ચાનાં પાના પર [[ઢાંચો:સ્વાગત|સ્વાગત સંદેશ]]નો ઉમેરો
886433
wikitext
text/x-wiki
{{ઢાંચો:સ્વાગત|realName=|name=Ayushm47}}
-- [[સભ્ય:Aniket|Aniket]] ([[સભ્યની ચર્ચા:Aniket|ચર્ચા]]) ૧૬:૫૩, ૧૪ જૂન ૨૦૨૫ (IST)
81sbgo6zi0ffssrt6i39myq05looe5i