Difference between revisions of "Module:Jctint/core/sandbox"

From blackwiki
Jump to navigation Jump to search
blackwiki>Chinissai
m (Removed unused new parameter.)
m (50 revisions imported)
 
(4 intermediate revisions by one other user not shown)
Line 8: Line 8:
 
-- Local version of string formatting function
 
-- Local version of string formatting function
 
local format = mw.ustring.format
 
local format = mw.ustring.format
-- Store functions in a local variable to avoid expensive table lookups.
+
-- Local version of string trimming function
local concat = table.concat
+
local trim = mw.text.trim
 +
-- Store this function in a local variable to avoid expensive table lookups.
 
local insert = table.insert
 
local insert = table.insert
-- Road data functions
 
local roadDataModule = require("Module:Road data")
 
-- Road data utility functions
 
local util = require("Module:Road data/util")
 
  
 
-- mw.html object for the generated row
 
-- mw.html object for the generated row
Line 20: Line 17:
 
-- Default row span for all columns (`jspan` = "junction span")
 
-- Default row span for all columns (`jspan` = "junction span")
 
local jspan
 
local jspan
 
+
-- Any error messages produced that will be added to the output
-- Error types
+
local errorMsg = {}
local errorTypes = {
 
convert = {cat = "Category:Jctint template using non-numeric parameter values", prefix = "#"},
 
type = {cat = "Category:Jctint template with invalid type"}
 
}
 
 
 
-- Error messages to be added to the output
 
local errors = {}
 
  
 
-- A specification for self-closing HTML tag.
 
-- A specification for self-closing HTML tag.
Line 40: Line 30:
  
 
-- Import module to convert length.
 
-- Import module to convert length.
 +
local util = require("Module:Road data/util")
 
local lengths = util.convertLengths({[unitdef] = unit})
 
local lengths = util.convertLengths({[unitdef] = unit})
 
if lengths.error then -- An error occurred during conversion.
 
if lengths.error then -- An error occurred during conversion.
errors.convert = lengths.error
+
-- Add the transcluding page to an error tracking category.
 +
local page = mw.title.getCurrentTitle() -- Get transcluding page's title
 +
local pagename = page.prefixedText -- Extract page's full title as string
 +
-- Create category string
 +
local category = format("[[Category:Jctint template using non-numeric parameter values|# %s]]", pagename)
 +
insert(errorMsg, category) -- Add error category to error message table.
 
end
 
end
 
return lengths
 
return lengths
end
 
 
--- Prepare wikitext for a given location cell.
 
local function locnSub(args, config, subType)
 
local locns = {warnings = {}}
 
local origParam
 
local group
 
local subTypeConfig = config[subType .. "_params"] or {subType}
 
if args[subType .. "_special"] then
 
locns[subType] = args[subType .. "_special"]
 
return locns
 
end
 
local paramPrefix = subTypeConfig[#subTypeConfig]
 
if paramPrefix and args[paramPrefix .. "_special"] then
 
locns[subType] = args[paramPrefix .. "_special"]
 
return locns
 
end
 
-- Find parameter.
 
for _,p in ipairs(subTypeConfig) do
 
if args[p] then
 
origParam = p
 
group = false
 
break
 
elseif args[p .. 1] then
 
origParam = p
 
group = true
 
break
 
end
 
end
 
if origParam then
 
local subParts = {}
 
-- Retrieve wikilinks for subdivisions.
 
local num = 1
 
local paramSuffix = group and num or ""
 
while num == 1 or group and args[origParam .. paramSuffix] do
 
local param = origParam
 
local saved = {}
 
saved[param] = args[param] or false
 
args[param] = args[origParam .. paramSuffix]
 
if subType == "sub1" then
 
-- Handle sub1area
 
local sub1area_param = config.sub1area_param or "sub1area"
 
saved.sub1area = args.sub1area or false
 
args.sub1area = args[sub1area_param .. paramSuffix]
 
elseif subType == "sub2" then
 
-- Handle sub1dab and sub2area
 
local sub1dab_param = config.sub1dab_param or "sub1dab"
 
saved.sub1dab = args.sub1dab or false
 
args.sub1dab = args[sub1dab_param .. paramSuffix] or args[sub1dab_param]
 
local sub2area_param = config.sub2area_param or "sub2area"
 
saved.sub2area = args.sub2area or false
 
args.sub2area = args[sub2area_param .. paramSuffix]
 
end
 
while config[param] and config[param].alias do
 
-- Resolve alias and save parameters.
 
for key,value in pairs(config[param].alias) do
 
if key == "param" then
 
saved[value] = args[value] or false
 
args[value] = args[param]
 
param = value
 
else
 
saved[key] = args[key] or false
 
args[key] = value
 
end
 
end
 
end
 
local subTypeCall = roadDataModule.locations(args, "jctint", group)
 
insert(subParts, subTypeCall[subType])
 
util.addAll(locns.warnings, subTypeCall.warnings, true)
 
-- Restore parameters.
 
for key,value in pairs(saved) do
 
args[key] = value or nil
 
end
 
num = num + 1
 
paramSuffix = group and num or ""
 
end
 
if group then
 
if #subParts > 1 then
 
-- Construct wikitext for multiple subs.
 
local textParts = {}
 
insert(textParts, subParts[1])
 
for i = 2, #subParts do
 
insert(textParts, "–")
 
if i % 2 ~= 0 then
 
-- Odd subs after first begin a new line.
 
insert(textParts, "<br>")
 
end
 
insert(textParts, subParts[i])
 
end
 
local groupSuffix = args[origParam .. "_group"] or config[origParam] and config[origParam].group
 
if groupSuffix then
 
insert(textParts,
 
format("%s%s", #subParts % 2 == 0 and "<br>" or " ", groupSuffix))
 
end
 
if #subParts == 2 then
 
local borderSuffix = config.bordersuffix or "border"
 
if borderSuffix ~= "" then
 
insert(textParts, " " .. borderSuffix)
 
end
 
elseif #subParts == 3 then
 
insert(textParts, " tripoint")
 
elseif #subParts == 4 then
 
insert(textParts, " quadripoint")
 
else
 
insert(textParts, " [[Quadripoint#Multipoints of greater numerical complexity|multipoint]]")
 
end
 
locns[subType] = concat(textParts)
 
else
 
locns.warnings.group = "Singleton list for " .. origParam
 
end
 
else
 
locns[subType] = concat(subParts)
 
end
 
end
 
return locns
 
end
 
 
--- Prepare wikitext for location cells.
 
local function locns(args)
 
local parserModule = require(parserModuleName)
 
local config = parserModule.parser(args, "jctrow", " config ") or {}
 
 
local locns = {warnings = {}}
 
 
local regionCall = roadDataModule.locations(args, "jctint")
 
locns.region = regionCall.region
 
util.addAll(locns.warnings, regionCall.warnings, true)
 
 
local indepCityCall = locnSub(args, config, "indep_city")
 
locns.indep_city = indepCityCall.indep_city
 
util.addAll(locns.warnings, indepCityCall.warnings, true)
 
if locns.indep_city then return locns end
 
 
local sub1Call = locnSub(args, config, "sub1")
 
locns.sub1 = sub1Call.sub1
 
util.addAll(locns.warnings, sub1Call.warnings, true)
 
 
local sub2Call = locnSub(args, config, "sub2")
 
locns.sub2 = sub2Call.sub2
 
util.addAll(locns.warnings, sub2Call.warnings, true)
 
 
return locns
 
 
end
 
end
  
Line 203: Line 57:
 
end
 
end
  
-- Prepare text for location cells.
 
local locns = locns(args)
 
 
-- Create cells for regular location columns.
 
-- Create cells for regular location columns.
  
-- Row span for region; must be specified to display a region cell.
+
-- Region, for disambiguation and potentially for display
local regionSpan = args.regionspan
+
local region = args.region
if regionSpan then
+
if region or args.region_special then
row:tag('td') -- Create a region cell
+
-- Row span for region; must be specified to display a region cell.
:attr('rowspan', regionSpan)
+
local regionSpan = args.regionspan
-- Store region text in the cell.
+
if regionSpan then
-- `region_special` argument overrides wikilinked `region` argument.
+
row:tag('td') -- Create a region cell
:wikitext(args.region_special or locns.region)
+
:attr('rowspan', regionSpan)
 +
-- Store region text in the cell.
 +
-- `region_special` argument overrides wikilinked `region` argument.
 +
:wikitext(args.region_special or format("[[%s]]", region))
 +
end
 
end
 
end
 +
 +
-- Primary topic requires no specialization to supplied locations.
 +
local primaryTopic = args.primary_topic ~= 'no'
  
 
-- Note below main text in the next column
 
-- Note below main text in the next column
Line 223: Line 82:
  
 
-- Independent city
 
-- Independent city
local indepCityText = locns.indep_city -- Value to span both subdivision columns.
+
local indepCityText -- Value to span both subdivision columns.
 +
if args.indep_city_special then
 +
indepCityText = args.indep_city_special -- Overrides `indep_city` argument.
 +
elseif args.indep_city then
 +
local indepCity = args.indep_city
 +
local cityLink -- Wikilink for independent city
 +
if primaryTopic then
 +
cityLink = format('[[%s]]', indepCity)
 +
elseif region then
 +
-- Specialize independent city to the region.
 +
cityLink = format('[[%s, %s|%s]]', indepCity, region, indepCity)
 +
end
 +
if cityLink then
 +
indepCityText = "[[Independent city|City]] of " .. cityLink
 +
end
 +
end
 
if indepCityText then -- Display independent city.
 
if indepCityText then -- Display independent city.
 
-- Text alignment of the cell contents, default to "left".
 
-- Text alignment of the cell contents, default to "left".
Line 243: Line 117:
  
 
-- First-level subdivision, e.g., county
 
-- First-level subdivision, e.g., county
local sub1Text = locns.sub1 -- Value for first-level subdivision column.
+
-- Name of the type of subdivision, e.g., "County" and "Parish"
 +
local sub1name = args.sub1name -- check existence later
 +
local sub1Text -- Value for first-level subdivision column.
 +
if args.sub1_special then
 +
sub1Text = args.sub1_special -- Overrides `sub1` argument.
 +
elseif args.sub1 then
 +
local sub1 = args.sub1
 +
if primaryTopic then
 +
-- Add type (if specified) to wikilink for first-level subdivision.
 +
local sub1Link = sub1name and format("%s %s", sub1, sub1name) or sub1
 +
sub1Text = format('[[%s|%s]]', sub1Link, sub1)
 +
elseif region and sub1name then
 +
-- Add type to first-level subdivision.
 +
local sub1Typed = trim(format('%s %s', sub1, sub1name))
 +
-- Specialize first-level subdivision, with type added, to the region.
 +
sub1Text = format('[[%s, %s|%s]]', sub1Typed, region, sub1)
 +
end
 +
end
 
if sub1Text then -- Display first-level subdivision.
 
if sub1Text then -- Display first-level subdivision.
 
-- Row span for first-level subdivision, default to `jspan`.
 
-- Row span for first-level subdivision, default to `jspan`.
Line 258: Line 149:
  
 
-- Second-level subdivision, e.g., city and town
 
-- Second-level subdivision, e.g., city and town
local sub2Text = locns.sub2 -- Value for second-level subdivision column.
+
local sub2Text -- Value for second-level subdivision column.
 +
if args.sub2_special then
 +
sub2Text = args.sub2_special -- Overrides `sub2` argument.
 +
elseif args.sub2 then
 +
local sub2 = args.sub2
 +
if sub2 == "none" or sub2 == "&nbsp;" then
 +
sub2Text = "&#8203;" -- Zero-width space
 +
elseif primaryTopic then
 +
sub2Text = format("[[%s]]", sub2)
 +
else
 +
local sub2Link = {sub2}
 +
local sub2Name = sub2
 +
-- Type of area, e.g., city and village, as a form of disambiguation
 +
local area = args.area
 +
if area then
 +
insert(sub2Link, format(' (%s)', area)) -- Add area to wikilink.
 +
local areas = { -- table of different area types
 +
borough = "Borough",
 +
city = "City",
 +
community = "Community",
 +
CDP = "Community",
 +
hamlet = "Hamlet",
 +
town = "Town",
 +
village = "Village",
 +
["unorganized territory"] = "Unorganized Territory"
 +
}
 +
-- Add area name to displayed wikitext.
 +
sub2Name = format("%s of %s", areas[area], sub2Name)
 +
end
 +
insert(sub2Link, ", ")
 +
-- Some second-level subdivisions are not unique in a given region.
 +
-- `sub1dab` is the first-level subdivision to be used for disambiguation.
 +
local sub1dab = args.sub1dab
 +
if sub1dab and sub1name then
 +
insert(sub2Link, trim(format('%s %s', sub1dab, sub1name)) .. ", ")
 +
end
 +
if region then
 +
insert(sub2Link, region) -- Add region to wikilink
 +
end
 +
 
 +
sub2Text = format("[[%s|%s]]", table.concat(sub2Link), sub2Name)
 +
end
 +
end
 
if sub2Text then -- Display second-level subdivision.
 
if sub2Text then -- Display second-level subdivision.
 
row:tag('td') -- Create second-level subdivision cell
 
row:tag('td') -- Create second-level subdivision cell
Line 296: Line 229:
 
:attr('rowspan', uspan)
 
:attr('rowspan', uspan)
 
-- Store the primary distance and any conversion error message in the cell.
 
-- Store the primary distance and any conversion error message in the cell.
:wikitext(lengths[lengths.orig])
+
:wikitext(lengths[lengths.orig], lengths.error)
local secondary = row:tag('th'):attr('scope', 'row') -- Create the secondary unit cell.
+
local secondary = row:tag('td') -- Create the secondary unit cell.
 
:css('text-align', 'right')
 
:css('text-align', 'right')
 +
:css('background-color', '#eaecf0')
 
:attr('rowspan', uspan)
 
:attr('rowspan', uspan)
 
:wikitext(lengths[lengths.comp]) -- Store the secondary distance in the cell.
 
:wikitext(lengths[lengths.comp]) -- Store the secondary distance in the cell.
 +
 +
local unit_ref = args.unit_ref
 +
if unit_ref then -- A reference is provided for the distance.
 +
primary:wikitext(unit_ref) -- Add reference to the primary distance cell.
 +
end
  
 
local unit2 = args.unit2
 
local unit2 = args.unit2
Line 317: Line 256:
 
local lengths2 = convert(unit2, unitdef)
 
local lengths2 = convert(unit2, unitdef)
 
-- Add the second distance and any conversion error message to the primary distance cell.
 
-- Add the second distance and any conversion error message to the primary distance cell.
primary:wikitext(lengths2[lengths2.orig])
+
primary:wikitext(lengths2[lengths2.orig], lengths2.error)
 
-- Add the converted second distance to the secondary distance cell.
 
-- Add the converted second distance to the secondary distance cell.
 
secondary:wikitext(lengths2[lengths2.comp])
 
secondary:wikitext(lengths2[lengths2.comp])
end
+
end
 
+
local unit_ref = args.unit_ref
+
local unit2_ref = args.unit2_ref
if unit_ref then -- A reference is provided for the distance.
+
if unit2_ref then -- A reference is provided for the distance.
primary:wikitext(unit_ref) -- Add reference to the primary distance cell.
+
primary:wikitext(unit2_ref) -- Add reference to the primary distance cell.
 
end
 
end
 
end
 
end
 
end
 
end
  
-- Spec for formatting cell background colors and tooltip based on types
+
-- Color specified by any supplied type
local typesSpec = {}
+
local color
 +
-- Tooltip specified by any supplied type
 +
local title
  
 
--- Apply any type-derived coloring and tooltip to the given cell.
 
--- Apply any type-derived coloring and tooltip to the given cell.
local function applyTypeStyle(cell, multiple)
+
local function applyTypeStyle(cell)
cell:attr('title', typesSpec.title)
+
cell:attr('title', title):css('background-color', color)
:css('background', multiple and typesSpec.colors or typesSpec.color)
 
if multiple and typesSpec.minWidth then
 
-- minimum cell width to make enough room for all colors
 
cell:css('min-width', format('%dpx', typesSpec.minWidth))
 
end
 
 
end
 
end
  
Line 362: Line 298:
 
:attr('rowspan', pspan)
 
:attr('rowspan', pspan)
 
:wikitext(place) -- Store the place in the cell
 
:wikitext(place) -- Store the place in the cell
applyTypeStyle(placeCell, true)
+
applyTypeStyle(placeCell)
 
end
 
end
  
Line 371: Line 307:
  
 
if exit == 'old' then -- Add old exit number cell
 
if exit == 'old' then -- Add old exit number cell
local oldExits = {}
 
if args.old then insert(oldExits, args.old) end
 
local num = 0
 
repeat
 
num = num + 1
 
local key = "old" .. num
 
if args[key] then insert(oldExits, args[key]) end
 
until num > 1 and args[key] == nil
 
 
 
-- Row span (`ospan` = "old span")
 
-- Row span (`ospan` = "old span")
 
local ospan = args.ospan or jspan
 
local ospan = args.ospan or jspan
local oldExitCell = row:tag('td') -- Create old exit number cell
+
row:tag('td') -- Create old exit number cell
 
:css('text-align', 'center')
 
:css('text-align', 'center')
 
:css('background-color', '#d3d3d3')
 
:css('background-color', '#d3d3d3')
 
:attr('title', 'Former exit number')
 
:attr('title', 'Former exit number')
 
:attr('rowspan', ospan)
 
:attr('rowspan', ospan)
if #oldExits > 2 then
+
:wikitext(args.old) -- Store the old exit number in the cell
-- Create a collapsible table for many old exit numbers.
 
local numOldExits = #oldExits
 
local tbl = oldExitCell:tag("table")
 
:attr("class", "collapsible collapsed")
 
:css("margin", "auto")
 
:css("border-collapse", "collapse")
 
tbl:tag("th"):attr("scope", "col")
 
local ul = tbl:tag("tr"):tag("td"):tag("div")
 
:attr("class", "plainlist")
 
:tag("ul")
 
:css("text-align", "center")
 
for num,oldExit in ipairs(oldExits) do
 
if num < numOldExits then
 
ul:tag("li"):wikitext(oldExit)
 
end
 
end
 
-- Most recent number is always shown below the table.
 
oldExitCell:wikitext(oldExits[numOldExits])
 
else
 
-- Create an hlist for few old exit numbers.
 
local ul = oldExitCell:tag("div")
 
:attr("class", "hlist")
 
:tag("ul")
 
:css("text-align", "center")
 
for _,oldExit in ipairs(oldExits) do
 
ul:tag("li"):wikitext(oldExit)
 
end
 
end
 
 
end
 
end
  
Line 463: Line 362:
 
:attr('rowspan', nspan)
 
:attr('rowspan', nspan)
 
:wikitext(notes) -- Store the notes in the cell
 
:wikitext(notes) -- Store the notes in the cell
applyTypeStyle(notesCell, true)
+
applyTypeStyle(notesCell)
 
end
 
end
  
Line 474: Line 373:
 
local argType = args.type
 
local argType = args.type
 
if argType then -- {{{type}}} was passed
 
if argType then -- {{{type}}} was passed
local types = mw.text.split(argType, ",")
 
table.sort(types, function(a, b) return a > b end)
 
 
-- Type-based data for colors and tooltips
 
-- Type-based data for colors and tooltips
local definedTypes = mw.loadData("Module:Road data/RJL types")
+
if argType == 'mplex' then
local colors = {}
+
local page = mw.title.getCurrentTitle() -- Get transcluding page's title
local titles = {}
+
local pagename = page.prefixedText -- Extract page's full title as string
for _,type in ipairs(types) do
+
insert(errorMsg,
local typeSpec = definedTypes[string.lower(type)] -- Retrieve the type spec
+
format("[[Category:Jctint template with invalid type|$ %s]]", pagename))
if typeSpec then
 
insert(colors, typeSpec.color)
 
insert(titles, 1, typeSpec.jctint)
 
title = typeSpec.jctint -- Store the tooltip globally
 
else
 
errors.type = util.err("Invalid type: " .. argType)
 
end
 
 
end
 
end
local colorWidth = 36
+
local types = mw.loadData("Module:Road data/RJL types")
local colorSpec = {}
+
local typeData = types[string.lower(argType)] -- Retrieve the type data
local lastColor
+
if typeData then
local colorCount = 0
+
color = typeData.color -- Store the color globally
for _,color in ipairs(colors) do
+
title = typeData.jctint -- Store the tooltip globally
if lastColor then
+
else
insert(colorSpec, format("%s %dpx", lastColor, colorCount * colorWidth))
+
-- Add error category to error message table.
end
+
local page = mw.title.getCurrentTitle() -- Get transcluding page's title
insert(colorSpec, format("%s %dpx", color, colorCount * colorWidth))
+
local pagename = page.prefixedText -- Extract page's full title as string
lastColor = color
+
insert(errorMsg,
colorCount = colorCount + 1
+
format("[[Category:Jctint template with invalid type|%s]]", pagename))
end
 
if colorCount > 1 then
 
typesSpec.colors = format("linear-gradient(-90deg, %s);", concat(colorSpec, ", "))
 
typesSpec.minWidth = (colorCount - 1) * colorWidth
 
 
end
 
end
typesSpec.color = lastColor
 
typesSpec.title = concat(titles, ", ")
 
 
end
 
end
  
Line 524: Line 408:
 
end
 
end
  
-- Prepare error messages.
+
-- Return the HTML code in the mw.html object as a string, plus any error messages
local errorMsg = {}
+
return tostring(root) .. table.concat(errorMsg)
for key,msg in pairs(errors) do
 
-- Add the transcluding page to an error tracking category.
 
local prefix = errorTypes[key].prefix and errorTypes[key].prefix .. " "  or ""
 
insert(errorMsg, format("%s[[%s|%s%%page%%]]", msg, errorTypes[key].cat, prefix))
 
end
 
if #errorMsg > 0 then
 
local page = mw.title.getCurrentTitle().prefixedText -- Get transcluding page's title
 
-- Add error cell
 
local msg = mw.ustring.gsub(concat(errorMsg, " "), "%%page%%", page)
 
row:tag("td"):wikitext(msg)
 
end
 
 
 
-- Return the HTML code in the mw.html object as a string
 
return tostring(root)
 
 
end
 
end
  

Latest revision as of 05:49, 27 September 2020

Documentation for this module may be created at Module:Jctint/core/sandbox/doc

local p = {} -- Package to be exported

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

local parserModuleName = "Module:Road data/parser" .. moduleSuffix

-- Local version of string formatting function
local format = mw.ustring.format
-- Local version of string trimming function
local trim = mw.text.trim
-- Store this function in a local variable to avoid expensive table lookups.
local insert = table.insert

-- mw.html object for the generated row
local row
-- Default row span for all columns (`jspan` = "junction span")
local jspan
-- Any error messages produced that will be added to the output
local errorMsg = {}

-- A specification for self-closing HTML tag.
local selfClosing = {selfClosing = true}

---
-- Converts the distance specified in unit from `unit` specified in `unitdef`
-- to the other supported unit.
local function convert(unit, unitdef)
	if unit == nil or unitdef == nil then return {} end

	-- Import module to convert length.
	local util = require("Module:Road data/util")
	local lengths = util.convertLengths({[unitdef] = unit})
	if lengths.error then -- An error occurred during conversion.
		-- Add the transcluding page to an error tracking category.
		local page = mw.title.getCurrentTitle() -- Get transcluding page's title
		local pagename = page.prefixedText -- Extract page's full title as string
		-- Create category string
		local category = format("[[Category:Jctint template using non-numeric parameter values|# %s]]", pagename)
		insert(errorMsg, category) -- Add error category to error message table.
	end
	return lengths
end

--- Creates cells for the location columns.
local function locations(args)
	-- Unitary, e.g., state line
	local unitary = args.unitary -- Value to span all of the location columns
	if unitary then
		-- Text alignment of the cell contents, default to "left".
		local align = args.unitary_align or 'left'
		row:tag('td') -- Create unitary cell
			:attr('colspan', 3) -- spanning three possible columns
			:css('text-align', align)
			:wikitext(unitary) -- Store the contents of unitary in the cell.
		return
	end

	-- Create cells for regular location columns.

	-- Region, for disambiguation and potentially for display
	local region = args.region
	if region or args.region_special then
		-- Row span for region; must be specified to display a region cell.
		local regionSpan = args.regionspan
		if regionSpan then
			row:tag('td') -- Create a region cell
				:attr('rowspan', regionSpan)
				-- Store region text in the cell.
				-- `region_special` argument overrides wikilinked `region` argument.
				:wikitext(args.region_special or format("[[%s]]", region))
		end
	end

	-- Primary topic requires no specialization to supplied locations.
	local primaryTopic = args.primary_topic ~= 'no'

	-- Note below main text in the next column
	local sub1note = args.sub1_note -- check existence later
	-- Row span for the last location column, default to `jspan`
	local sub2span = args.sub2span or jspan

	-- Independent city
	local indepCityText -- Value to span both subdivision columns.
	if args.indep_city_special then
		indepCityText = args.indep_city_special -- Overrides `indep_city` argument.
	elseif args.indep_city then
		local indepCity = args.indep_city
		local cityLink -- Wikilink for independent city
		if primaryTopic then
			cityLink = format('[[%s]]', indepCity)
		elseif region then
			-- Specialize independent city to the region.
			cityLink = format('[[%s, %s|%s]]', indepCity, region, indepCity)
		end
		if cityLink then
			indepCityText = "[[Independent city|City]] of " .. cityLink
		end
	end
	if indepCityText then -- Display independent city.
		-- Text alignment of the cell contents, default to "left".
		local align = args.indep_city_align or 'left'
		local indepCityCell = row:tag('td') -- Create independent city cell
			:attr('colspan', 2) -- spanning two columns
			:attr('rowspan', sub2span) -- with the calculated row span.
			:css('text-align', align)
			:wikitext(indepCityText) -- Store the independent city in the cell.
		if sub1note then -- A note is provided.
			indepCityCell:tag('br', selfClosing) -- Add a line break to the cell.
			-- Add the note to the cell, within an HTML <small> tag.
			indepCityCell:tag('small'):wikitext(sub1note)
		end
		return
	end

	-- Create two cells for the first- and second-level subdivisions.

	-- First-level subdivision, e.g., county
	-- Name of the type of subdivision, e.g., "County" and "Parish"
	local sub1name = args.sub1name -- check existence later
	local sub1Text -- Value for first-level subdivision column.
	if args.sub1_special then
		sub1Text = args.sub1_special -- Overrides `sub1` argument.
	elseif args.sub1 then
		local sub1 = args.sub1
		if primaryTopic then
			-- Add type (if specified) to wikilink for first-level subdivision.
			local sub1Link = sub1name and format("%s %s", sub1, sub1name) or sub1
			sub1Text = format('[[%s|%s]]', sub1Link, sub1)
		elseif region and sub1name then
			-- Add type to first-level subdivision.
			local sub1Typed = trim(format('%s %s', sub1, sub1name))
			-- Specialize first-level subdivision, with type added, to the region.
			sub1Text = format('[[%s, %s|%s]]', sub1Typed, region, sub1)
		end
	end
	if sub1Text then -- Display first-level subdivision.
		-- Row span for first-level subdivision, default to `jspan`.
		local sub1span = args.sub1span or jspan
		local sub1Cell = row:tag('td') -- Create first-level subdivision cell
			:attr('rowspan', sub1span) -- with the calculated row span.
			:wikitext(sub1Text) -- Store the first-level subdivision in the cell.
		if sub1note then -- A note is provided.
			sub1Cell:tag('br', selfClosing) -- Add a line break to the cell.
			-- Add the note to the cell, within an HTML <small> tag.
			sub1Cell:tag('small'):wikitext(sub1note)
		end
	end

	-- Second-level subdivision, e.g., city and town
	local sub2Text -- Value for second-level subdivision column.
	if args.sub2_special then
		sub2Text = args.sub2_special -- Overrides `sub2` argument.
	elseif args.sub2 then
		local sub2 = args.sub2
		if sub2 == "none" or sub2 == "&nbsp;" then
			sub2Text = "&#8203;" -- Zero-width space
		elseif primaryTopic then
			sub2Text = format("[[%s]]", sub2)
		else
			local sub2Link = {sub2}
			local sub2Name = sub2
			-- Type of area, e.g., city and village, as a form of disambiguation
			local area = args.area
			if area then
				insert(sub2Link, format(' (%s)', area)) -- Add area to wikilink.
				local areas = { -- table of different area types
					borough = "Borough",
					city = "City",
					community = "Community",
					CDP = "Community",
					hamlet = "Hamlet",
					town = "Town",
					village = "Village",
					["unorganized territory"] = "Unorganized Territory"
				}
				-- Add area name to displayed wikitext.
				sub2Name = format("%s of %s", areas[area], sub2Name)
			end
			insert(sub2Link, ", ")
			-- Some second-level subdivisions are not unique in a given region.
			-- `sub1dab` is the first-level subdivision to be used for disambiguation.
			local sub1dab = args.sub1dab 
			if sub1dab and sub1name then
				insert(sub2Link, trim(format('%s %s', sub1dab, sub1name)) .. ", ")
			end
			if region then
				insert(sub2Link, region) -- Add region to wikilink
			end

			sub2Text = format("[[%s|%s]]", table.concat(sub2Link), sub2Name)
		end
	end
	if sub2Text then -- Display second-level subdivision.
		row:tag('td') -- Create second-level subdivision cell
			:attr('rowspan', sub2span) -- with the calculated row span.
			:wikitext(sub2Text) -- Store the second-level subdivision in the cell.
	end
end

--- Creates cells for the distance columns.
local function units(args)
	-- Alternate units, e.g., California's postmiles.
	local alt_unit = args.altunit
	if alt_unit then -- Alternate units override standard units.
		-- Row span (`auspan` = "alt[ernate] unit span")
		local auspan = args.auspan or jspan
		-- Create the alternate unit cell as a header cell for the row,
		-- since it is usually unique within the table.
		row:tag('th'):attr('scope', 'row')
			:css('text-align', 'right')
			:attr('rowspan', auspan)
			:wikitext(alt_unit) -- Store the contents of alt_unit in the cell.
	else
		-- Convert numeric distances to a secondary unit, and display both units.
		-- Distance in the primary unit, or 'none'
		local unit = args.unit
		-- If `unit` is "none", no cells are displayed.
		if unit == "none" then return end
		local unitdef = args.unitdef or "km" -- The primary unit ('mi' or 'km')
		-- Convert and format the distance.
		local lengths = convert(unit, unitdef)
		-- Row span (`uspan` = "unit span")
		local uspan = args.uspan or jspan
		-- Create the primary unit cell as a header cell for the row,
		-- since it is usually unique within the table.
		local primary = row:tag('th'):attr('scope', 'row')
			:css('text-align', 'right')
			:attr('rowspan', uspan)
			-- Store the primary distance and any conversion error message in the cell.
			:wikitext(lengths[lengths.orig], lengths.error)
		local secondary = row:tag('td') -- Create the secondary unit cell.
			:css('text-align', 'right')
			:css('background-color', '#eaecf0')
			:attr('rowspan', uspan)
			:wikitext(lengths[lengths.comp]) -- Store the secondary distance in the cell.

		local unit_ref = args.unit_ref
		if unit_ref then -- A reference is provided for the distance.
			primary:wikitext(unit_ref) -- Add reference to the primary distance cell.
		end

		local unit2 = args.unit2
		if unit2 then -- A second distance is provided.
			local line = args.line -- A horizontal rule may be requested between the distances.
			if line then
				-- Add a horizontal rule to both cells.
				primary:tag('hr', selfClosing)
				secondary:tag('hr', selfClosing)
			else
				-- Add an en-dash and a line break to both cells.
				primary:wikitext('–'):tag('br', selfClosing)
				secondary:wikitext('–'):tag('br', selfClosing)
			end
			-- Convert and format the second distance.
			local lengths2 = convert(unit2, unitdef)
			-- Add the second distance and any conversion error message to the primary distance cell.
			primary:wikitext(lengths2[lengths2.orig], lengths2.error)
			-- Add the converted second distance to the secondary distance cell.
			secondary:wikitext(lengths2[lengths2.comp])
			end
		
		local unit2_ref = args.unit2_ref
		if unit2_ref then -- A reference is provided for the distance.
			primary:wikitext(unit2_ref) -- Add reference to the primary distance cell.
		end
	end
end

-- Color specified by any supplied type
local color
-- Tooltip specified by any supplied type
local title

--- Apply any type-derived coloring and tooltip to the given cell.
local function applyTypeStyle(cell)
	cell:attr('title', title):css('background-color', color)
end

--- Creates a cell for places, such as bridges and rest areas.
local function place(args)
	local place = args.place -- Contents of the place cell
	-- Do nothing if `place` is "none"
	if place == "none" then return end
	local colspan = 2 -- Initial column span
	local exit = args[1] -- Whether this table has exit number columns
	local named = args[2] -- Whether this table has named junction column
	-- Adjust column span
	if exit == "old" then colspan = colspan + 2
	elseif exit == "exit" then colspan = colspan + 1
	end
	if named == "name" then colspan = colspan + 1 end
	-- Row span (`pspan` = "place span")
	local pspan = args.pspan or jspan
	local placeCell = row:tag('td') -- Create place cell
		:css('text-align', 'center')
		:attr('colspan', colspan)
		:attr('rowspan', pspan)
		:wikitext(place) -- Store the place in the cell
	applyTypeStyle(placeCell)
end

--- Creates cells for exit number and named junction columns.
local function exits(args)
	local exit = args[1] -- 'exit', 'old', or nil
	local named = args[2] -- 'name' or nil

	if exit == 'old' then -- Add old exit number cell
		-- Row span (`ospan` = "old span")
		local ospan = args.ospan or jspan
		row:tag('td') -- Create old exit number cell
			:css('text-align', 'center')
			:css('background-color', '#d3d3d3')
			:attr('title', 'Former exit number')
			:attr('rowspan', ospan)
			:wikitext(args.old) -- Store the old exit number in the cell
	end

	if exit then -- "exit" or "old" is defined; add current exit number cell
		-- Row span (`espan` = "exit span")
		local espan = args.espan or jspan
		local exitCell = row:tag('td') -- Create exit number cell
			:css('text-align', 'center')
			:attr('rowspan', espan)
			:wikitext(args.exit) -- Store the exit number in the cell
		applyTypeStyle(exitCell)
	end

	if named then -- Junction list has a junction name column
		local namespan = args.namespan or jspan -- Row span
		local nameCell = row:tag('td') -- Create junction name cell
			:attr('rowspan', namespan)
			:wikitext(args.name) -- Store the junction name in the cell
		applyTypeStyle(nameCell)
	end
end

--- Creates cell for the destinations column.
local function destinations(args)
	local road = args.road -- Contents of the destinations cell
	-- Do nothing if `road` is "none"
	if road == "none" then return end
	-- Column span (`rcspan` = "road column span"), default to 1
	local rcspan = args.rcspan or 1
	-- Row span (`rspan` = "road span")
	local rspan = args.rspan or jspan
	local destCell = row:tag('td') -- Create destination cell
		:attr('colspan', rcspan)
		:attr('rowspan', rspan)
		:wikitext(road) -- Store the destination in the cell
	applyTypeStyle(destCell)
end

--- Creates cell for the notes column.
local function notes(args)
	local notes = args.notes -- Contents of the notes cell
	-- Do nothing if `notes` is "none"
	if notes == "none" then return end
	-- Row span (`nspan` = "notes span")
	local nspan = args.nspan or jspan
	local notesCell = row:tag('td') -- Create notes cell
		:attr('rowspan', nspan)
		:wikitext(notes) -- Store the notes in the cell
	applyTypeStyle(notesCell)
end

---
-- Returns a row in the junction list.
-- Accessible from other Lua modules
function p._jctint(args)
	jspan = args.jspan or 1 -- Global row span for all columns; defaults to 1
	-- {{{type}}} argument to determine color and tooltips
	local argType = args.type
	if argType then -- {{{type}}} was passed
		-- Type-based data for colors and tooltips
		if argType == 'mplex' then
			local page = mw.title.getCurrentTitle() -- Get transcluding page's title
			local pagename = page.prefixedText -- Extract page's full title as string
			insert(errorMsg,
				format("[[Category:Jctint template with invalid type|$ %s]]", pagename))
		end
		local types = mw.loadData("Module:Road data/RJL types")
		local typeData = types[string.lower(argType)] -- Retrieve the type data
		if typeData then
			color = typeData.color -- Store the color globally
			title = typeData.jctint -- Store the tooltip globally
		else
			-- Add error category to error message table.
			local page = mw.title.getCurrentTitle() -- Get transcluding page's title
			local pagename = page.prefixedText -- Extract page's full title as string
			insert(errorMsg,
				format("[[Category:Jctint template with invalid type|%s]]", pagename))
		end
	end

	local root = mw.html.create() -- Create the root mw.html object to return
	-- Create the table row and store it globally
	row = root:tag('tr'):css('text-align', 'left')

	locations(args) -- Handle location arguments
	units(args) -- Handle distance arguments
	if args.place then -- {{{place}}} spans all columns to the right of the distances
		place(args) -- Create cell for place
	else
		exits(args) -- Handle exit/named junction arguments
		destinations(args) -- Handle destinations
		notes(args) -- Handle notes
	end

	-- Return the HTML code in the mw.html object as a string, plus any error messages
	return tostring(root) .. table.concat(errorMsg)
end

--- Entry function for {{jctint/core}}
function p.jctint(frame)
	-- Import module function to work with passed arguments
	local getArgs = require('Module:Arguments').getArgs
	-- Gather passed arguments into easy-to-use table
	local args = getArgs(frame)
	return p._jctint(args)
end

return p -- Return package