Jump to content

Module:Footnotes/sandbox

From Wikipedia, the free encyclopedia
require('strict');localgetArgs=require('Module:Arguments').getArgs;--[[--------------------------< A R G S _ D E F A U L T >------------------------------------------------------a table to specify initial values.]]localargs_default={group='',bracket_left='',bracket_right='',bracket_year_left='',bracket_year_right='',postscript='',page='',pages='',location='',page_sep=", p.&nbsp;",pages_sep=", pp.&nbsp;",ref='',template='harv',-- if template name not provided in {{#invoke:}} use this};--[[--------------------------< T A R G E T _ C H E C K >------------------------------------------------------look for anchor_id (CITEREF name-list and year or text from |ref=) in anchor_id_listthe 'no target' error may be suppressed with |ignore-err=yes when target cannot be found because target is insidea template that wraps another template; 'multiple targets' error may not be suppressed]]localfunctiontarget_check(anchor_id,args)localnamespace=mw.title.getCurrentTitle().namespace;localanchor_id_list_module=mw.loadData('Module:Footnotes/anchor_id_list');localanchor_id_list=anchor_id_list_module.anchor_id_list;localarticle_whitelist=anchor_id_list_module.article_whitelist;localtemplate_list=anchor_id_list_module.template_list;localciteref_patterns=anchor_id_list_module.citeref_patternslocalwhitelist_module=mw.loadData('Module:Footnotes/whitelist');localwhitelist=whitelist_module.whitelist;localtally=anchor_id_list[anchor_id];-- nil when anchor_id not in list; else a tallylocalmsg;localcategory;ifnottallythenifargs.ignorethenreturn'';-- if ignore is true then no message, no categoryendifarticle_whitelistandarticle_whitelist[anchor_id]then-- if an article-local whitelist and anchor ID is in itreturn'';-- doneendlocalwl_anchor_id=anchor_id;-- copy to be modified to index into the whitelistifargs.yearthen-- for anchor IDs created by this template (not in |ref=) that have a dateifargs.year:match('%d%l$')or-- use the date value to determine if we should remove the disambiguatorargs.year:match('n%.d%.%l$')orargs.year:match('nd%l$')thenwl_anchor_id=wl_anchor_id:gsub('%l$','');-- remove the disambiguatorendendlocalt_tbl=whitelist[wl_anchor_id];-- get list of templates associated with this anchor IDift_tblthen-- when anchor ID not whitelisted t_tbl is nilfor_,tinipairs(t_tbl)do-- spin through the list of templates associated with this anchor IDiftemplate_list[t]then-- if associated template is found in the list of templates in the articlereturn'';-- anchor ID is whitlisted and article has matching template so no errorendendendfor_,patterninipairs(citeref_patterns)do-- load patterns for wrapper templates on this pageifanchor_id:match(pattern)then-- spin through the special patterns and try to matchreturn''endendmsg='no target: '..anchor_id;-- anchor_id not foundmw.log(msg)ifnamespace==10andnotargs.showthen-- do not generate error message in template namespacereturn''endcategory='[[Category:Harv and Sfn no-target errors]]';elseif1<tallythenmsg='multiple targets ('..tally..'×): '..anchor_id;-- more than one anchor_id in this articlemw.log(msg)ifnamespace==10andnotargs.showthen-- do not generate error message in template namespacereturn''endcategory=0==namespaceand'[[Category:Harv and Sfn multiple-target errors]]'or'';-- only categorize in article spacereturn'<span class="error harv-error" style="display: inline; font-size:100%"> '..args.template..' error: '..msg..' ([[:Category:Harv and Sfn template errors|help]])</span>'..category;end-- category = 0 == namespace and '[[Category:Harv and Sfn template errors]]' or ''; -- only categorize in article spacecategory=0==namespaceandcategoryor'';-- only categorize in article space-- display based on args.show (no display by default)localdisplay=args.showand'inline'or'none'returnmsgand'<span class="error harv-error" style="display: '..display..'; font-size:100%"> '..args.template..' error: '..msg..' ([[:Category:Harv and Sfn template errors|help]])</span>'..categoryor'';end--[[--------------------------< I S _ Y E A R >----------------------------------------------------------------evaluates param to see if it is one of these forms with or without lowercase letter disambiguator: YYYY n.d. n.d.- (This will be suffixed by a letter when using author-date citations for the same author.) nd  c. YYYY YYYY–YYYY (separator is endash) YYYY–YY (separator is endash)return true when param has a recognized form; false else]]localpatterns_date={'^%d%d%d%d?%l?$','^n%.d%.%l?$','^n%.d%.%-%l$','^nd%l?$','^c%. %d%d%d%d?%l?$','^%d%d%d%d–%d%d%d%d%l?$','^%d%d%d%d–%d%d%l?$',}localfunctionis_year(param,args)args.year='';-- used for harv error; for_,patterninipairs(patterns_date)doifmw.ustring.match(param,pattern)thenargs.year=param;-- used for harv error; returntrue;endendend--[[--------------------------< C O R E >----------------------------------------------------------------------returns an anchor link (CITEREF) formed from one to four author names, year, and insource location (|p=, |pp=, loc=)]]localfunctioncore(args)localresult;localerr_msg=''ifargs.P5~=''thenifis_year(args.P5,args)thenresult=table.concat({args.P1,' et al. ',args.bracket_year_left,args.P5,args.bracket_year_right});elseargs.P5='';-- when P5 not a year don't include in anchorresult=table.concat({args.P1,' et al.'});-- and don't render itendelseifargs.P4~=''thenifis_year(args.P4,args)thenresult=table.concat({args.P1,', ',args.P2,' &amp; ',args.P3,' ',args.bracket_year_left,args.P4,args.bracket_year_right});-- three names and a yearelseresult=table.concat({args.P1,' et al.'});-- four namesendelseifargs.P3~=''thenifis_year(args.P3,args)thenresult=table.concat({args.P1,' &amp; ',args.P2,' ',args.bracket_year_left,args.P3,args.bracket_year_right});-- two names and a yearelseresult=table.concat({args.P1,', ',args.P2,' ',' &amp; ',args.P3});-- three namesendelseifargs.P2~=''thenifis_year(args.P2,args)thenresult=table.concat({args.P1,' ',args.bracket_year_left,args.P2,args.bracket_year_right});-- one name and yearelseresult=table.concat({args.P1,' &amp; ',args.P2});-- two namesendelseresult=args.P1;-- one nameend-- when author-date result ends with a dot (typically when the last positional parameter holds 'n.d.')-- and when no in-source location (no |p=, |pp=, or |loc=)-- and when the first or only character in args.postscript is a dot-- remove the author-date result trailing dot-- the author-date result trailing dot will be replaced later with the content of args.postscript (usually a dot)if('.'==result:sub(-1))and('.'==args.postscript:sub(1))and(''==args.page)and(''==args.pages)and(''==args.location)thenresult=result:gsub('%.$','');endifargs.ref~='none'thenlocalanchor_id;ifargs.ref~=''thenanchor_id=mw.uri.anchorEncode(args.ref);err_msg=target_check(anchor_id,args);result=table.concat({'[[#',anchor_id,'|',result,']]'});elseanchor_id=mw.uri.anchorEncode(table.concat({'CITEREF',args.P1,args.P2,args.P3,args.P4,args.P5}));err_msg=target_check(anchor_id,args);result=table.concat({'[[#',anchor_id,'|',result,']]'});endendifargs.page~=''thenresult=table.concat({result,args.page_sep,args.page});elseifargs.pages~=''thenresult=table.concat({result,args.pages_sep,args.pages});endifargs.location~=''thenresult=table.concat({result,', ',args.location});endresult=table.concat({args.bracket_left,result,args.bracket_right,args.postscript}):gsub('%s+',' ');-- strip redundant spacesreturnresult..err_msg;end--[[--------------------------< H Y P H E N _ T O _ D A S H >--------------------------------------------------Converts a hyphen to a dash under certain conditions. The hyphen must separatelike items; unlike items are returned unmodified. These forms are modified: letter - letter (A - B) digit - digit (4-5) digit separator digit - digit separator digit (4.1-4.5 or 4-1-4-5) letterdigit - letterdigit (A1-A5) (an optional separator between letter and digit is supported – a.1-a.5 or a-1-a-5) digitletter - digitletter (5a - 5d) (an optional separator between letter and digit is supported – 5.a-5.d or 5-a-5-d)any other forms are returned unmodified.str may be a comma- or semicolon-separated listThis code copied from Module:Citation/CS1. The only modification is to require Module:Citation/CS1/Utilitiesso that it has access to the functions is_set() and has_accept_as_written()]]localfunctionhyphen_to_dash(str)localutilities=require('Module:Citation/CS1/Utilities');-- only modification so that this function has access to is_set() and has_accept_as_written()ifnotutilities.is_set(str)thenreturnstr;endlocalaccept;-- Booleanstr=str:gsub('&[nm]dash;',{['&ndash;']='–',['&mdash;']='—'});-- replace &mdash; and &ndash; entities with their characters; semicolon mucks up the text.splitstr=str:gsub('&#45;','-');-- replace HTML numeric entity with hyphen characterstr=str:gsub('&nbsp;',' ');-- replace &nbsp; entity with generic keyboard space characterlocalout={};locallist=mw.text.split(str,'%s*[,;]%s*');-- split str at comma or semicolon separators if there are anyfor_,iteminipairs(list)do-- for each item in the listitem,accept=utilities.has_accept_as_written(item);-- remove accept-this-as-written markup when it wraps all of itemifnotacceptandmw.ustring.match(item,'^%w*[%.%-]?%w+%s*[%-–—]%s*%w*[%.%-]?%w+$')then-- if a hyphenated range or has endash or emdash separatorsifitem:match('^%a+[%.%-]?%d+%s*%-%s*%a+[%.%-]?%d+$')or-- letterdigit hyphen letterdigit (optional separator between letter and digit)item:match('^%d+[%.%-]?%a+%s*%-%s*%d+[%.%-]?%a+$')or-- digitletter hyphen digitletter (optional separator between digit and letter)item:match('^%d+[%.%-]%d+%s*%-%s*%d+[%.%-]%d+$')or-- digit separator digit hyphen digit separator digititem:match('^%d+%s*%-%s*%d+$')or-- digit hyphen digititem:match('^%a+%s*%-%s*%a+$')then-- letter hyphen letteritem=item:gsub('(%w*[%.%-]?%w+)%s*%-%s*(%w*[%.%-]?%w+)','%1–%2');-- replace hyphen, remove extraneous space characterselseitem=mw.ustring.gsub(item,'%s*[–—]%s*','–');-- for endash or emdash separated ranges, replace em with en, remove extraneous whitespaceendendtable.insert(out,item);-- add the (possibly modified) item to the output tableendlocaltemp_str='';-- concatenate the output table into a comma separated stringtemp_str,accept=utilities.has_accept_as_written(table.concat(out,', '));-- remove accept-this-as-written markup when it wraps all of concatenated outifacceptthentemp_str=utilities.has_accept_as_written(str);-- when global markup removed, return original str; do it this way to suppress boolean second return valuereturntemp_str;elsereturntemp_str;-- else, return assembled temp_strendend--[[--------------------------< A R G S _ F E T C H >---------------------------------------------------------Because all of the templates share a common set of parameters, a single common function to fetch those parametersfrom frame and parent frame.]]localfunctionargs_fetch(frame,ps)localargs=args_default;-- create a copy of the default tablelocalpframe=frame:getParent();-- point to the template's parameter tablefork,vinpairs(frame.args)do-- override defaults with values provided in the #invoke: if anyargs[k]=v;endargs.postscript=pframe.args.postscriptorpframe.args.psorps;if'none'==args.postscriptthenargs.postscript='';endargs.group=pframe.args.groupor'';args.page=pframe.args.porpframe.args.pageor'';args.pages=pframe.args.pporpframe.args.pagesor'';args.pages=(''~=args.pages)andhyphen_to_dash(args.pages)or'';args.location=pframe.args.atorpframe.args.locor'';args.ref=pframe.args.reforpframe.args.Refor'';args.ignore=('yes'==pframe.args['ignore-false-positive'])or('yes'==pframe.args['ignore-err']);fori,vinipairs({'P1','P2','P3','P4','P5'})do-- loop through the five positional parameters and trim if set else empty stringargs[v]=(pframe.args[i]andmw.text.trim(pframe.args[i]))or'';endifargs.P5andnotis_year(args.P5,args)thenlocali=6;-- initialize the indexer to the sixth positional parameterwhilepframe.args[i]do-- in case there are too many authors loop through the authors looking for a yearlocalv=mw.text.trim(pframe.args[i]);-- trimifis_year(v,args)then-- if a yearargs.P5=v;-- overwrite whatever was in args.P5 with yearbreak;-- and abandon the searchendi=i+1;-- bump the indexerendendreturnargs;end--[[--------------------------< H A R V A R D _ C I T A T I O N >----------------------------------------------common entry point for: {{harvard citation}} aka {{harv}} {{Harvard citation no brackets}} aka {{harvnb}} {{harvcol}} {{harvcolnb}} {{harvcoltxt}} {{Harvard citation text}} aka {{harvtxt}} {{Harvp}}Distinguishing features (brackets and page separators) are specified in this module's {{#invoke}} in the respective templates.]]localfunctionharvard_citation(frame)localargs=args_fetch(frame,'');-- get the template and invoke parameters; default postscript is empty stringreturncore(args);end--[[--------------------------< S T R I P _ U R L >------------------------------------------------------------used by sfn() and sfnm(). This function fixes an issue with reference tooltip gadget where the tooltip is not displayedwhen an insource locator (|p=, |pp=, |loc=) has an external wikilink that contains a # characterstrip uri-reserved characters from urls in |p=, |pp-, and |loc= parameters The researved characters are: !#$&'()*+,/:;=?@[]]]localfunctionstrip_url(pages)localescaped_uri;ifnotpagesor(''==pages)thenreturnpages;endforuriinpages:gmatch('%[(%a[%w%+%.%-]*://%S+)')do-- for each external link get the uriescaped_uri=uri:gsub("([%(%)%.%%%+%-%*%?%[%^%$%]])","%%%1");-- save a copy with lua pattern characters escapeduri=uri:gsub("[!#%$&'%(%)%*%+,/:;=%?@%[%]%.%%]",'');-- remove reserved characters and '%' because '%20' (space character) is a lua 'invalid capture index'pages=pages:gsub(escaped_uri,uri,1);-- replace original uri with the stripped versionendreturnpages;end--[[--------------------------< S F N >------------------------------------------------------------------------entry point for {{sfn}} and {{sfnp}}]]localfunctionsfn(frame)localargs=args_fetch(frame,'.');-- get the template and invoke parameters; default postscript is a dotlocalresult=core(args);-- go make a CITEREF anchor-- put it all together and then strip redundant spaceslocalname=table.concat({'FOOTNOTE',args.P1,args.P2,args.P3,args.P4,args.P5,strip_url(args.page),strip_url(args.pages),strip_url(args.location)}):gsub('%s+',' ');returnframe:extensionTag({name='ref',args={group=args.group,name=name},content=result});end--[[--------------------------< S F N M >----------------------------------------------------------------------common entry point for {{sfnm}} and {{sfnmp}}Distinguishing features (brackets) are specified in this module's {{#invoke}} in the respective templates.]]localfunctionsfnm(frame)localargs=args_default;-- create a copy of the default tablelocalpframe=frame:getParent();-- point to the template's parameter tablelocaln=1;-- index of source; this is the 'n' in na1, ny, etclocalfirst_pnum=1;-- first of a pair of positional parameterslocalsecond_pnum=2;-- second of a pair of positional parameterslocallast_ps=0;-- index of the last source with |nps= setlocallast_index=0;-- index of the last source; these used to determine which of |ps= or |nps= will terminate the whole renderinglocalout={};-- table to hold rendered sourceslocalfootnote={'FOOTNOTE'};-- all author, date, insource location stuff becomes part of the reference's footnote id; added as we gofork,vinpairs(frame.args)do-- override defaults with values provided in the #invoke: if anyargs[k]=v;endwhiletruedoifnotpframe.args[table.concat({n,'a1'})]andnotpframe.args[first_pnum]thenbreak;-- no na1 or matching positional parameter so doneendifpframe.args[table.concat({n,'a1'})]then-- does this source use named parameters?for_,vinipairs({'P1','P2','P3','P4','P5'})do-- initialize for this sourceargs[v]='';endfori,vinipairs({'P1','P2','P3','P4','P5'})do-- extract author and year parameters for this sourceargs[v]=pframe.args[table.concat({n,'a',i})]or'';-- attempt to assign author nameif''==args[v]then-- when there wasn't an author nameargs[v]=pframe.args[table.concat({n,'y'})]or'';-- attempt to assign yearbreak;-- done with author/date for this sourceendendelse-- this source uses positional parametersargs.P1=mw.text.trim(pframe.args[first_pnum]);-- yes, only one author supportedargs.P2=(pframe.args[second_pnum]andmw.text.trim(pframe.args[second_pnum]))or'';-- when positional author, year must also be positionalfor_,vinipairs({'P3','P4','P5'})do-- blank the rest of these for this sourceargs[v]='';endfirst_pnum=first_pnum+2;-- source must use positional author and positional yearsecond_pnum=first_pnum+1;-- bump these for possible next positional sourceendargs.postscript=pframe.args[table.concat({n,'ps'})]or'';if'none'==args.postscriptthen-- this for compatibility with other footnote templates; does nothingargs.postscript='';endargs.group=pframe.args.groupor'';-- reference groupargs.ref=pframe.args[table.concat({n,'ref'})]or'';-- alternate reference for this sourceargs.page=pframe.args[table.concat({n,'p'})]or'';-- insource locations for this sourceargs.pages=pframe.args[table.concat({n,'pp'})]or'';args.pages=(''~=args.pages)andhyphen_to_dash(args.pages)or'';args.location=pframe.args[table.concat({n,'loc'})]orpframe.args[table.concat({n,'at'})]or'';args.ignore=('yes'==pframe.args[table.concat({n,'ignore-false-positive'})])or('yes'==pframe.args[table.concat({n,'ignore-err'})]);table.insert(out,core(args));-- save the rendering of this sourcefork,vinipairs({'P1','P2','P3','P4','P5'})do-- create the FOOTNOTE idif''~=args[v]thentable.insert(footnote,args[v]);endendfork,vinipairs({'page','pages','location'})do-- these done separately so that we can strip uri-reserved characters from extlinked page numbers if''~=args[v]thentable.insert(footnote,strip_url(args[v]))endendlast_index=n;-- flags used to select terminal postscript from nps or from end_psif''~=args.postscriptthenlast_ps=n;endn=n+1;-- bump for the next oneendlocalname=table.concat(footnote):gsub('%s+',' ');-- put the footnote together and strip redundant spaceargs.end_ps=pframe.args.postscriptorpframe.args.psor'.';-- this is the postscript for the whole not for the individual sourcesif'none'==args.end_psthen-- not an original sfnm parameter value; added for compatibility with other footnote templatesargs.end_ps='';endlocalresult=table.concat({table.concat(out,'; '),(last_index==last_ps)and''orargs.end_ps});returnframe:extensionTag({name='ref',args={group=args.group,name=name},content=result});end--[[--------------------------< S F N R E F >------------------------------------------------------------------implements {{sfnref}}]]localfunctionsfnref(frame)localargs=getArgs(frame);localout={};fori=1,5do-- get the first five args if there are five argsifargs[i]thenout[i]=args[i];elsebreak;-- less than 5 args break outendendif5==#outthen-- when we have seen five args there may bemorelocali=6;-- initialize the indexer to the sixth positional parameterwhileargs[i]do-- in case there are too many authors loop through the authors looking for a yearifis_year(args[i],args)then-- if a yearout[5]=args[i];-- overwrite whatever was in args[5] with yearbreak;-- and abandon the searchendi=i+1;-- bump the indexerendendreturnmw.uri.anchorEncode('CITEREF'..table.concat(out));end--[[--------------------------< E X P O R T E D F U N C T I O N S >------------------------------------------]]return{harvard_citation=harvard_citation,sfn=sfn,sfnm=sfnm,sfnref=sfnref,target_check=target_check,};
close