Module:Footnotes/anchor id list

require('Module:No globals');

local citerefs = {}; local redirects_citation = { '[Cc]itation', '[Cc]ite', '[Cc]ite citation', '[Cc]ite study', '[Cc]ite [Tt]echnical standard', } local redirects_sfnref = { '^[Ss]fn[Rr]ef$', '^[Hh]arvid$', } local aliases_author = { 'last#', 'author#', 'surname#', 'author-last#', 'author#-last', 'subject#', 'host#', } local aliases_editor = { 'editor#', 'editor-last#', 'editor#-last', 'editor-surname#', 'editor#-surname', } local aliases_date = { 'year', 'date', 'publicationdate', 'publication-date', } local patterns_date = { '^(%d%d%d%d–%d%d%d%d%l?)$',													-- YYYY–YYYY four-digit year range; with or without dab '^(%d%d%d%d–%d%d%l?)$',														-- YYYY–YY two-digit year range; 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 end 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 '^(c%. %d%d%d%d?%l?$)$',													-- three- or four-digit circa year; with or without dab }

--[[--< I S _ T E M P L A T E >

is the template one that is listed in the redirects table; returns true if found; nil else.

]]

local function is_template (template, redirects) for _, redirect in ipairs (redirects) do									-- spin through the redirects table if template:match (redirect) then										-- if found return true															-- done end end return false;																-- not found and done end

--[[--< S F N R E F _ G E T >--

make a CITEREF 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

if is_template (parts[1], redirects_sfnref) then return table.concat (parts, '', 2);										-- assume that sfnref template is properly formed, concatenate and done end

return nil; end

--[[--< D A T E _ G E T >--

extract year from one of |year=, |date=, |publicationdate=, or |publication-date in that order. Does not error check (that is left to the cs1|2 templates to do)

]]

local function date_get (params) local date; for _, alias in ipairs (aliases_date) do									-- spin through the date aliases if params[alias] then													-- if cs1|2 template has this date-alias parameter for _, pattern in ipairs (patterns_date) do							-- spin through the recognized date formats date = params[alias]:match (pattern);							-- has a year-portion is recognized; nil else if date then return date;												-- matched so return end end end end

return '';																	-- no date param or date param doesn't hold a recognized date; empty string for concatenation end

--[[--< N A M E S _ G E T >

get the author names from the cs1|2 template; if there are no authors names, attempt to get editor names.

returns concatenated names in enumeration order when successful; nil else

missing names (missing or empty |lastn= parameter) are omitted but the other names are included.

]]

local function names_get (params, aliases_list) local names = {};															-- first four author or editor names go here local enum_alias;															-- alias with '#' replaced with a digit

for i, alias in ipairs (aliases_list) do		for enum=1, 5 do			enum_alias = alias:gsub ('#', enum);								-- replace '#' to make 'lastn' if 1 == enum then													-- because |last= and last1= are exact aliases if params[enum_alias] then										-- test |last1= first names[enum] = params[enum_alias];							-- found so save the value assigned to |last1= else enum_alias = alias:gsub ('#', '');							-- replace '#' to make 'last' if params[enum_alias] then names[enum] = params[enum_alias];						-- found so save the value assigned to |last= end end else																-- here for enum 2, 3, 4 if params[enum_alias] then names[enum] = params[enum_alias];							-- found so save the value assigned to |lastn= end end end end return 0 ~= #names and table.concat (names) or nil							-- return a concatenation of the names; nil else end

--[[--< T E M P L A T E _ S T R I P >--

Templates are not allowed in parameters that are made part of COinS metadata; yet, they will appear. cs1|2 does not see the template markup but instead sees the result of the template as html. cs1|2 strips the html which leaves the displayed value for the CITEREF. We can't necessarily do that here so, because templates aren't allowed in parameters, we simply discard any templates found in the cs1|2 template.

this may leave a |lastn= parameter empty which will be treated as if it were really empty as cs1|2 do (three authors,
 * last2= empty -> CITEREFLast1Last3YYYY (the harv and sfn render: 'Last1, & Last3 YYYY' with CITEREFLast1Last3YYYY).

]]

local function template_strip (template) template = template:gsub ('^$', '', 1);					-- remove outer 🇦🇩 (cs1|2 template delimiters) template = template:gsub ('%b{}', '');										-- remove any templates from the cs1|2 template return template; end

--[=[-< W I K I L I N K _ S T R I P >--

Wikilink markup des not belong in CITEREF and can / does confuse the code that parses apart the cs1|2 template so here we remove any wiki markup: label -> label text link -> link text ]=]

local function wikilink_strip (template) template = template:gsub ('%[%[.-|(.-)%]%]', '%1');							-- replace complex lable wikilinks with label text template = template:gsub ('%[%[(.-)%]%]', '%1');							-- replace simple link wikilinks with link text return template; end

--[[--< C I T E R E F _ M A K E >--

inspect |ref= to decide what to do: |ref=harv									- get names and date from template parameters |ref=CITEREF... - take everything after 'CITEREF' |ref=	- assemble CITEREF from positional parameters |ref=	- assemble CITEREF from positional parameters |ref=none									- skip; do nothing because CITEREF intentionally suppressed; TODO: keep with a type code of '0'? |ref=										- empty or missing for cs1: skip for cs2: get names and date from template parameters |ref=??? - skip; not CITEREF compatible

]]

local function citeref_make (template) local ref;																	-- content of |ref= local template_name;														-- name of the template for cs2 detection local citeref;																-- the assembled CITEREF from this template local params = {};															-- table of cs1|2 parameters template_name = template:match ('{{%s*([^|]+)'):lower;					-- get lowercase template name

ref = template:match ('|%s*ref%s*=%s*(%b{})');								-- first look for |ref= or |ref= if not ref then																-- if not found if template:match ('|%s*ref%s*=([^|}]+)') then							-- if there is a |ref= param with an assigned value ref = template:match ('|%s*ref%s*=([^|}]+)'):gsub ('%s*(.-)%s*', '%1');	-- because we will strip templates from the cs1|2 template and ref migh hold |ref= or |ref= else																	-- here when |ref= missing or empty if is_template (template, redirects_citation) then					-- could be cs2 ref = 'harv';													-- spoof to handle cs2 as if it were cs1 with |ref=harv end end end template = wikilink_strip (template);										-- because piped wikilinks confuses code that builds params{} and because plain wikilinks not allowed in CITEREF -- strip templates after getting |ref= value because |ref= and |ref= are allowed template = template_strip (template);										-- because template markup can confuse code that builds params{} and because templates in name parameters are not allowed

for param, value in template:gmatch ('|%s*([^=]-)%s*=%s*([^|}]+)') do		-- build a table of template parameters and their values params[param] = mw.text.trim (value); end if 'harv' == ref then														-- |ref=harv citeref = names_get (params, aliases_author) or names_get (params, aliases_editor);	-- get author name; if none then get editor names if citeref then															-- if names were gotten citeref = citeref .. date_get (params);								-- get date portion end

elseif ref:match ('CITEREF(.+)') then										-- for hand-created CITEREFs, take everything but the 'CITEREF' prefix citeref = ref:match ('CITEREF(.+)'); elseif ref:match ('%b{}') then												-- ref holds a template citeref = sfnref_get (ref);												-- returns content of or ; nil else

elseif 'none' == ref then													-- |ref=none return nil;																-- CITEREF expicitly suppressed elseif '' ~= ref then														-- |ref=??? TODO: keep this content because etc has |ref= parameter that overrides CITEREF? return nil;																-- text other than something acceptible as a CITEREF end return citeref;																-- citeref text without 'CITEREF' prefix if found or decoded; nil else end

--[[--< H A R V _ L I N K _ T E S T >--

]]

local function harv_link_test --	local 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 if '' == article_content then												-- when there is no article content return '';																-- no point in continuing end local tstart, tend, _ = article_content:find ('{{%s*[Vv]?[Cc]it[ae]');		-- find the first cs1|2 template (or vcite xxx template) local template;																-- place to hold the template that we found local citeref;																-- place to hold CITEREFs as they are extracted / decoded

while tstart do																-- nil when cs1|2 / vcite template not found template = article_content:match ('%b{}', tstart);						-- get the whole template

if template then														-- necessary? citeref = citeref_make (template);									-- extract CITEREF from this template if citeref then														-- if there was a CITEREF extracted citeref = mw.uri.anchorEncode (citeref);						-- encode to remove wikimarkup, convert spaces to underscores etc if not citerefs[citeref] then									-- if not already saved citerefs[citeref] = 1;										-- save it 				else															-- here when this CITEREF already saved citerefs[citeref] = 2;										-- to indicate that there are multiple same name/date citations end end end tstart = tend;															-- reset the search starting index tstart, tend, _ = article_content:find ('{{%s*[Vv]?[Cc]it[ae]', tstart);	-- search for another cs1|2 / vcite template end --	return mw.dumpObject (citerefs); return citerefs; end

----< E X P O R T E D _ T A B L E S >

return { citerefs = citerefs,														-- table of CITEREFs available in this article --	harv_link_test = harv_link_test,	-- FOR DEBUG ONLY }