# Module:Track gauge

-- This module implements the template.  
-- Data is in Module:Track gauge/data  
local p = {}  
local gaugeDataAll = nil  
local dataPageName = 'Module:Track gauge/data' -- set /data/sandbox here  
---
-- prepareArgs -- Normalise Arguments coming from an #invoke or from a module  
---
local function prepareArgs  
local origArgs  
if == mw.getCurrentFrame then  
origArgs = :getParent.args  
for k, v in pairs(.args) do  
origArgs = .args  
break  
end  
else  
origArgs =  
end  
local args = {}  
-- searchAlias is the cleaned value of args[1]. args[1] is kept as rawInput for error message  
local searchAlias = ''  
local rawDisp  
for k, v in pairs(origArgs) do  
if tonumber(k) == nil then -- Named argment  
if k == 'disp' then  
rawDisp = v -- Keep raw disp input to pass through plain (wiki)text  
args[k] = mw.ustring.lower(v)  
elseif k == 'first' then  
v = mw.ustring.lower(v)  
if v == 'met' or v == 'metric' then  
v = 'met'  
elseif v == 'imp' or v == 'imperial' then  
v = 'imp'  
else k = 'trashparam_first' end  
args[k] = v  
elseif k == 'nowrap' or k == 'wrap' then -- wrap=y deprecated; reading: nowrap=off  
v = mw.ustring.lower(v)  
if v == '' or v == 'off' or v == 'on' or v == 'all' then  
elseif v == 'inline' or (k == 'wrap' and v == 'y') then  
v = 'off'  
else v = '' end  
args['nowrap'] = v  
else  
args[k] = mw.ustring.lower(v)  
end  
else  
args[k] = v -- Keep rawInput in [1] for error message  
if k == 1 then  
-- Unnamed argument, the alias to be searched  
-- Cleanup  
searchAlias = p.normaliseAliasInput(v)  
end  
end  
end  
args['searchAlias'] = searchAlias  
if rawDisp then args['rawDisp'] = rawDisp end  
return args  
end  
---
-- normaliseAliasInput  
---
function p.normaliseAliasInput(aliasIn)  
local a  
a = mw.ustring.lower(mw.ustring.gsub(aliasIn, '[%s%,]', ''))  
a = mw.ustring.gsub(a, '&nbsp;', '')  
a = mw.ustring.gsub(a, 'gauge$', '')  
a = mw.ustring.gsub(a, "'", "ft")  
a = mw.ustring.gsub(a, '"', 'in')  
a = mw.ustring.gsub(a, '⁄', '/')  
a = mw.ustring.gsub(a, '&frasl;', '/')  
return a  
end  
---
-- debugReturnArgs -- Debug function.  
---
function p.debugReturnArgs  
local args = prepareArgs  
local retArgs = {}  
for k, a in pairs(args) do  
table.insert(retArgs, k .. '=' .. a)  
end  
return 'Args: ' .. table.concat(retArgs, '; ')  
end  
---
-- getTrackGaugeEntry -- Find entry data for a single gauge (alias)  
---
function p.getTrackGaugeEntry(searchAlias)  
gaugeDataAll = mw.loadData(dataPageName)  
if searchAlias == '' then  
return nil  
end  
local tgEntry = nil  
for i, tgEntry in ipairs(gaugeDataAll) do  
for j, alias in ipairs(tgEntry.aliases) do  
if alias == searchAlias then  
return tgEntry  
end  
end  
end  
end  
---
-- noWrap -- Add span tags to prevent a string from wrapping.  
---
local function noWrap(s)  
return mw.ustring.format('<span>%s</span>', s)  
end  
---
-- frac -- A slimmed-down version of the template (a nowrap is to be added with the unit)  
---
local function frac(whole, num, den)  
return mw.ustring.format(  
'<span>%s%s<sup>%s</sup>&frasl;<sub>%s</sub></span>',  
whole or *, whole and '<span>&nbsp;</span>' or *,  
num, den)  
end  
---
-- catMentions -- Wikicode for "article mentions gauge" categories  
---
function p.catMentions(tgEntry, sortlabel, doReturn)  
local ns = 'Category:'  
local cat  

if tgEntry == nil then  
-- Parent, the container cat  
cat = 'Articles that mention a specific track gauge'  
else  
cat = 'Articles that mention track gauge ' .. tgEntry.id .. ' mm'  
end  
-- Argument 'label' can be used to add a catsort. Catsort is not used (as of 20 May 2014)  
if sortlabel ~= nil then  
sortlabel = '|' .. sortlabel  
else  
sortlabel = ''  
end  
if doReturn ~= nil then  
if doReturn == 'fullpagename' then  
return ns .. cat  
elseif doReturn == 'pagename' then -- plaintext, no namespace  
return cat  
elseif doReturn == 'show' then -- colontrick  
return ':' .. ns .. cat .. sortlabel .. ''  
else -- unknown arg value  
return ns .. cat  
end  
else -- Returns straight categorisation (wikitext)  
return * .. ns .. cat .. sortlabel .. *  
end  
end  
---
-- formatImp -- Formats imperial units size into a single text element  
---
function p.formatImp(tgEntry, measurementToLink, setNowrap, addUnitlink)  
local ret = {}  
local ft = tgEntry.ft  
if ft then  
local ftlink = addUnitlink and measurementToLink ~= 'imp' and 'ft' or 'ft'  
table.insert(ret, mw.ustring.format('%s&nbsp;%s', ft, ftlink))  
end  
local inches = tgEntry['in']  
local num = tgEntry.num  
local den = tgEntry.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 = addUnitlink and measurementToLink ~= 'imp' and 'in' or 'in'  
table.insert(ret, incheslink)  
end  
local gaugeSize  
if setNowrap then  
gaugeSize = noWrap(table.concat(ret, '&nbsp;'))  
else  
gaugeSize = table.concat(ret, '&nbsp;')  
end  
if measurementToLink == 'imp' and tgEntry.pagename ~= nil then  
return mw.ustring.format('%s', tgEntry.pagename, gaugeSize)  
else  
return gaugeSize  
end  
end  
---
-- formatMet -- Formats metric measurements into a single formatted text element. Public for autodocument  
---
function p.formatMet(tgEntry, measurementToLink, setNowrap, addUnitlink)  
local m = tgEntry.m  
local gaugeSize  
if m then  
local mUnit = addUnitlink and measurementToLink ~= 'met' and 'm' or 'm'  
gaugeSize = mw.ustring.format('%s&nbsp;%s', m, mUnit)  
else  
local mm = tgEntry.mm  
mm = tonumber(mm)  
if mm then  
mm = mw.getContentLanguage:formatNum(mm)  
end  
local mmUnit = addUnitlink and measurementToLink ~= 'met' and 'mm' or 'mm'  
gaugeSize = mw.ustring.format('%s&nbsp;%s', mm, mmUnit)  
end  
if setNowrap then  
gaugeSize = noWrap(gaugeSize)  
end  
if measurementToLink == 'met' and tgEntry.pagename ~= nil then  
return mw.ustring.format('%s', tgEntry.pagename, gaugeSize)  
else  
return gaugeSize  
end  
end  
---
-- formatAltName  
---
function formatAltName(tgEntry, addGaugeName, addGaugeNameLink, disp, setNowrap, engvar)  
-- Assumed: at least one to add is true.  
if tgEntry.name == nil then  
-- Not checked: link does exist alone  
return ''  
end  
local retAlt = {}  
if disp == 'br' then  
table.insert(retAlt, '  
')  
else  
table.insert(retAlt, ' ')  
end  
if setNowrap then  
table.insert(retAlt, '<span>')  
end  
if addGaugeNameLink then  
if engvar == 'en-us' then  
-- Current implementations (2016): metER for metRE (1000-met, 1009-met)  
table.insert(retAlt, tgEntry.en_US_link or tgEntry.link or tgEntry.name)  
else  
table.insert(retAlt, tgEntry.link or tgEntry.name)  
end  
else -- so must be unlinked .name to add  
if engvar == 'en-us' then  
-- Current implementations (2016): metER for metRE (1000-met, 1009-met)  
table.insert(retAlt, tgEntry.en_US_name or tgEntry.name)  
else  
table.insert(retAlt, tgEntry.name)  
end  
end  
if setNowrap then --close tag  
table.insert(retAlt, '</span>')  
end  
return table.concat(retAlt, '')  
end  
---
-- main -- The basic module  
---
function p.main  
-- In general: the tgEntry object (from TG/data) is passed to the functions, while arguments are processed here.  
local title = mw.title.getCurrentTitle  
local args = prepareArgs  
local tgEntry = p.getTrackGaugeEntry(args.searchAlias)  

-- Categorise & preview warning when no track gauge definition was found.  
if tgEntry == nil then  
local input = args[1] or ''  
local errorTail = ''  

if :preprocess("") == "" then  
errorTail = '<div><strong>Warning:</strong> ' .. 'Track gauge ' .. (input .. ' ' or '') .. 'not in List of defined track gauges (talk).' .. ' (This message is shown only in preview.)</div>'  
end  

if title:inNamespaces(0, 14) then -- mainspace and category space  
errorTail = errorTail .. ""  
end  
return input .. errorTail  
end  

-- Check and set args & tgEntry props: disp, first, nowrap, first  
local disp = args.disp or ''  
local first = args.first or tgEntry.def1  
local unitlink = args.unitlink or ''  
local nowrap = args.nowrap or ''  
local setNowrapElement = (nowrap == '' or nowrap == 'off') -- To prevent nested nowrap tags  
local measurementToLink  
if args.lk == 'on' then  
if disp == '1' then  
measurementToLink = first -- Can make metric text links to the imp linked page  
else  
measurementToLink = tgEntry.def1 -- When first=swapped, this could link 2nd measure.  
end  
end  
-- String the text elements together (compose the return table)  
local ret = {}  
-- nowrap opening tag  
if nowrap == 'all' or nowrap == 'on' then  
table.insert(ret, '<span>')  
end  
-- First measure  
if first == 'met' then  
table.insert(ret,  
p.formatMet(tgEntry, measurementToLink, setNowrapElement, unitlink == 'on'))  
else  
table.insert(ret,  
p.formatImp(tgEntry, measurementToLink, setNowrapElement, unitlink == 'on'))  
end  
-- The joint and the second measure  
if disp == '1' then  
else  
local joinText = ''  
local closeDisp = ''  
if disp == 's' or disp == '/' then  
joinText = '&nbsp;/ ' --spaces  
elseif disp == 'br' then  
joinText = '  
('  
closeDisp = ')'  
elseif disp == '[' or disp == '[]' then  
joinText = ' ['  
closeDisp = ']'  
elseif disp ~= '' then -- Is anytext  
joinText = ' ' .. args['rawDisp'] .. ' '  
else  
joinText = ' ('  
closeDisp = ')'  
end  
table.insert(ret, joinText)  
if first ~= 'met' then  
table.insert(ret,  
p.formatMet(tgEntry, measurementToLink, setNowrapElement, unitlink == 'on'))  
else  
table.insert(ret,  
p.formatImp(tgEntry, measurementToLink, setNowrapElement, unitlink == 'on'))  
end  
table.insert(ret, closeDisp) -- Could be ''  
end  
if nowrap == 'on' then -- Closing tag  
table.insert(ret, '</span>')  
end  
-- Alternative name  
if args.al == 'on' or args.allk == 'on' then  
local setNowrapAltname = (nowrap == '' or nowrap == 'on') -- Logic applied to prevent nested nowrap tags  
table.insert(ret, formatAltName(tgEntry, args.al == 'on', args.allk == 'on', disp, setNowrapAltname, args.engvar))  
end  
-- Closing nowrap tag  
if nowrap == 'all' then  
table.insert(ret, '</span>')  
end  

-- Category mentionings (maintenance)  
if args.addcat or '' == 'no' then  
-- No categorization  
elseif title:inNamespaces(0) then  
-- switched off per  
-- 2016-12-19  
-- table.insert(ret, p.catMentions(tgEntry))  
end  

-- Now sting the table together  
return table.concat(ret, '')  
end  

return p  
--20161219: maintenance categorisation switched off per CfD  
--20170602: fix bug, show name when al=on  
--20180708: show preview warning when gauge is not in list  
--20190124: with disp=/ (slash) value separator: surround by spaces