Module:RailGauge
From Wiki
Documentation for this module may be created at Module:RailGauge/doc
-- 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 nowrap">%s<sup>%s%s</sup>⁄<sub>%s</sub></span>', whole or '', whole and ' ' or '', num, den ) end -- Formats imperial measurements. Same functionality as {{RailGauge/format imp}}. local function formatImp( data, link ) local ret = {} local ft = data.ft if ft then local ftlink = link 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 = link and '[[inch|in]]' or 'in' table.insert( ret, incheslink ) end return noWrap( table.concat( ret, ' ' ) ) end -- Formats metric measurements. Same functionality as {{RailGauge/format met}} local function formatMet( data, link ) local m = data.m if m then local mlink = link and '[[metre|m]]' or 'm' return noWrap( mw.ustring.format( '%s %s', m, mlink ) ) else local mm = data.mm mm = tonumber( mm ) if mm then mm = mw.getContentLanguage():formatNum( mm ) end local mmlink = link and '[[millimetre|mm]]' or 'mm' return noWrap( mw.ustring.format( '%s %s', mm, mmlink ) ) 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 imp = formatImp( data, args.unitlink == 'on' ) local met = formatMet( data, args.unitlink == 'on' ) 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:Pages with incorrect use of RailGauge template|%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, ' ' .. gaugeLink ) elseif args.al == 'on' and gaugeName then table.insert( ret, ' ' .. gaugeName ) 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' 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