Modulo:Webarchive: differenze tra le versioni

Da Tematiche di genere.
Vai alla navigazione Vai alla ricerca
(use require('strict') instead of require('Module:No globals'))
m (modulo no globals obsoleto)
Riga 1: Riga 1:
--[[ ----------------------------------
--[[ ----------------------------------
  Modulo Lua che implementa il template {{Webarchive}} e permette di
  decifrare la data negli URL di alcuni archivi anche ad altri moduli.
]]


Lua module implementing the {{webarchive}} template.
require('strict')


A merger of the functionality of three templates: {{wayback}}, {{webcite}} and {{cite archives}}
local getArgs = require('Modulo:Arguments').getArgs
local cfg = mw.loadData('Modulo:Webarchive/Configurazione')
]]
 
 
--[[--------------------------< D E P E N D E N C I E S >------------------------------------------------------
]]
 
require('strict');
local getArgs = require ('Module:Arguments').getArgs;
 
 
--[[--------------------------< F O R W A R D  D E C L A R A T I O N S >--------------------------------------
]]
 
local categories = {}; -- category names
local config = {}; -- global configuration settings
local digits = {}; -- for i18n; table that translates local-wiki digits to western digits
local err_warn_msgs = {}; -- error and warning messages
local excepted_pages = {};
local month_num = {}; -- for i18n; table that translates local-wiki month names to western digits
local prefixes = {}; -- service provider tail string prefixes
local services = {}; -- archive service provider data from
local s_text = {}; -- table of static text strings used to build final rendering
local uncategorized_namespaces = {}; -- list of namespaces that we should not categorize
local uncategorized_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 >----------------------------------
]]
 
local non_western_digits; -- boolean flag set true when data.digits.enable is true
local this_page = mw.title.getCurrentTitle();
 
local track = {}; -- Associative array to hold tracking categories
local ulx = {}; -- 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.
 
]]
 
local function substitute (msg, args)
return args and mw.message.newRawMessage (msg, args):plain() or msg;
end
 
 
--[[--------------------------< tableLength >-----------------------
 
Given a 1-D table, return number of elements
 
]]
 
local function tableLength(T)
local count = 0
for _ in pairs(T) do count = count + 1 end
return count
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.
 
]=]
 
local function make_wikilink (link, display, no_link)
if nil == no_link then
if link and ('' ~= link) then
if display and ('' ~= display) then
return table.concat ({'[[', link, '|', display, ']]'});
else
return table.concat ({'[[', link, ']]'});
end
end
return display or ''; -- link not set so return the display text
 
else -- no_link
if display and ('' ~= display) then -- if there is display text
return display; -- return that
else
return link or ''; -- return the target article name or empty string
end
end
end
 
 
--[[--------------------------< createTracking >-----------------------
 
Return data in track[] ie. tracking categories
 
]]
 
local function createTracking()
if not excepted_pages[this_page.fullText] then -- namespace:title/fragment is allowed to be categorized (typically this module's / template's testcases page(s))
if uncategorized_namespaces[this_page.nsText] then
return ''; -- this page not to be categorized so return empty string
end
for _,v in ipairs (uncategorized_subpages) do -- cycle through page name patterns
if this_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
 
local out = {};
if tableLength(track) > 0 then
for key, _ in pairs(track) do -- loop through table
table.insert (out, make_wikilink (key)); -- and convert category names to links
end
end
return table.concat (out); -- concat into one big string; empty string if table is empty
 
end


local p = {}
local track = {}  -- array associativo per accumulare le categorie di tracciamento
local maxurls = 10 -- massimo numero di URL permessi
local encoded_date


--[[--------------------------< inlineError >-----------------------
--[[--------------------------< inlineError >-----------------------
 
Errore critico. Formatta l'output completamente in rosso. Aggiunge una categoria di tracciamento.
Critical error. Render output completely in red. Add to tracking category.
]]
 
local function inlineError(arg, msg)
This function called as the last thing before abandoning this module
track['Categoria:Errori di compilazione del template Webarchive'] = 1
 
return '<span style="font-size:100%" class="error citation-comment">Errore di compilazione del template Webarchive: controllare il valore di <code style="color:inherit; border:inherit; padding:inherit;">&#124;' .. arg .. '=</code> (' .. msg .. ').</span>'
]]
 
local function inlineError (msg, args)
track[categories.error] = 1
return table.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
end


--[[--------------------------< inlineRed >-----------------------
--[[--------------------------< inlineRed >-----------------------
 
Formatta un frammento di testo in rosso, quale ad esempio un avvertimento da integrare nell'output finale.
Render a text fragment in red, such as a warning as part of the final output.
Aggiunge una categoria di tracciamento.
Add tracking category.
 
  ]]
  ]]
local function inlineRed(msg, trackmsg)
local function inlineRed(msg, trackmsg)
if trackmsg == "warning" then
if trackmsg == 'warning' then
track[categories.warning] = 1;
track['Categoria:Errori di compilazione del template Webarchive - Avvisi'] = 1
elseif trackmsg == "error" then
elseif trackmsg == 'error' then
track[categories.error] = 1;
track['Categoria:Errori di compilazione del template Webarchive'] = 1
end
end
return '<span style="font-size:100%" class="error citation-comment">' .. msg .. '</span>'
return '<span style="font-size:100%" class="error citation-comment">' .. msg .. '</span>'
end
end
Riga 163: Riga 37:


--[[--------------------------< base62 >-----------------------
--[[--------------------------< base62 >-----------------------
 
Converte base-62 in base-10
Convert base-62 to base-10
Crediti: https://de.wikipedia.org/wiki/Modul:Expr
Credit: https://de.wikipedia.org/wiki/Modul:Expr  
]]
 
]]
 
local function base62( value )
local function base62( value )
local r = 1 -- default return value is input value is malformed
local r = 1
 
if value:match('^%w+$') then
if value:match ('%W') then -- value must only be in the set [0-9a-zA-Z]
local n = #value
return; -- nil return when value contains extraneous characters
local k = 1
end
local c
 
r = 0
local n = #value -- number of characters in value
for i = n, 1, -1 do
local k = 1
c = value:byte( i, i )
local c
if c >= 48 and c <= 57 then
r = 0
c = c - 48
for i = n, 1, -1 do -- loop through all characters in value from ls digit to ms digit
elseif c >= 65 and c <= 90 then
c = value:byte( i, i )
c = c - 55
if c >= 48 and c <= 57 then -- character is digit 0-9
elseif c >= 97 and c <= 122 then
c = c - 48
c = c - 61
elseif c >= 65 and c <= 90 then -- 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
 
return r
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
 
]]
 
local function decode_date (date_str)
local patterns = {
['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
};
local t = {};
 
if non_western_digits then -- 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
 
if date_str:match ('^%d%d%d%d%-%d%d%-%d%d$') then -- already an iso format date, return western digits form
return date_str, 'iso';
end
for k, v in pairs (patterns) do
local c1, c2, c3 = mw.ustring.match (date_str, patterns[k][1]); -- c1 .. c3 are captured but we don't know what they hold
if c1 then -- 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,
};
if month_num[t.m] then -- when month not already a number
t.m = month_num[t.m]; -- replace valid month name with a number
else
else
return nil, 'iso'; -- not a valid date form because month not valid
r = 1
break
end
end
 
r = r + c * k
return mw.ustring.format ('%.4d-%.2d-%.2d', t.y, t.m, t.d), k; -- return date in iso format
k = k * 62
end
end
end
end
return nil, 'iso'; -- date could not be decoded; return nil and default iso date
return r
end
end


--[[--------------------------< tableLength >-----------------------
--[[--------------------------< makeDate >-----------------------
Restituisce il numero di elementi in una tabella
 
]]
Given year, month, day numbers, (zero-padded or not) return a full date in df format
local function tableLength(t)
where df may be one of:
local count = 0
mdy, dmy, iso, ymd
for _ in pairs(t) do count = count + 1 end
 
return count
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
 
]]
 
local function makeDate (year, month, day, df)
local format = {
['dmy'] = 'j F Y',
['mdy'] = 'F j, Y',
['ymd'] = 'Y F j',
['iso'] = 'Y-m-d',
};
 
local date = table.concat ({year, month, day}, '-'); -- assemble year-initial numeric-format date (zero padding not required here)
 
if non_western_digits then -- this wiki uses non-western digits?
date = mw.ustring.gsub (date, '%d', digits); -- convert this wiki's non-western digits to western digits
end
 
return mw.getContentLanguage():formatDate (format[df], date);
end
end


 
--[[--------------------------< formatDate >-----------------------
--[[--------------------------< I S _ V A L I D _ D A T E >----------------------------------------------------
Verifica il formato di una data (dmy o iso) e se lo riconosce
 
la riformatta in dmy, altrimenti restituisce il valore così com'è
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
local function formatDate(date)
Gregorian leapyear rules.
local y, m, d
 
local try_year
all arguments are required
local split = mw.text.split(date, '-')
 
if tableLength(split) == 3 then
]]
try_year = tonumber(split[1])
 
if try_year and try_year > 1900 and try_year < 2200 then -- iso
local function is_valid_date (year, month, day)
y, m, d = split[1], cfg.month_localized[tonumber(split[2])], split[3]
local days_in_month = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
local month_length;
local y, m, d;
local today = os.date ('*t'); -- fetch a table of current date parts
 
if not year or '' == year or not month or '' == month or not day or '' == day then
return false; -- something missing
end
y = tonumber (year);
m = tonumber (month);
d = tonumber (day);
 
if 1900 > y or today.year < y or 1 > m or 12 < m then -- year and month are within bounds TODO: 1900?
return false;
end
 
if (2==m) then -- if February
month_length = 28; -- then 28 days unless
if (0==(y%4) and (0~=(y%100) or 0==(y%400))) then -- is a leap year?
month_length = 29; -- if leap year then 29 days in February
end
end
else
else
month_length=days_in_month[m];
split = mw.text.split(date, ' ')
if tableLength(split) == 3 then
try_year = tonumber(split[3])
if try_year and try_year > 1900 and try_year < 2200 and
(split[1] == '1º' or tonumber(split[1])) then -- dmy
d, m, y = split[1], split[2], split[3]
end
end
end
end
d = tonumber(d) or d
if d == 1 then d = '1º' end
return m and mw.ustring.format('%s %s %s', d, m, y) or date
end


if 1 > d or month_length < d then -- day is within bounds
--[[--------------------------< formatUrlDate >-----------------------
return false;
Controlla la data prelevata automaticamente dall'url di un archivio.
Se è valida la formatta in dmy, altrimenti restituisce nil.
]]
local function formatUrlDate(y, m, d)
local current_year = tonumber(os.date('%Y'))
y, m, d = tonumber(y), tonumber(m), tonumber(d)
if not y or y == '' or not m or m == '' or not d or d == '' or d > 31 or
m < 1 or m > 12 or y < 1900 or y > current_year then
return nil
end
end
-- here when date parts represent a valid date
m = cfg.month_localized[m]
return os.time({['year']=y, ['month']=m, ['day']=d, ['hour']=0}) <= os.time(); -- date at midnight must be less than or equal to current date/time
if d == 1 then d = '' end
return mw.ustring.format('%s %s %s', d, m, y)
end
end


--[[--------------------------< decodeWebciteDate >-----------------------
--[[--------------------------< decodeWebciteDate >-----------------------
Ricava la data da un URI-path a Webcite (es. /67xHmVFWP)
]]
local function decodeWebciteDate(path)
local path_elements = mw.text.split(path, '/')


Given a URI-path to Webcite (eg. /67xHmVFWP) return the encoded date in df format
-- formati URL validi che non sono base62:
 
returns date string in df format - webcite date is a unix timestamp encoded as bae62
or the string 'query'
 
]]
 
local function decodeWebciteDate(path, df)
 
local dt = {};
local decode;
 
dt = mw.text.split(path, "/")
 
-- valid URL formats that are not base62


-- http://www.webcitation.org/query?id=1138911916587475
-- http://www.webcitation.org/query?id=1138911916587475
Riga 345: Riga 133:
-- http://www.webcitation.org/getfile.php?fileid=1c46e791d68e89e12d0c2532cc3cf629b8bc8c8e
-- http://www.webcitation.org/getfile.php?fileid=1c46e791d68e89e12d0c2532cc3cf629b8bc8c8e


if dt[2]:find ('query', 1, true) or  
if not path_elements[2] or path_elements[2] == '' then
dt[2]:find ('cache', 1, true) or
return
dt[2]:find ('getfile', 1, true) or
elseif mw.ustring.find(path_elements[2], 'query') or
tonumber(dt[2]) then
mw.ustring.find(path_elements[2], 'cache') or
return 'query';
mw.ustring.find(path_elements[2], 'getfile') or
end
tonumber(path_elements[2]) then
 
encoded_date = false
decode = base62(dt[2]); -- base62 string -> exponential number
return
if not decode then
return nil; -- nil return when dt[2] contains characters not in %w
end
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
local snapdate = os.date('%Y %m %d', string.sub(string.format('%d', base62(path_elements[2])),1,10))
 
local dt = mw.text.split(snapdate, ' ')
decode = makeDate (dt.year, dt.month, dt.day, 'iso'); -- date comparisons are all done in iso format with western digits
local fulldate = formatUrlDate(dt[1], dt[2], dt[3])
if non_western_digits then -- this wiki uses non-western digits?
return fulldate
decode = mw.ustring.gsub (decode, '%d', digits); -- convert this wiki's non-western digits to western digits
end
 
return decode;
end
end


 
--[[--------------------------< snapDateToString >-----------------------
--[[--------------------------< decodeWaybackDate >-----------------------
Ricava la data da un URI-path a Wayback (es. /web/20160901010101/http://example.com ).
 
Gestisce anche le non cifre come "re_", "-" e "*".
Given a URI-path to Wayback (eg. /web/20160901010101/http://example.com )
]]
or Library of Congress Web Archives (eg. /all/20160901010101/http://example.com)
local function decodeWaybackDate(path)
or UK Government Web Archive (eg. /ukgwa/20160901010101/http://example.com or /tna/20160901010101/http://example.com)
local snapdate = string.gsub(path, '^/all/', '') -- rimuove la sequenza iniziale "/all/"
 
snapdate = string.gsub(snapdate, '^/w?e?b?/?', '') -- rimuove la sequenza iniziale "/web/" o "/"
return the formatted date eg. "September 1, 2016" in df format
local path_elements = mw.text.split(snapdate, '/')
Handle non-digits in snapshot ID such as "re_" and "-" and "*"
snapdate = path_elements[1]
 
if snapdate == '*' then return end
returns two values:
snapdate = string.gsub(snapdate, '[a-z][a-z]_[0-9]?$', '')
first value is one of these:
snapdate = string.gsub(snapdate, '[-]', '')
valid date string in df format - wayback date is valid (including the text string 'index' when date is '/*/')
snapdate = string.gsub(snapdate, '[*]$', '')
empty string - wayback date is malformed (less than 8 digits, not a valid date)
local fulldate
nil - wayback date is '/save/' or otherwise not a number
if tonumber(snapdate) and string.len(snapdate) >= 8 then
local year = string.sub(snapdate, 1, 4)
second return value is an appropriate 'message' may or may not be formatted
local month = string.sub(snapdate, 5, 6)
 
local day = string.sub(snapdate, 7, 8)
]]
fulldate = formatUrlDate(year, month, day)
 
local function decodeWaybackDate(path, df)
 
local msg, snapdate;
 
snapdate = path:gsub ('^/web/', ''):gsub ('^/all/', ''):gsub ('^/ukgwa/', ''):gsub ('^/tna/', ''):gsub ('^/', ''); -- remove leading /web/, /all/, /ukgwa/, /tna/, or /
snapdate = snapdate:match ('^[^/]+'); -- get timestamp
if snapdate == "*" 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 = '';
if snapdate: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
 
if not tonumber(snapdate) then
return nil, 'ts_nan'; -- return nil (fatal error flag) and message selector
end
 
local dlen = snapdate:len();
if dlen < 8 then -- 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
 
local year, month, day = snapdate:match ('(%d%d%d%d)(%d%d)(%d%d)'); -- no need for snapdatelong here
 
if not is_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
if 14 == dlen then
return snapdate, msg; -- return date with message if any
else
return snapdate, msg .. inlineRed (err_warn_msgs.ts_len, 'warning'); -- return date with warning message(s)
end
end
return fulldate
end
end


--[[--------------------------< decodeArchiveisDate >-----------------------
--[[--------------------------< decodeArchiveisDate >-----------------------
 
Ricava la data dall'URI-path di un link esteso ad Archive.is (es. /2016.08.28-144552/http://example.com).
Given an Archive.is "long link" URI-path (e.g. /2016.08.28-144552/http://example.com)
Gestisce "." e "-" nella data, rendendo 2016.08.28-144552 uguale a 20160828144552.
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
local function decodeArchiveisDate(path)
 
    local path_elements = mw.text.split(path, '/')
returns two values:
    local snapdate = path_elements[2]
first value is one of these:
    if not path_elements[2] or path_elements[2] == '' then return end
valid date string in df format - archive.is date is valid (including the text string 'short link' when url is the short form)
    snapdate = string.gsub(snapdate, '[%.%-]', '')
empty string - wayback date is malformed (not a number, less than 8 digits, not a valid date)
    if not tonumber(snapdate) then encoded_date = false return end -- formato link breve
nil - wayback date is '/save/'
local fulldate
if string.len(snapdate) >= 8 then
second return value is an appropriate 'message' may or may not be formatted
local year = string.sub(snapdate, 1, 4)
 
local month = string.sub(snapdate, 5, 6)
]]
local day = string.sub(snapdate, 7, 8)
 
fulldate = formatUrlDate(year, month, day)
local function decodeArchiveisDate(path, df)
local snapdate
 
if path: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
 
local dlen = string.len(snapdate)
if dlen < 8 then -- 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
 
local year, month, day = snapdate:match ('(%d%d%d%d)(%d%d)(%d%d)'); -- no need for snapdatelong here
 
if not is_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
if 14 == dlen then
return snapdate; -- return date
else
return snapdate, inlineRed (err_warn_msgs.ts_len, 'warning'); -- return date with warning message
end
end
return fulldate
  end
  end


--[[--------------------------< serviceName >-----------------------
--[[--------------------------< serviceName >-----------------------
 
Imposta la stringa di coda e l'ID del servizio in base al dominio
Given a domain extracted by mw.uri.new() (eg. web.archive.org) set tail string and service ID
estratto da mw.uri.new() (es. web.archive.org)
 
]]
]]
local function serviceName(url_data, nolink, notail)
 
local tracking = 'Categoria:Template Webarchive - collegamenti ad altri archivi'
local function serviceName(host, no_link)
local bracketopen, bracketclose = nolink and '' or '[[', nolink and '' or ']]'
local tracking;
encoded_date = nil -- reset
local index;
for _,servizio in ipairs(cfg.servizi) do
if string.gsub(url_data.host, 'www%.', '') == servizio.signature then
host = host:lower():gsub ('^web%.(.+)', '%1'):gsub ('^www%.(.+)', '%1'); -- lowercase, remove web. and www. subdomains
url_data.service = servizio.service or 'altri'
 
if not notail and servizio.tailbracket then
if services[host] then
url_data.tail = mw.ustring.format(servizio.tailbracket, bracketopen, bracketclose)
index = host;
elseif not notail then
else
url_data.tail = servizio.tail
for k, _ in pairs (services) do
if host:find ('%f[%a]'..k:gsub ('([%.%-])', '%%%1')) then
index = k;
break;
end
end
tracking = servizio.tracking or tracking
encoded_date = servizio.service and true
break
end
end
end
end
if url_data.service == nil then
if index then
tracking = 'Categoria:Template Webarchive - collegamenti ad archivi sconosciuti'
local out = {''}; -- empty string in [1] so that concatenated result has leading single space
url_data.tail = ' su ' .. url_data.host .. ' ' .. inlineRed('URL di servizio di archiviazione sconosciuto')
ulx.url1.service = services[index][4] or 'other';
tracking = services[index][5] or categories.other;
-- build tail string
if false == services[index][1] then -- select prefix
table.insert (out, prefixes.at);
elseif true == 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
if services[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
end
track[tracking] = 1
track[tracking] = 1
end
end


 
--[[--------------------------< createTracking >-----------------------
--[[--------------------------< parseExtraArgs >-----------------------
Restituisce le categorie di tracciamento inserite in track[]
 
]]
Parse numbered arguments starting at 2, such as url2..url10, date2..date10, title2..title10
local function createTracking()
For example: {{webarchive |url=.. |url4=.. |url7=..}}
-- procede solo nel namespace 0
Three url arguments not in numeric sequence (1..4..7).
local current_namespace = mw.title.getCurrentTitle().namespace
Function only processes arguments numbered 2 or greater (in this case 4 and 7)
if current_namespace ~= 0 then return '' end
It creates numeric sequenced table entries like:
local sand = ''
urlx.url2.url = <argument value for url4>
if tableLength(track) > 0 then
urlx.url3.url = <argument value for url7>
for key,_ in pairs(track) do
Returns the number of URL arguments found numbered 2 or greater (in this case returns "2")
sand = sand .. '[[' .. key .. ']]'
 
]]
 
local function parseExtraArgs(args)
 
local i, j, argurl, argurl2, argdate, argtitle
 
j = 2
for i = 2, config.maxurls do
argurl = "url" .. i
if args[argurl] then
argurl2 = "url" .. j
ulx[argurl2] = {}
ulx[argurl2]["url"] = args[argurl]
argdate = "date" .. i
if args[argdate] then
ulx[argurl2]["date"] = args[argdate]
else
ulx[argurl2]["date"] = inlineRed (err_warn_msgs.date_miss, 'warning');
end
argtitle = "title" .. i
if args[argtitle] then
ulx[argurl2]["title"] = args[argtitle]
else
ulx[argurl2]["title"] = nil
end
j = j + 1
end
end
end
end
return sand
end


if j == 2 then
--[[--------------------------< createRendering >-----------------------
return 0
Restituisce la resa dei dati in url_data[][]
]]
local function createRendering(url_data)
local sand
local day = url_data[1].date and mw.ustring.match(url_data[1].date, '^%d+')
local article = (day == '8' or day == '11') and 'l\'' or 'il '
if not url_data[1].title and not url_data[1].date then
sand = mw.ustring.format('[%s Archiviato]%s.', url_data[1].url, url_data[1].tail)
elseif not url_data[1].title and url_data[1].date then
sand = mw.ustring.format('[%s Archiviato] %s%s%s.', url_data[1].url, article, url_data[1].date, url_data[1].tail)
elseif url_data[1].title and not url_data[1].date then
sand = mw.ustring.format('[%s %s]%s.', url_data[1].url, url_data[1].title, url_data[1].tail)
elseif url_data[1].title and url_data[1].date then
sand = mw.ustring.format('[%s %s]%s&#32;(archiviato %s%s).', url_data[1].url, url_data[1].title, url_data[1].tail, article, url_data[1].date)
else
else
return j - 2
return nil
end
end
if #url_data > 1 then -- per più URL di archivio
sand = sand .. ' Archivi aggiuntivi: '
local archives_output = {}
for i = 2, #url_data do
archives_output[#archives_output+1] = mw.ustring.format('[%s %s]%s%s', url_data[i].url, url_data[i].title or url_data[i].date, url_data[i].title and (' (' .. url_data[i].date .. ')') or '', url_data[i].tail or '')
end
sand = sand .. table.concat(archives_output, ', ') .. '.'
end
return sand
end
end


 
--[[--------------------------------------------------------------------
--[[--------------------------< comma >-----------------------
Entry point per chiamata diretta da un modulo.
 
Riceve l'URL di un archivio e ne restituisce la data se riesce a decodificarla.
Given a date string, return "," if it's MDY
]]
 
function p.decodeArchiveDate(url)
]]
local uri = mw.uri.new(url)
 
local host, path = uri.host, uri.path
local function comma(date)
if not url or not host or path == '' then return end
return (date and date:match ('%a+ +%d%d?(,) +%d%d%d%d')) or '';
host = string.gsub(host, 'www%.', '')
end
for _, servizio in ipairs(cfg.servizi) do
 
if host == servizio.signature then
 
if servizio.service == 'wayback' then
--[[--------------------------< createRendering >-----------------------
return decodeWaybackDate(path)
 
elseif servizio.service == 'webcite' then
Return a rendering of the data in ulx[][]
return decodeWebciteDate(path)
 
elseif servizio.service == 'archiveis' then
]]
return decodeArchiveisDate(path)
 
local function createRendering()
 
local displayfield
local out = {};
local index_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.format then -- For {{wayback}}, {{webcite}}
table.insert (out, '['); -- open extlink markup
table.insert (out, ulx.url1.url); -- add url
 
if ulx.url1.title then
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
if ulx.url1.date then
table.insert (out, '&#32;('); -- open date text; TODO: why the html entity? replace with regular space?
table.insert (out, 'index' == ulx.url1.date and s_text.archive or s_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
if index_date then -- when url date is 'index'
table.insert (out, table.concat ({' ', s_text.Archive_index, ']'})); -- add the index link label
table.insert (out, msg or ''); -- 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
if ulx.url1.date then
if 'index' ~= ulx.url1.date then
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
 
if 0 < ulx.url1.extraurls then -- For multiple archive URLs
local tot = ulx.url1.extraurls + 1
table.insert (out, '.') -- terminate first url
table.insert (out, table.concat ({' ', s_text.addlarchives, ': '})); -- add header text
 
for i=2, tot do -- loop through the additionals
local index = 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==tot and '.' or ', '); -- add terminator
end
end
end
end
return table.concat (out); -- make a big string and done
else -- For {{cite archives}}
if 'addlarchives' == ulx.url1.format then -- 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
local tot = ulx.url1.extraurls + 1;
for i=1, tot do -- loop through the additionals
local index = 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.format then
if not displayfield then
displayfield = ulx[index]['date']
end
else -- must be addlpages
if not displayfield then
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==tot and '.' or ', ')); -- add terminator
end
return table.concat (out); -- make a big string and done
end
end
return
end
end


 
--[[--------------------------------------------------------------------
--[[--------------------------< P A R A M E T E R _ N A M E _ X L A T E >--------------------------------------
Funzione di interfaccia principale per implementazione del
 
Template:Webarchive
for internaltionalization, translate local-language parameter names to their English equivalents
]]
 
function p.webarchive(frame)
TODO: return error message if multiple aliases of the same canonical parameter name are found?
-- carica in args i parametri e se sono nulli li ignora, eccetto che per il parametro nolink
 
local args = getArgs(frame, {
returns two tables:
valueFunc = function(key, value)
new_args - holds canonical form parameters and their values either from translation or because the parameter was already in canonical form
if value then
origin - maps canonical-form parameter names to their untranslated (local language) form for error messaging in the local language
if key == 'nolink' then
 
return true
unrecognized parameters are ignored
else
 
value = mw.text.trim(value)
]]
if value ~= '' then return value end
 
local function parameter_name_xlate (args, params, enum_params)
local name; -- holds modifiable name of the parameter name during evaluation
local enum; -- for enumerated parameters, holds the enumerator during evaluation
local found = false; -- flag used to break out of nested for loops
local new_args = {}; -- a table that holds canonical and translated parameter k/v pairs
local origin = {}; -- a table that maps original (local language) parameter names to their canonical name for local language error messaging
local unnamed_params; -- set true when unsupported positional parameters are detected
for k, v in pairs (args) do -- loop through all of the arguments in the args table
name = k; -- copy of original parameter name
 
if 'string' == type (k) then
if non_western_digits then -- 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
if not enum then -- 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
for pname, aliases in pairs (params) do -- loop through each parameter the params table
for _, alias in ipairs (aliases) do -- loop through each alias in the parameter's aliases table
if name == alias then
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
if found then -- 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
for pname, aliases in pairs (enum_params) do -- loop through each parameter the num_params table
for _, alias in ipairs (aliases) do -- loop through each alias in the parameter's aliases table
if name == alias then
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
if found then -- 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
end
end
else
return nil
unnamed_params = true; -- flag for unsupported positional parameters
end
end
end -- for k, v
})
return new_args, origin, unnamed_params;
local url_data = {}
end
local i = 1
 
while true do
 
local n = i == 1 and args.url and '' or i
--[[--------------------------< W E B A R C H I V E >----------------------------------------------------------
local url = i == 1 and (args.url or args.url1) or args['url' .. i]
 
template entry point
 
]]
 
local function webarchive(frame)
local args = getArgs (frame);
 
local data = 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;
if data.digits.enable then
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;
 
local origin = {}; -- holds a map of English to local language parameter names used in the current template; not currently used
local unnamed_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
 
local date, format, msg, udate, uri, url;
local ldf = 'iso'; -- when there is no |date= parameter, render url dates in iso format
if args.url and args.url1 then -- URL argument (first)
return inlineError (data.crit_err_msgs.conflicting, {origin.url, origin.url1});
end
url = args.url or args.url1;
if not url then
return inlineError (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
]]
 
if not (url:lower():find ('^http') or url:find ('^//')) then
return inlineError (data.crit_err_msgs.invalid_url );
end
 
ulx.url1 = {}
ulx.url1.url = url
 
ulx.url1.extraurls = parseExtraArgs(args)


local good = false;
-- verifica eventuali errori nel parametro url
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
if i == 1 and not url then
return inlineError('url', 'vuoto') .. createTracking()
if not good or nil == uri.host then -- abandon when ulx.url1.url is malformed
elseif not url or i > maxurls then
return inlineError (data.crit_err_msgs.invalid_url);
break
end
elseif mw.ustring.find(url, 'https://web.http') then
track['Categoria:Errori di compilazione del template Webarchive'] = 1
serviceName(uri.host, args.nolink)
return inlineError('url' .. n, 'https://web.http') .. createTracking()
 
elseif url == 'https://web.archive.org/http:/' then
if args.date and args.date1 then -- Date argument
track['Categoria:Errori di compilazione del template Webarchive'] = 1
return inlineError (data.crit_err_msgs.conflicting, {origin.date, origin.date1});
return inlineError('url' .. n, 'URL non valido') .. createTracking()
end
date = args.date or args.date1;
date = date and date:gsub (' +', ' '); -- replace multiple spaces with a single space
 
if date and config.verifydates then
if '*' == date then
date = 'index';
ldf = 'iso'; -- set to default format
elseif 'mdy' == date then
date = nil; -- if date extracted from URL,
ldf = 'mdy'; -- then |date=mdy overrides iso
elseif 'dmy' == date then
date = nil; -- if date extracted from URL,
ldf = 'dmy'; -- then |date=dmy overrides iso
elseif 'ymd' == date then
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
end
url_data[i] = {}
 
url_data[i].url = url
if 'wayback' == ulx.url1.service or 'locwebarchives' == ulx.url1.service or 'ukgwa' == ulx.url1.service then
url_data[i].uri = mw.uri.new(url)
if date then
url_data[i].host, url_data[i].path = url_data[i].uri.host, url_data[i].uri.path
if config.verifydates then
if not url_data[i].host or url_data[i].path == '' then
if ldf then
return inlineError('url' .. n, 'URL non valido') .. createTracking()
udate, msg = decodeWaybackDate (uri.path); -- get the url date in iso format and format of date in |date=; 'index' when wayback url date is *
if not udate then -- this is the only 'fatal' error return
return inlineError (data.crit_err_msgs[msg]);
end
 
if udate ~= date then -- 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);
 
if not udate then -- this is the only 'fatal' error return
return inlineError (data.crit_err_msgs[msg]);
end
 
if '' == udate then
date = nil; -- unset
else
date = udate;
end
end
end
serviceName(url_data[i], args.nolink, i > 1 and true)


elseif 'webcite' == ulx.url1.service then
-- gestione delle date
local date = i == 1 and (args.date or args.date1 or args.data or args.data1) or
args['date' .. i] or args['data' .. i]
if date then
if date then
if config.verifydates then
date = formatDate(date)
if ldf then
local udate = url_data[i].service == 'wayback' and decodeWaybackDate(url_data[i].path) or
udate = decodeWebciteDate (uri.path); -- get the url date in iso format
url_data[i].service == 'webcite' and decodeWebciteDate(url_data[i].path) or
if 'query' ~= udate then -- skip if query
url_data[i].service == 'archiveis' and decodeArchiveisDate(url_data[i].path)
if udate ~= date then -- date comparison using iso format dates
if udate and udate ~= date then
date = udate;
date = date .. ' ' .. inlineRed('Data nell\'URL non combaciante: ' .. udate, 'warning')
msg = table.concat ({
elseif not udate and encoded_date == true then
inlineRed (err_warn_msgs.mismatch, 'warning'),
date = date .. ' ' .. inlineRed('Data nell\'URL indecifrabile', 'error')
});
end
end
end
end
end
elseif url_data[i].service == 'wayback' then
date = decodeWaybackDate(url_data[i].path)
elseif url_data[i].service == 'webcite' then
date = decodeWebciteDate(url_data[i].path)
elseif url_data[i].service == 'archiveis' then
date = decodeArchiveisDate(url_data[i].path)
else
else
date = decodeWebciteDate( uri.path, "iso" )
date = inlineRed('Data mancante', 'warning')
if date == "query" then
date = nil; -- unset
msg = inlineRed (err_warn_msgs.date_miss, 'warning');
elseif not date then -- invalid base62 string
date = inlineRed (err_warn_msgs.date1, 'error');
end
end
end
elseif 'archiveis' == ulx.url1.service then
if date then
if config.verifydates then
if ldf then
udate, msg = decodeArchiveisDate (uri.path) -- get the url date in iso format
if 'short link' ~= udate then -- skip if short link
if udate ~= date then -- 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" )
if udate == "short link" then
date = nil; -- unset
msg = inlineRed (err_warn_msgs.date_miss, 'warning');
elseif '' == udate then
date = nil; -- unset
else
date = udate;
end
end
else -- some other service
if not date then
if not date then
msg = inlineRed (err_warn_msgs.date_miss, 'warning');
date = encoded_date == false and inlineRed('Data mancante', 'warning') or
end
inlineRed('Data nell\'URL indecifrabile', 'error')
end
 
if 'index' == date then
ulx.url1.date = date .. (msg or ''); -- create index + message (if there is one)
elseif date then
ulx.url1.date = makeDate (date, nil, nil, ldf) .. (msg or ''); -- 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
 
if not format then
format = "none"
else
for k, v in pairs (data.format_vals) do -- |format= accepts two specific values loop through a table of those values
local found; -- declare a nil flag
for _, p in ipairs (v) do -- loop through local language variants
if format == p then -- 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
if found then
break;
end
end
end
url_data[i].date = date


if format == "addlpages" then
-- gestione del titolo
if not ulx.url1.date then
url_data[i].title = i == 1 and (args.title or args.title1 or args.titolo or args.titolo1) or
format = "none"
args['title' .. i] or args['titolo' .. i]
end
elseif format == "addlarchives" then
format = "addlarchives"
else
format = "none"
end
end
ulx.url1.format = format


if args.title and args.title1 then -- Title argument
i = i + 1
return inlineError (data.crit_err_msgs.conflicting, {origin.title, origin.title1});
end
end
 
local rend = createRendering(url_data)
ulx.url1.title = args.title or args.title1;
 
local rend = createRendering()
if not rend then
if not rend then
return inlineError (data.crit_err_msgs.unknown);
track['Categoria:Errori di compilazione del template Webarchive'] = 1
rend = '<span style="font-size:100%" class="error citation-comment">Errori in [[:Template:Webarchive]]: problema sconosciuto. Si prega di segnalarlo nella [[Discussioni template:Webarchive|pagina di discussione]] del template.</span>'
end
end


return rend .. ((unnamed_params and inlineRed (err_warn_msgs.unnamed_params, 'warning')) or '') .. createTracking();
return rend .. createTracking()
 
end
end


 
return p
--[[--------------------------< E X P O R T E D F U N C T I O N S >------------------------------------------
]]
 
return {webarchive = webarchive};

Versione delle 21:27, 22 ott 2022

La documentazione per questo modulo può essere creata in Modulo:Webarchive/man

Errore script: Errore Lua: errore interno - l'interprete è uscito con stato 1.

--[[ ----------------------------------
  Modulo Lua che implementa il template {{Webarchive}} e permette di
  decifrare la data negli URL di alcuni archivi anche ad altri moduli.
	]]

require('strict')

local getArgs = require('Modulo:Arguments').getArgs
local cfg = mw.loadData('Modulo:Webarchive/Configurazione')

local p = {}
local track = {}   -- array associativo per accumulare le categorie di tracciamento
local maxurls = 10 -- massimo numero di URL permessi
local encoded_date

--[[--------------------------< inlineError >-----------------------
	Errore critico. Formatta l'output completamente in rosso. Aggiunge una categoria di tracciamento.
 ]]
local function inlineError(arg, msg)
	track['Categoria:Errori di compilazione del template Webarchive'] = 1
	return '<span style="font-size:100%" class="error citation-comment">Errore di compilazione del template Webarchive: controllare il valore di <code style="color:inherit; border:inherit; padding:inherit;">&#124;' .. arg .. '=</code> (' .. msg .. ').</span>'
end

--[[--------------------------< inlineRed >-----------------------
	Formatta un frammento di testo in rosso, quale ad esempio un avvertimento da integrare nell'output finale.
	Aggiunge una categoria di tracciamento.
 ]]
local function inlineRed(msg, trackmsg)
	if trackmsg == 'warning' then
		track['Categoria:Errori di compilazione del template Webarchive - Avvisi'] = 1
	elseif trackmsg == 'error' then
		track['Categoria:Errori di compilazione del template Webarchive'] = 1
	end
	return '<span style="font-size:100%" class="error citation-comment">' .. msg .. '</span>'
end


--[[--------------------------< base62 >-----------------------
	Converte base-62 in base-10
	Crediti: https://de.wikipedia.org/wiki/Modul:Expr
	]]
local function base62( value )
	local r = 1
	if value:match('^%w+$') then
		local n = #value
		local k = 1
		local c
		r = 0
		for i = n, 1, -1 do
			c = value:byte( i, i )
			if c >= 48 and c <= 57 then
				c = c - 48
			elseif c >= 65 and c <= 90 then
				c = c - 55
			elseif c >= 97 and c <= 122 then
				c = c - 61
			else
				r = 1
				break
			end
			r = r + c * k
			k = k * 62
		end
	end
	return r
end

--[[--------------------------< tableLength >-----------------------
	Restituisce il numero di elementi in una tabella
	]]
local function tableLength(t)
	local count = 0
	for _ in pairs(t) do count = count + 1 end
	return count
end

--[[--------------------------< formatDate >-----------------------
	Verifica il formato di una data (dmy o iso) e se lo riconosce
	la riformatta in dmy, altrimenti restituisce il valore così com'è
	]]
local function formatDate(date)
	local y, m, d
	local try_year
	local split = mw.text.split(date, '-')
	if tableLength(split) == 3 then
		try_year = tonumber(split[1])
		if try_year and try_year > 1900 and try_year < 2200 then -- iso
			y, m, d = split[1], cfg.month_localized[tonumber(split[2])], split[3]
		end
	else
		split = mw.text.split(date, ' ')
		if tableLength(split) == 3 then
			try_year = tonumber(split[3])
			if try_year and try_year > 1900 and try_year < 2200 and
					(split[1] == '1º' or tonumber(split[1])) then -- dmy
				d, m, y = split[1], split[2], split[3]
			end
		end
	end
	d = tonumber(d) or d
	if d == 1 then d = '1º' end
	return m and mw.ustring.format('%s %s %s', d, m, y) or date
end

--[[--------------------------< formatUrlDate >-----------------------
	Controlla la data prelevata automaticamente dall'url di un archivio.
	Se è valida la formatta in dmy, altrimenti restituisce nil.
 ]]
local function formatUrlDate(y, m, d)
	local current_year = tonumber(os.date('%Y'))
	y, m, d = tonumber(y), tonumber(m), tonumber(d)
	if not y or y == '' or not m or m == '' or not d or d == '' or d > 31 or
			m < 1 or m > 12 or y < 1900 or y > current_year then
		return nil
	end
	m = cfg.month_localized[m]
	if d == 1 then d = '1º' end
	return mw.ustring.format('%s %s %s', d, m, y)
end

--[[--------------------------< decodeWebciteDate >-----------------------
	Ricava la data da un URI-path a Webcite (es. /67xHmVFWP)
	]]
local function decodeWebciteDate(path)
	local path_elements = mw.text.split(path, '/')

	-- formati URL validi che non sono 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

	if not path_elements[2] or path_elements[2] == '' then
		return
	elseif mw.ustring.find(path_elements[2], 'query') or
			mw.ustring.find(path_elements[2], 'cache') or
			mw.ustring.find(path_elements[2], 'getfile') or
			tonumber(path_elements[2]) then
		encoded_date = false
		return
	end
	local snapdate = os.date('%Y %m %d', string.sub(string.format('%d', base62(path_elements[2])),1,10))
	local dt = mw.text.split(snapdate, ' ')
	local fulldate = formatUrlDate(dt[1], dt[2], dt[3])
	return fulldate
end

--[[--------------------------< snapDateToString >-----------------------
	Ricava la data da un URI-path a Wayback (es. /web/20160901010101/http://example.com ).
	Gestisce anche le non cifre come "re_", "-" e "*".
 ]]
local function decodeWaybackDate(path)
	local snapdate = string.gsub(path, '^/all/', '') -- rimuove la sequenza iniziale "/all/"
	snapdate = string.gsub(snapdate, '^/w?e?b?/?', '') -- rimuove la sequenza iniziale "/web/" o "/"
	local path_elements = mw.text.split(snapdate, '/')
	snapdate = path_elements[1]
	if snapdate == '*' then return end
	snapdate = string.gsub(snapdate, '[a-z][a-z]_[0-9]?$', '')
	snapdate = string.gsub(snapdate, '[-]', '')
	snapdate = string.gsub(snapdate, '[*]$', '')
	local fulldate
	if tonumber(snapdate) and string.len(snapdate) >= 8 then
		local year = string.sub(snapdate, 1, 4)
		local month = string.sub(snapdate, 5, 6)
		local day = string.sub(snapdate, 7, 8)
		fulldate = formatUrlDate(year, month, day)
	end
	return fulldate
end

--[[--------------------------< decodeArchiveisDate >-----------------------
	Ricava la data dall'URI-path di un link esteso ad Archive.is (es. /2016.08.28-144552/http://example.com).
	Gestisce "." e "-" nella data, rendendo 2016.08.28-144552 uguale a 20160828144552.
  ]]
local function decodeArchiveisDate(path)
    local path_elements = mw.text.split(path, '/')
    local snapdate = path_elements[2]
    if not path_elements[2] or path_elements[2] == '' then return end
    snapdate = string.gsub(snapdate, '[%.%-]', '')
    if not tonumber(snapdate) then encoded_date = false return end -- formato link breve
	local fulldate
	if string.len(snapdate) >= 8 then
		local year = string.sub(snapdate, 1, 4)
		local month = string.sub(snapdate, 5, 6)
		local day = string.sub(snapdate, 7, 8)
		fulldate = formatUrlDate(year, month, day)
	end
	return fulldate
 end

--[[--------------------------< serviceName >-----------------------
	Imposta la stringa di coda e l'ID del servizio in base al dominio
	estratto da mw.uri.new() (es. web.archive.org)
	]]
local function serviceName(url_data, nolink, notail)
	local tracking = 'Categoria:Template Webarchive - collegamenti ad altri archivi'
	local bracketopen, bracketclose = nolink and '' or '[[', nolink and '' or ']]'
	encoded_date = nil -- reset
	for _,servizio in ipairs(cfg.servizi) do
		if string.gsub(url_data.host, 'www%.', '') == servizio.signature then
			url_data.service = servizio.service or 'altri'
			if not notail and servizio.tailbracket then
				url_data.tail = mw.ustring.format(servizio.tailbracket, bracketopen, bracketclose)
			elseif not notail then
				url_data.tail = servizio.tail
			end
			tracking = servizio.tracking or tracking
			encoded_date = servizio.service and true
			break
		end
	end
	if url_data.service == nil then
		tracking = 'Categoria:Template Webarchive - collegamenti ad archivi sconosciuti'
		url_data.tail = ' su ' .. url_data.host .. ' ' .. inlineRed('URL di servizio di archiviazione sconosciuto')
	end
	track[tracking] = 1
end

--[[--------------------------< createTracking >-----------------------
	Restituisce le categorie di tracciamento inserite in track[]
	]]
local function createTracking()
	-- procede solo nel namespace 0
	local current_namespace = mw.title.getCurrentTitle().namespace
	if current_namespace ~= 0 then return '' end
	local sand = ''
	if tableLength(track) > 0 then
		for key,_ in pairs(track) do
			sand = sand .. '[[' .. key .. ']]'
		end
	end
	return sand
end

--[[--------------------------< createRendering >-----------------------
	Restituisce la resa dei dati in url_data[][]
	]]
local function createRendering(url_data)
	local sand
	local day = url_data[1].date and mw.ustring.match(url_data[1].date, '^%d+')
	local article = (day == '8' or day == '11') and 'l\'' or 'il '
	if not url_data[1].title and not url_data[1].date then
		sand = mw.ustring.format('[%s Archiviato]%s.', url_data[1].url, url_data[1].tail)
	elseif not url_data[1].title and url_data[1].date then
		sand = mw.ustring.format('[%s Archiviato] %s%s%s.', url_data[1].url, article, url_data[1].date, url_data[1].tail)
	elseif url_data[1].title and not url_data[1].date then
		sand = mw.ustring.format('[%s %s]%s.', url_data[1].url, url_data[1].title, url_data[1].tail)
	elseif url_data[1].title and url_data[1].date then
		sand = mw.ustring.format('[%s %s]%s&#32;(archiviato %s%s).', url_data[1].url, url_data[1].title, url_data[1].tail, article, url_data[1].date)
	else
		return nil
	end
	if #url_data > 1 then -- per più URL di archivio
		sand = sand .. ' Archivi aggiuntivi: '
		local archives_output = {}
		for i = 2, #url_data do
			archives_output[#archives_output+1] = mw.ustring.format('[%s %s]%s%s', url_data[i].url, url_data[i].title or url_data[i].date, url_data[i].title and (' (' .. url_data[i].date .. ')') or '', url_data[i].tail or '')
		end
		sand = sand .. table.concat(archives_output, ', ') .. '.'
	end
	return sand
end

--[[--------------------------------------------------------------------
	Entry point per chiamata diretta da un modulo.
	Riceve l'URL di un archivio e ne restituisce la data se riesce a decodificarla.
	]]
function p.decodeArchiveDate(url)
	local uri = mw.uri.new(url)
	local host, path = uri.host, uri.path
	if not url or not host or path == '' then return end
	host = string.gsub(host, 'www%.', '')
	for _, servizio in ipairs(cfg.servizi) do
		if host == servizio.signature then
			if servizio.service == 'wayback' then
				return decodeWaybackDate(path)
			elseif servizio.service == 'webcite' then
				return decodeWebciteDate(path)
			elseif servizio.service == 'archiveis' then
				return decodeArchiveisDate(path)
			end
		end
	end
	return
end

--[[--------------------------------------------------------------------
	Funzione di interfaccia principale per implementazione del
	Template:Webarchive
	]]
function p.webarchive(frame)
	-- carica in args i parametri e se sono nulli li ignora, eccetto che per il parametro nolink
	local args = getArgs(frame, {
		valueFunc = function(key, value)
			if value then
				if key == 'nolink' then
					return true
				else
					value = mw.text.trim(value)
					if value ~= '' then return value end
				end
			end
			return nil
		end
	})
	local url_data = {}
	local i = 1
	while true do
		local n = i == 1 and args.url and '' or i
		local url = i == 1 and (args.url or args.url1) or args['url' .. i]

		-- verifica eventuali errori nel parametro url
		if i == 1 and not url then
			return inlineError('url', 'vuoto') .. createTracking()
		elseif not url or i > maxurls then
			break
		elseif mw.ustring.find(url, 'https://web.http') then
			track['Categoria:Errori di compilazione del template Webarchive'] = 1
			return inlineError('url' .. n, 'https://web.http') .. createTracking()
		elseif url == 'https://web.archive.org/http:/' then
			track['Categoria:Errori di compilazione del template Webarchive'] = 1
			return inlineError('url' .. n, 'URL non valido') .. createTracking()
		end
		url_data[i] = {}
		url_data[i].url = url
		url_data[i].uri = mw.uri.new(url)
		url_data[i].host, url_data[i].path = url_data[i].uri.host, url_data[i].uri.path
		if not url_data[i].host or url_data[i].path == '' then
			return inlineError('url' .. n, 'URL non valido') .. createTracking()
		end
		serviceName(url_data[i], args.nolink, i > 1 and true)

		-- gestione delle date
		local date = i == 1 and (args.date or args.date1 or args.data or args.data1) or
				args['date' .. i] or args['data' .. i]
		if date then
			date = formatDate(date)
			local udate = url_data[i].service == 'wayback' and decodeWaybackDate(url_data[i].path) or
					url_data[i].service == 'webcite' and decodeWebciteDate(url_data[i].path) or
					url_data[i].service == 'archiveis' and decodeArchiveisDate(url_data[i].path)
			if udate and udate ~= date then
				date = date .. ' ' .. inlineRed('Data nell\'URL non combaciante: ' .. udate, 'warning')
			elseif not udate and encoded_date == true then
				date = date .. ' ' .. inlineRed('Data nell\'URL indecifrabile', 'error')
			end
		elseif url_data[i].service == 'wayback' then
			date = decodeWaybackDate(url_data[i].path)
		elseif url_data[i].service == 'webcite' then
			date = decodeWebciteDate(url_data[i].path)
		elseif url_data[i].service == 'archiveis' then
			date = decodeArchiveisDate(url_data[i].path)
		else
			date = inlineRed('Data mancante', 'warning')
		end
		if not date then
			date = encoded_date == false and inlineRed('Data mancante', 'warning') or
					inlineRed('Data nell\'URL indecifrabile', 'error')
		end
		url_data[i].date = date

		-- gestione del titolo
		url_data[i].title = i == 1 and (args.title or args.title1 or args.titolo or args.titolo1) or
				args['title' .. i] or args['titolo' .. i]

		i = i + 1
	end
	local rend = createRendering(url_data)
	if not rend then
		track['Categoria:Errori di compilazione del template Webarchive'] = 1
		rend = '<span style="font-size:100%" class="error citation-comment">Errori in [[:Template:Webarchive]]: problema sconosciuto. Si prega di segnalarlo nella [[Discussioni template:Webarchive|pagina di discussione]] del template.</span>'
	end

	return rend .. createTracking()
end

return p