Module:Routelist row/sandbox 2
Implements {{Routelist row}}
local p = {} -- Package to be exported
-- Change to "" upon deployment.
local moduleSuffix = ""
local lang = mw.getContentLanguage() -- Built-in locale for date formatting
local format = mw.ustring.format -- String formatting function
local insert = table.insert
local concat = table.concat
local util = require("Module:Road data/util")
local frame = mw.getCurrentFrame()
local parserModuleName = "Module:Road data/parser" .. moduleSuffix
local statenameModuleName = "Module:Jct/statename" .. moduleSuffix -- TODO transition
local concat = table.concat
local insert = table.insert
local format = mw.ustring.format
local trim = mw.text.trim
local parserModule = require(parserModuleName)
local parser = parserModule.parser
-- Shields
local 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, {shield, shieldSize})
end
end
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, {filename, bannersize})
end
elseif banner ~= '' then
insert(banners, {banner, bannerSize})
end
return banners
end
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, {
shield = {filename, size},
banners = bannerSpec(shieldBanner, shieldBannerSize, shieldBannerSuffix, route)
})
end
elseif shield ~= '' then
if shieldsize == "" then shieldsize = nil end
insert(shieldSpec, {
shield = {shield, shieldsize},
banners = bannerSpec(banner, bannersize, bannersuffix, route)
})
end
return shieldSpec
end
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("[[File:%s|%spx|link=|alt=]]", shield[1], size)
if not banners[1] then return shieldCode end
for _,banner in ipairs(banners) do
shieldCode = format("[[File:%s|%spx|link=|alt=]]<br>%s",
banner[1],
defaultShieldSize,
shieldCode)
end
return '<span style="display: inline-block; vertical-align: baseline; line-height: 0; text-align: center;">' .. shieldCode .. '</span>'
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), missingShields
end
-- Links/abbreviations
function 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|%s]]", link, abbr)
end
end
end
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
--------------------------
--[[-
@type status
@field #string row: The start of the row, for this particular type (color)
@field #string established: The string to be output in the "Formed" column.
For future routes, "proposed" is displayed here.
Otherwise, display the year passed in the established parameter.
@field #string removed: The string to be output in the "Removed" column.
In the case of routeStates.former, the year that the route was
decommissioned is output instead.
]]
--[[-
Route statuses.
@list <#status>
]]
local routeStatuses = {
-- current routes
current = {
row = "|-",
removed = "current"
},
-- future routes
future = {
row = '|- style="background-color:#ffdead;" title="Future route"',
established = "proposed",
removed = "—"
},
-- former routes
former = {
row = '|- style="background-color:#d3d3d3;" title="Former route"'
},
-- routes marked as former by override
-- deprecated
formeroverride = {
row = '|- style="background-color:#d3d3d3;" title="Former route"',
removed = "—"
},
-- route with unknown status
unknown = {
row = "|-",
removed = "—"
}
}
--[[-
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
end
end
--[[-
A limited replacement for {{dts}}.
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 derived
]]
local 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 {dtsStr, year}
end
local function dtsYear(date, circa)
local success, result = pcall(dtsYearCore, date)
if not success then
result = {
"00001-01-01",
util.err(format('Invalid date "%s".', date))
}
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 {{circa}}.
year = "<span style=\"white-space:nowrap;\"><abbr title=\"circa\">c.</abbr> " .. year .. "</span>"
end
return dtsStyle, year
end
--- 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
end
end
--- 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{ title = 'convert', args = { mi, "mi", "km", disp = "table"}}
else
return format("|") .. frame:expandTemplate{ title = 'convert', args = { km, "km", "mi", disp = "table"}}
end
end
--- 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 ''
end
end
--- 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
end
end
--- 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)
end
end
--- 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 = {rowdef, route, length, termini, localname, dates, notes}
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
Content Disclaimer
Informasi ini disarikan dari Wikipedia dan disajikan kembali untuk tujuan edukasi. Konten tersedia di bawah lisensi CC BY-SA 3.0. Kami tidak bertanggung jawab atas ketidakakuratan data yang bersumber dari kontribusi publik tersebut.
- The information displayed on this website is sourced in part or in whole from Wikipedia and has been adapted for the purpose of restating it. We strive to provide accurate and relevant information, however:
- There is no guarantee of absolute accuracy. Wikipedia is an open, collaborative project that can be edited by anyone, so information is subject to change.
- It is not intended to constitute professional advice. The content displayed is for informational and educational purposes only. For important decisions (e.g., medical, legal, or financial), please consult a professional.
- Content copyright. Wikipedia is licensed under the Creative Commons Attribution-ShareAlike License (CC BY-SA). This means that content may be reused with appropriate attribution and shared under a similar license.
- Responsible use. Any risk arising from the use of information from this website is entirely the responsibility of the user.