Module:Routelist row/sandbox 2 explained

local p = -- Package to be exported

-- Change to "" upon deployment.local moduleSuffix = ""

local lang = mw.getContentLanguage -- Built-in locale for date formattinglocal format = mw.ustring.format -- String formatting functionlocal insert = table.insertlocal concat = table.concatlocal util = require("Module:Road data/util")local frame = mw.getCurrentFrame

local parserModuleName = "Module:Road data/parser" .. moduleSuffixlocal statenameModuleName = "Module:Jct/statename" .. moduleSuffix -- TODO transition

local concat = table.concatlocal insert = table.insertlocal format = mw.ustring.formatlocal trim = mw.text.trim

local parserModule = require(parserModuleName)local parser = parserModule.parser

-- Shieldslocal defaultShieldSize = 28

local function addContextBanner(args, name, suffix, bannerSpec) local bannerModule = 'Module:Road data/banners/' .. string.upper(args.country) local shieldfield = name .. 'shield' local shield = parser(args, shieldfield) if shield

nil then -- This route type does not define shield. -- Find shield in the default banner table. shield = parser(args, 'shield', name, bannerModule) if shield and shield ~= then if suffix

nil then suffix = parser(args, 'shield', 'suffix', bannerModule) end if suffix and suffix ~= then shield = shield .. " " .. suffix end shield = shield .. ".svg" end end if shield and shield ~= then local shieldSize = defaultShieldSize -- Add banner plate. insert(bannerSpec,) endend

local function bannerSpec(banner, bannerSize, bannerSuffix, route) local banners = if type(banner)

"table" then local bannerSizeIsNotTable = type(bannerSize) ~= "table" for i,filename in ipairs(banner) do local bannersize = bannerSizeIsNotTable and bannerSize or bannerSize[i] or defaultShieldSize insert(banners,) end elseif banner ~= then insert(banners,) end

return bannersend

local function shieldSpec(args, mainShield, shieldList) local shieldSpec =

local shield

if not shield then shield = parser(args, 'shieldlist') or parser(args, 'shield') or end if shield

then return shieldSpec end local orientation = parser(args, 'orientation')

local function size(args) if orientation

"upright" then return defaultShieldSize else return "x" .. defaultShieldSize end end local shieldsize = size(args) local banner = parser(args, 'banner') or local bannersize = defaultShieldSize local bannersuffix = parser(args, 'bannersuffix')

local bannerIsNotTable = type(banner) ~= "table" local bannersizeIsNotTable = type(bannersize) ~= "table" local bannersuffixIsNotTable = type(bannersuffix) ~= "table"

if type(shield)

"table" then for i,filename in ipairs(shield) do local size = shieldsize[i] or shieldsize if size

"" then size = nil end -- banner.all describes banners that apply to all multiple shields. local shieldBanner = bannerIsNotTable and banner or (banner[i] or banner.all or) -- Banner size is default if the corresponding entry -- in bannerSize table is not set. local shieldBannerSize = bannersizeIsNotTable and bannersize or (bannersize[i] or bannersize.all or defaultShieldSize) local shieldBannerSuffix = bannersuffix and (bannersuffixIsNotTable and bannersuffix or bannersuffix[i]) insert(shieldSpec,) end elseif shield ~= then if shieldsize

"" then shieldsize = nil end insert(shieldSpec,) end

return shieldSpecend

local missingShields

local shieldExistsCache =

local function render(shieldEntry, scale, showLink) local shield = shieldEntry.shield local banners = shieldEntry.banners

local size if shield[2] then local width, height = mw.ustring.match(shield[2], "(%d*)x?(%d*)") width = tonumber(width) height = tonumber(height) local sizeparts = if width then insert(sizeparts, format("%d", width * scale)) end if height then insert(sizeparts, format("x%d", height * scale)) end size = concat(sizeparts) else size = format("%s%d", landscape and "x" or "", defaultShieldSize * scale) end local shieldCode = format("", shield[1], size) if not banners[1] then return shieldCode end

for _,banner in ipairs(banners) do shieldCode = format("
%s", banner[1], defaultShieldSize, shieldCode) end return '

' .. shieldCode .. ''end

function p.shield(args, scale, showLink, mainShield, shieldList) missingShields =

scale = scale or 1

local rendered = for _,entry in ipairs(shieldSpec(args, mainShield, shieldList)) do insert(rendered, render(entry, scale, showLink)) end return concat(rendered), missingShieldsend

-- Links/abbreviationsfunction p.link(args) local nolink = args.nolink local abbr = parser(args, 'abbr') if nolink then return abbr else local link = parser(args, 'link') if not link or link

then return abbr else return format("%s", link, abbr) end endend

local function stateName(args) -- TODO transition local data = mw.loadData(statenameModuleName) local abbr = args.state or args.province local countryData = data[args.country] return countryData and countryData[abbr]end

-------------------------------Route statuses.@list <#status>local routeStatuses =

---Return the route status.@param #string established `established` argument passed to the module@param #string decommissioned `decommissioned` argument passed to the module@return #status the status of the route.local function getRouteStatus(established, decommissioned) if decommissioned

'yes' then -- a former route with no decommission information return routeStatuses.formeroverride elseif decommissioned then -- If the route is decommissioned, then it must be a former route. return routeStatuses.former elseif not established then -- Without the establishment date, there is not enough information -- to determine the status of the route. return routeStatuses.unknown elseif established

'proposed' then -- a future route return routeStatuses.future else -- a current route return routeStatuses.current endend

---A limited replacement for .Derive the sort key from a given date.@param #string date@param #string circa "yes" if `date` is tagged as circa@return #string true the hidden sort key, along with the year of the original date@return #boolean false if the sort key cannot be derivedlocal function dtsYearCore(date) local year = lang:formatDate('Y', date) -- year for this date if year

date then -- If the provided date is just the year, -- tack on January 1 for the sort key to work right. date = date .. "-01-01" end local month = lang:formatDate('m', date) -- month for this date local day = lang:formatDate('d', date) -- day for this date -- Create and store the formatted hidden sort key. -- The year must be five digits, per convention. local dtsStr = format("%05d-%02d-%02d", year, month, day) -- Return the hidden sort key and the year for this date. return end

local function dtsYear(date, circa) local success, result = pcall(dtsYearCore, date) if not success then result = end -- Generate the HTML code necessary for the hidden sort key. local dtsStyle = format("style=\"white-space:nowrap;\" data-sort-value=\"%s\"", result[1]) local year = result[2] if circa

'yes' then -- If the date is tagged as circa, -- add the circa abbreviation to the display. Derived from . year = "

c.&thinsp;" .. year .. "" end return dtsStyle, yearend

--- Return formatting and output for a date column.local function date(text, date, circa, ref) -- Returns the text if specified, or the dtsYear-formatted date, and an em-dash. local style, output if text then output = text elseif date then style, output = dtsYear(date, circa) else output = "—" end return format("|align=center %s|%s%s", style or "", output, ref)end

--- Return output for the date columns for a given route.local function dates(established, decommissioned, routeStatus, args) local established_ref = args.established_ref or -- Reference for date established local decommissioned_ref = args.decommissioned_ref or -- Reference for date decommissioned return format("%s\n%s", date(routeStatus.established, established, args.circa_established, established_ref), date(routeStatus.removed, decommissioned, args.circa_decommissioned, decommissioned_ref))end

--- Return output for the termini columns for a given route.local function termini(args) local beltway = args["beltway"] if beltway then -- The given route is a beltway. -- `beltway` text will span both termini columns. return "|colspan=2 align=center|" .. beltway else local terminus_a = args["terminus_a"] or '—' -- Southern or western terminus local terminus_b = args["terminus_b"] or '—' -- Northern or eastern terminus -- Fill in the termini columns return '|' .. terminus_a .. '||' .. terminus_b endend

--- Return output for the length columns for a given route, with the appropriate conversions.local function length(args) local km = args["length_km"] or -- Length in kilometers local mi = args["length_mi"] or -- Length in miles local ref = args["length_ref" ] or

if mi

and km

then return format("|align=right|—||align=right|—") elseif mi ~= '0' and km

then return format("|") .. frame:expandTemplate else return format("|") .. frame:expandTemplate endend

--- Generate a "Local names" cell if necessary.local function localname(args) local enabled = args[1] or if enabled

"local" then local localName = args["local"] or return "|" .. localName else return endend

--- Generate a "Notes" cell if necessary.local function notes(notes) if notes

'none' then return '| ' --create empty cell elseif notes then return '|' .. notes --display notes in cell else return --create no cell endend

--- Derive the sort key from a given route.local function sortkey(abbr) -- Split `abbr` into three possibly empty parts, with number in the middle. local prefix, num, suffix = mw.ustring.match(abbr, "([^0-9]*)(%d*)(.*)") -- If `abbr` does not contain a number, the entry appears at the bottom. num = tonumber(num) num = type(num)

"number" and format("%04d", num) or "" -- The sort key is `abbr`, but with route number zero-padded to 4 digits -- and prefix moved to the end. return mw.text.trim(mw.ustring.gsub(format("%s%s %s", num, suffix, prefix), " ", " "), "- ")end

local function route(args, shieldSize) local link, abbr = p.link(args) -- Use the sort key if already specified. local sortkey = args.sortkey or sortkey(abbr or "") local shield = p.shield(args)

if shield

nil or args.noshield then return format('!scope="row" class="nowrap" data-sort-value="%s"|%s', sortkey, link) else return format('!scope="row" class="nowrap" data-sort-value="%s"|%s %s', sortkey, shield, link) endend

--- Derive the anchor from a given route.local function anchor(routeType, routeNo) -- Split `routeNo` into three possibly empty parts, with number in the middle. local prefix, num, suffix = mw.ustring.match(routeNo, "([^0-9]*)(%d*)(.*)") -- Zero-pad route number to 4 digits if `routeNo` does contain a number. num = tonumber(num) num = type(num)

"number" and format("%04d", num) or "" -- The anchor is the concatenation of `type` and zero-padded `routeNo`. return format("%s%s%s%s", routeType, prefix, num, suffix)end

function p._row(args) local established = args.established local decommissioned = args.decommissioned local routeStatus = getRouteStatus(established, decommissioned) local anchor = args.anchor or anchor(args.type, args.route) local rowdef = format('%s id="%s"', routeStatus.row, anchor) local route = route(args) local length = length(args) local termini = termini(args) local localname = localname(args) local dates = dates(established, decommissioned, routeStatus, args) local notes = notes(args.notes)

local row = return concat(row, '\n')end

function p.row(frame) -- Import module function to work with passed arguments local getArgs = require('Module:Arguments').getArgs local args = getArgs(frame) -- Gather passed arguments into easy-to-use table return p._row(args);end

return p