Jump to content

Module:Footnotes/anchor id list

Permanently protected module
From Wikipedia, the free encyclopedia

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=nillocalglobal_anchor_id_list=nil-- exported tableslocalglobal_template_list=nillocalglobal_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 citationtemplates inside the tags as valid tagets; they are not.]]localfunctionarticle_content_get()ifglobal_article_contentthenreturnglobal_article_contentendlocalarticle_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:T221625for_,taginipairs(patterns_tags)doarticle_content=article_content:gsub(tag,'');-- remove certain html-like tags and their contentendglobal_article_content=article_contentreturnarticle_contentend--[[--------------------------< 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 trimlocalparts=mw.text.split(template,'%s*|%s*');-- split at the pipe and remove extraneous space characterslocalanchor_id={};ifredirects_sfnref[parts[1]:lower()]thenanchor_id[1]='CITEREF';elsereturnnil;-- not an sfnref or harvid templateendlocali=2;-- indexer into parts{} tablelocalj=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 savedj=j+1;-- bump the anchor_id{} indexerendi=i+1;-- bump the parts{} indexerendreturntable.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 errorcheck (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 patternsrvalue=tostring(template):match(pattern);-- is this |<date alias>= used (tostring() because something makes match() think template is a table)ifrvaluethenrvalue=tostring(template):match(pattern..'(%b{})');-- is rvalue a template?ifrvaluethenrvalue=rvalue:gsub('{{%s*(.-)%s*}}','%1');-- strip bounding template markup and trimlocalparts=mw.text.split(rvalue,'%s*|%s*');-- split at the pipe and remove extraneous space charactersifredirects_date[parts[1]:lower()]then-- if parts[1] names {{date}} or redirectrvalue=parts[2];-- assume that date template is properly formed, first positional parameter is the dateelsereturn'';-- |date= holds some other template than {{date}} or redirectendelservalue=template:match(pattern..'([^|}]+)');ifrvaluethen-- if rvalue is somethingrvalue=mw.text.trim(rvalue);-- trim itendifnotrvalueor''==rvaluethen-- if rvale was nothing or trimed to nothingrvalue=nil;-- ensure that it is unset so we can try the next parameter in the listendendifrvaluethenfor_,patterninipairs(patterns_date)do-- spin through the recognized date formats-- date = rvalue:match (pattern); -- attempt to extract year portion according to the patternlocaldate,date2=rvalue:match(pattern);-- attempt to extract year portion according to the pattern; <date2> gets second year in any rangeifdatethenifdate2then-- when a second yeardate=table.concat({date,'–',date2});-- build a date rangeendreturndate;-- matched so return;endendbreak;-- found a date but it was malformed so abandonendendendreturn'';-- no date param or date param doesn't hold a recognized date; empty string for concatenationend--[[--------------------------< 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 herelocalsplit={};-- temp table to assist in decoding accept-as-witten-markupifparams[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 indexerwhilesplit[i]doifsplit[i]:match('^%(%(.*[^%)][^%)]$')then-- first segment of comma-separated accept-as-witten; this segment has the opening doubled parenslocalname=split[i];i=i+1;-- bump indexer to next segmentwhilesplit[i]doname=name..', '..split[i];-- concatenate with previous segmentsifsplit[i]:match('^.*%)%)$')then-- if this table member has the closing doubled parensbreak;-- and done reassembling soendi=i+1;-- bump indexerendtable.insert(vnames,name);-- and add accept-as-witten name to the vnames tableelsetable.insert(vnames,split[i]);-- and add name to the vnames tableendi=i+1;-- bump indexerif5==ithenbreak;end-- limit to four namesendfori,vnameinipairs(vnames)doifnotvname:match('%(%(.-%)%)')then-- without accept-this-value-as-written markupvnames[i]=vname:gsub('(.-)%s+%u+$','%1');-- extract and save surname(s)endendfori,vnameinipairs(vnames)do-- repeat, this time for accept-this-value-as-written markupvnames[i]=vname:gsub('%(%((.-)%)%)','%1');-- remove markup if present and save the whole nameendendreturn0~=#vnamesandtable.concat(vnames)ornil-- return a concatenation of the vnames; nil elseend--[[--------------------------< N A M E S _ G E T >------------------------------------------------------------cs1|2 makes anchor id from contributor, author, or editor name-lists in that orderget 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 elseempty name (nameholding parameter n is present without value) and missing name (nameholding parameter n is notpresent) are included as empty string with all other names]]localfunctionnames_get(params,aliases_list)localnames={};-- first four author or editor names go herelocalenum_alias;-- alias with '#' replaced with a digitforenum=1,4do-- four names onlyfori,aliasinipairs(aliases_list)doifnotnames[enum]then-- hanven't found a previous alias with this [enum]? see if we can find this alias with this enumenum_alias=alias:gsub('#',enum);-- replace '#' to make 'lastn'if1==enumthen-- because |last= and last1= are exact aliasesifparams[enum_alias]then-- test |last1= firstnames[enum]=params[enum_alias];-- found so save the value assigned to |last1=break;-- next enumelseenum_alias=alias:gsub('#','');-- replace '#' to make 'last'ifparams[enum_alias]thennames[enum]=params[enum_alias];-- found so save the value assigned to |last=break;-- next enumendendelse-- here for enum 2, 3, 4ifparams[enum_alias]thennames[enum]=params[enum_alias];-- found so save the value assigned to |lastn=break;-- next enumendendendendendforenum=1,4do-- spin through the names table andlocalname=names[enum];ifnotnamethen-- when nameholding parameter n is not present (nil)name='';-- convert to empty string for concatenationendname=name:gsub('%(%((.-)%)%)','%1');-- remove accept-as-written markup if presentnames[enum]=name;-- save the modified nameendlocalname_str=table.concat(names);-- concatenate the namesreturn''~=name_strandname_strornil;-- return the concatenation if not empty string; nil elseend--[[--------------------------< 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 thecitation or harvc template.Templates are not allowed in parameters that are made part of COinS metadata; yet, they will appear. cs1|2 doesnot see the template markup but instead sees the result of the template as html. cs1|2 strips the html whichleaves the displayed value for the anchor id. We can't do that here so, because templates aren't allowed inparameters, 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 templatereturntemplate;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 likestring.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 charactersreturnargument;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 andharvc templates so here we remove any wiki markup: [[link|label]] -> label [[link]] -> link]=]localfunctionwikilink_strip(template)forwikilinkintemplate:gmatch('%[%b[]%]')do-- get a wikilinktemplate=template:gsub('%[%b[]%]','__57r1P__',1);-- install a markerifwikilink:match('%[%[.-|(.-)%]%]')thenwikilink=wikilink:match('%[%[.-|(.-)%]%]');-- extract label from complex [[link|label]] wikilinkelsewikilink=wikilink:match('%[%[(.-)%]%]');-- extract link from simple [[link]] wikilinksendwikilink=escape_lua_magic_chars(wikilink);-- in case there are lua magic characters in wikilinktemplate=template:gsub('__57r1P__',wikilink,1);-- replace the marker with the appropriate textendreturntemplate;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 functionsifnottemplate_nameortemplate_name:match('^#')then-- parser functions, magic words don't count as templatesreturnnil;-- 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 removedreturnLang_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 isthe 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 allowedtemplate=template_strip(template);-- because template markup can confuse code that builds params_t{} and because templates in name parameters are not allowedlocaltemp_t=mw.text.split(template,'%s*|%s*');--split on the pipefor_,paraminipairs(temp_t)doifparam:find('=',1,true)then-- a named parameter?localk,v=param:match('%s*([^=]-)%s*=%s*([^|}]+)');ifvthen-- there must be a valueif''~=vandnotv:match('^%s$')then-- skip when value is empty string or only whitespaceparams_t[k]=mw.text.trim(v);-- add trimmed value elseendendendendend--[[--------------------------< 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 elselocalanchor_id;localparams={};-- table of harvc parameterslocalid;-- custom anchor id for this {{harvc}} templateid=template:match('|%s*id%s*=%s*(%b{})');-- in case |id={{sfnref}}; done here because templates will be strippedtemplate_params_get(template,params);-- build a table of template parameters and their values; this strips wikilinks and templatesifidthen-- when set is {{sfnref}} or {{harvid}} templatereturnsfnref_get(id);-- returns content of {{sfnref}} or {{harvid}}; nil elseendifparams.idthen-- custom anchor for this {{harvc}} template (text)returnparams.id;-- |id= value as writtenendanchor_id=names_get(params,aliases_harvc_author);-- get the harvc contributor namesifanchor_idthen-- if names were gottenreturn'CITEREF'..anchor_id..date;endreturnnil;-- no names; no anchor_idend--[[--------------------------< A N C H O R _ I D _ M A K E _ W R A P P E R >----------------------------------for wrapper templatesinspect 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 templatelocalanchor_id;-- the assembled anchor id from this templatelocaldate;localname_default;localdate_default;localvol;localparams={};-- table of template parameterstemplate_name=template_name_get(template);-- get first char uppercase trimmed template name; ignore subpages ~/new, ~/sandboxifnottemplate_nameortemplate_skip[template_name]thenreturnnil;-- could not extract template name from (possibly corrupted) template (extraneous opening { in the template will cause this)enddate=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-- endref=template:match('|%s*ref%s*=%s*(%b{})');-- first look for |ref={{sfnref}} or |ref={{harvid}} because we will strip templates from the wrapper templateifnotreftheniftemplate:match('|%s*ref%s*=([^|}]+)')then-- |ref={{template}} not found; if there is a |ref= param with an assigned valueref=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 whitespaceif''==refthen-- trimming a string of whitespace makes an empty stringref=nil;-- make empty ref same as missing refendendendendtemplate_params_get(template,params);-- build a table of template parameters and their valueslocalwrap_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 namesdate_default=wrap_data[2];-- get the default dateelsevol=params['volume']or'default';localfascicle=params['fascicle']-- some templates use "fascicle" to mean "subvolume"iffasciclethenlocalsubvol=vol..'/'..fascicle-- if fascicle is used, subvolume = "vol/fascicle"ifwrap_data[subvol]then-- if subvolume exists, use it, otherwise fall back to volumevol=subvolendendifnotwrap_data[vol]then-- make sure this volume existsvol='default';-- doesn't exist, use default volumeendname_default=wrap_data[vol][1];-- get the default namesdate_default=wrap_data[vol][2];-- get the default dateendif'harv'==refornotrefthen-- |ref=harv specified or |ref= missing or emptyanchor_id=names_get(params,aliases_contributor)or-- get contributor, author, or editor namesnames_get(params,aliases_author)orvnames_get(params,'vauthors')or-- |vauthors=names_get(params,aliases_editor)orvnames_get(params,'veditors')or-- |veditors=name_default;-- default names from whitelist-- whitelist.wrapper_templates[template_name][1]; -- default names from whitelistif''==datethen-- if date not provided in the templatedate=date_default;-- use the default date from whitelistendifanchor_idthen-- if names were gottenanchor_id='CITEREF'..anchor_id..date;endelseifref:match('%b{}')then-- ref holds a templateanchor_id=sfnref_get(ref);-- returns content of {{sfnref}} or {{harvid}}; nil elseelseif'none'==refthen-- |ref=nonereturnnil;-- anchor id expicitly suppressedelseanchor_id=ref;-- |ref=<text> may match an anchor id override value in {{harv}} template |ref= parameterendreturnanchor_id;-- anchor_id text; nil elseend--[[--------------------------< A N C H O R _ I D _ M A K E _ C S 1 2 >----------------------------------------for cs1|2 template and cs1-like templatesinspect |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 templatelocalanchor_id;-- the assembled anchor id from this templatelocaldate;localparams={};-- table of template parameterstemplate_name=template_name_get(template);-- get first char uppercase trimmed template name; ignore subpages ~/new, ~/sandboxifnottemplate_nameortemplate_skip[template_name]thenreturnnil;-- could not extract template name from (possibly corrupted) template (extraneous opening { in the template will cause this)endifredirects_patent[template_name]thendate=date_get(template,alias_patterns_patent_date);-- get date; done here because might be in {{date}} elsedate=date_get(template,alias_patterns_date);endref=template:match('|%s*ref%s*=%s*(%b{})');-- first look for |ref={{sfnref}} or |ref={{harvid}} because we will strip templates from the cs1|2 templateifnotreftheniftemplate:match('|%s*ref%s*=([^|}]+)')then-- |ref={{template}} not found; if there is a |ref= param with an assigned valueref=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 whitespaceif''==refthen-- trimming a string of whitespace makes an empty stringref=nil;-- make empty ref same as missing refendendendendtemplate_params_get(template,params);-- build a table of template parameters and their valuesif'harv'==refornotrefthen-- |ref=harv specified or |ref= missing or emptyifredirects_patent[template_name]then-- if this is a cite patent templateanchor_id=names_get(params,aliases_inventor);-- inventor names onlyelse-- cs1|2 templateanchor_id=names_get(params,aliases_contributor)or-- get contributor, author, or editor namesnames_get(params,aliases_author)orvnames_get(params,'vauthors')or-- |vauthors=names_get(params,aliases_editor)orvnames_get(params,'veditors');-- |veditors=endifanchor_idthen-- if names were gottenanchor_id='CITEREF'..anchor_id..date;endelseifref:match('%b{}')then-- ref holds a templateanchor_id=sfnref_get(ref);-- returns content of {{sfnref}} or {{harvid}}; nil elseelseif'none'==refandnotredirects_patent[template_name]then-- |ref=none; not supported by cite patentreturnnil;-- anchor id expicitly suppressedelseanchor_id=ref;-- |ref=<text> may match an anchor id override value in {{harv}} template |ref= parameterendreturnanchor_id;-- anchor_id text; nil elseend--[[--------------------------< 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 itemifencodethen-- for anchor IDs ...item=mw.uri.anchorEncode(item);-- encode to remove wikimarkup, convert spaces to underscores etcendifnotlist[item]then-- if not already savedlist[item]=1;-- save it else-- here when this item already savedlist[item]=list[item]+1;-- to indicate that there are multiple itemsendendend--[[--------------------------< 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 ofanchors it may hold.]]localfunctionanchor_id_make_anchor(template,anchor_id_list)template=template:gsub('^{{[^|]+|',''):gsub('}}$','',1);-- remove outer {{ and }} and template nametemplate=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 ittemplate=template:gsub('%b{}','',1);-- remove it from source templateendfor_,tinipairs(params)do-- spin through the templates in paramsanchor_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 listendendtemplate=template:gsub('|%s*|','|');-- when pipe follows pipe with or without white space, remove extraneous pipetemplate=template:gsub('^|',''):gsub('|$','');-- remove extraneous leading and trailing pipesparams=mw.text.split(template,'%s*|%s*');-- split at the pipe and remove extraneous space charactersfor_,tinipairs(params)do-- spin through the anchor IDsanchor_id=mw.text.trim(t);-- trim white spaceif''~=anchor_idthen-- should always have somethinglist_add(anchor_id,anchor_id_list,true);-- add anchor ID to the listendendend--[[--------------------------< 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 wordstemplate=mw.text.trim(template);-- trim whitespacetemplate=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) tallyendend--[[--------------------------< 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 templatesBecause 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 thatlink 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 whitelistcreates 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 contentifarticle_content==''then-- when there is no article contentreturn'';-- no point in continuingendlocaltemplate;-- place to hold the template that we foundlocaltemplate_name;localanchor_id;-- place to hold an anchor id as it is extracted / decodedlocalfind_pattern='%f[{]{{[^{]';localtstart,tend=article_content:find(find_pattern);-- find the first template; do not find template variables: {{{template var|}}} whiletstartdotemplate=article_content:match('%b{}',tstart);-- get the whole templateifnottemplatethenbreak;-- template is nil for some reason (last template missing closing }} for example) so declare ourselves doneendtemplate_name=template_name_get(template);-- get first char uppercase trimmed template name; ignore subpages ~/new, ~/sandboxtemplate_list_add(template,template_list);-- add this template's name to the listifdata.known_templates_cs12[template_name]thenanchor_id=anchor_id_make_cs12(template);-- extract an anchor id from this templatelist_add(anchor_id,anchor_id_list,true)elseifdata.known_templates_vcite[template_name]thenlocalref=template:match('|%s*ref%s*=%s*(%b{})');-- first look for |ref={{sfnref}} or |ref={{harvid}} because we will strip templates from the vcite templateifrefthen-- |ref={{template}}anchor_id=sfnref_get(ref);-- returns content of {{sfnref}} or {{harvid}}; nil elselist_add(anchor_id,anchor_id_list,true);elselocalparams={};template_params_get(template,params);-- build a table of template parameters and their valuesanchor_id=params['ref'];-- when both set, vcite uses value from |ref=ifnotanchor_idandparams['harvid']thenanchor_id='CITEREF'..params['harvid'];-- in vcite, |harvid= auto-adds 'CITEREF' prefix to the value in |harvid=endlist_add(anchor_id,anchor_id_list,true);endelseifdata.known_templates_harvc[template_name]thenanchor_id=anchor_id_make_harvc(template);-- extract an anchor id from this templatelist_add(anchor_id,anchor_id_list,true);elseifdata.known_templates_wikicite[template_name]thenlocalref=template:match('|%s*ref%s*=%s*(%b{})');-- first look for |ref={{sfnref}} or |ref={{harvid}}ifrefthenanchor_id=sfnref_get(ref);elseiftemplate:match('|%s*ref%s*=([^|}]+)')thenanchor_id=template:match('|%s*ref%s*=([^|}]+)');-- plain-textelseiftemplate:match('|%s*id%s*=%s*(%b{})')thenref=template:match('|%s*id%s*=%s*(%b{})');anchor_id='Reference-'..sfnref_get(ref);elseiftemplate:match('|%s*id%s*=([^|}]+)')thenanchor_id='Reference-'..template:match('|%s*id%s*=([^|}]+)');-- plain-textelseanchor_id=nil;-- no matches, ensure that anchor_id has no valueendifanchor_idthenlist_add(anchor_id,anchor_id_list,true);endelseifdata.known_templates_anchor[template_name]thenanchor_id_make_anchor(template,anchor_id_list);-- extract anchor ids from this template if anyelseifdata.known_templates_sfn_whitelist[template_name]thentemplate=template:gsub('^{{[^|]+|',''):gsub('}}$','',1);-- remove outer {{ and }} and template nametemplate=mw.text.trim(template,'%s|');-- trim leading trailing white space and pipestemplate=mw.text.split(template,'%s*|%s*');-- make a table of the template's parametersfor_,anchor_idinipairs(template)do-- spin through this template's parameterif''~=anchor_idandnotarticle_whitelist[anchor_id]thenanchor_id=mw.uri.anchorEncode(anchor_id)article_whitelist[anchor_id]=1;-- add to the whitelistendendelseiftemplate_nameandwhitelist.wrapper_templates[template_name]thenanchor_id=anchor_id_make_wrapper(template);-- extract an anchor id from this template if possiblelist_add(anchor_id,anchor_id_list,true);elseiftemplate_nameandtemplate_name:match('^Cit[ea]')then-- not known, not known wrapper; last gasp, try as cs1-likeanchor_id=anchor_id_make_cs12(template);-- extract an anchor id from this template if possiblelist_add(anchor_id,anchor_id_list,true);endtstart,tend=article_content:find(find_pattern,tend);-- search for another template; begin at end of last searchendmw.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_listglobal_template_list=template_listglobal_article_whitelist=article_whitelistend--[[--------------------------< 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_listthenreturnendlocalciteref_patterns={}localtemplate_patterns=whitelist.wrapper_template_patternsfor_,pinipairs(template_patterns)dofor_,tinipairs(p[1])do-- loop through list of template wrappersifglobal_template_list[t]then-- if wrapper is found in article, record corresponding patternsfor_,patinipairs(p[2])dotable.insert(citeref_patterns,pat)endbreakendendendmw.logObject(citeref_patterns,'citeref_patterns')returnciteref_patternsend--[[--------------------------< 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_whitelistanchor_id_list_make()-- Then stuff them (and derived tables) into return tablereturn{anchor_id_list=global_anchor_id_listor{},-- table of anchor ids available in this articlearticle_whitelist=global_article_whitelistor{},-- table of anchor ids with false-positive error message to be suppressedtemplate_list=global_template_listor{},-- table of templates used in this articleciteref_patterns=citeref_patterns_make()or{}-- table of Lua patterns to search for citeref from wrappers}
close