require('strict');
local data = mw.loadData ('Module:Footnotes/anchor id list/data');
local whitelist = mw.loadData ('Module:Footnotes/whitelist');
local Lang_obj = mw.language.getContentLanguage; -- used by template_list_add to uppercase first letter of template name TODO: better way to do that?  
local redirects_date = {
['date'] = true,
['datetomos'] = true,
['formatdate'] = true,
['isotodmymdy'] = true,
['isotomos'] = true,
}
local redirects_patent = { -- special case cs1-like templates because uses different parameters for name and date in anchor ID
['Cite patent'] = true,
['Citeref patent'] = true,
['Ref patent'] = true,
}
local redirects_sfnref = {
['sfnref'] = true,
['harvid'] = true,
}
local aliases_author = { -- these use pseudo-patterns in the same way as cs1|2; '#' represents 1 or more enumerator digits
'last#',
'author#',
'surname#',
'author-last#',
'author#-last',
'subject#',
'host#',
}
local aliases_contributor = {
'contributor#',
'contributor-last#',
'contributor#-last',
'contributor-surname#',
'contributor#-surname',
}
local aliases_editor = {
'editor#',
'editor-last#',
'editor#-last',
'editor-surname#',
'editor#-surname',
}
local aliases_harvc_author = {
'last#',
'author#',
}
local aliases_inventor = { -- cite patent
'inventor#',
'inventor-last#',
'inventor#-last',
'inventor-surname#',
'inventor#-surname',
'invent#',
'invent-#',
}
local alias_patterns_date = { -- normal lua patterns for most cs1|2-like templates
'|%s*year%s*=%s*',
'|%s*date%s*=%s*',
'|%s*publication%-?date%s*=%s*',
'|%s*air%-?date%s*=%s*',
}
local alias_patterns_harvc_date = { -- normal lua patterns for harvc template
'|%s*anchor%-year%s*=%s*',
'|%s*year%s*=%s*',
}
local alias_patterns_patent_date = { -- normal lua patterns for cite patent templates
'|%s*issue%-date%s*=%s*',
'|%s*gdate%s*=%s*',
'|%s*publication%-date%s*=%s*',
'|%s*pubdate%s*=%s*',
}
local patterns_date = { -- normal lua patterns
'(%d%d%d%d–%d%d%d%d%l?)$', -- YYYY–YYYY four-digit year range at end (Season YYYY–YYYY); with or without dab
'^(%d%d%d%d–%d%d%l?)$', -- YYYY–YY two-digit year range; with or without dab
'^(c%. %d%d%d%d?%l?)$', -- three- or four-digit circa year; with or without dab
'(%d%d%d%d?%l?)$', -- three- or four-digit year at end of date (dmy or mdy); with or without dab
'^(%d%d%d%d?%l?)', -- three- or four-digit year at beginning of date (ymd or YYYY); with or without dab
'^(n%.d%.%l?)$', -- 'no date' with dots; with or without dab
'^(nd%l?)$', -- 'no date' without dots; with or without dab
}
local patterns_tags = {
'',
'<!%-%-.-%-%->',
'<pre>.-</pre>',
'<syntaxhighlight.->.-</syntaxhighlight>',
'<source.->.-</source>', -- deprecated alias of syntaxhighlight tag
}
local template_skip = { -- templates to be skipped for whatever reason; mostly because they resemble cs1-like templates
['Citation-attribution'] = true,
}
local Article_content;  
local anchor_id_list = {}; -- exported tables
local template_list = {};
local article_whitelist = {};  
----------------------------< A R T I C L E _ C O N T E N T _ G E T >----------------------------------------  
get article content, remove certain html-like tags and their content so that this code doesn't include any citation
templates inside the tags as valid tagets; they are not.  
local function article_content_get
if not Article_content then
Article_content = mw.title.getCurrentTitle: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
for _, tag in ipairs (patterns_tags) do
Article_content = Article_content:gsub (tag, ''); -- remove certain html-like tags and their content
end
end
end  
----------------------------< S F N R E F _ G E T >----------------------------------------------------------  
make an anchor id from the contents of or . this function assumes that and
are correctly formed.  
local function sfnref_get (template)
template = template:gsub ('', '%1'); -- strip bounding template markup and trim
local parts = mw.text.split (template, '%s*|%s*'); -- split at the pipe and remove extraneous space characters
local anchor_id = {};  
if redirects_sfnref[parts[1]:lower] then
anchor_id[1] = 'CITEREF';
else
return nil; -- not an sfnref or harvid template
end  
local i = 2; -- indexer into parts{} table
local j = 2; -- indexer into anchor_id{} table which already has 'CITEREF' at [1]
while parts[i] and 7 > j do -- loop through what should be just positional parameters for names and year (2-6 four names and a date)
if not parts[i]:find ('=') then -- look for equal sign (named paraneter in a template that doesn't support named parameters)
anchor_id[j] = parts[i]; -- positional parameters are saved
j = j+1; -- bump the anchor_id{} indexer
end
i = i+ 1; -- bump the parts{} indexer
end  
return table.concat (anchor_id, '');
end  
--year=, |date=, |publicationdate=, or |publication-date in that order. Does not error
check (that is to the cs1|2 templates to do)  
also gets date from |<date alias>=  
local function date_get (template, aliases)
local date;
local rvalue;  
for _, pattern in ipairs (aliases) do -- spin through the date alias patterns
rvalue = tostring(template):match (pattern); -- is this |<date alias>= used (tostring because something makes match think template is a table)
if rvalue then
rvalue = tostring(template):match (pattern .. '(%b{})'); -- is rvalue a template?
if rvalue then
rvalue = rvalue:gsub ('', '%1'); -- strip bounding template markup and trim
local parts = mw.text.split (rvalue, '%s*|%s*'); -- split at the pipe and remove extraneous space characters  
if redirects_date[parts[1]:lower] then -- if parts[1] names or redirect
rvalue = parts[2]; -- assume that date template is properly formed, first positional parameter is the date
else
return ''; -- |date= holds some other template than or redirect
end
else
rvalue = template:match (pattern .. '([^|}]+)');
if rvalue then -- if rvalue is something
rvalue = mw.text.trim (rvalue); -- trim it
end  
if not rvalue or '' == rvalue then -- if rvale was nothing or trimed to nothing
rvalue = nil; -- ensure that it is unset so we can try the next parameter in the list
end
end  
if rvalue then
for _, pattern in ipairs (patterns_date) do -- spin through the recognized date formats
date = rvalue:match (pattern); -- attempt to extract year portion according to the pattern
if date then
return date; -- matched so return;
end
end
break; -- found a date but it was malformed so abandon
end
end
end  
return ''; -- no date param or date param doesn't hold a recognized date; empty string for concatenation
end  
--vauthors= or |veditors=; there is no |vcontributors= parameter.  
splits the v parameter value at the comma; correctly handles accept-as-witten markup when used to wrap a comma-
separated names (corporate)  
local function vnames_get (params, vparam)
local vnames = {}; -- first four author or editor names go here
local split = {}; -- temp table to assist in decoding accept-as-witten-markup  
if params[vparam] then -- test for |vauthors= or |veditor=
split = mw.text.split (params[vparam], '%s*,%s*'); -- this will separate portions of ((Black, Brown, White, an Co.))  
local i = 1; -- an indexer  
while split[i] do
if split[i]:match ('^%(%(.*[^%)][^%)]$') then -- first segment of comma-separated accept-as-witten; this segment has the opening doubled parens
local name = split[i];
i=i+1; -- bump indexer to next segment
while split[i] do
name = name .. ', ' .. split[i]; -- concatenate with previous segments
if split[i]:match ('^.*%)%)$') then -- if this table member has the closing doubled parens
break; -- and done reassembling so
end
i=i+1; -- bump indexer
end
table.insert (vnames, name); -- and add accept-as-witten name to the vnames table  
else
table.insert (vnames, split[i]); -- and add name to the vnames table
end
i=i+1; -- bump indexer
if 5 == i then break; end -- limit to four names
end  
for i, vname in ipairs (vnames) do