Jump to content
Wikipedia The Free Encyclopedia

Module:Webarchive

From Wikipedia, the free encyclopedia
Module documentation[view] [edit] [history] [purge]
This module is rated as ready for general use. It has reached a mature state, is considered relatively stable and bug-free, and may be used wherever appropriate. It can be mentioned on help pages and other Wikipedia resources as an option for new users. To minimise server load and avoid disruptive output, improvements should be developed through sandbox testing rather than repeated trial-and-error editing.
Page template-protected This module is currently protected from editing.
See the protection policy and protection log for more details. Please discuss any changes on the talk page; you may submit an edit request to ask an administrator to make an edit if it is uncontroversial or supported by consensus. You may also request that this page be unprotected.
This module is rated as ready for general use. It has reached a mature state, is considered relatively stable and bug-free, and may be used wherever appropriate. It can be mentioned on help pages and other Wikipedia resources as an option for new users. To minimise server load and avoid disruptive output, improvements should be developed through sandbox testing rather than repeated trial-and-error editing.
Page template-protected This module is currently protected from editing.
See the protection policy and protection log for more details. Please discuss any changes on the talk page; you may submit an edit request to ask an administrator to make an edit if it is uncontroversial or supported by consensus. You may also request that this page be unprotected.
Warning This Lua module is used on approximately 649,000 pages, or roughly 1% of all 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 module implements Template:webarchive  (talk · links · edit).

This module uses Module:Webarchive/data to support configuration control and internationalization.

Tracking categories

tracking archive sites
tracking warnings and errors
The above documentation is transcluded from Module:Webarchive/doc. (edit | history)
Editors can experiment in this module's sandbox (edit | diff) and testcases (edit | run) pages.
Subpages of this module.

 --[[ ----------------------------------

 Lua module implementing the {{webarchive}} template. 

 A merger of the functionality of three templates: {{wayback}}, {{webcite}} and {{cite archives}}

 ]]


 --[[--------------------------< D E P E N D E N C I E S >------------------------------------------------------
 ]]

 require('strict');
 localgetArgs=require('Module:Arguments').getArgs;


 --[[--------------------------< F O R W A R D D E C L A R A T I O N S >--------------------------------------
 ]]

 localcategories={};-- category names
 localconfig={};-- global configuration settings
 localdigits={};-- for i18n; table that translates local-wiki digits to western digits
 localerr_warn_msgs={};-- error and warning messages
 localexcepted_pages={};
 localmonth_num={};-- for i18n; table that translates local-wiki month names to western digits
 localprefixes={};-- service provider tail string prefixes
 localservices={};-- archive service provider data from
 locals_text={};-- table of static text strings used to build final rendering
 localuncategorized_namespaces={};-- list of namespaces that we should not categorize
 localuncategorized_subpages={};-- list of subpages that should not be categorized


 --[[--------------------------< P A G E S C O P E I D E N T I F I E R S >----------------------------------
 ]]

 localnon_western_digits;-- boolean flag set true when data.digits.enable is true
 localthis_page=mw.title.getCurrentTitle();

 localtrack={};-- Associative array to hold tracking categories
 localulx={};-- Associative array to hold template data 


 --[[--------------------------< S U B S T I T U T E >----------------------------------------------------------

 Populates numbered arguments in a message string using an argument table.

 ]]

 localfunctionsubstitute(msg,args)
 returnargsandmw.message.newRawMessage(msg,args):plain()ormsg;
 end


 --[[--------------------------< tableLength >-----------------------

 Given a 1-D table, return number of elements

 ]]

 localfunctiontableLength(T)
 localcount=0
 for_inpairs(T)docount=count+1end
 returncount
 end


 --[=[-------------------------< M A K E _ W I K I L I N K >----------------------------------------------------

 Makes a wikilink; when both link and display text is provided, returns a wikilink in the form [[L|D]]; if only
 link is provided, returns a wikilink in the form [[L]]; if neither are provided or link is omitted, returns an
 empty string.

 ]=]

 localfunctionmake_wikilink(link,display,no_link)
 ifnil==no_linkthen
 iflinkand(''~=link)then
 ifdisplayand(''~=display)then
 returntable.concat({'[[',link,'|',display,']]'});
 else
 returntable.concat({'[[',link,']]'});
 end
 end
 returndisplayor'';-- link not set so return the display text

 else-- no_link
 ifdisplayand(''~=display)then-- if there is display text
 returndisplay;-- return that
 else
 returnlinkor'';-- return the target article name or empty string
 end
 end
 end


 --[[--------------------------< createTracking >-----------------------

 Return data in track[] ie. tracking categories

 ]]

 localfunctioncreateTracking()
 ifnotexcepted_pages[this_page.fullText]then-- namespace:title/fragment is allowed to be categorized (typically this module's / template's testcases page(s))
 ifuncategorized_namespaces[this_page.nsText]then
 return'';-- this page not to be categorized so return empty string
 end
 for_,vinipairs(uncategorized_subpages)do-- cycle through page name patterns
 ifthis_page.text:match(v)then-- test page name against each pattern
 return'';-- this subpage type not to be categorized so return empty string
 end
 end
 end

 localout={};
 iftableLength(track)>0then
 forkey,_inpairs(track)do-- loop through table
 table.insert(out,make_wikilink(key));-- and convert category names to links
 end
 end
 returntable.concat(out);-- concat into one big string; empty string if table is empty

 end


 --[[--------------------------< inlineError >-----------------------

 Critical error. Render output completely in red. Add to tracking category.

 This function called as the last thing before abandoning this module

 ]]

 localfunctioninlineError(msg,args)
 track[categories.error]=1
 returntable.concat({
 '<span style="font-size:100%" class="error citation-comment">Error in ',-- open the error message span
 config.tname,-- insert the local language template name
 ' template: ',
 substitute(msg,args),-- insert the formatted error message
 '.</span>',-- close the span
 createTracking()-- add the category
 })
 end


 --[[--------------------------< inlineRed >-----------------------

 Render a text fragment in red, such as a warning as part of the final output.
 Add tracking category.

  ]]

 localfunctioninlineRed(msg,trackmsg)
 iftrackmsg=="warning"then
 track[categories.warning]=1;
 elseiftrackmsg=="error"then
 track[categories.error]=1;
 end

 return'<span style="font-size:100%" class="error citation-comment">'..msg..'</span>'
 end


 --[[--------------------------< base62 >-----------------------

 Convert base-62 to base-10
 Credit: https://de.wikipedia.org/wiki/Modul:Expr 

 ]]

 localfunctionbase62(value)
 localr=1-- default return value is input value is malformed

 ifvalue:match('%W')then-- value must only be in the set [0-9a-zA-Z]
 return;-- nil return when value contains extraneous characters
 end

 localn=#value-- number of characters in value
 localk=1
 localc
 r=0
 fori=n,1,-1do-- loop through all characters in value from ls digit to ms digit
 c=value:byte(i,i)
 ifc>=48andc<=57then-- character is digit 0-9
 c=c-48
 elseifc>=65andc<=90then-- character is ascii a-z
 c=c-55
 else-- must be ascii A-Z
 c=c-61
 end
 r=r+c*k-- accumulate this base62 character's value
 k=k*62-- bump for next
 end-- for i

 returnr
 end


 --[[--------------------------< D E C O D E _ D A T E >--------------------------------------------------------

 Given a date string, return it in iso format along with an indicator of the date's format. Except that month names
 must be recognizable as legitimate month names with proper capitalization, and that the date string must match one
 of the recognized date formats, no error checking is done here; return nil else

 ]]

 localfunctiondecode_date(date_str)
 localpatterns={
 ['dmy']={'^(%d%d?) +([^%s%d]+) +(%d%d%d%d)$','d','m','y'},-- %a does not recognize unicode combining characters used by some languages
 ['mdy']={'^([^%s%d]+) (%d%d?), +(%d%d%d%d)$','m','d','y'},
 ['ymd']={'^(%d%d%d%d) +([^%s%d]+) (%d%d?)$','y','m','d'},-- not mos compliant at en.wiki but may be acceptible at other wikis
 };

 localt={};

 ifnon_western_digitsthen-- this wiki uses non-western digits?
 date_str=mw.ustring.gsub(date_str,'%d',digits);-- convert this wiki's non-western digits to western digits
 end

 ifdate_str:match('^%d%d%d%d%-%d%d%-%d%d$')then-- already an iso format date, return western digits form
 returndate_str,'iso';
 end

 fork,vinpairs(patterns)do
 localc1,c2,c3=mw.ustring.match(date_str,patterns[k][1]);-- c1 .. c3 are captured but we don't know what they hold

 ifc1then-- set on match
 t={-- translate unspecified captures to y, m, and d
 [patterns[k][2]]=c1,-- fill the table of captures with the captures
 [patterns[k][3]]=c2,-- take index names from src_pattern table and assign sequential captures
 [patterns[k][4]]=c3,
 };
 ifmonth_num[t.m]then-- when month not already a number
 t.m=month_num[t.m];-- replace valid month name with a number
 else
 returnnil,'iso';-- not a valid date form because month not valid
 end

 returnmw.ustring.format('%.4d-%.2d-%.2d',t.y,t.m,t.d),k;-- return date in iso format
 end
 end
 returnnil,'iso';-- date could not be decoded; return nil and default iso date
 end


 --[[--------------------------< makeDate >-----------------------

 Given year, month, day numbers, (zero-padded or not) return a full date in df format
 where df may be one of:
 	mdy, dmy, iso, ymd

 on entry, year, month, day are presumed to be correct for the date that they represent; all are required

 in this module, makeDate() is sometimes given an iso-format date in year:
 	makeDate (2018年09月20日, nil, nil, df)
 this works because table.concat() sees only one table member

 ]]

 localfunctionmakeDate(year,month,day,df)
 localformat={
 ['dmy']='j F Y',
 ['mdy']='F j, Y',
 ['ymd']='Y F j',
 ['iso']='Y-m-d',
 };

 localdate=table.concat({year,month,day},'-');-- assemble year-initial numeric-format date (zero padding not required here)

 ifnon_western_digitsthen-- this wiki uses non-western digits?
 date=mw.ustring.gsub(date,'%d',digits);-- convert this wiki's non-western digits to western digits
 end

 returnmw.getContentLanguage():formatDate(format[df],date);
 end


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

 Returns true if date is after 31 December 1899 (why is 1900 the min year? shouldn't the internet's date-of-birth
 be min year?), not after today's date, and represents a valid date (29 February 2017 is not a valid date). Applies
 Gregorian leapyear rules.

 all arguments are required

 ]]

 localfunctionis_valid_date(year,month,day)
 localdays_in_month={31,28,31,30,31,30,31,31,30,31,30,31};
 localmonth_length;
 localy,m,d;
 localtoday=os.date('*t');-- fetch a table of current date parts

 ifnotyearor''==yearornotmonthor''==monthornotdayor''==daythen
 returnfalse;-- something missing
 end

 y=tonumber(year);
 m=tonumber(month);
 d=tonumber(day);

 if1900>yortoday.year<yor1>mor12<mthen-- year and month are within bounds	TODO: 1900?
 returnfalse;
 end

 if(2==m)then-- if February
 month_length=28;-- then 28 days unless
 if(0==(y%4)and(0~=(y%100)or0==(y%400)))then-- is a leap year?
 month_length=29;-- if leap year then 29 days in February
 end
 else
 month_length=days_in_month[m];
 end

 if1>dormonth_length<dthen-- day is within bounds
 returnfalse;
 end
 -- here when date parts represent a valid date
 returnos.time({['year']=y,['month']=m,['day']=d,['hour']=0})<=os.time();-- date at midnight must be less than or equal to current date/time
 end


 --[[--------------------------< decodeWebciteDate >-----------------------

 Given a URI-path to Webcite (eg. /67xHmVFWP) return the encoded date in df format

 returns date string in df format - webcite date is a unix timestamp encoded as bae62
 or the string 'query'

 ]]

 localfunctiondecodeWebciteDate(path,df)

 localdt={};
 localdecode;

 dt=mw.text.split(path,"/")

 -- valid URL formats that are not base62

 -- http://www.webcitation.org/query?id=1138911916587475
 -- http://www.webcitation.org/query?url=http..&date=2012年06月01日+21:40:03
 -- http://www.webcitation.org/1138911916587475
 -- http://www.webcitation.org/cache/73e53dd1f16cf8c5da298418d2a6e452870cf50e
 -- http://www.webcitation.org/getfile.php?fileid=1c46e791d68e89e12d0c2532cc3cf629b8bc8c8e

 ifdt[2]:find('query',1,true)or
 dt[2]:find('cache',1,true)or
 dt[2]:find('getfile',1,true)or
 tonumber(dt[2])then
 return'query';
 end

 decode=base62(dt[2]);-- base62 string -> exponential number
 ifnotdecodethen
 returnnil;-- nil return when dt[2] contains characters not in %w
 end
 dt=os.date('*t',string.format("%d",decode):sub(1,10))-- exponential number -> text -> first 10 characters (a unix timestamp) -> a table of date parts

 decode=makeDate(dt.year,dt.month,dt.day,'iso');-- date comparisons are all done in iso format with western digits
 ifnon_western_digitsthen-- this wiki uses non-western digits?
 decode=mw.ustring.gsub(decode,'%d',digits);-- convert this wiki's non-western digits to western digits
 end

 returndecode;
 end


 --[[--------------------------< decodeWaybackDate >-----------------------

 Given a URI-path to Wayback (eg. /web/20160901010101/http://example.com )
 or Library of Congress Web Archives (eg. /all/20160901010101/http://example.com)
 or UK Government Web Archive (eg. /ukgwa/20160901010101/http://example.com or /tna/20160901010101/http://example.com)

 return the formatted date eg. "September 1, 2016" in df format 
 Handle non-digits in snapshot ID such as "re_" and "-" and "*"

 returns two values:
 	first value is one of these:
 		valid date string in df format - wayback date is valid (including the text string 'index' when date is '/*/')
 		empty string - wayback date is malformed (less than 8 digits, not a valid date)
 		nil - wayback date is '/save/' or otherwise not a number

 	second return value is an appropriate 'message' may or may not be formatted

 ]]

 localfunctiondecodeWaybackDate(path,df)

 localmsg,snapdate;

 snapdate=path:gsub('^/web/',''):gsub('^/all/',''):gsub('^/ukgwa/',''):gsub('^/tna/',''):gsub('^/','');-- remove leading /web/, /all/, /ukgwa/, /tna/, or /
 snapdate=snapdate:match('^[^/]+');-- get timestamp
 ifsnapdate=="*"then-- eg. /web/*/http.., etc.
 return'index';-- return indicator that this url has an index date
 end

 snapdate=snapdate:gsub('%a%a_%d?$',''):gsub('%-','');-- from date, remove any trailing "re_", dashes

 msg='';
 ifsnapdate:match('%*$')then-- a trailing '*' causes calendar display at archive .org
 snapdate=snapdate:gsub('%*$','');-- remove so not part of length calc later
 msg=inlineRed(err_warn_msgs.ts_cal,'warning');-- make a message
 end

 ifnottonumber(snapdate)then
 returnnil,'ts_nan';-- return nil (fatal error flag) and message selector
 end

 localdlen=snapdate:len();
 ifdlen<8then-- we need 8 digits TODO: but shouldn't this be testing for 14 digits?
 return'',inlineRed(err_warn_msgs.ts_short,'error');-- return empty string and error message
 end

 localyear,month,day=snapdate:match('(%d%d%d%d)(%d%d)(%d%d)');-- no need for snapdatelong here

 ifnotis_valid_date(year,month,day)then
 return'',inlineRed(err_warn_msgs.ts_date,'error');-- return empty string and error message
 end

 snapdate=table.concat({year,month,day},'-');-- date comparisons are all done in iso format
 if14==dlenthen
 returnsnapdate,msg;-- return date with message if any
 else
 returnsnapdate,msg..inlineRed(err_warn_msgs.ts_len,'warning');-- return date with warning message(s)
 end
 end


 --[[--------------------------< decodeArchiveisDate >-----------------------

 Given an Archive.is "long link" URI-path (e.g. /2016.08.28-144552/http://example.com)
 return the date in df format (e.g. if df = dmy, return 28 August 2016)
 Handles "." and "-" in snapshot date, so 2016年08月28日-144552 is same as 20160828144552

 returns two values:
 	first value is one of these:
 		valid date string in df format - archive.is date is valid (including the text string 'short link' when url is the short form)
 		empty string - wayback date is malformed (not a number, less than 8 digits, not a valid date)
 		nil - wayback date is '/save/'

 	second return value is an appropriate 'message' may or may not be formatted

 ]]

 localfunctiondecodeArchiveisDate(path,df)
 localsnapdate

 ifpath:match('^/%w+$')then-- short form url path is '/' followed by some number of base 62 digits and nothing else
 return"short link"-- e.g. http://archive.is/hD1qz
 end

 snapdate=mw.text.split(path,'/')[2]:gsub('[%.%-]','');-- get snapshot date, e.g. 2016年08月28日-144552; remove periods and hyphens

 localdlen=string.len(snapdate)
 ifdlen<8then-- we need 8 digits TODO: but shouldn't this be testing for 14 digits?
 return'',inlineRed(err_warn_msgs.ts_short,'error');-- return empty string and error message
 end

 localyear,month,day=snapdate:match('(%d%d%d%d)(%d%d)(%d%d)');-- no need for snapdatelong here

 ifnotis_valid_date(year,month,day)then
 return'',inlineRed(err_warn_msgs.ts_date,'error');-- return empty string and error message
 end

 snapdate=table.concat({year,month,day},'-');-- date comparisons are all done in iso format
 if14==dlenthen
 returnsnapdate;-- return date
 else
 returnsnapdate,inlineRed(err_warn_msgs.ts_len,'warning');-- return date with warning message
 end
 end


 --[[--------------------------< serviceName >-----------------------

 Given a domain extracted by mw.uri.new() (eg. web.archive.org) set tail string and service ID

 ]]

 localfunctionserviceName(host,no_link)
 localtracking;
 localindex;

 host=host:lower():gsub('^web%.(.+)','%1'):gsub('^www%.(.+)','%1');-- lowercase, remove web. and www. subdomains

 ifservices[host]then
 index=host;
 else
 fork,_inpairs(services)do
 ifhost:find('%f[%a]'..k:gsub('([%.%-])','%%%1'))then
 index=k;
 break;
 end
 end
 end

 ifindexthen
 localout={''};-- empty string in [1] so that concatenated result has leading single space
 ulx.url1.service=services[index][4]or'other';
 tracking=services[index][5]orcategories.other;
 -- build tail string
 iffalse==services[index][1]then-- select prefix
 table.insert(out,prefixes.at);
 elseiftrue==services[index][1]then
 table.insert(out,prefixes.atthe);
 else
 table.insert(out,services[index][1]);
 end

 table.insert(out,make_wikilink(services[index][2],services[index][3],no_link));-- add article wikilink
 ifservices[index][6]then-- add tail postfix if it exists
 table.insert(out,services[index][6]);
 end

 ulx.url1.tail=table.concat(out,' ');-- put it all together; result has leading space character

 else-- here when unknown archive
 ulx.url1.service='other';
 tracking=categories.unknown;
 ulx.url1.tail=table.concat({'',prefixes.at,host,inlineRed(err_warn_msgs.unknown_url,error)},' ');
 end

 track[tracking]=1
 end


 --[[--------------------------< parseExtraArgs >-----------------------

 Parse numbered arguments starting at 2, such as url2..url10, date2..date10, title2..title10
 	For example: {{webarchive |url=.. |url4=.. |url7=..}}
 		Three url arguments not in numeric sequence (1..4..7). 
 			Function only processes arguments numbered 2 or greater (in this case 4 and 7)
 				It creates numeric sequenced table entries like:
 				urlx.url2.url = <argument value for url4>
 				urlx.url3.url = <argument value for url7>
 			Returns the number of URL arguments found numbered 2 or greater (in this case returns "2")

  ]]

 localfunctionparseExtraArgs(args)

 locali,j,argurl,argurl2,argdate,argtitle

 j=2
 fori=2,config.maxurlsdo
 argurl="url"..i
 ifargs[argurl]then
 argurl2="url"..j
 ulx[argurl2]={}
 ulx[argurl2]["url"]=args[argurl]
 argdate="date"..i
 ifargs[argdate]then
 ulx[argurl2]["date"]=args[argdate]
 else
 ulx[argurl2]["date"]=inlineRed(err_warn_msgs.date_miss,'warning');
 end

 argtitle="title"..i
 ifargs[argtitle]then
 ulx[argurl2]["title"]=args[argtitle]
 else
 ulx[argurl2]["title"]=nil
 end
 j=j+1
 end
 end

 ifj==2then
 return0
 else
 returnj-2
 end
 end


 --[[--------------------------< comma >-----------------------

 Given a date string, return "," if it's MDY 

 ]]

 localfunctioncomma(date)
 return(dateanddate:match('%a+ +%d%d?(,) +%d%d%d%d'))or'';
 end


 --[[--------------------------< createRendering >-----------------------

 Return a rendering of the data in ulx[][]

 ]]

 localfunctioncreateRendering()

 localdisplayfield
 localout={};

 localindex_date,msg=ulx.url1.date:match('(index)(.*)');-- when ulx.url1.date extract 'index' text and message text (if there is a message)
 ulx.url1.date=ulx.url1.date:gsub('index.*','index');-- remove message

 if'none'==ulx.url1.formatthen-- For {{wayback}}, {{webcite}}
 table.insert(out,'[');-- open extlink markup
 table.insert(out,ulx.url1.url);-- add url

 ifulx.url1.titlethen
 table.insert(out,' ')-- the required space
 table.insert(out,ulx.url1.title)-- the title
 table.insert(out,']');-- close extlink markup
 table.insert(out,ulx.url1.tail);-- tail text
 ifulx.url1.datethen
 table.insert(out,'&#32;(');-- open date text; TODO: why the html entity? replace with regular space?
 table.insert(out,'index'==ulx.url1.dateands_text.archiveors_text.archived);-- add text
 table.insert(out,' ');-- insert a space
 table.insert(out,ulx.url1.date);-- add date
 table.insert(out,')');-- close date text
 end
 else-- no title
 ifindex_datethen-- when url date is 'index' 
 table.insert(out,table.concat({' ',s_text.Archive_index,']'}));-- add the index link label
 table.insert(out,msgor'');-- add date mismatch message when url date is /*/ and |date= has valid date
 else
 table.insert(out,table.concat({' ',s_text.Archived,'] '}));-- add link label for url has timestamp date (will include mismatch message if there is one)
 end
 ifulx.url1.datethen
 if'index'~=ulx.url1.datethen
 table.insert(out,ulx.url1.date);-- add date when data is not 'index'
 end
 table.insert(out,comma(ulx.url1.date));-- add ',' if date format is mdy
 table.insert(out,ulx.url1.tail);-- add tail text
 else-- no date
 table.insert(out,ulx.url1.tail);-- add tail text
 end
 end

 if0<ulx.url1.extraurlsthen-- For multiple archive URLs
 localtot=ulx.url1.extraurls+1
 table.insert(out,'.')-- terminate first url
 table.insert(out,table.concat({' ',s_text.addlarchives,': '}));-- add header text

 fori=2,totdo-- loop through the additionals
 localindex=table.concat({'url',i});-- make an index
 displayfield=ulx[index]['title']and'title'or'date';-- choose display text
 table.insert(out,'[');-- open extlink markup
 table.insert(out,ulx[index]['url']);-- add the url
 table.insert(out,' ');-- the required space
 table.insert(out,ulx[index][displayfield]);-- add the label
 table.insert(out,']');-- close extlink markup
 table.insert(out,i==totand'.'or', ');-- add terminator
 end
 end
 returntable.concat(out);-- make a big string and done

 else-- For {{cite archives}}																	
 if'addlarchives'==ulx.url1.formatthen-- Multiple archive services 
 table.insert(out,table.concat({s_text.addlarchives,': '}));-- add header text
 else-- Multiple pages from the same archive 
 table.insert(out,table.concat({s_text.addlpages,' '}));-- add header text
 table.insert(out,ulx.url1.date);-- add date to header text
 table.insert(out,': ');-- close header text
 end

 localtot=ulx.url1.extraurls+1;
 fori=1,totdo-- loop through the additionals
 localindex=table.concat({'url',i});-- make an index
 table.insert(out,'[');-- open extlink markup
 table.insert(out,ulx[index]['url']);-- add url
 table.insert(out,' ');-- add required space

 displayfield=ulx[index]['title'];
 if'addlarchives'==ulx.url1.formatthen
 ifnotdisplayfieldthen
 displayfield=ulx[index]['date']
 end
 else-- must be addlpages
 ifnotdisplayfieldthen
 displayfield=table.concat({s_text.Page,' ',i});
 end
 end
 table.insert(out,displayfield);-- add title, date, page label text
 table.insert(out,']');-- close extlink markup
 table.insert(out,(i==totand'.'or', '));-- add terminator
 end
 returntable.concat(out);-- make a big string and done
 end
 end


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

 for internaltionalization, translate local-language parameter names to their English equivalents

 TODO: return error message if multiple aliases of the same canonical parameter name are found?

 returns two tables:
 	new_args - holds canonical form parameters and their values either from translation or because the parameter was already in canonical form
 	origin - maps canonical-form parameter names to their untranslated (local language) form for error messaging in the local language

 unrecognized parameters are ignored

 ]]

 localfunctionparameter_name_xlate(args,params,enum_params)
 localname;-- holds modifiable name of the parameter name during evaluation
 localenum;-- for enumerated parameters, holds the enumerator during evaluation
 localfound=false;-- flag used to break out of nested for loops
 localnew_args={};-- a table that holds canonical and translated parameter k/v pairs
 localorigin={};-- a table that maps original (local language) parameter names to their canonical name for local language error messaging
 localunnamed_params;-- set true when unsupported positional parameters are detected

 fork,vinpairs(args)do-- loop through all of the arguments in the args table
 name=k;-- copy of original parameter name

 if'string'==type(k)then
 ifnon_western_digitsthen-- true when non-western digits supported at this wiki
 name=mw.ustring.gsub(name,'%d',digits);-- convert this wiki's non-western digits to western digits
 end

 enum=name:match('%d+$');-- get parameter enumerator if it exists; nil else

 ifnotenumthen-- no enumerator so looking for non-enumnerated parameters
 -- TODO: insert shortcut here? if params[name] then name holds the canonical parameter name; no need to search further
 forpname,aliasesinpairs(params)do-- loop through each parameter the params table
 for_,aliasinipairs(aliases)do-- loop through each alias in the parameter's aliases table
 ifname==aliasthen
 new_args[pname]=v;-- create a new entry in the new_args table
 origin[pname]=k;-- create an entry to make canonical parameter name to original local language parameter name
 found=true;-- flag so that we can break out of these nested for loops
 break;-- no need to search the rest of the aliases table for name so go on to the next k, v pair
 end
 end

 iffoundthen-- true when we found an alias that matched name
 found=false;-- reset the flag
 break;-- go do next args k/v pair
 end
 end
 else-- enumerated parameters
 name=name:gsub('%d$','#');-- replace enumeration digits with place holder for table search
 -- TODO: insert shortcut here? if num_params[name] then name holds the canonical parameter name; no need to search further
 forpname,aliasesinpairs(enum_params)do-- loop through each parameter the num_params table
 for_,aliasinipairs(aliases)do-- loop through each alias in the parameter's aliases table
 ifname==aliasthen
 pname=pname:gsub('#$',enum);-- replace the '#' place holder with the actual enumerator
 new_args[pname]=v;-- create a new entry in the new_args table
 origin[pname]=k;-- create an entry to make canonical parameter name to original local language parameter name
 found=true;-- flag so that we can break out of these nested for loops
 break;-- no need to search the rest of the aliases table for name so go on to the next k, v pair
 end
 end

 iffoundthen-- true when we found an alias that matched name
 found=false;-- reset the flag
 break;-- go do next args k/v pair
 end
 end
 end
 else
 unnamed_params=true;-- flag for unsupported positional parameters
 end
 end-- for k, v
 returnnew_args,origin,unnamed_params;
 end


 --[[--------------------------< W E B A R C H I V E >----------------------------------------------------------

 template entry point

 ]]

 localfunctionwebarchive(frame)
 localargs=getArgs(frame);

 localdata=mw.loadData(table.concat({-- make a data module name; sandbox or live
 'Module:Webarchive/data',
 frame:getTitle():find('sandbox',1,true)and'/sandbox'or''-- this instance is ./sandbox then append /sandbox
 }));
 categories=data.categories;-- fill in the forward declarations
 config=data.config;
 ifdata.digits.enablethen
 digits=data.digits;-- for i18n; table of digits in the local wiki's language
 non_western_digits=true;-- use_non_western_digits
 end
 err_warn_msgs=data.err_warn_msgs;
 excepted_pages=data.excepted_pages;
 month_num=data.month_num;-- for i18n; table of month names in the local wiki's language
 prefixes=data.prefixes;
 services=data.services;
 s_text=data.s_text;
 uncategorized_namespaces=data.uncategorized_namespaces;
 uncategorized_subpages=data.uncategorized_subpages;

 localorigin={};-- holds a map of English to local language parameter names used in the current template; not currently used
 localunnamed_params;-- boolean set to true when template call has unnamed parameters
 args,origin,unnamed_params=parameter_name_xlate(args,data.params,data.enum_params);-- translate parameter names in args to English

 localdate,format,msg,udate,uri,url;
 localldf='iso';-- when there is no |date= parameter, render url dates in iso format

 ifargs.urlandargs.url1then-- URL argument (first)
 returninlineError(data.crit_err_msgs.conflicting,{origin.url,origin.url1});
 end

 url=args.urlorargs.url1;

 ifnoturlthen
 returninlineError(data.crit_err_msgs.empty);
 end
 -- these iabot bugs perportedly fixed; removing these causes lua script error
 --[[																				-- at Template:Webarchive/testcases/Production; resolve that before deleting these tests
 	if mw.ustring.find( url, "https://web.http", 1, true ) then					-- track bug - TODO: IAbot bug; not known if the bug has been fixed; deferred
 		track[categories.error] = 1;
 		return inlineError (data.crit_err_msgs.iabot1);
 	end 
 	if url == "https://web.archive.org/http:/" then								 -- track bug - TODO: IAbot bug; not known if the bug has been fixed; deferred
 		track[categories.error] = 1;
 		return inlineError (data.crit_err_msgs.iabot2);
 	end
 ]]

 ifnot(url:lower():find('^http')orurl:find('^//'))then
 returninlineError(data.crit_err_msgs.invalid_url);
 end

 ulx.url1={}
 ulx.url1.url=url

 ulx.url1.extraurls=parseExtraArgs(args)

 localgood=false;
 good,uri=pcall(mw.uri.new,ulx.url1.url);-- get a table of uri parts from this url; protected mode to prevent lua error when ulx.url1.url is malformed

 ifnotgoodornil==uri.hostthen-- abandon when ulx.url1.url is malformed
 returninlineError(data.crit_err_msgs.invalid_url);
 end

 serviceName(uri.host,args.nolink)

 ifargs.dateandargs.date1then-- Date argument
 returninlineError(data.crit_err_msgs.conflicting,{origin.date,origin.date1});
 end

 date=args.dateorargs.date1;
 date=dateanddate:gsub(' +',' ');-- replace multiple spaces with a single space

 ifdateandconfig.verifydatesthen
 if'*'==datethen
 date='index';
 ldf='iso';-- set to default format
 elseif'mdy'==datethen
 date=nil;-- if date extracted from URL,
 ldf='mdy';-- then |date=mdy overrides iso
 elseif'dmy'==datethen
 date=nil;-- if date extracted from URL,
 ldf='dmy';-- then |date=dmy overrides iso
 elseif'ymd'==datethen
 date=nil;-- if date extracted from URL,
 ldf='ymd';-- then |date=ymd overrides iso
 else
 date,ldf=decode_date(date);-- get an iso format date from date and get date's original format
 end
 end

 if'wayback'==ulx.url1.serviceor'locwebarchives'==ulx.url1.serviceor'ukgwa'==ulx.url1.servicethen
 ifdatethen
 ifconfig.verifydatesthen
 ifldfthen
 udate,msg=decodeWaybackDate(uri.path);-- get the url date in iso format and format of date in |date=; 'index' when wayback url date is *
 ifnotudatethen-- this is the only 'fatal' error return
 returninlineError(data.crit_err_msgs[msg]);
 end

 ifudate~=datethen-- date comparison using iso format dates
 date=udate;
 msg=table.concat({
 inlineRed(err_warn_msgs.mismatch,'warning'),-- add warning message
 msg,-- add message if there is one
 });
 end
 end
 end
 else-- no |date=
 udate,msg=decodeWaybackDate(uri.path);

 ifnotudatethen-- this is the only 'fatal' error return
 returninlineError(data.crit_err_msgs[msg]);
 end

 if''==udatethen
 date=nil;-- unset
 else
 date=udate;
 end
 end

 elseif'webcite'==ulx.url1.servicethen
 ifdatethen
 ifconfig.verifydatesthen
 ifldfthen
 udate=decodeWebciteDate(uri.path);-- get the url date in iso format
 if'query'~=udatethen-- skip if query
 ifudate~=datethen-- date comparison using iso format dates
 date=udate;
 msg=table.concat({
 inlineRed(err_warn_msgs.mismatch,'warning'),
 });
 end
 end
 end
 end
 else
 date=decodeWebciteDate(uri.path,"iso")
 ifdate=="query"then
 date=nil;-- unset
 msg=inlineRed(err_warn_msgs.date_miss,'warning');
 elseifnotdatethen-- invalid base62 string
 date=inlineRed(err_warn_msgs.date1,'error');
 end
 end

 elseif'archiveis'==ulx.url1.servicethen
 ifdatethen
 ifconfig.verifydatesthen
 ifldfthen
 udate,msg=decodeArchiveisDate(uri.path)-- get the url date in iso format
 if'short link'~=udatethen-- skip if short link
 ifudate~=datethen-- date comparison using iso format dates
 date=udate;
 msg=table.concat({
 inlineRed(err_warn_msgs.mismatch,'warning'),-- add warning message
 msg,-- add message if there is one
 });
 end
 end
 end
 end
 else-- no |date=
 udate,msg=decodeArchiveisDate(uri.path,"iso")
 ifudate=="short link"then
 date=nil;-- unset
 msg=inlineRed(err_warn_msgs.date_miss,'warning');
 elseif''==udatethen
 date=nil;-- unset
 else
 date=udate;
 end
 end

 else-- some other service
 ifnotdatethen
 msg=inlineRed(err_warn_msgs.date_miss,'warning');
 end
 end

 if'index'==datethen
 ulx.url1.date=date..(msgor'');-- create index + message (if there is one)
 elseifdatethen
 ulx.url1.date=makeDate(date,nil,nil,ldf)..(msgor'');-- create a date in the wiki's local language + message (if there is one)
 else
 ulx.url1.date=msg;
 end

 format=args.format;-- Format argument 

 ifnotformatthen
 format="none"
 else
 fork,vinpairs(data.format_vals)do-- |format= accepts two specific values loop through a table of those values
 localfound;-- declare a nil flag
 for_,pinipairs(v)do-- loop through local language variants
 ifformat==pthen-- when |format= value matches 
 format=k;-- use name from table key
 found=true;-- declare found so that we can break out of outer for loop
 break;-- break out of inner for loop
 end
 end

 iffoundthen
 break;
 end
 end

 ifformat=="addlpages"then
 ifnotulx.url1.datethen
 format="none"
 end
 elseifformat=="addlarchives"then
 format="addlarchives"
 else
 format="none"
 end
 end
 ulx.url1.format=format

 ifargs.titleandargs.title1then-- Title argument
 returninlineError(data.crit_err_msgs.conflicting,{origin.title,origin.title1});
 end

 ulx.url1.title=args.titleorargs.title1;

 localrend=createRendering()
 ifnotrendthen
 returninlineError(data.crit_err_msgs.unknown);
 end

 returnrend..((unnamed_paramsandinlineRed(err_warn_msgs.unnamed_params,'warning'))or'')..createTracking();

 end


 --[[--------------------------< E X P O R T E D 	 F U N C T I O N S >------------------------------------------
 ]]

 return{webarchive=webarchive};

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