----------------------------< 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 '&nbsp;',
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 '&nbsp;', -- 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  
--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 = 1576022400
The medrxiv identifier has a start date of 2020-01-01. The Unix timestamp for that date is = 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