Module:Jctint/core

From Shipbucket Wiki
Jump to: navigation, search

This module implements the {{Jctint/core}} template. Please see the template page for usage instructions.

Usage

{{#invoke:Jctint|jctint}}

Tracking/maintenance categories


local p = {} -- Package to be exported

local format = string.format -- Local version of string formatting function

local types = mw.loadData("Module:Road data/RJL types") -- Import type-based data for colors and tooltips

local row -- mw.html object for the generated row
local jspan -- Junction span argument is stored here for efficiency
local color -- Color specified by any supplied type
local title -- Tooltip specified by any supplied type
local errorMsg = {} -- Any error messages produced that will be added to the output

local function conversion(unit, unitdef)
	-- This function converts the distance specified in unit from the unit specified in unitdef to the other supported unit
	local primary = unit and tonumber(unit) or -1 -- If unit is nil, primary will be -1. If unit is a non-numeric string, primary will be nil. Otherwise, primary will be the string as a number.
	if not primary then -- If primary is nil (unit was non-numeric), 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
		local category = format("[[Category:Jctint template using non-numeric parameter values|# %s]]", pagename) -- Create category string
		table.insert(errorMsg, category) -- Add error category to error message table.
		return unit -- Return supplied unit
	elseif primary == -1 then -- If primary is -1 (unit is nil), simply return nil
		return nil
	end
	local mwmath = require "Module:Math" -- Import math module
	local precision = mwmath._precision -- Function to determine a numeric string's precision
	local round = mwmath._precision_format -- Function to display a number rounded to a given number of digits
	local converted -- Distance converted into the secondary unit
	if unitdef == 'mi' then -- If the primary unit is miles
		converted = primary * 1.609344 -- Convert into kilometers
	else -- Otherwise
		converted = primary / 1.609344 -- Convert into miles
	end
	local prec = precision(unit) -- Retrieve precision of supplied distance
	if prec < 0 then prec = 0 end -- Precision must be at least 0
	return round(converted, prec) -- Return the converted distance rounded to the same number of digits as the supplied distance
end

local function locations(args)
	-- This function generates the cells for locations.
	local unitary = args.unitary -- The contents of this parameter will span all of the location columns.
	if unitary then
		local tag = row:tag('td'):attr('colspan', 3):wikitext(unitary) -- Simply create a cell that spans all three possible columns, and store the contents of unitary in the cell.
		local align = args.unitary_align or 'left' -- Determine the alignment of the cell contents, using the unitary_align argument with 'left' as a default.
		tag:css('text-align', align) -- Apply the text alignment to the cell.
	else -- We don't have a single cell to create, so now comes the hard work.
		local areas = {village = "Village", city = "City", town = "Town", community = "Community", CDP = "Community", hamlet = "Hamlet", ["unorganized territory"] = "Unorganized Territory"} -- This is a table of different area types.
		
		local notPrimaryTopic = args.primary_topic == 'no' -- If the primary_topic argument equals 'no', then this is true. Otherwise, this is false.
		
		-- Regions
		local regionSpan = args.regionspan -- If this is supplied, a region cell will be created with this as its row span.
		local region = args.region -- This will be the region stored in a possible region cell, and will also be used for disambiguating other locations.
		if regionSpan then
			local regionCell = row:tag('td'):attr('rowspan', regionSpan) -- Create a region cell with the supplied row span.
			local regionSpecial = args.region_special or ('[[' .. region .. ']]') -- The region_special argument overrides the region argument. If it is not provided, simply wikilink the supplied region.
			regionCell:wikitext(regionSpecial) -- Store either the contents of the region_special argument or the wikilinked region in the cell.
		end
		
		-- Independent Cities
		local indepCity = args.indep_city -- Independent cities span both subdivision columns.
		local indepCitySpecial = args.indep_city_special -- This will override the indep_city argument.
		local sub1note = args.sub1_note -- This may be used to place a note in small text below the name of an independent city or other first-level subdivision.
		local sub2span = args.sub2span or 1 -- This is the row span for any independent city or second-level subdivision. 1 is the default.
		if indepCity or indepCitySpecial then -- If an independent city is provided, create the appropriate cell.
			local indepCityCell = row:tag('td'):attr('colspan', 2):attr('rowspan', sub2span) -- Create a cell that spans two columns, and has the calculated row span.
			local align = args.indep_city_align or 'left' -- Determine the alignment of the cell contents, using the indep_city_align argument with 'left' as a default.
			indepCityCell:css('text-align', align) -- Apply the text alignment to the cell.
			if indepCitySpecial then -- If indep_city_special is supplied, simply stuff it in the cell.
				indepCityCell:wikitext(indepCitySpecial)
			elseif notPrimaryTopic then -- Otherwise, if primary_topic == 'no', then create a wikilink with the independent city and region, and store it in the cell.
				local text = format('[[Independent city|City]] of [[%s, %s|%s]]', indepCity, region, indepCity)
				indepCityCell:wikitext(text)
			else -- If neither is true, then create a wikilink with just the independent city and store it in the cell.
				local text = format('[[Independent city|City]] of [[%s]]', indepCity)
				indepCityCell:wikitext(text)
			end
			if sub1note then -- If a note is provided, store that in the cell as well.
				indepCityCell:tag('br', {selfClosing = true}) -- Add a line break to the cell.
				indepCityCell:tag('small'):wikitext(sub1note) -- Add the note to the cell, within an HTML <small> tag.
			end
		else -- If no independent city is provided, create two cells for the first- and second-level subdivisions.
			-- First-level Subdivisions
			local sub1 = args.sub1 -- First-level subdivisions include counties and other similar areas.
			local sub1Special = args.sub1_special -- This will override sub1.
			local sub1name = args.sub1name -- This is the name of the type of subdivision, like "County" or "Parish"
			if sub1 or sub1Special then -- If a first-level subdivision is provided, create a table cell for it.
				local sub1span = args.sub1span or 1 -- This is the row span for the first-level subdivision. 1 is the default.
				local sub1Cell = row:tag('td'):attr('rowspan', sub1span) -- Create a cell for the location, with the appropriate row span
				if sub1Special then -- If sub1_special is provided, stuff it in the cell.
					sub1Cell:wikitext(sub1Special)
				elseif notPrimaryTopic then -- Otherwise, if primary_topic == 'no', then create a wikilink with the subdivision name, type, and region, and store it in the cell.
					local text = format('[[%s %s, %s|%s]]', sub1, sub1name, region, sub1)
					sub1Cell:wikitext(text)
				else -- If neither is true, create a wikilink with the subdivision name, and optionally the type, and store it in the cell.
					local text = '[[' .. sub1
					if sub1name then -- The type is optional in this case.
						text = text .. format(' %s|%s', sub1name, sub1)
					end
					sub1Cell:wikitext(text .. ']]')
				end
				if sub1note then -- If a note is provided, store that in the cell as well.
					sub1Cell:tag('br', {selfClosing = true}) -- Add a line break to the cell.
					sub1Cell:tag('small'):wikitext(sub1note) -- Add the note to the cell, within an HTML <small> tag.
				end
			end
			
			-- Second-level Subdivisions
			local sub2 = args.sub2 -- Second-level subdivisions include cities and towns
			local sub2Special = args.sub2_special -- This will override sub2
			if (sub2 == 'none') or (sub2 == '&nbsp;') then -- If sub2 is 'none' or a non-breaking space, store a non-breaking space in the cell.
				row:tag('td'):wikitext("&nbsp;") -- Create a cell and store a non-breaking space in it.
			elseif sub2Special then -- If sub2_special is supplied, simply stuff it in the cell.
				row:tag('td'):attr('rowspan', sub2span):wikitext(sub2Special) -- Create a cell, apply the proper row span, and store the contents of sub2_special.
			elseif sub2 then -- If sub is supplied, then create and fill the cell.
				local sub2Cell = row:tag('td'):attr('rowspan', sub2span) -- Create a new cell, and apply the appropriate row span to it.
				if not notPrimaryTopic then -- If primary_topic ~= 'no', simply wikilink the supplied location and store it in the cell.
					sub2Cell:wikitext('[[' .. sub2 .. ']]')
				else -- Otherwise, create a wikilink that includes the region, and store it in the cell.
					local text = {'[[', sub2}
					local area = args.area -- This can include a type of area, such as city or village, as a form of disambiguation. The type will appear in the wikilink.
					local insert = table.insert -- Store this function in a local variable to avoid expensive table lookups.
					if area then -- If a specific type of area is supplied, put it in the wikilink.
						insert(text, format(' (%s)', area))
					end
					insert(text, ", ")
					local sub1dab = args.sub1dab -- Some location names are shared by multiple locations in the same state. This allows for the county to be used to disambiguate the location.
					if sub1dab then
						insert(text, format('%s %s, ', sub1dab, sub1name))
					end
					insert(text, region .. '|')
					if area then -- If a specific type of area is supplied, put a properly-formatted form of it in the wikilink.
						local linktext = areas[area]
						insert(text, linktext .. ' of ')
					end
					insert(text, sub2 .. ']]')
					sub2Cell:wikitext(table.concat(text)) -- Concatenate the table's contents and store the result in the cell.
				end
			end
		end
	end
end

function units(args)
	-- This function creates the table cells for the distance.
	local alt_unit = args.altunit -- Alternate units include California's postmiles.
	if alt_unit then -- Alternate units take precedence over standard units.
		local tag = row:tag('th'):attr('scope', 'row'):css('text-align', 'right') -- Create the alternate unit cell. It is treated as a header cell for the row, since it is usually unique within the table.
		local span = args.auspan or jspan or 1 -- Determine row span ("auspan" = "alt[ernate] unit span"), using global row span argument jspan and 1 as backups, in that order.
		tag:attr('rowspan', span) -- Apply row span to cell.
		tag:attr('title', title) -- Apply any type-derived tooltip.
		tag:wikitext(alt_unit) -- Store the contents of alt_unit in the cell.
	else -- Numeric distances will be converted to a secondary unit, and both will be displayed.
		local unit = args.unit -- Numeric distance in the primary unit, or 'none' if no cells are to be displayed
		if unit ~= 'none' then -- If unit == 'none', don't display any cells.
			local primary = row:tag('th'):attr('scope', 'row'):css('text-align', 'right') -- Create cell for distance in primary unit. As with alt_unit, this cell is a header cell for the row.
			local span = args.uspan or jspan or 1 -- Determine row span ("uspan" = "unit span"), using global row span argument jspan and 1 as backups, in that order.
			primary:attr('rowspan', span) -- Apply row span to cell.
			primary:attr('title', title) -- Apply any type-derived tooltip.
			
			local secondary = row:tag('td'):css('text-align', 'right'):css('background-color', '#f2f2f2'):attr('rowspan', span) -- Create the cell for the distance in the secondary unit, and format it like the cell for the primary unit.
			secondary:attr('title', title) -- Apply any type-derived tooltip.
			
			local unitdef = args.unitdef -- The unit of the supplied distance ('mi' or 'km')
			primary:wikitext(unit) -- Store the supplied distance in the primary distance cell.
			secondary:wikitext(conversion(unit, unitdef)) -- Convert the distance into the secondary unit, as determined by unitdef, and store it in the secondary distance cell.
			
			local unit2 = args.unit2 -- A second distance may be provided.
			if unit2 then -- If one is provided, repeat the process.
				local line = args.line -- A horizontal line may be displayed between the distances.
				if line then -- If a line is requested, add one to both cells.
					primary:tag('hr', {selfClosing = true})
					secondary:tag('hr', {selfClosing = true})
				else -- Otherwise, add an en-dash and line break.
					primary:wikitext('–'):tag('br', {selfClosing = true})
					secondary:wikitext('–'):tag('br', {selfClosing = true})
				end
				primary:wikitext(unit2) -- Add the second distance to the primary distance cell
				secondary:wikitext(conversion(unit2, unitdef)) -- Convert the second distance into the secondary unit, and add it to the secondary distance cell.
			end
			
			local unit_ref = args.unit_ref -- A reference may be provided for the distance.
			if unit_ref then primary:wikitext(unit_ref) end -- If one is provided, simply add it to the primary distance cell.
		end
	end
end

function place(args)
	-- This function generates a cell for places, such as bridges and rest areas
	local tag = row:tag('td'):css('text-align', 'center') -- Create cell and center-align its future contents
	local pspan = args.pspan or jspan or 1 -- Determine row span ("pspan" = "place span"), using global row span argument jspan and 1 as backups, in that order
	tag:attr('rowspan', pspan) -- Apply row span to cell
	
	local colspan -- Create local variable to store column span
	local exit = args[1] -- Whether this table has exit numbers, both old and current exit numbers, or neither
	local named = args[2] -- Whether this table includes named junctions
	if exit == 'exit' then -- Calculate column span
		if named == 'name' then
			colspan = 4
		else
			colspan = 3
		end
	elseif exit == 'old' then
		if named == 'name' then
			colspan = 5
		else
			colspan = 4
		end
	else
		colspan = 2
	end
	tag:attr('colspan', colspan) -- Apply column span to cell
	tag:attr('title', title):css('background-color', color) -- Apply any type-derived coloring and tooltip
	tag:wikitext(args.place) -- Store the place in the cell
end

function exits(args)
	-- This function generates table cells for exit and named junction data
	local exit = args[1] -- Is either 'exit', 'old', or nil
	local named = args[2] -- Is either 'name' or nil
	
	if exit == 'old' then -- Add old exit number cell
		local oldTag = row:tag('td'):css('text-align', 'center'):css('background-color', '#d3d3d3'):attr('title', 'Former exit number') -- Create cell and properly style it
		local ospan = args.ospan or jspan or 1 -- Determine row span ("ospan" = "old span"), using global row span argument jspan and 1 as backups, in that order
		oldTag:attr('rowspan', ospan):wikitext(args.old) -- Apply row span and add old exit number
	end
	
	if exit == 'exit' or exit == 'old' then -- If either is true, add current exit number cell
		local exitTag = row:tag('td'):css('text-align', 'center') -- Create cell and center-align its contents
		local espan = args.espan or jspan or 1 -- Determine row span ("espan" = "exit span"), using global row span argument jspan and 1 as backups, in that order
		exitTag:attr('rowspan', espan) -- Apply row span to cell
		exitTag:attr('title', title):css('background-color', color) -- Apply any type-derived coloring and tooltip
		exitTag:wikitext(args.exit) -- Store the exit number in the cell
	end
	
	if named == 'name' then -- This junction list has a junction name column, so a cell must be produced
		local nameTag = row:tag('td') -- Create a cell for the junction name
		local namespan = args.namespan or jspan or 1 -- Determine row span, using global row span argument jspan and 1 as backups, in that order
		nameTag:attr('rowspan', namespan) -- Apply row span to cell
		nameTag:attr('title', title):css('background-color', color) -- Apply any type-derived coloring and tooltip
		nameTag:wikitext(args.name) -- Store the junction name in the cell
	end
end

function destinations(args)
	-- This function creates the cells for destinations and notes
	local destTag = row:tag('td') -- Create destination cell
	local rcspan = args.rcspan or 1 -- Determine column span ("rcspan" = "road column span"), using 1 as a default
	destTag:attr('colspan', rcspan) -- Apply column span to cell
	local rspan = args.rspan or jspan or 1 -- Determine row span ("rspan" = "road span"), using global row span argument jspan and 1 as backups, in that order
	destTag:attr('rowspan', rspan) -- Apply row span to cell
	destTag:attr('title', title):css('background-color', color) -- Apply any type-derived coloring and tooltip
	destTag:wikitext(args.road) -- Store the supplied cell contents
	
	local notes = args.notes -- Contents of the notes cell
	if notes ~= 'none' then -- If the contents equal the string 'none', then no cell will be produced. Otherwise...
		local notesTag = row:tag('td') -- Create notes cell
		local nspan = args.nspan or jspan or 1 -- Determine row span ("nspan" = "notes span"), using global row span argument jspan and 1 as backups, in that order
		notesTag:attr('rowspan', nspan) -- Apply row span to cell
		notesTag:attr('title', title):css('background-color', color) -- Apply any type-derived coloring and tooltip
		notesTag:wikitext(notes) -- Store the notes in the cell
	end
end

function p._jctint(args)
	-- This function creates the table row and calls other functions to popluate it.
	-- This function is accessible from other Lua modules
	jspan = args.jspan -- Junction span (backup for row span that is applicable to all columns)
	local argType = args.type -- {{{type}}} argument to determine color and tooltips
	if argType then -- If a type was passed
		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 -- If there is no type data, add an error category.
			local page = mw.title.getCurrentTitle() -- Get transcluding page's title
			local pagename = page.prefixedText -- Extract page's full title as string
			table.insert(errorMsg, format("[[Category:Jctint template with invalid type|%s]]", pagename)) -- Add error category to error message table.
		end
	end
	local root = mw.html.create() -- Create the root mw.html object
	row = root:tag('tr'):css('text-align', 'left') -- Create the table row and store it globally
	
	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 and notes
	end
	
	return tostring(root) .. table.concat(errorMsg) -- Return the HTML code in the mw.html object as a string, plus any error messages
end

function p.jctint(frame)
	-- Entry function for {{jctint/core}}
	return p._jctint(require('Module:Arguments').getArgs(frame)) -- Simply call another function with those arguments to actually create the row
end

return p -- Return package