local p = {} -- Package to be exported  -- 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("# %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', indepCity, region, indepCity) 		end 		if cityLink then 			indepCityText = "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', 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', 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 == " " then 			sub2Text = "​" -- 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", 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 	--  argument to determine color and tooltips 	local argType = args.type 	if argType then --  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("$ %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("%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 --  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  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