local p = { } -- Package to be exported
local getArgs = require('Module:Arguments').getArgs -- Import module function to work with passed arguments
local lang = mw.getContentLanguage() -- Retrieve built-in locale for date formatting
local format = mw.ustring.format -- String formatting function
local frame = mw.getCurrentFrame()
local routeStates = { } -- Table with route statuses.
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.
]]--
routeStates.current = {row = "|- \n", removed = "current"} -- Data for current routes
routeStates.future = {row = '|- style="background-color:#ffdead;" title="Future route"\n', established = "proposed", removed = "–—"} -- Data for future routes
routeStates.former = {row = '|- style="background-color:#d3d3d3;" title="Former route"\n'} -- Data for former routes
routeStates.unknownformeroverride = {row = "'|- \nstyle="background-color:#d3d3d3;" title="Former route"', removed = "—"} -- Data for routeroutes marked as withformer unknownby statusoverride
routeStates.unknown = {row = "|-", removed = "—"} -- Data for route with unknown status
function getRouteState(argsestablished, decommissioned)
--[[ This function is passed the dates given for the established and decommissioned fields to the template.
It then returns the entry in the routeStates table corresponding to the status of the route.
]]--
if decommissioned == 'yes' then --If the decommissioned property just says "yes", then mark it as a former route and display default data.
local established = args.established -- Date passed to the module via the "established" parameter of {{routelist row}}
return routeStates.formeroverride
local decommissioned = args.decommissioned -- Date passed to the module via the "decommissioned" parameter of {{routelist row}}
if elseif decommissioned ~= '' then -- If the route is decommissioned, then it must be a former route.
return routeStates.former
elseif elseifnot established == '' then -- Without the establishment date, there is not enough information to determine the status of the route.
return routeStates.unknown
elseif established == 'proposed' then -- If the "established date" is the string 'proposed', then it must be a future route.
return routeStates.future
else -- If none of the first three conditions are true, then it must be a current route.
return routeStates.current
end
end
function getLengthdtsYearCore(argsdate, circa)
-- A limited replacement --for This{{dts}}. functionThis is passed thea lengthdate fieldsand derives a sort key from it. It returns a string with the {{routelisthidden row}}sort invocationkey, andalong calculateswith the missingyear lengthof fromthe theoriginal otherdate.
if not date then return false end -- If the date is an empty string, stop and go back to whence it came.
local math = require "Module:Math" -- This module contains functions needed later in this function.
local year = lang:formatDate('Y', date) -- This invocation of lang:formatDate returns just the year.
local precision = math._precision -- The math._precision function provides the precision of a given string representing a number.
if year == date then -- If the provided date is just the year:
local round = math._round -- This method rounds a given number to the given number of digits. In Lua, storing these functions locally results in more efficient execution.
date = date .. "-01-01" -- Tack on January 1 for the sort key to work right.
local length = {} -- This table will store the computed lengths.
end
local km = args["length_km"] -- The kilometer length from the {{routelist row}} call.
local month = lang:formatDate('m', date) -- Stores the month of the date.
local mi = args["length_mi"] -- The length in miles as passed to {{routelist row}}.
local day = lang:formatDate('d', date) -- Stores the day for this date.
if '' == km then -- This signifies that a length in kilometers was not passed.
local dtsStr = string.format("%05d-%02d-%02d", year, month, day) -- Create and store the formatted hidden sort key. The year must be five digits, per convention.
local n = tonumber(mi) -- The first step is to convert the miles (passed as a string from the template) into a number.
local spanParams = {style = "display:none; speak:none"} -- These CSS properties hide the sort key from normal view.
local prec = precision(mi) -- Retrieve the precision of the passed mile value (as a string).
local dtsSpan = mw.text.tag({name='span', content=dtsStr, attrs=spanParams}) -- This generates the HTML code necessary for the hidden sort key.
if n then -- If the passed mile value is an empty string, n will equal nil, which would make this statement false. Otherwise, the length in kilometers is computed and stored.
if circa == 'yes' then -- If the date is tagged as circa,
length.km = round(n * 1.609344, prec) -- Compute and round the length in kilometers, and store it in the length table.
return dtsSpan .. "<abbr title=\"circa\">c.</abbr><span style=\"white-space:nowrap;\"> " .. year .. "</span>" -- Add the circa abbreviation to the display. Derived from {{circa}}
else -- No mile value was passed
else -- Otherwise,
length.km = 0
return dtsSpan .. year -- Return the hidden sort key concatenated with the year for this date.
end
end
else -- If the length in kilometers was passed, the computed lengths table will simply contain the passed length.
length.km = km
end
if '' == mi then -- The same as above, but this time converting kilometers to mile if necessary.
local n = tonumber(km) -- Kilometers as a number
local prec = precision(km) -- Precision of the passed length
if n then -- If a kilometer value was passed:
length.mi = round(n / 1.609344, prec) -- Compute and store the conversion into miles.
else -- If not:
length.mi = 0 -- Store 0.
end
else -- And if the length in miles was passed:
length.mi = mi -- Simply store it.
end
return length -- Return the length table with the computed lengths.
end
function dtsYear(date, circa)
local success, result = pcall(dtsYearCore, date, circa)
-- A limited replacement for {{dts}}. This is passed a date and derives a sort key from it. It returns a string with the hidden sort key, along with the year of the original date.
if success then
if date == '' then return false end -- If the date is an empty string, stop and go back to whence it came.
return result
local year = lang:formatDate('Y', date) -- This invocation of lang:formatDate returns just the year.
else
if year == date then -- If the provided date is just the year:
return string.format('%s<span class="error">Error: Invalid date "%s".</span>', circa and '<abbr title="circa">c.</abbr> ' or '', date)
date = date .. "-01-01" -- Tack on January 1 for the sort key to work right.
end
local month = lang:formatDate('m', date) -- Stores the month of the date.
local day = lang:formatDate('d', date) -- Stores the day for this date.
local dtsStr = string.format("%05d-%02d-%02d", year, month, day) -- Create and store the formatted hidden sort key. The year must be five digits, per convention.
local spanParams = {style = "display:none; speak:none"} -- These CSS properties hide the sort key from normal view.
local dtsSpan = mw.text.tag({name='span', contents=dtsStr, params=spanParams}) -- This generates the HTML code necessary for the hidden sort key.
if circa == 'yes' then -- If the date is tagged as circa,
return dtsSpan .. "<abbr title=\"circa\">c.</abbr><span style=\"white-space:nowrap;\"> " .. year .. "</span>" -- Add the circa abbreviation to the display. Derived from {{circa}}
else -- Otherwise,
return dtsSpan .. year -- Return the hidden sort key concatenated with the year for this date.
end
end
function removed(routeState, decommissioned, circa)
-- This function returns the proper value for the removed column.
return routeState.removed or dtsYear(decommissioned, circa) -- Returns the removed attribute of the provided routeState table or, if empty, the dtsYear-formatted decommissioned date.
end
function formed(routeState, established, circa)
-- This function returns the proper value for the formed column.
return routeState.established or dtsYear(established, circa) or "—" -- Returns 'proposed' if the route is proposed, the dtsYear-formatted established date if one was provided, or an em-dash.
end
function sortkey(Key, Type, Routeargs)
-- This function return the sort key for the route (not to be confused with the previous function, which generates a sort key for the established and decommissioned dates.)
local key = args.sortkey
if Key ~= '' then -- If a sort key already exists:
local type = args.type
return Key -- Simply return it.
local route = args.route or ''
else -- Otherwise:
if key then -- If a sort key already exists:
local PaddedRoute = string.format('%04d', Route) -- This invocation is equivalent to the {{0000expr}} template. It zero-pads the given route number up to 4 digits.
return key -- Simply return it.
return Type .. PaddedRoute -- Return the sort key for this route, composed of the type and zero-padded route number.
else -- Otherwise:
end
local routeKey
local routeNum = tonumber(route)
if routeNum then
routeKey = string.format('%04d', route) -- This invocation is equivalent to the {{0000expr}} template. It zero-pads the given route number up to 4 digits.
else
local num, suffix = string.match(route, "(%d*)(.+)")
routeKey = (tonumber(num) and string.format('%04d', num) or '') .. suffix
end
return type .. routeKey -- Return the sort key for this route, composed of the type and zero-padded route number.
end
end
function p.rowcolortermini(frameargs)
-- This function determines if this is a future route, present route,beltway or decommissioned routenot, and colorsdisplays the rowtermini columns appropriately.
local beltway = args["beltway"] -- Text in this parameter will span both termini columns.
local pframe = frame:getParent()
local terminus_a = args["terminus_a"] or '—' -- Southern or western terminus
local config = frame.args -- the arguments passed BY the template, in the wikitext of the template itself
local terminus_b = args["terminus_b"] or '—' -- Northern or eastern terminus
local args = pframe.args -- the arguments passed TO the template, in the wikitext that transcludes the template
if beltway then
-- Import parameters from template
return "|colspan=2 align=center|" .. beltway -- This text will, again, span both columns.
local established = args["established"] or '' -- Date established
else
local decommissioned = args["decommissioned"] or '' -- Date decommissioned
return '|' .. terminus_a .. '||' .. terminus_b -- Fill in the termini columns
end
local dates = {["established"] = established, ["decommissioned"] = decommissioned} -- This table encapsulates the given dates into one value...
local routeState = getRouteState(dates) -- which is passed to getRouteState, which returns one of the entries in routeStates,
return routeState.row -- whose row attribute is returned
end
function dates(established, decommissioned, routeState, args)
function p.termini(frame)
-- This function determines if this is a beltway or not, and displays the terminidate columns appropriately.
local pframe = frame:getParent()
local config = frame.args -- the arguments passed BY the template, in the wikitext of the template itself
if args.gazette == 'yes' then
local args = pframe.args -- the arguments passed TO the template, in the wikitext that transcludes the template
local established = args.established or "—"
local established_ref = args.established_ref or ''
-- Import parameters from template
local beltway = args["beltway"] or '' -- Text in this parameter will span both termini columns.
return "|align=center|" .. established .. established_ref
local terminus_a = args["terminus_a"] or '—' -- Southern or western terminus
else
local terminus_b = args["terminus_b"] or '—' -- Northern or eastern terminus
local established_ref = args.established_ref or '' -- Reference for date established
local decommissioned_ref = args.decommissioned_ref or '' -- Reference for date decommissioned
if beltway ~= '' then -- The template passes this function an empty string if {{{beltway}}} is not specified by the template user.
return "|colspan=2 align=center|" .. beltway -- This text willformed(routeState, againestablished, spanargs.circa_established) both columns..
established_ref .. "||align=center|" .. removed(routeState, decommissioned, args.circa_decommissioned) ..
else
decommissioned_ref
return '|' .. terminus_a .. '||' .. terminus_b -- Fill in the termini columns
end
end
--- Return output for the length columns for a given route, with the appropriate conversions.
function p.dates(frame)
-- Thislocal function displays the date columns.length(args)
local km = args["length_km"] or '' -- Length in kilometers
local pframe = frame:getParent()
local mi = args["length_mi"] or '' -- Length in miles
local config = frame.args -- the arguments passed BY the template, in the wikitext of the template itself
local ref = args["length_ref" ] or ''
local args = pframe.args -- the arguments passed TO the template, in the wikitext that transcludes the template
-- Import parameters from template
local established = args.established or '' -- Date established
local decommissioned = args.decommissioned or '' -- Date decommissioned
local dates = {["established"] = established, ["decommissioned"] = decommissioned} -- This table encapsulates the given dates into one value...
local routeState = getRouteState(dates) -- which is passed to getRouteState, which returns one of the entries in routeStates,
local output = "|align=center|" .. formed(routeState, established, args.circa_established) .. "||align=center|" .. removed(routeState, decommissioned, args.circa_decommissioned) -- which is passed to the removed function. This line generates the Wikitext for this part of the row.
return output
end
if mi == '' and km == '' then
function p.length(frame)
return format("|align=right|—||align=right|—")
-- This function generate the length columns, with the appropriate conversions.
elseif mi ~= '0' and km == '' then
local pframe = frame:getParent()
return format("|align=right|") .. mi .. ref .. format("||align=right|") .. frame:expandTemplate{ title = 'convert', args = { mi, "mi", "km", disp = "output number only"}}
local config = frame.args -- the arguments passed BY the template, in the wikitext of the template itself
else
local args = pframe.args -- the arguments passed TO the template, in the wikitext that transcludes the template
return format("|align=right|") .. km .. ref .. format("||align=right|") .. frame:expandTemplate{ title = 'convert', args = { km, "km", "mi", disp = "output number only"}}
end
local miles = args["length_mi"] or '' -- Length in miles
local kilometers = args["length_km"] or '' -- Length in kilometers
local lengths = {length_mi = miles, length_km = kilometers} -- This time, we compile the lengths into a table,
local Lengths = getLength(lengths) -- which makes for an easy parameter. This function call will return the lengths in both miles and kilometers,
local lengthRef = args["length_ref"] or ''
local first, second
if kilometers ~= '' then
first = Lengths.km
second = Lengths.mi
else
first = Lengths.mi
second = Lengths.km
end
return "|align=right|" .. first .. lengthRef .. "||align=right|" .. second -- which are then spliced in here and returned to the template.
end
function p.sortkeylocalname(frameargs)
-- This function generates thea "Local names" route'scell sortif keynecessary
local enabled = args[1] or ''
local pframe = frame:getParent()
local localName = args["local"] or ''
local config = frame.args -- the arguments passed BY the template, in the wikitext of the template itself
if mw.text.trim(enabled) == "local" then
local args = pframe.args -- the arguments passed TO the template, in the wikitext that transcludes the template
return "|" .. localName
else
local SortKey = args["sortkey"] or '' -- If a sort key is given, store it here.
return ''
local Type = args["type"] or '' -- The route type (Interstate, State highway, etc.)
end
local Route = args["route"] or '' -- The route number.
return sortkey(SortKey, Type, Route) -- And then return the sort key from this function to the template.
end
function p.localnamenotes(framenotes)
-- This function generates a "Local namesNotes" cell if necessary.
if notes == 'none' then
local pframe = frame:getParent()
return '| ' --create empty cell
local config = frame.args -- the arguments passed BY the template, in the wikitext of the template itself
elseif notes then
local args = pframe.args -- the arguments passed TO the template, in the wikitext that transcludes the template
return '|' .. notes --display notes in cell
else
local enabled = args[1]
return '' --create no cell
local localName = args["local"] or ''
end
if enabled == "local" then
return "|" .. localName
else
return ''
end
end
function p.notesgap(frameargs)
local text = args.text or "''Number not designated''"
-- This function generates a "Notes" cell if necessary.
local pframe = frame:getParent()
if notes then
local config = frame.args -- the arguments passed BY the template, in the wikitext of the template itself
return '|align=center colspan=7|' .. text --display notes in cell
local args = pframe.args -- the arguments passed TO the template, in the wikitext that transcludes the template
else
return '|align=center colspan=6|' .. text --display notes in cell
local notes = args["notes"] or ''
end
if notes == 'none' then
return '| ' --create empty cell
elseif notes ~= '' then
return '|' .. notes --display notes in cell
else
return '' --create no cell
end
end
function p.route(frameargs)
-- This function displays the shield and link.
local pframe = frame:getParent()
local config = frame.args -- the arguments passed BY the template, in the wikitext of the template itself
local args = pframe.args -- the arguments passed TO the template, in the wikitext that transcludes the template
local format = mw.ustring.format
local parserModule = require "Module:Road data/parser"
local parser = parserModule.parser
local doubleShieldTypesnoshield = {Both = true}args.noshield
local bannerFile = parser(args, 'banner')
local banner
if not noshield and bannerFile and bannerFile ~= '' then
local widthCode = parser(args, 'width') or 'square'
if widthCode == 'square' then
banner = format("[[File:%s|25px|link=|alt=]]", bannerFile)
elseif widthCode == 'expand' then
local route = args.route
if #route >= 3 then
banner = format("[[File:No image.svg|3px|link=|alt=]][[File:%s|25px|link=|alt=]][[File:No image.svg|3px|link=|alt=]]", bannerFile)
else
banner = format("[[File:%s|25px|link=|alt=]]", bannerFile)
end
elseif widthCode == 'wide' then
banner = format("[[File:No image.svg|3px|link=|alt=]][[File:%s|25px|link=|alt=]][[File:No image.svg|3px|link=|alt=]]", bannerFile)
elseif widthCode == 'MOSupp' then
local route = args.route
if #route >= 2 then
banner = format("[[File:No image.svg|3px|link=|alt=]][[File:%s|25px|link=|alt=]][[File:No image.svg|3px|link=|alt=]]", bannerFile)
else
banner = format("[[File:%s|25px|link=|alt=]]", bannerFile)
end
elseif widthCode == 'US1926' then
banner = format("[[File:%s|25px|link=|alt=]][[File:No image.svg|1px|link=|alt=]]", bannerFile)
elseif args.state == 'CA' then
local route = args.route
local type = args.type
if type == 'US-Bus' then
if #route >= 3 then
banner = format("[[File:No image.svg|2px|link=|alt=]][[File:%s|25px|link=|alt=]][[File:No image.svg|2px|link=|alt=]]", bannerFile)
else
banner = format("[[File:%s|25px|link=|alt=]]", bannerFile)
end
elseif type == 'CA-Bus' or type == 'SR-Bus' then
if #route >= 3 then
banner = format("[[File:No image.svg|1px|link=|alt=]][[File:%s|25px|link=|alt=]][[File:No image.svg|2px|link=|alt=]]", bannerFile)
else
banner = format("[[File:%s|24px|link=|alt=]]", bannerFile)
end
end
end
banner = banner .. '<br>'
else
banner = ''
end
local shield
if doubleShieldTypes[args.type]not noshield then
local shieldFile1shieldFile, second = parser(args, 'shieldshieldlist') or parser(args, 1'shield') or ''
if shieldFile == nil or shieldFile == '' then
local shield1 = format("[[File:%s|x25px|alt=|link=]]", shieldFile1)
shield = ''
local shieldFile2 = parser(args, 'shield', 2)
elseif type(shieldFile) == 'table' then
local shield2 = format("[[File:%s|x25px|alt=|link=]]", shieldFile2)
shieldFile, second = shieldFile[1], shieldFile[2]
shield = shield1 .. shield2
end
if second and type(second) == 'string' then
local shield1 = format("[[File:%s|x25px|alt=|link=]]", shieldFile)
local shield2 = format("[[File:%s|x25px|alt=|link=]]", second)
shield = shield1 .. shield2
elseif shield == '' then
shield = ''
else
shield = shieldFile and format("[[File:%s|x25px|alt=|link=]]", shieldFile) or ''
end
else
shield = ''
local shieldFile = parser(args, 'shield')
shield = shieldFile and format("[[File:%s|x25px|alt=|link=]]", shieldFile) or ''
end
local linkTarget = (not args.nolink) and parser(args, 'link')
local abbr = parser(args, 'abbr')
local link
link = abbr
end
if not link then error("Type not in database.: ", 0.. args.type) end
local sortkey = p.sortkey(frameargs)
local sortedLink = format("<span styledata-sort-value=\"display:none\" class=\"sortkey\">%s !</span><span class=\"sorttext\">%s</span>", sortkey, link)
local route = banner .. shield .. ' ' .. sortedLink
return '|' .. route
end
function p.row(frame)
local args = getArgs(frame) -- Gather passed arguments into easy-to-use table
local established = args.established
local decommissioned = args.decommissioned
local routeState = getRouteState(established, decommissioned)
local anchor = args.anchor or sortkey(args)
local rowdef = routeState.row .. string.format(' id="%s"', anchor)
local route = route(args)
local length = length(args)
local termini = termini(args)
local localname = localname(args)
local dates = dates(established, decommissioned, routeState, args)
local notesArg = args.notes
local notes = notes(notesArg)
local row = {rowdef, route, length, termini, localname, dates, notes}
return table.concat(row, '\n')
end
function p.gap(frame)
local args = getArgs(frame) -- Gather passed arguments into easy-to-use table
local routeState = getRouteState(established, decommissioned)
local anchor = args.anchor or sortkey(args)
local rowdef = routeState.row .. string.format(' id="%s"', anchor)
local route = route(args)
local gap = gap(args)
local row = {rowdef, route, gap}
return table.concat(row, '\n')
end
|