Jump to content
Wikipedia The Free Encyclopedia

Module:Footnotes/anchor id list

From Wikipedia, the free encyclopedia
Module documentation[view] [edit] [history] [purge]
Warning This Lua module is used on approximately 254,000 pages .
To avoid major disruption and server load, any changes should be tested in the module's /sandbox or /testcases subpages, or in your own module sandbox. The tested changes can be added to this page in a single edit. Consider discussing changes on the talk page before implementing them.
This module depends on the following other modules:

This code scans the article contents for templates which generates anchor ids for footnotes.

The above documentation is transcluded from Module:Footnotes/anchor id list/doc. (edit | history)
Editors can experiment in this module's sandbox (edit | diff) and testcases (create) pages.
Subpages of this module.

 require('strict');
 localdata=mw.loadData('Module:Footnotes/anchor id list/data');
 localwhitelist=mw.loadData('Module:Footnotes/whitelist');
 localLang_obj=mw.language.getContentLanguage();-- used by template_list_add() to uppercase first letter of template name TODO: better way to do that?

 localredirects_date={
 ['date']=true,
 ['datetomos']=true,
 ['formatdate']=true,
 ['isotodmymdy']=true,
 ['isotomos']=true,
 }
 localredirects_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,
 }
 localredirects_sfnref={
 ['sfnref']=true,
 ['harvid']=true,
 }
 localaliases_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#',
 }
 localaliases_contributor={
 'contributor#',
 'contributor-last#',
 'contributor#-last',
 'contributor-surname#',
 'contributor#-surname',
 }
 localaliases_editor={
 'editor#',
 'editor-last#',
 'editor#-last',
 'editor-surname#',
 'editor#-surname',
 }
 localaliases_harvc_author={
 'last#',
 'author#',
 }
 localaliases_inventor={-- cite patent
 'inventor#',
 'inventor-last#',
 'inventor#-last',
 'inventor-surname#',
 'inventor#-surname',
 'invent#',
 'invent-#',
 }
 localalias_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*',
 }
 localalias_patterns_harvc_date={-- normal lua patterns for harvc template
 '|%s*anchor%-year%s*=%s*',
 '|%s*year%s*=%s*',
 }
 localalias_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*',
 }
 localpatterns_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%d%d%d%l?)$',-- any range with four-digit years; with or without dab; not two captures
 '^(%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
 }
 localpatterns_tags={
 '<nowiki>.-</nowiki>',
 '<!%-%-.-%-%->',
 '<pre>.-</pre>',
 '<syntaxhighlight.->.-</syntaxhighlight>',
 '<source.->.-</source>',-- deprecated alias of syntaxhighlight tag
 }
 localtemplate_skip={-- templates to be skipped for whatever reason; mostly because they resemble cs1-like templates
 ['Citation-attribution']=true,
 }
 localglobal_article_content=nil

 localglobal_anchor_id_list=nil-- exported tables
 localglobal_template_list=nil
 localglobal_article_whitelist=nil



 --[[--------------------------< 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.

 ]]

 localfunctionarticle_content_get()
 ifglobal_article_contentthenreturnglobal_article_contentend
 localarticle_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_,taginipairs(patterns_tags)do
 article_content=article_content:gsub(tag,'');-- remove certain html-like tags and their content
 end
 global_article_content=article_content
 returnarticle_content
 end


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

 make an anchor id from the contents of {{sfnref}} or {{harvid}}. this function assumes that {{sfnref}} and {{harvid}}
 are correctly formed.

 ]]

 localfunctionsfnref_get(template)
 template=template:gsub('{{%s*(.-)%s*}}','%1');-- strip bounding template markup and trim
 localparts=mw.text.split(template,'%s*|%s*');-- split at the pipe and remove extraneous space characters
 localanchor_id={};

 ifredirects_sfnref[parts[1]:lower()]then
 anchor_id[1]='CITEREF';
 else
 returnnil;-- not an sfnref or harvid template
 end

 locali=2;-- indexer into parts{} table
 localj=2;-- indexer into anchor_id{} table which already has 'CITEREF' at [1]
 whileparts[i]and7>jdo-- loop through what should be just positional parameters for names and year (2-6 four names and a date)
 ifnotparts[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

 returntable.concat(anchor_id,'');
 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)

 also gets date from |<date alias>={{date|...}}

 ]]

 localfunctiondate_get(template,aliases)
 localrvalue;

 for_,patterninipairs(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)
 ifrvaluethen
 rvalue=tostring(template):match(pattern..'(%b{})');-- is rvalue a template?
 ifrvaluethen
 rvalue=rvalue:gsub('{{%s*(.-)%s*}}','%1');-- strip bounding template markup and trim
 localparts=mw.text.split(rvalue,'%s*|%s*');-- split at the pipe and remove extraneous space characters

 ifredirects_date[parts[1]:lower()]then-- if parts[1] names {{date}} 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 {{date}} or redirect
 end
 else
 rvalue=template:match(pattern..'([^|}]+)');
 ifrvaluethen-- if rvalue is something
 rvalue=mw.text.trim(rvalue);-- trim it
 end

 ifnotrvalueor''==rvaluethen-- 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

 ifrvaluethen
 for_,patterninipairs(patterns_date)do-- spin through the recognized date formats
 --					date = rvalue:match (pattern);								-- attempt to extract year portion according to the pattern
 localdate,date2=rvalue:match(pattern);-- attempt to extract year portion according to the pattern; <date2> gets second year in any range
 ifdatethen
 ifdate2then-- when a second year
 date=table.concat({date,'–',date2});-- build a date range
 end
 returndate;-- 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


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

 extract names from |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)

 ]]

 localfunctionvnames_get(params,vparam)
 localvnames={};-- first four author or editor names go here
 localsplit={};-- temp table to assist in decoding accept-as-witten-markup

 ifparams[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.))

 locali=1;-- an indexer

 whilesplit[i]do
 ifsplit[i]:match('^%(%(.*[^%)][^%)]$')then-- first segment of comma-separated accept-as-witten; this segment has the opening doubled parens
 localname=split[i];
 i=i+1;-- bump indexer to next segment
 whilesplit[i]do
 name=name..', '..split[i];-- concatenate with previous segments
 ifsplit[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
 if5==ithenbreak;end-- limit to four names
 end

 fori,vnameinipairs(vnames)do
 ifnotvname:match('%(%(.-%)%)')then-- without accept-this-value-as-written markup
 vnames[i]=vname:gsub('(.-)%s+%u+$','%1');-- extract and save surname(s)
 end
 end
 fori,vnameinipairs(vnames)do-- repeat, this time for accept-this-value-as-written markup
 vnames[i]=vname:gsub('%(%((.-)%)%)','%1');-- remove markup if present and save the whole name
 end
 end

 return0~=#vnamesandtable.concat(vnames)ornil-- return a concatenation of the vnames; nil else
 end


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

 cs1|2 makes anchor id from contributor, author, or editor name-lists in that order

 get the names from the cs1|2 template; if there are no contributor names, try author names, then try editor names.

 returns concatenated names in enumeration order when successful; nil else

 empty name (nameholding parameter n is present without value) and missing name (nameholding parameter n is not
 present) are included as empty string with all other names

 ]]

 localfunctionnames_get(params,aliases_list)
 localnames={};-- first four author or editor names go here
 localenum_alias;-- alias with '#' replaced with a digit

 forenum=1,4do-- four names only
 fori,aliasinipairs(aliases_list)do
 ifnotnames[enum]then-- hanven't found a previous alias with this [enum]? see if we can find this alias with this enum
 enum_alias=alias:gsub('#',enum);-- replace '#' to make 'lastn'

 if1==enumthen-- because |last= and last1= are exact aliases
 ifparams[enum_alias]then-- test |last1= first
 names[enum]=params[enum_alias];-- found so save the value assigned to |last1=
 break;-- next enum
 else
 enum_alias=alias:gsub('#','');-- replace '#' to make 'last'
 ifparams[enum_alias]then
 names[enum]=params[enum_alias];-- found so save the value assigned to |last=
 break;-- next enum
 end
 end
 else-- here for enum 2, 3, 4
 ifparams[enum_alias]then
 names[enum]=params[enum_alias];-- found so save the value assigned to |lastn=
 break;-- next enum
 end
 end
 end
 end
 end

 forenum=1,4do-- spin through the names table and
 localname=names[enum];
 ifnotnamethen-- when nameholding parameter n is not present (nil)
 name='';-- convert to empty string for concatenation
 end
 name=name:gsub('%(%((.-)%)%)','%1');-- remove accept-as-written markup if present
 names[enum]=name;-- save the modified name
 end

 localname_str=table.concat(names);-- concatenate the names
 return''~=name_strandname_strornil;-- return the concatenation if not empty string; nil else
 end


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

 removes the citation or havrc template's {{ and }} markup then removes, in whole, any templates found inside the
 citation or harvc template.

 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 anchor id. We can't 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).

 ]]

 localfunctiontemplate_strip(template)
 template=template:gsub('^{{%s*',''):gsub('%s*}}$','',1);-- remove outer {{ and }} (cs1|2 template delimiters with trailing/leading whitespace)
 template=template:gsub('%b{}','');-- remove any templates from the cs1|2 template
 returntemplate;
 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.
 ]]

 localfunctionescape_lua_magic_chars(argument)
 argument=argument:gsub("%%","%%%%");-- replace % with %%
 argument=argument:gsub("([%^%$%(%)%.%[%]%*%+%-%?])","%%%1");-- replace all other lua magic pattern characters
 returnargument;
 end


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

 Wikilink markup does not belong in an anchor id and can / does confuse the code that parses apart citation and
 harvc templates so here we remove any wiki markup:
 	[[link|label]] -> label
 	[[link]] -> link

 ]=]

 localfunctionwikilink_strip(template)
 forwikilinkintemplate:gmatch('%[%b[]%]')do-- get a wikilink
 template=template:gsub('%[%b[]%]','__57r1P__',1);-- install a marker
 ifwikilink:match('%[%[.-|(.-)%]%]')then
 wikilink=wikilink:match('%[%[.-|(.-)%]%]');-- extract label from complex [[link|label]] wikilink
 else
 wikilink=wikilink:match('%[%[(.-)%]%]');-- extract link from simple [[link]] wikilinks
 end
 wikilink=escape_lua_magic_chars(wikilink);-- in case there are lua magic characters in wikilink
 template=template:gsub('__57r1P__',wikilink,1);-- replace the marker with the appropriate text
 end

 returntemplate;
 end


 --[[--------------------------< T E M P L A T E _ N A M E _ G E T >--------------------------------------------

 return the citation or harvc template's name; convert to lower case and trim leading and trailing whitespace;

 when the template is a sandbox the subpage portion of the template name is omitted from the returned template name
 	{{Cite book/new |...}} returns cite book

 ]]

 localfunctiontemplate_name_get(template)
 localtemplate_name=template:match('^{{%s*([^/|}]+)');-- get template name; ignore subpages ~/new, ~/sandbox; parser functions

 ifnottemplate_nameortemplate_name:match('^#')then-- parser functions, magic words don't count as templates
 returnnil;-- could not get template name from (possibly corrupt) template; extraneous opening { mid template can cause this;
 end;
 template_name=template_name:gsub('%s*$','');-- trim trailing whitespace; leading whitespace already removed
 returnLang_obj:ucfirst(template_name);-- first character in template name must be uppercase (same as canonical template name) TODO: better way to do this?
 end


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

 parse apart a template's parameters and store in the params table where key is the parameter's name and value is
 the parameter's value; empty parameters are not saved

 ]]

 localfunctiontemplate_params_get(template,params_t)
 template=wikilink_strip(template);-- because piped wikilinks confuse code that builds params_t{} and because wikilinks not allowed in an anchor id
 -- strip templates after getting |ref= value because |ref={{sfnref}} and |ref={{harvid}} are allowed
 template=template_strip(template);-- because template markup can confuse code that builds params_t{} and because templates in name parameters are not allowed

 localtemp_t=mw.text.split(template,'%s*|%s*');--split on the pipe
 for_,paraminipairs(temp_t)do
 ifparam:find('=',1,true)then-- a named parameter?
 localk,v=param:match('%s*([^=]-)%s*=%s*([^|}]+)');
 ifvthen-- there must be a value
 if''~=vandnotv:match('^%s$')then-- skip when value is empty string or only whitespace
 params_t[k]=mw.text.trim(v);-- add trimmed value else
 end
 end
 end
 end
 end


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

 makes anchor_id from {{harvc}} or redirects

 ]]

 localfunctionanchor_id_make_harvc(template)
 localdate=date_get(template,alias_patterns_harvc_date);-- get date; done here because might be in {{date}}; return date if valid; empty string else
 localanchor_id;
 localparams={};-- table of harvc parameters
 localid;-- custom anchor id for this {{harvc}} template

 id=template:match('|%s*id%s*=%s*(%b{})');-- in case |id={{sfnref}}; done here because templates will be stripped

 template_params_get(template,params);-- build a table of template parameters and their values; this strips wikilinks and templates

 ifidthen-- when set is {{sfnref}} or {{harvid}} template
 returnsfnref_get(id);-- returns content of {{sfnref}} or {{harvid}}; nil else
 end
 ifparams.idthen-- custom anchor for this {{harvc}} template (text)
 returnparams.id;-- |id= value as written
 end

 anchor_id=names_get(params,aliases_harvc_author);-- get the harvc contributor names

 ifanchor_idthen-- if names were gotten
 return'CITEREF'..anchor_id..date;
 end
 returnnil;-- no names; no anchor_id
 end


 --[[--------------------------< A N C H O R _ I D _ M A K E _ W R A P P E R >----------------------------------

 for wrapper templates

 inspect externally visible |ref= to decide what to do:
 	|ref=										- empty or missing: get names and date from whitelist defaults; override defaults from externally visible template parameters
 	|ref=harv									- same as empty or missing
 	|ref={{SfnRef|name|name|name|name|year}}	- assemble an anchor id from {{sfnref}} positional parameters
 	|ref={{Harvid|name|name|name|name|year}}	- assemble an anchor id from {{harvid}} positional parameters
 	|ref=none									- skip; do nothing because an anchor id intentionally suppressed; TODO: keep with a type code of '0'?
 	|ref=<text>									- save param value because may match an anchor id override value in {{harv}} template |ref= parameter or {{harvc}} |id= parameter

 ]]

 localfunctionanchor_id_make_wrapper(template)
 localref;-- content of |ref=
 localtemplate_name;-- name of the template
 localanchor_id;-- the assembled anchor id from this template
 localdate;
 localname_default;
 localdate_default;
 localvol;
 localparams={};-- table of template parameters

 template_name=template_name_get(template);-- get first char uppercase trimmed template name; ignore subpages ~/new, ~/sandbox
 ifnottemplate_nameortemplate_skip[template_name]then
 returnnil;-- could not extract template name from (possibly corrupted) template (extraneous opening { in the template will cause this)
 end

 date=date_get(template,alias_patterns_date);-- get date; done here because might be in {{date}}
 --	if '' == date then
 --		date = whitelist.wrapper_templates[template_name][2] or '';				-- no externally visible date so get default date
 --	end

 ref=template:match('|%s*ref%s*=%s*(%b{})');-- first look for |ref={{sfnref}} or |ref={{harvid}} because we will strip templates from the wrapper template
 ifnotrefthen
 iftemplate:match('|%s*ref%s*=([^|}]+)')then-- |ref={{template}} not found; if there is a |ref= param with an assigned value
 ref=template:match('|%s*ref%s*=([^|}]+)');-- get the value; whitespace is a 'value'
 ifrefthen-- nil when |ref=|... or when |ref=}} (no spaces between assignment operator and pipe or closing brace)
 ref=mw.text.trim(ref);-- something, could be just whitespace, so trim leading / trailing whitespace
 if''==refthen-- trimming a string of whitespace makes an empty string
 ref=nil;-- make empty ref same as missing ref
 end
 end
 end
 end

 template_params_get(template,params);-- build a table of template parameters and their values

 localwrap_data=whitelist.wrapper_templates[template_name]

 ifwrap_data[1]then-- is this wrapper a simple-default wrapper?
 name_default=wrap_data[1];-- get the default names
 date_default=wrap_data[2];-- get the default date
 else
 vol=params['volume']or'default';
 localfascicle=params['fascicle']-- some templates use "fascicle" to mean "subvolume"
 iffasciclethen
 localsubvol=vol..'/'..fascicle-- if fascicle is used, subvolume = "vol/fascicle"
 ifwrap_data[subvol]then-- if subvolume exists, use it, otherwise fall back to volume
 vol=subvol
 end
 end
 ifnotwrap_data[vol]then-- make sure this volume exists
 vol='default';-- doesn't exist, use default volume
 end
 name_default=wrap_data[vol][1];-- get the default names
 date_default=wrap_data[vol][2];-- get the default date
 end

 if'harv'==refornotrefthen-- |ref=harv specified or |ref= missing or empty
 anchor_id=names_get(params,aliases_contributor)or-- get contributor, author, or editor names
 names_get(params,aliases_author)or
 vnames_get(params,'vauthors')or-- |vauthors=
 names_get(params,aliases_editor)or
 vnames_get(params,'veditors')or-- |veditors=
 name_default;-- default names from whitelist
 --			whitelist.wrapper_templates[template_name][1];						-- default names from whitelist

 if''==datethen-- if date not provided in the template
 date=date_default;-- use the default date from whitelist
 end

 ifanchor_idthen-- if names were gotten
 anchor_id='CITEREF'..anchor_id..date;
 end

 elseifref:match('%b{}')then-- ref holds a template
 anchor_id=sfnref_get(ref);-- returns content of {{sfnref}} or {{harvid}}; nil else

 elseif'none'==refthen-- |ref=none
 returnnil;-- anchor id expicitly suppressed

 else
 anchor_id=ref;-- |ref=<text> may match an anchor id override value in {{harv}} template |ref= parameter
 end

 returnanchor_id;-- anchor_id text; nil else
 end


 --[[--------------------------< A N C H O R _ I D _ M A K E _ C S 1 2 >----------------------------------------

 for cs1|2 template and cs1-like templates

 inspect |ref= to decide what to do:
 	|ref=										- empty or missing: get names and date from template parameters; all cs1|2 create CITEREF anchor IDs
 	|ref=harv									- get names and date from template parameters
 	|ref={{SfnRef|name|name|name|name|year}}	- assemble an anchor id from {{sfnref}} positional parameters
 	|ref={{Harvid|name|name|name|name|year}}	- assemble an anchor id from {{harvid}} positional parameters
 	|ref=none									- skip; do nothing because an anchor id intentionally suppressed; TODO: keep with a type code of '0'?
 	|ref=<text>									- save param value because may match an anchor id override value in {{harv}} template |ref= parameter or {{harvc}} |id= parameter

 ]]

 localfunctionanchor_id_make_cs12(template)
 localref;-- content of |ref=
 localtemplate_name;-- name of the template
 localanchor_id;-- the assembled anchor id from this template
 localdate;
 localparams={};-- table of template parameters

 template_name=template_name_get(template);-- get first char uppercase trimmed template name; ignore subpages ~/new, ~/sandbox
 ifnottemplate_nameortemplate_skip[template_name]then
 returnnil;-- could not extract template name from (possibly corrupted) template (extraneous opening { in the template will cause this)
 end

 ifredirects_patent[template_name]then
 date=date_get(template,alias_patterns_patent_date);-- get date; done here because might be in {{date}} 
 else
 date=date_get(template,alias_patterns_date);
 end

 ref=template:match('|%s*ref%s*=%s*(%b{})');-- first look for |ref={{sfnref}} or |ref={{harvid}} because we will strip templates from the cs1|2 template
 ifnotrefthen
 iftemplate:match('|%s*ref%s*=([^|}]+)')then-- |ref={{template}} not found; if there is a |ref= param with an assigned value
 ref=template:match('|%s*ref%s*=([^|}]+)');-- get the value; whitespace is a 'value'
 ifrefthen-- nil when |ref=|... or when |ref=}} (no spaces between assignment operator and pipe or closing brace)
 ref=mw.text.trim(ref);-- something, could be just whitespace, so trim leading / trailing whitespace
 if''==refthen-- trimming a string of whitespace makes an empty string
 ref=nil;-- make empty ref same as missing ref
 end
 end
 end
 end

 template_params_get(template,params);-- build a table of template parameters and their values

 if'harv'==refornotrefthen-- |ref=harv specified or |ref= missing or empty
 ifredirects_patent[template_name]then-- if this is a cite patent template
 anchor_id=names_get(params,aliases_inventor);-- inventor names only
 else-- cs1|2 template
 anchor_id=names_get(params,aliases_contributor)or-- get contributor, author, or editor names
 names_get(params,aliases_author)or
 vnames_get(params,'vauthors')or-- |vauthors=
 names_get(params,aliases_editor)or
 vnames_get(params,'veditors');-- |veditors=
 end

 ifanchor_idthen-- if names were gotten
 anchor_id='CITEREF'..anchor_id..date;
 end

 elseifref:match('%b{}')then-- ref holds a template
 anchor_id=sfnref_get(ref);-- returns content of {{sfnref}} or {{harvid}}; nil else

 elseif'none'==refandnotredirects_patent[template_name]then-- |ref=none; not supported by cite patent
 returnnil;-- anchor id expicitly suppressed

 else
 anchor_id=ref;-- |ref=<text> may match an anchor id override value in {{harv}} template |ref= parameter
 end

 returnanchor_id;-- anchor_id text; nil else
 end


 --[[--------------------------< L I S T _ A D D >--------------------------------------------------------------

 adds an <item> to <list> table; for anchor IDs, the boolean <encode> argument must be set true; no return value

 ]]

 localfunctionlist_add(item,list,encode)
 ifitemthen-- if there was an item
 ifencodethen-- for anchor IDs ...
 item=mw.uri.anchorEncode(item);-- encode to remove wikimarkup, convert spaces to underscores etc
 end

 ifnotlist[item]then-- if not already saved
 list[item]=1;-- save it 
 else-- here when this item already saved
 list[item]=list[item]+1;-- to indicate that there are multiple items
 end
 end
 end


 --[[--------------------------< A N C H O R _ I D _ M A K E _ A N C H O R >------------------------------------

 make anchor IDs from {{anchor}}; there may be more than one because {{anchor}} is not limited to the number of
 anchors it may hold.

 ]]

 localfunctionanchor_id_make_anchor(template,anchor_id_list)
 template=template:gsub('^{{[^|]+|',''):gsub('}}$','',1);-- remove outer {{ and }} and template name

 template=wikilink_strip(template);-- strip any wikilink markup (there shouldn't be any but just in case)

 localparams={};
 localanchor_id;

 forparamintemplate:gmatch('%b{}')do-- loop through the template; remove and save templates (presumed to be sfnref or harvid)
 table.insert(params,param);-- save it
 template=template:gsub('%b{}','',1);-- remove it from source template
 end

 for_,tinipairs(params)do-- spin through the templates in params
 anchor_id=sfnref_get(t);-- attempt to decode {{sfnref}} and {{harvid}}
 ifanchor_idthen-- nil when not {{sfnref}} or {{harvid}}
 list_add(anchor_id,anchor_id_list,true);-- add anchor ID to the list
 end
 end

 template=template:gsub('|%s*|','|');-- when pipe follows pipe with or without white space, remove extraneous pipe
 template=template:gsub('^|',''):gsub('|$','');-- remove extraneous leading and trailing pipes

 params=mw.text.split(template,'%s*|%s*');-- split at the pipe and remove extraneous space characters

 for_,tinipairs(params)do-- spin through the anchor IDs
 anchor_id=mw.text.trim(t);-- trim white space
 if''~=anchor_idthen-- should always have something
 list_add(anchor_id,anchor_id_list,true);-- add anchor ID to the list
 end
 end
 end


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

 makes a list of templates use in the article.

 ]]

 localfunctiontemplate_list_add(template,template_list)
 localtemplate=template:match('{{%s*(.-)[|}]');-- keep the case of the template - this is different from template_name_get()
 iftemplateandnottemplate:match('^#')then-- found a template or magic word; ignore magic words
 template=mw.text.trim(template);-- trim whitespace
 template=Lang_obj:ucfirst(template);-- first character in template name must be uppercase (same as canonical template name) TODO: better way to do this?
 list_add(template,template_list);-- add to list with (unused) tally
 end
 end


 --[[--------------------------< A N C H O R _ I D _ L I S T _ M A K E >----------------------------------------

 makes a list of anchor ids from cs1|2, cs1|2-like, vcite xxx, harvc, anchor, wikicite templates

 Because cs1|2 wrapper templates can, and often do, hide the author and date parameters inside the wrapper,
 these parameters are not available in the article's wikisource so {{harv}}, {{sfn}}, and {{harvc}} templates that
 link correctly to those wrapper templates will incorrectly show error messages. Use |ignore-err=yes in the {{harv}},
 {{sfn}}, and {{harvc}} templates to supress the error message.

 creates a list of templates used in the article for use with the whitelist

 creates a list of article-local whitelisted anchor IDs from {{sfn whitelist}}

 ]]

 localfunctionanchor_id_list_make()
 localanchor_id_list={}
 localtemplate_list={}
 localarticle_whitelist={}
 localarticle_content=article_content_get();-- attempt to get this article's content

 ifarticle_content==''then-- when there is no article content
 return'';-- no point in continuing
 end

 localtemplate;-- place to hold the template that we found
 localtemplate_name;
 localanchor_id;-- place to hold an anchor id as it is extracted / decoded

 localfind_pattern='%f[{]{{[^{]';
 localtstart,tend=article_content:find(find_pattern);-- find the first template; do not find template variables: {{{template var|}}} 

 whiletstartdo
 template=article_content:match('%b{}',tstart);-- get the whole template
 ifnottemplatethen
 break;-- template is nil for some reason (last template missing closing }} for example) so declare ourselves done
 end

 template_name=template_name_get(template);-- get first char uppercase trimmed template name; ignore subpages ~/new, ~/sandbox
 template_list_add(template,template_list);-- add this template's name to the list

 ifdata.known_templates_cs12[template_name]then
 anchor_id=anchor_id_make_cs12(template);-- extract an anchor id from this template
 list_add(anchor_id,anchor_id_list,true)

 elseifdata.known_templates_vcite[template_name]then
 localref=template:match('|%s*ref%s*=%s*(%b{})');-- first look for |ref={{sfnref}} or |ref={{harvid}} because we will strip templates from the vcite template
 ifrefthen-- |ref={{template}}
 anchor_id=sfnref_get(ref);-- returns content of {{sfnref}} or {{harvid}}; nil else
 list_add(anchor_id,anchor_id_list,true);
 else
 localparams={};

 template_params_get(template,params);-- build a table of template parameters and their values

 anchor_id=params['ref'];-- when both set, vcite uses value from |ref=
 ifnotanchor_idandparams['harvid']then
 anchor_id='CITEREF'..params['harvid'];-- in vcite, |harvid= auto-adds 'CITEREF' prefix to the value in |harvid=
 end
 list_add(anchor_id,anchor_id_list,true);
 end

 elseifdata.known_templates_harvc[template_name]then
 anchor_id=anchor_id_make_harvc(template);-- extract an anchor id from this template
 list_add(anchor_id,anchor_id_list,true);

 elseifdata.known_templates_wikicite[template_name]then
 localref=template:match('|%s*ref%s*=%s*(%b{})');-- first look for |ref={{sfnref}} or |ref={{harvid}}

 ifrefthen
 anchor_id=sfnref_get(ref);

 elseiftemplate:match('|%s*ref%s*=([^|}]+)')then
 anchor_id=template:match('|%s*ref%s*=([^|}]+)');-- plain-text

 elseiftemplate:match('|%s*id%s*=%s*(%b{})')then
 ref=template:match('|%s*id%s*=%s*(%b{})');
 anchor_id='Reference-'..sfnref_get(ref);

 elseiftemplate:match('|%s*id%s*=([^|}]+)')then
 anchor_id='Reference-'..template:match('|%s*id%s*=([^|}]+)');-- plain-text

 else
 anchor_id=nil;-- no matches, ensure that anchor_id has no value
 end

 ifanchor_idthen
 list_add(anchor_id,anchor_id_list,true);
 end

 elseifdata.known_templates_anchor[template_name]then
 anchor_id_make_anchor(template,anchor_id_list);-- extract anchor ids from this template if any

 elseifdata.known_templates_sfn_whitelist[template_name]then
 template=template:gsub('^{{[^|]+|',''):gsub('}}$','',1);-- remove outer {{ and }} and template name
 template=mw.text.trim(template,'%s|');-- trim leading trailing white space and pipes
 template=mw.text.split(template,'%s*|%s*');-- make a table of the template's parameters

 for_,anchor_idinipairs(template)do-- spin through this template's parameter
 if''~=anchor_idandnotarticle_whitelist[anchor_id]then
 anchor_id=mw.uri.anchorEncode(anchor_id)
 article_whitelist[anchor_id]=1;-- add to the whitelist
 end
 end

 elseiftemplate_nameandwhitelist.wrapper_templates[template_name]then
 anchor_id=anchor_id_make_wrapper(template);-- extract an anchor id from this template if possible
 list_add(anchor_id,anchor_id_list,true);


 elseiftemplate_nameandtemplate_name:match('^Cit[ea]')then-- not known, not known wrapper; last gasp, try as cs1-like
 anchor_id=anchor_id_make_cs12(template);-- extract an anchor id from this template if possible
 list_add(anchor_id,anchor_id_list,true);
 end

 tstart,tend=article_content:find(find_pattern,tend);-- search for another template; begin at end of last search
 end

 mw.logObject(anchor_id_list,'anchor_id_list');
 mw.logObject(template_list,'template_list');
 mw.logObject(article_whitelist,'article_whitelist');


 global_anchor_id_list=anchor_id_list
 global_template_list=template_list
 global_article_whitelist=article_whitelist
 end

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

 Scans template_list to look for wrapper templates that generate citerefs that require Lua patterns.

 This scan is only done once per page load, to save time

 ]]

 localfunctionciteref_patterns_make()
 ifnotglobal_template_listthenreturnend
 localciteref_patterns={}
 localtemplate_patterns=whitelist.wrapper_template_patterns
 for_,pinipairs(template_patterns)do
 for_,tinipairs(p[1])do-- loop through list of template wrappers
 ifglobal_template_list[t]then-- if wrapper is found in article, record corresponding patterns
 for_,patinipairs(p[2])do
 table.insert(citeref_patterns,pat)
 end
 break
 end
 end
 end
 mw.logObject(citeref_patterns,'citeref_patterns')
 returnciteref_patterns
 end


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

 -- First create global_anchor_id_list, global_template_list, global_article_whitelist
 anchor_id_list_make()

 -- Then stuff them (and derived tables) into return table
 return{
 anchor_id_list=global_anchor_id_listor{},-- table of anchor ids available in this article
 article_whitelist=global_article_whitelistor{},-- table of anchor ids with false-positive error message to be suppressed
 template_list=global_template_listor{},-- table of templates used in this article
 citeref_patterns=citeref_patterns_make()or{}-- table of Lua patterns to search for citeref from wrappers
 }

AltStyle によって変換されたページ (->オリジナル) /