Module:Jctint/core

local p = {} -- Package to be exported

-- Local version of string formatting function local format = mw.ustring.format -- 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("", 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 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 -- Specialize first-level subdivision, with type added, to the region. sub1Text = format('%s', sub1, sub1name, 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 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 = "&#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 village = "Village", city = "City", town = "Town", community = "Community", CDP = "Community", hamlet = "Hamlet", ["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, 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 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 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 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 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("", 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