Module:Jctint/core/sandbox

From blackwiki
< Module:Jctint/core
Revision as of 03:22, 26 January 2015 by blackwiki>Happy5214 (Displaying primary unit value with thousands separator by default and accepting similar parameters for primary unit value)
Jump to navigation Jump to search

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

local p = {} -- Package to be exported

local format = string.format -- Local version of string formatting function
local lang = mw.getContentLanguage()

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 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 primary
	if unit then -- If unit is nil, the function will return nil. If unit is a non-numeric string, primary will be nil. Otherwise, primary will be the string as a number.
		primary = lang:parseFormattedNumber(unit)
	else
		return nil
	end
	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
	end
	
	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')
			local unitnum = lang:parseFormattedNumber(unit) -- Convert unit to number, only to convert it back into a string in the next line.
			primary:wikitext(lang:formatNum(unitnum)) -- 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