-- 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(frame) 	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 	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, ' ', ) 	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, '⁄', '/') 	return a	 end  -- debugReturnArgs -- Debug function.  function p.debugReturnArgs(frame) 	local args = prepareArgs(frame) 	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('%s', 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( 		'%s%s%s⁄%s', 		whole or , whole and ' ' 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 'ফুট' or 'ফুট' 		table.insert(ret, mw.ustring.format('%s %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 'ইঞ্চি' or 'ইঞ্চি' 		table.insert(ret, incheslink) 	end 	local gaugeSize 	if setNowrap then 		gaugeSize = noWrap(table.concat(ret, ' ')) 	else 		gaugeSize = table.concat(ret, ' ') 	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 'মিটার' or 'মিটার' 		gaugeSize = mw.ustring.format('%s %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 'মিলিমিটার' or 'মিলিমিটার' 		gaugeSize = mw.ustring.format('%s %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, '') 	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, '') 	end 	return table.concat(retAlt, ) end  -- main -- The basic module  function p.main(frame) 	-- 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(frame) 	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 frame:preprocess("") == "" then  				errorTail = '<div class="hatnote" style="color:red">Warning: ' .. '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 .. "Category:Articles using template 'Track gauge' with unrecognized input" 		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, '') 	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 = ' / ' --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, '') 	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 Wikipedia:Categories_for_discussion/Log/2016_December_6#Category:Articles_that_mention_a_specific_track_gauge 		-- 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