Module:Webarchive

विकिपीडिया से
Jump to navigation Jump to search

--[[--------------------------< C O N F I G U R A T I O N >----------------------------------------------------

global configuration settings

]]

local config = { maxurls = 10, -- Max number of URLs allowed. tname = 'Webarchive', -- name of calling template. Change if template rename. verifydates = true, -- See documentation. Set false to disable. }


--[[--------------------------< U N C A T E G O R I Z E D _ N A M E S P A C E S >------------------------------

List of namespaces that should not be included in citation error categories.

Note: Namespace names should use underscores instead of spaces.

]]

local uncategorized_namespaces = { -- same list as specified at Module:Citation/CS1/Configuration ['User']=true, ['Talk']=true, ['User_talk']=true, ['Wikipedia_talk']=true, ['File_talk']=true, ['Template_talk']=true, ['Help_talk']=true, ['Category_talk']=true, ['Portal_talk']=true, ['Book_talk']=true, ['Draft_talk']=true, ['Education_Program_talk']=true, ['Module_talk']=true, ['MediaWiki_talk']=true, }

local uncategorized_subpages = {'/[Ss]andbox', '/[Tt]estcases'}; -- list of Lua patterns found in page names of pages we should not categorize

local excepted_pages = { -- these pages will be categorized if set true; set to nil to disable ['Module talk:Webarchive/testcases'] = true, -- test cases pages used during development ['Template:Webarchive/testcases/Production'] = true, }


--[[--------------------------< C A T E G O R I E S >----------------------------------------------------------

this is a table of all categories supported by Module:Webarchive

]]

local categories = { archiveis = 'Category:Webarchive template archiveis links', error = 'Category:Webarchive template errors', other = 'Category:Webarchive template other archives', unknown = 'Category:Webarchive template unknown archives', warning = 'Category:Webarchive template warnings', wayback = 'Category:वेबग्रंथागार साँचा वेबैक कड़ियाँ', webcite = 'Category:Webarchive template webcite links', }


--[[--------------------------< P R E F I X E S >--------------------------------------------------------------

used only with serviceName(), this table holds the two generic tail-text prefixes specified by services['<service name>'][1]

]]

local prefixes = { at = 'at', atthe = 'at the', }


--[=[-------------------------< S E R V I C E S >--------------------------------------------------------------

this is a table of tables for archive services. Each service table has: [1]=prefix; may be boolean true or false, or text string where: true indicates that the prefix is taken from prefixes.atthe false indicates that the prefix is taken from prefixes.at 'text string' is used in lieu of the typical 'at' or 'at the' prefix [2]=wikilink target article that describes the service; set to nil if not used [3]=wikilink label; the label in label; set to nil if not used; when there is not article ([2] is nil) use this to name the service; see wikiwix in the table [4]=service ID; set to nil if not used [5]=tracking category key from the categories table; set to nil if not used [6]=postfix; text string to be appended at the end of the tail string - see webarchive.loc.gov in the table

]=]

local services = { ['archive.is'] = {false, 'Archive.is', nil, 'archiveis', categories.archiveis}, ['archive.ec'] = {false, 'Archive.is', nil, 'archiveis', categories.archiveis}, ['archive.fo'] = {false, 'Archive.is', nil, 'archiveis', categories.archiveis}, ['archive.li'] = {false, 'Archive.is', nil, 'archiveis', categories.archiveis}, ['archive.org'] = {true, 'वेबैक मशीन', nil, 'wayback', categories.wayback}, ['archive.today'] = {false, 'Archive.is', nil, 'archiveis', categories.archiveis}, ['archive-it.org'] = {false, 'Archive-It', nil, 'archiveit'}, ['arquivo.pt'] = {true, nil, 'Portuguese Web Archive'}, ['bibalex.org'] = {false, 'Bibliotheca Alexandrina#Internet Archive partnership', 'Bibliotheca Alexandrina'}, ['collectionscanada'] = {true, 'Canadian Government Web Archive'}, ['europarchive.org'] = {true, 'National Library of Ireland'}, ['freezepage.com'] = {false, nil, 'Freezepage'}, ['haw.nsk'] = {true, 'Croatian Web Archive (HAW)'}, ['langzeitarchivierung.bib-bvb.de'] = {false, 'Bavarian State Library'}, ['loc.gov'] = {true, 'Library of Congress'}, ['nationalarchives.gov.uk'] = {true, 'UK Government Web Archive'}, ['nlb.gov.sg'] = {false, 'Web Archive Singapore'}, ['pandora.nla.gov.au'] = {false, 'Pandora Archive'}, ['parliament.uk'] = {true, 'UK Parliament\'s Web Archive'}, ['perma.cc'] = {false, 'Perma.cc'}, ['perma-archives.cc'] = {false, 'Perma.cc'}, ['proni.gov'] = {true, 'Public Record Office of Northern Ireland'}, ['screenshots.com'] = {false, nil, 'Screenshots'}, ['stanford.edu'] = {true, 'Stanford University Libraries', 'Stanford Web Archive'}, ['timetravel.mementoweb.org'] = {false, 'Memento Project'}, ['uni-lj.si'] = {true, nil, 'Slovenian Web Archive'}, ['veebiarhiiv.digar.ee'] = {true, nil, 'Estonian Web Archive'}, ['vefsafn.is'] = {true, 'National and University Library of Iceland'}, ['webarchive.bac-lac.gc.ca'] = {false, 'Library and Archives Canada'}, ['webarchive.loc.gov'] = {true, 'Library of Congress', nil, 'locwebarchives', nil, 'Web Archives'}, ['webarchive.org.uk'] = {true, 'UK Web Archive'}, ['webcache.googleusercontent.com'] = {false, nil, 'Google Cache'}, ['webcitation.org'] = {false, 'WebCite', nil, 'webcite', categories.webcite}, ['webharvest.gov'] = {true, 'National Archives and Records Administration'}, ['webrecorder.io'] = {false, 'webrecorder.io'}, ['wikiwix.com'] = {false, nil, 'Wikiwix'}, ['yorku.ca'] = {false, 'York University Libraries', 'York University Digital Library'}, }


--[[--------------------------< S T A T I C T E X T >--------------------------------------------------------

for internationalzation

]]

local s_text = { addlarchives = 'Additional archives', addlpages = 'Additional pages archived on', -- TODO why the   there? replace with regular space? Archive_index = 'Archive index', Archived = 'Archived', archived = 'archived', archive = 'archive', Page = 'Page', }


--[[--------------------------< E R R _ W A R N _ M S G S >----------------------------------------------------

these tables hold error and warning message text

]]

local err_warn_msgs = { date_err = '[Date error]', -- decodeWebciteDate, decodeWaybackDate, decodeArchiveisDate date_miss = '[Date missing]', -- parseExtraArgs ts_short = '[Timestamp date length]', -- decodeWaybackDate timestamp less than 8 digits ts_date = '[Timestamp date invalid]', -- decodeWaybackDate timestamp not a valid date unknown_url = '[Error: unknown archive URL]', -- serviceName unnamed_params = '[Positional parameters ignored]',

--warnings mismatch = '[Date mismatch]', -- webarchive ts_len = '[Timestamp length]', -- decodeWaybackDate, decodeArchiveisDate timestamp not 14 digits ts_cal = '[Calendar]', -- decodeWaybackDate timestamp has trailing splat }


local crit_err_msgs = { -- critical error messages conflicting = 'Conflicting |$1= and |$2=', empty = 'Empty url', -- iabot1 = 'https://web.http', -- TODO: these iabot bugs perportedly fixed; removing these causes lua script error -- iabot2 = 'Invalid URL', -- at Template:Webarchive/testcases/Production; resolve that before deleting these messages invalid_url = 'Invalid URL', ts_nan = 'Timestamp not a number', unknown = 'Unknown problem. Please report on template talk page', }


--[[--------------------------< D A T E I N T E R N A T I O N A L I Z A T I O N >----------------------------

these tables hold data that is used when converting date formats from non-English languages (because mw.language.getContentLanguage:formatDate() doesn't understand non-English month names)

]]

local month_num = { -- retain English language names even though they may not be strictly required on the local wiki -- from enwiki ['January'] = 1, ['February'] = 2, ['March'] = 3, ['April'] = 4, ['May'] = 5, ['June'] = 6, ['July'] = 7, ['August'] = 8, ['September'] = 9, ['October'] = 10, ['November'] = 11, ['December'] = 12, ['Jan'] = 1, ['Feb'] = 2, ['Mar'] = 3, ['Apr'] = 4, ['May'] = 5, ['Jun'] = 6, ['Jul'] = 7, ['Aug'] = 8, ['Sep'] = 9, ['Oct'] = 10, ['Nov'] = 11, ['Dec'] = 12, -- for hiwiki

   ['जनवरी'] = 1, ['फ़रवरी'] = 2, ['फरवरी'] = 2, ['मार्च'] = 3, ['अप्रैल'] = 4, ['मई'] = 5, ['जून'] = 6, ['जुलाई'] = 7, ['अगस्त'] = 8, ['सितम्बर'] = 9, ['अक्टूबर'] = 10, ['नवम्बर'] = 11, ['दिसम्बर'] = 12, ['सितंबर'] = 9, ['अक्तूबर'] = 10, ['नवंबर'] = 11, ['दिसंबर'] = 12,

-- add local wiki month-names to number translation here -- [] = 1, [] = 2, [] = 3, [] = 4, [] = 5, [] = 6, [] = 7, [] = 8, [] = 9, [] = 10, [] = 11, [] = 12, };

-- when the local wiki uses non-western digits in dates, local wiki digits must be -- translated to western digits; lua only understands western digits local digits = { -- use this table to aid translation -- [] = 0, [] = 1, [] = 2, [] = 3, [] = 4, [] = 5, [] = 6, [] = 7, [] = 8, [] = 9, -- fill these table indexes with local digits enable = false -- set to true to enable local-digit to western-digit translation };


--[[--------------------------< P A R A M E T E R I N T E R N A T I O N A L I Z A T I O N >------------------

this table holds tables of parameter names and their non-English aliases. In the enum_params table '#' is a single character placeholder for 1 or more digit characters

parameter names in this table shall be lowercase ]]

local params = { ['url'] = {'url'}, ['date'] = {'date', 'datum'}, ['title'] = {'title', 'titel'}, ['nolink'] = {'nolink'}, ['format'] = {'format'} }

local enum_params = { ['url#'] = {'url#'}, ['date#'] = {'date#', 'datum#'}, ['title#'] = {'title#', 'titel#'}, }

local format_vals = { -- |format= accepts two values; add local language variants here ['addlpages'] = {'addlpages'}, ['addlarchives'] = {'addlarchives'}, }


--[[--------------------------< E X P O R T E D T A B L E S >------------------------------------------------ ]]

return { categories = categories, config = config, crit_err_msgs = crit_err_msgs, digits = digits, enum_params = enum_params, err_warn_msgs = err_warn_msgs, excepted_pages = excepted_pages, format_vals = format_vals, month_num = month_num, params = params, prefixes = prefixes, services = services, s_text = s_text, uncategorized_namespaces = uncategorized_namespaces, uncategorized_subpages = uncategorized_subpages, }


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

     Lua module implementing the {{webarchive}} template. 

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

local p = {}

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

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

 ]]

local function inlineError(arg, msg)

  track["श्रेणी:वेबआर्काइव टेम्पलेट खराबी"] = 1
  return '<span style="font-size:100%" class="error citation-comment">Error in webarchive template: Check <code style="color:inherit; border:inherit; padding:inherit;">&#124;' .. arg .. '=</code> value. ' .. msg .. '</span>'

end

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

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

 ]]

local function inlineRed(msg, trackmsg)

  if trackmsg == "warning" then
    track["श्रेणी:वेबआर्काइव टेम्पलेट चेतावनी"] = 1 
  elseif trackmsg == "error" then
    track["श्रेणी:वेबआर्काइव टेम्पलेटखराबी"] = 1 
  end

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

end

--[[--------------------------< trimArg >-----------------------

     trimArg returns nil if arg is "" while trimArg2 returns 'true' if arg is "" 
     trimArg2 is for args that might accept an empty value, as an on/off switch like nolink=

 ]]

local function trimArg(arg)
  if arg == "" or arg == nil then
    return nil
  else
    return mw.text.trim(arg)
  end
end
local function trimArg2(arg)
  if arg == nil then
    return nil
  else
    return mw.text.trim(arg)
  end
end

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

     Convert base-62 to base-10
     Credit: 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    -- How comes?
                r = 1
                break    -- for i
            end
            r = r + c * k
            k = k * 62
        end -- for i
    end
    return r
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


--[[--------------------------< dateFormat >-----------------------

     Given a date string, return its format: dmy, mdy, iso, ymd
       If unable to determine return nil

  ]]

local function dateFormat(date)

  local dt = {}
  dt.split = {}

  dt.split = mw.text.split(date, "-")
  if tableLength(dt.split) == 3 then
    if tonumber(dt.split[1]) > 1900 and tonumber(dt.split[1]) < 2200 and tonumber(dt.split[2]) and tonumber(dt.split[3]) then
      return "iso"
    else
      return nil
    end
  end  

  dt.split = mw.text.split(date, " ")
  if tableLength(dt.split) == 3 then
    if tonumber(dt.split[3]) then
      if tonumber(dt.split[3]) > 1900 and tonumber(dt.split[3]) < 2200 then
        if tonumber(dt.split[1]) then
          return "dmy"
        else
          return "mdy"
        end 
      else
        if tonumber(dt.split[1]) then
          if tonumber(dt.split[1]) > 1900 and tonumber(dt.split[1]) < 2200 then
            return "ymd"
          end
        end
      end
    end
  end
  return nil

end

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

     Given a zero-padded 4-digit year, 2-digit month and 2-digit day, return a full date in df format
     df = mdy, dmy, iso, ymd

 ]]

local function makeDate(year, month, day, df)

  if not year or year == "" or not month or month == "" or not day or day == "" then
    return nil
  end

  local zmonth = month                                                      -- month with leading 0
  month = month:match("0*(%d+)")                                            -- month without leading 0
  if tonumber(month) < 1 or tonumber(month) > 12 then
    return year
  end
  local nmonth = os.date("%B", os.time{year=2000, month=month, day=1} )     -- month in name form       
  if not nmonth then
    return year
  end

  local zday = day
  day = zday:match("0*(%d+)")
  if tonumber(day) < 1 or tonumber(day) > 31 then
    if df == "mdy" or df == "dmy" then
      return nmonth .. " " .. year
    elseif df == "iso" then
      return year .. "-" .. zmonth 
    elseif df == "ymd" then
      return year .. " " .. nmonth
    else
      return nmonth .. " " .. year
    end
  end                                       

  if df == "mdy" then
    return nmonth .. " " .. day .. ", " .. year         -- September 1, 2016
  elseif df == "dmy" then
    return day .. " " .. nmonth .. " " .. year          -- 1 September 2016
  elseif df == "iso" then
    return year .. "-" .. zmonth .. "-" .. zday         -- 2016-09-01
  elseif df == "ymd" then
    return year .. " " .. nmonth .. " " .. cday          -- 2016 September 1
  else
    return nmonth .. " " .. day .. ", " .. year         -- September 1, 2016
  end

end


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

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

  ]]
local function decodeWebciteDate(path, df)

    local dt = {}
    dt.split = {}

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

    -- valid URL formats that are not base62
    --  http://www.webcitation.org/query?id=1138911916587475
    --  http://www.webcitation.org/1138911916587475
    --  http://www.webcitation.org/cache/73e53dd1f16cf8c5da298418d2a6e452870cf50e
    if dt.split[2] == "query" or dt.split[2] == "cache" or tonumber(dt.split[2]) then
      return "query"
    end

    dt.full = os.date("%Y %m %d", string.sub(string.format("%d", base62(dt.split[2])),1,10) )
    dt.split = mw.text.split(dt.full, " ")
    dt.year = dt.split[1]
    dt.month = dt.split[2]
    dt.day = dt.split[3]

    if not tonumber(dt.year) or not tonumber(dt.month) or not tonumber(dt.day) then
      return inlineRed("[Date error] (1)", "error")
    end

    if tonumber(dt.month) > 12 or tonumber(dt.day) > 31 or tonumber(dt.month) < 1 then
      return inlineRed("[Date error] (2)", "error")
    end
    if tonumber(dt.year) > tonumber(os.date("%Y")) or tonumber(dt.year) < 1900 then
      return inlineRed("[Date error] (3)", "error")
    end

    fulldate = makeDate(dt.year, dt.month, dt.day, df)
    if not fulldate then
      return inlineRed("[Date error] (4)", "error")
    else
      return fulldate
    end

end

--[[--------------------------< snapDateToString >-----------------------

Given a URI-path to Wayback (eg. /web/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 "*"

 ]]

local function decodeWaybackDate(path, df)

    local snapdate, snapdatelong, currdate, fulldate

    local safe = path
    snapdate = string.gsub(safe, "^/w?e?b?/?", "")                      -- Remove leading "/web/" or "/"
    safe = snapdate
    local N = mw.text.split(safe, "/")
    snapdate = N[1]
    if snapdate == "*" then                                             -- eg. /web/*/http..
      return "index"
    end
    safe = snapdate
    snapdate = string.gsub(safe, "[a-z][a-z]_[0-9]?$", "")              -- Remove any trailing "re_" from date 
    safe = snapdate
    snapdate = string.gsub(safe, "[-]", "")                             -- Remove dashes from date eg. 2015-01-01 
    safe = snapdate
    snapdate = string.gsub(safe, "[*]$", "")                            -- Remove trailing "*" 

    if not tonumber(snapdate) then
      return inlineRed("[Date error] (2)", "error")
    end
    local dlen = string.len(snapdate)
    if dlen < 4 then
      return inlineRed("[Date error] (3)", "error")
    end
    if dlen < 14 then
      snapdatelong = snapdate .. string.rep("0", 14 - dlen)
    else
      snapdatelong = snapdate
    end
    local year = string.sub(snapdatelong, 1, 4)
    local month = string.sub(snapdatelong, 5, 6)
    local day = string.sub(snapdatelong, 7, 8)
    if not tonumber(year) or not tonumber(month) or not tonumber(day) then
      return inlineRed("[Date error] (4)", "error")
    end
    if tonumber(month) > 12 or tonumber(day) > 31 or tonumber(month) < 1 then
      return inlineRed("[Date error] (5)", "error")
    end
    currdate = os.date("%Y")
    if tonumber(year) > tonumber(currdate) or tonumber(year) < 1900 then
      return inlineRed("[Date error] (6)", "error")
    end

    fulldate = makeDate(year, month, day, df)
    if not fulldate then
      return inlineRed("[Date error] (7)", "error")
    else
      return fulldate
    end

end


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

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

  ]]

local function serviceName(host, nolink)

  local tracking = "श्रेणी:वेबआर्काइव टेम्पलेट अन्य आर्काइव"

  local bracketopen = "[["
  local bracketclose = "]]"
  if nolink then
    bracketopen = ""
    bracketclose = ""
  end

  ulx.url1.service = "other"
  ulx.url1.tail = " at " .. ulx.url1.host .. " " .. inlineRed("Error: unknown archive URL")

  if mw.ustring.find( host, "archive.org", 1, plain ) then
    ulx.url1.service = "wayback"
    ulx.url1.tail = bracketopen .. " वेबैक मशीन" .. bracketclose .." पर "
    tracking = "श्रेणी:वेबआर्काइव टेम्पलेट वेबैक कड़ी"
  elseif mw.ustring.find( host, "webcitation.org", 1, plain ) then
    ulx.url1.service = "webcite"
    ulx.url1.tail = bracketopen .. "WebCite" .. bracketclose .." पर "
    tracking = "श्रेणी:वेबआर्काइव टेम्पलेट वेबसाइट कड़ी"
  elseif mw.ustring.find( host, "archive.is", 1, plain ) then
    ulx.url1.service = "archiveis"
    ulx.url1.tail = bracketopen .. " आर्काइव.इज" .. bracketclose .. " पर "
    tracking = "श्रेणी:वेबआर्काइव टेम्पलेट आर्काइवइज कड़ी"
  elseif mw.ustring.find( host, "archive.fo", 1, plain ) then
    ulx.url1.service = "archiveis"
    ulx.url1.tail = " at " .. bracketopen .. "Archive.is" .. bracketclose
    tracking = "श्रेणी:Webarchive template archiveis links"
  elseif mw.ustring.find( host, "archive.today", 1, plain ) then
    ulx.url1.service = "archiveis"
    ulx.url1.tail = " at " .. bracketopen .. "Archive.is" .. bracketclose
    tracking = "श्रेणी:Webarchive template archiveis links"
  elseif mw.ustring.find( host, "archive[-]it.org", 1, plain ) then
    ulx.url1.service = "archiveit"
    ulx.url1.tail = " at " .. bracketopen .. "Archive-It" .. bracketclose
  elseif mw.ustring.find( host, "arquivo.pt", 1, plain) then
    ulx.url1.tail = " at the " .. "Portuguese Web Archive" 
  elseif mw.ustring.find( host, "loc.gov", 1, plain ) then
    ulx.url1.tail = " at the " .. bracketopen .. "Library of Congress" .. bracketclose
  elseif mw.ustring.find( host, "webharvest.gov", 1, plain ) then
    ulx.url1.tail = " at the " .. bracketopen .. "National Archives and Records Administration" .. bracketclose
  elseif mw.ustring.find( host, "bibalex.org", 1, plain ) then
    ulx.url1.tail = " at " .. "[[Bibliotheca_Alexandrina#Internet_Archive_partnership|Bibliotheca Alexandrina]]"
  elseif mw.ustring.find( host, "collectionscanada", 1, plain ) then
    ulx.url1.tail = " at the " .. "Canadian Government Web Archive"
  elseif mw.ustring.find( host, "haw.nsk", 1, plain ) then
    ulx.url1.tail = " at the " .. "Croatian Web Archive (HAW)"
  elseif mw.ustring.find( host, "veebiarhiiv.digar.ee", 1, plain ) then
    ulx.url1.tail = " at the " .. "Estonian Web Archive"
  elseif mw.ustring.find( host, "vefsafn.is", 1, plain ) then
    ulx.url1.tail = " at the " .. "[[National and University Library of Iceland]]"
  elseif mw.ustring.find( host, "proni.gov", 1, plain ) then
    ulx.url1.tail = " at the " .. bracketopen .. "Public Record Office of Northern Ireland" .. bracketclose
  elseif mw.ustring.find( host, "uni[-]lj.si", 1, plain ) then
    ulx.url1.tail = " at the " .. "Slovenian Web Archive"
  elseif mw.ustring.find( host, "stanford.edu", 1, plain ) then
    ulx.url1.tail = " at the " .. "[[Stanford University Libraries|Stanford Web Archive]]"
  elseif mw.ustring.find( host, "nationalarchives.gov.uk", 1, plain ) then
    ulx.url1.tail = " at the " .. bracketopen .. "UK Government Web Archive" .. bracketclose
  elseif mw.ustring.find( host, "parliament.uk", 1, plain ) then
    ulx.url1.tail = " at the " .. bracketopen .. "UK Parliament's Web Archive" .. bracketclose
  elseif mw.ustring.find( host, "webarchive.org.uk", 1, plain ) then
    ulx.url1.tail = " at the " .. bracketopen .. "UK Web Archive" .. bracketclose
  elseif mw.ustring.find( host, "nlb.gov.sg", 1, plain ) then
    ulx.url1.tail = " at " .. "Web Archive Singapore" 
  elseif mw.ustring.find( host, "pandora.nla.gov.au", 1, plain ) then
    ulx.url1.tail = " at " .. bracketopen .. "Pandora Archive" .. bracketclose 
  elseif mw.ustring.find( host, "screenshots.com", 1, plain ) then
    ulx.url1.tail = " at " .. "Screenshots" 
  elseif mw.ustring.find( host, "wikiwix.com", 1, plain ) then
    ulx.url1.tail = " विकिविक्स" .. " पर "
  elseif mw.ustring.find( host, "perma.cc", 1, plain ) then
    ulx.url1.tail = " at " .. bracketopen .. "Perma.cc" .. bracketclose
  else
    tracking = "श्रेणी:वेबआर्काइव टेम्पलेट नामालूम आर्काइव"
  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")

 ]]

local function parseExtraArgs()

  local i, j, argurl, argurl2, argdate, argtitle

  j = 2
  for i = 2, maxurls do
    argurl = "url" .. i
    if trimArg(args[argurl]) then
      argurl2 = "url" .. j
      ulx[argurl2] = {}
      ulx[argurl2]["url"] = args[argurl]
      argdate = "date" .. j
      if trimArg(args[argdate]) then
        ulx[argurl2]["date"] = args[argdate]
      else
        ulx[argurl2]["date"] = inlineRed("[Date missing]", "warning")
      end
      argtitle = "title" .. j
      if trimArg(args[argtitle]) then
        ulx[argurl2]["title"] = args[argtitle]
      else
        ulx[argurl2]["title"] = nil
      end
      j = j + 1
    end
  end

  if j == 2 then
    return 0
  else
    return j - 2
  end

end

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

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

  ]]

local function comma(date)
  local N = mw.text.split(date, " ")
  local O = mw.text.split(N[1], "-") -- for ISO
  if O[1] == "index" then return "" end
  if not tonumber(O[1]) then
    return ","
  else
    return ""
  end
end

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

     Return data in track[] ie. tracking categories

  ]]

local function createTracking()

  local sand = ""
  if tableLength(track) > 0 then                        
    for key,_ in pairs(track) do
      sand = sand .. "[[" .. key .. "]]"
    end
  end
  return sand

end

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

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

  ]]

local function createRendering()

    local sand, displayheader, displayfield

    local period1 = ""   -- For backwards compat with {{wayback}}
    local period2 = "."                                                            
  
    local indexstr = "archived"
    if ulx.url1.date == "index" then
      indexstr = "archive"
    end  
                                                                                          -- For {{wayback}}, {{webcite}}

    if ulx.url1.format == "none" then                                                     
      if not ulx.url1.title and not ulx.url1.date then                                    -- No title. No date
        sand = "[" .. ulx.url1.url .. " Archived]" .. ulx.url1.tail
      elseif not ulx.url1.title and ulx.url1.date then                                    -- No title. Date.
        if ulx.url1.service == "wayback" then 
          period1 = "."
          period2 = "" 
        end
        sand = "[" .. ulx.url1.url .. " Archived] " .. ulx.url1.date .. comma(ulx.url1.date) .. ulx.url1.tail .. period1
      elseif ulx.url1.title and not ulx.url1.date then                                    -- Title. No date.
        sand = "[" .. ulx.url1.url .. " " .. ulx.url1.title .. "]" .. ulx.url1.tail
      elseif ulx.url1.title and ulx.url1.date then                                        -- Title. Date.
        sand = "[" .. ulx.url1.url .. " " .. ulx.url1.title .. "]" .. ulx.url1.tail .. "&#32;(" .. indexstr .. " " .. ulx.url1.date .. ")"
      else
        return nil
      end
      if ulx.url1.extraurls > 0 then                                                      -- For multiple archive URLs
        local tot = ulx.url1.extraurls + 1
        sand = sand .. period2 .. " Additional archives: "
        for i=2,tot do
          local indx = "url" .. i
          if ulx[indx]["title"] then 
            displayfield = "title"
          else
            displayfield = "date"
          end
          sand = sand .. "[" .. ulx[indx]["url"] .. " " .. ulx[indx][displayfield] .. "]"
          if i == tot then
            sand = sand .. "."
          else
            sand = sand .. ", "
          end
        end
      else
        return sand  
      end
      return sand
                                                                                          -- For {{cite archives}}

    else                                                                  
      if ulx.url1.format == "addlarchives" then                           -- Multiple archive services 
        displayheader = "Additional archives: "
      else                                                                -- Multiple pages from the same archive 
        displayheader = "Additional pages archived&nbsp;on " .. ulx.url1.date .. ": "
      end
      local tot = 1 + ulx.url1.extraurls
      local sand = displayheader
      for i=1,tot do
        local indx = "url" .. i
        displayfield = ulx[indx]["title"]
        if ulx.url1.format == "addlarchives" then
          if not displayfield then 
            displayfield = ulx[indx]["date"]
          end
        else
          if not displayfield then 
            displayfield = "Page " .. i
          end
        end
        sand = sand .. "[" .. ulx[indx]["url"] .. " " .. displayfield .. "]"
        if i == tot then
          sand = sand .. "."
        else
          sand = sand .. ", "
        end
      end
      return sand
    end
end

function p.webarchive(frame)
  args = frame.args
  if (args[1]==nil) and (args["url"]==nil) then           -- if no argument provided than check parent template/module args
    args = frame:getParent().args 
  end
 
  local tname = "Webarchive"                              -- name of calling template. Change if template rename.
  ulx = {}                                                -- Associative array to hold template data 
  track = {}                                              -- Associative array to hold tracking categories
  maxurls = 10                                            -- Max number of URLs allowed. 
  local verifydates = "yes"                               -- See documentation. Set "no" to disable.

                                                          -- URL argument (first)

  local url1 = trimArg(args.url) or trimArg(args.url1)           
  if not url1 then
    return inlineError("url", "Empty.") .. createTracking()
  end
  if mw.ustring.find( url1, "https://web.http", 1, plain ) then    -- track bug 
    track["Category:Webarchive template errors"] = 1 
    return inlineError("url", "https://web.http") .. createTracking()
  end 
  if url1 == "https://web.archive.org/http:/" then                 -- track bug
    track["Category:Webarchive template errors"] = 1 
    return inlineError("url", "Invalid URL") .. createTracking()
  end

  ulx.url1 = {}
  ulx.url1.url = url1
  local uri1 = mw.uri.new(ulx.url1.url)
  ulx.url1.host = uri1.host
  ulx.url1.extraurls = parseExtraArgs()

                                                          -- Nolink argument 

  local nolink = trimArg2(args.nolink)

  serviceName(uri1.host, nolink)

                                                          -- Date argument

  local date = trimArg(args.date) or trimArg(args.date1)
  if date == "*" and ulx.url1.service == "wayback" then
    date = "index"
  elseif date and ulx.url1.service == "wayback" and verifydates == "yes" then 
    local ldf = dateFormat(date)
    if ldf then
      local udate = decodeWaybackDate( uri1.path, ldf )
      if udate ~= date then
        date = udate .. inlineRed("<sup>[Date mismatch]</sup>", "warning")       
      end
    end
  elseif date and ulx.url1.service == "webcite" and verifydates == "yes" then 
    local ldf = dateFormat(date)
    if ldf then
      local udate = decodeWebciteDate( uri1.path, ldf )
      if udate == "query" then -- skip
      elseif udate ~= date then
        date = udate .. inlineRed("<sup>[Date mismatch]</sup>", "warning")      
      end
    end
  elseif not date and ulx.url1.service == "wayback" then
    date = decodeWaybackDate( uri1.path, "iso" )
    if not date then 
      date = inlineRed("[Date error] (1)", "error") 
    end
  elseif not date and ulx.url1.service == "webcite" then
    date = decodeWebciteDate( uri1.path, "iso" )
    if date == "query" then
      date = inlineRed("[Date missing]", "warning")
    elseif not date then 
      date = inlineRed("[Date error] (1)", "error")
    end
  elseif not date then
    date = inlineRed("[Date missing]", "warning")
  end
  ulx.url1.date = date

                                                          -- Format argument 

  local format = trimArg(args.format)
  if not format then
    format = "none"
  else
    if format == "addlpages" then
      if not ulx.url1.date then
        format = "none"
      end
    elseif format == "addlarchives" then
      format = "addlarchives"
    else
      format = "none"
    end
  end
  ulx.url1.format = format

                                                          -- Title argument 

  local title = trimArg(args.title) or trimArg(args.title1)
  ulx.url1.title = title
  

  local rend = createRendering()
  if not rend then
    rend = '<span style="font-size:100%" class="error citation-comment">Error in [[:टेम्पलेट:' .. tname .. ']]: Unknown problem. Please report on template talk page.</span>'
    track["श्रेणी:Webarchive template errors"] = 1 
  end

  return rend .. createTracking()

end

return p