Module:Track gauge
Appearance
![]() | This module is rated as ready for general use. It has reached a mature form and is thought to be relatively bug-free and ready for use wherever appropriate. It is ready to mention on help pages and other Wikipedia resources as an option for new users to learn. To reduce server load and bad output, it should be improved by sandbox testing rather than repeated trial-and-error editing. |
![]() | This module is subject to page protection. It is a highly visible module in use by a very large number of pages, or is substituted very frequently. Because vandalism or mistakes would affect many pages, and even trivial editing might cause substantial load on the servers, it is protected from editing. |
![]() | This Lua module is used on approximately 25,000 pages and changes may be widely noticed. Test changes in the module's /sandbox or /testcases subpages, or in your own module sandbox. Consider discussing changes on the talk page before implementing them. |
![]() | This module depends on the following other modules: |
![]() | This module uses TemplateStyles: |
This module implements the {{Track gauge}} template. Please see the template page for documentation on how to use the main TrackGauge function.
Gauge data
The gauge information is stored at Module:Track gauge/data; to add new gauges, see the instructions there.
Data checks
This module includes a function that checks the data page for errors. It is used with the following code:
{{#invoke:Track gauge/autodocument|checkData|name of data page}}
The first positional parameter is the name of the data page that you want to be checked. If this is omitted, the module checks Module:Track gauge/data.
Tracking category
Module set
- Module:Track gauge
- Module:Track gauge/data
- Module:Track gauge/autodocument (track gauges list)
-- This module implements the {{RailGauge}} template.
local p = {}
-- Adds span tags to prevent a string from wrapping.
local function noWrap( s )
return mw.ustring.format( '<span class="nowrap">%s</span>', s )
end
-- A slimmed-down version of the {{frac}} template.
local function frac( whole, num, den )
return mw.ustring.format(
'<span class="frac">%s%s<sup>%s</sup>⁄<sub>%s</sub></span>',
whole or '', whole and '<span class="visualhide"> </span>' or '', num, den
)
end
-- Formats imperial measurements. Originally same functionality as {{RailGauge/format imp}}.
local function formatImp( data, ulink, articleLink, pageName )
local ret = {}
local ft = data.ft
if ft then
local ftlink = ulink and not articleLink and '[[Foot (unit)|ft]]' or 'ft'
table.insert( ret, mw.ustring.format( '%s %s', ft, ftlink ) )
end
local inches = data['in']
local num = data.num
local den = data.den
if inches and not num and not den then
table.insert( ret, inches )
elseif num and den then
table.insert( ret, frac( inches, num, den ) )
end
if inches or num and den then
local incheslink = ulink and not articleLink and '[[inch|in]]' or 'in'
table.insert( ret, incheslink )
end
local gaugeSize = noWrap( table.concat( ret, ' ' ) )
if articleLink then
return '[[' .. pageName .. '|' .. gaugeSize .. ']]'
else
return gaugeSize
end
end
-- Formats metric measurements. Originally same functionality as {{RailGauge/format met}}
local function formatMet( data, ulink, articleLink, pageName )
local m = data.m
local gaugeSize
if m then
local munit = link and not articleLink and '[[metre|m]]' or 'm'
gaugeSize = noWrap( mw.ustring.format( '%s %s', m, munit ) )
else
local mm = data.mm
mm = tonumber( mm )
if mm then
mm = mw.getContentLanguage():formatNum( mm )
end
local mmunit = ulink and not articleLink and '[[millimetre|mm]]' or 'mm'
gaugeSize = noWrap( mw.ustring.format( '%s %s', mm, mmunit ) )
end
if articleLink then
return '[[' .. pageName .. '|' .. gaugeSize .. ']]'
else
return gaugeSize
end
end
-- Composes the initial output from the gauge data taken from [[Module:RailGauge/data]].
-- Same functionality as {{RailGauge/compose}}.
local function compose( args, data )
local definition = data.dflt1
local pageName = data.pagename or nil
local imp = formatImp( data, args.unitlink == 'on', args.lk=='on' and definition=='imp' and pageName, pageName)
local met = formatMet( data, args.unitlink == 'on', args.lk=='on' and definition=='met' and pageName, pageName)
local first = args.first or data.dflt1
if first == 'met' or first == 'metric' then
first = 'met'
else
first = 'imp'
end
local ret = {}
if first == 'met' then
table.insert( ret, met )
else
table.insert( ret, imp )
end
local disp = args.disp
if disp ~= '1' then
local formatText
if disp == 's' then
formatText = '/​%s'
elseif disp == 'or' then
formatText = ' or %s'
else
formatText = ' (%s)'
end
if first == 'met' then
table.insert( ret, mw.ustring.format( formatText, imp ) )
else
table.insert( ret, mw.ustring.format( formatText, met ) )
end
end
ret = table.concat( ret )
if args.wrap == 'y' then
return ret
else
return noWrap( ret )
end
end
-- The basic data flow of the module.
local function _main( args )
local gaugeData = mw.loadData( 'Module:RailGauge/data' )
local searchKey = mw.ustring.lower( args[ 1 ] or '' )
searchKey = mw.ustring.gsub( searchKey, '%s', '' ) -- Remove all whitespace.
local title = mw.title.getCurrentTitle()
-- Get the gauge information from the /data subpage.
local data
for i, t in ipairs( gaugeData ) do
for j, alias in ipairs( t.aliases ) do
if alias == searchKey then
data = t
end
end
end
-- Categorise the page if no gauge information was found.
if not data then
local category = ''
local unknownAlias = args[ 1 ]
if title.namespace == 0 then
category = mw.ustring.format(
'[[Category:Articles with template RailGauge with unrecognized input|%s, %s]]',
unknownAlias or ' ', title.text
)
end
return ( unknownAlias or '' ) .. category
end
-- Assemble the output.
local ret = {}
table.insert( ret, compose( args, data ) )
local gaugeName = data.name
local gaugeLink = data.link
if args.allk == 'on' and gaugeLink then
table.insert( ret, ' ' .. noWrap( gaugeLink ) )
elseif args.al == 'on' and gaugeName then
table.insert( ret, ' ' .. noWrap( gaugeName ) )
end
local maintCat = data.maintcat
if maintCat and title.namespace == 0 then
category = mw.ustring.format(
'[[Category:Articles with template RailGauge that may need attention|%s, %s]]',
maintCat, title.text
)
table.insert( ret, ' ' .. category )
end
return table.concat( ret )
end
function p.main( frame )
-- If called via #invoke, use the args passed into the invoking
-- template, or the args passed to #invoke if any exist. Otherwise
-- assume args are being passed directly in from the debug console
-- or from another Lua module.
local origArgs
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
for k, v in pairs( frame.args ) do
origArgs = frame.args
break
end
else
origArgs = frame
end
-- Trim whitespace, make lower-case and remove blank arguments for all arguments but [1].
-- [1] is trimmed and blank values are removed, but capitalization is preserved when
-- when no gauge data is found.
local args = {}
for k, v in pairs( origArgs ) do
v = mw.text.trim( v )
if k == 1 and v ~= '' then
args[ 1 ] = v
elseif v ~= '' then
args[ k ] = mw.ustring.lower( v )
end
end
return _main( args )
end
-- Performs various checks on the /data subpage.
function p.checkData( frame )
local dataPage = frame and frame.args and frame.args[1] or 'Module:RailGauge/data'
local data = mw.loadData( dataPage )
local exists, dupes, dupeSort, ret = {}, {}, {}, {}
-- Check for duplicate aliases.
for ti, t in ipairs( data ) do
for ai, alias in ipairs( t.aliases or {} ) do
if not exists[ alias ] then
exists[ alias ] = { ti, ai }
else
if not dupes[ alias ] then
dupes[ alias ] = { exists[ alias ] }
end
table.insert( dupes[ alias ], { ti, ai } )
end
end
end
for alias in pairs( dupes ) do
table.insert( dupeSort, alias )
end
table.sort( dupeSort )
for i1, alias in ipairs( dupeSort ) do
local positions = {}
for i2, aliasKeys in ipairs( dupes[ alias ] ) do
local position = mw.ustring.format( 'gauge %d, alias %d (gauge id: <code>%s</code>)', aliasKeys[ 1 ], aliasKeys[ 2 ], data[ aliasKeys[ 1 ] ].id or '' )
table.insert( positions, position )
end
local aliasText = mw.ustring.format( 'Duplicate aliases "%s" detected at the following positions: %s.', alias, mw.text.listToText( positions, '; ' ) )
table.insert( ret, aliasText )
end
-- Check for numerators without denominators.
for ti, t in ipairs( data ) do
local num = t.num
local den = t.den
if num and not den then
table.insert( ret, mw.ustring.format( 'Numerator "%s" with no denominator detected at gauge %d (id: <code>%s</code>).', num, ti, t.id or '' ) )
elseif den and not num then
table.insert( ret, mw.ustring.format( 'Denominator "%s" with no numerator detected at gauge %d (id: <code>%s</code>).', den, ti, t.id or '' ) )
end
end
-- Check for gauges with no imperial or no metric measurements.
for ti, t in ipairs( data ) do
if not ( t.ft or t['in'] or t.num or t.den ) then
table.insert( ret, mw.ustring.format( 'No imperial measurements found for gauge %d (id: <code>%s</code>).', ti, t.id or '' ) )
end
if not ( t.m or t.mm ) then
table.insert( ret, mw.ustring.format( 'No metric measurements found for gauge %d (id: <code>%s</code>).', ti, t.id or '' ) )
end
end
-- Check for non-numeric measurements.
local measurements = { 'ft', 'in', 'num', 'den', 'm', 'mm' }
for ti, t in ipairs( data ) do
for mi, measurement in ipairs( measurements ) do
local measurementVal = t[ measurement ]
if measurementVal and not tonumber( measurementVal ) then
table.insert( ret, mw.ustring.format( 'Non-numeric <code>%s</code> measurement ("%s") found for gauge %d (id: <code>%s</code>).', measurement, measurementVal, ti, t.id or '' ) )
end
end
end
-- Check for gauges with no id.
for ti, t in ipairs( data ) do
if not t.id then
local aliases = {}
for i, alias in ipairs( t.aliases ) do
table.insert( aliases, mw.ustring.format( '<code>%s</code>', alias ) )
end
aliases = mw.ustring.format( ' (aliases: %s)', mw.text.listToText( aliases ) )
table.insert( ret, mw.ustring.format( 'No id found for gauge %d%s.', ti, aliases or '' ) )
end
end
-- Check for gauges with no aliases.
for ti, t in ipairs( data ) do
if type( t.aliases ) ~= 'table' then
table.insert( ret, mw.ustring.format( 'No aliases found for gauge %d (id: <code>%s</code>).', ti, t.id or '' ) )
else
local isAlias = false
for ai, alias in ipairs( t.aliases ) do
isAlias = true
break
end
if not isAlias then
table.insert( ret, mw.ustring.format( 'No aliases found for gauge %d (id: <code>%s</code>).', ti, t.id or '' ) )
end
end
end
-- Check for named gauges with no links and gauges with links but no names.
for ti, t in ipairs( data ) do
if t.name and not t.link then
table.insert( ret, mw.ustring.format( 'No link found for the named gauge "%s" at position %d (id: <code>%s</code>).', t.name, ti, t.id or '' ) )
elseif t.link and not t.name then
table.insert( ret, mw.ustring.format( 'No name found for the gauge with link "%s" at position %d (id: <code>%s</code>).', t.link, ti, t.id or '' ) )
end
end
-- Check for invalid dflt1 values.
for ti, t in ipairs( data ) do
local dflt1 = t.dflt1
if dflt1 ~= 'imp' and dflt1 ~= 'met' then
table.insert( ret, mw.ustring.format( 'Invalid dflt1 value "%s" found for gauge %d (id: <code>%s</code>).', dflt1 or '', ti, t.id or '' ) )
end
end
-- Check for unwanted whitespace.
for ti, t in ipairs( data ) do
for tkey, tval in pairs( t ) do
if tkey == 'aliases' and type( tval ) == 'table' then
for ai, alias in ipairs( tval ) do
if mw.ustring.find( alias, '%s' ) then
table.insert( ret, mw.ustring.format( 'Unwanted whitespace detected in gauge %d alias %d ("%s", gauge id: <code>%s</code>).', ti, ai, alias, t.id or '' ) )
end
end
elseif tkey == 'name' or tkey == 'link' or tkey == 'pagename' then
if tval ~= mw.text.trim( tval ) then
table.insert( ret, mw.ustring.format( 'Unwanted whitespace detected in <code>%s</code> field of gauge %d ("%s", gauge id: <code>%s</code>).', tkey, ti, tval, t.id or '' ) )
end
elseif mw.ustring.find( tval, '%s' ) then
table.insert( ret, mw.ustring.format( 'Unwanted whitespace detected in <code>%s</code> field of gauge %d ("%s", gauge id: <code>%s</code>).', tkey, ti, tval, t.id or '' ) )
end
end
end
-- Return any errors found.
for i, msg in ipairs( ret ) do
ret[ i ] = mw.ustring.format( '<span class="error">%s</span>', msg )
end
if #ret > 0 then
return mw.ustring.format( 'Found the following errors in %s:\n* %s', dataPage, table.concat( ret, '\n* ' ) )
else
return mw.ustring.format( 'No errors found in %s.', dataPage )
end
end
return p