Idi na sadržaj

Modul:Webarchive

S Wikipedije, slobodne enciklopedije

Dokumentaciju za ovaj modul možete napraviti na straniciModul:Webarchive/dok

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

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('Module:No globals');
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. n. Y.',
['mdy']='j. n. Y.',
['ymd']='Y-m-d',
['iso']='j. n. Y.',
};

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 (/all/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('^/all/',''):gsub('^/web/',''):gsub('^/','');-- remove leading '/all/', leading '/web/' or leading '/'
snapdate=snapdate:match('^[^/]+');-- get timestamp
ifsnapdate=="*"then-- eg. /web/*/http.. or /all/*/http..
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"..j
ifargs[argdate]then
ulx[argurl2]["date"]=args[argdate]
else
ulx[argurl2]["date"]=inlineRed(err_warn_msgs.date_miss,'warning');
end

argtitle="title"..j
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
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.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};