Module:Track gauge/sandbox

From blackwiki
< Module:Track gauge
Revision as of 17:38, 29 September 2020 by Blackwikiadmin (talk | contribs) (1,000 revisions imported)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Documentation for this module may be created at Module:Track gauge/sandbox/doc

-- This module implements the {{RailGauge}} template.
local p = {}
local gaugeDataAll = nil

-----------------------------------------------------------------------------------
-- format_mm, to pattern '0016.5 mm' to support tablesorting and catsorting.
-----------------------------------------------------------------------------------
local function format_mm( id, suffix )
    return string.rep('0', 4 - (string.len(math.floor(tonumber(id))))) .. id ..
    '&nbsp;mm' .. (suffix or '')
end

-----------------------------------------------------------------------------------
-- anchors as target
-----------------------------------------------------------------------------------
local function anchors( gData )
    -- todo: distinct here or there anchors; and imp or met
    local html = require('Module:HtmlBuilder')
    local anchImp = ''
    local anchMet = ''
    local ft = ''
    local frac = ''
    local inch = ''

    if gData == nil then
        return ''
    end

    anchMet = gData.id .. ' mm'

    if gData.ft then
        ft = gData.ft .. ' ft'
    end
    if gData["in"] then
        inch = ' ' .. gData["in"]
    end
    if gData.num then
        frac = ' ' .. gData.num .. '/' .. gData.den .. ' in'
    elseif inch ~= '' then
        inch = inch .. ' in'
    end
    anchImp = ft .. inch .. frac

    local buildM = html.create().tag('div').attr('id', anchMet).attr('style', 'vertical-align:top')
    local buildI = html.create().tag('div').attr('id', anchImp).attr('style', 'vertical-align:top')

    return tostring(buildM) .. tostring(buildI)
end

-----------------------------------------------------------------------------------
-- catMentionsSize, categorize in 'Gauge size mentioned in page'
-----------------------------------------------------------------------------------
local function catMentionsSize( mmSize, catSort, pagename, show, plaintext )
    local cat

    -- Category page name. mmSize has format of id (like '16.5').
    cat = 'User:DePiep/sandbox' .. ' ' .. mmSize .. '&nbsp;mm' --SANDBOX
    if plaintext then
        return cat
    elseif show then
    	-- uses colon and wikilabel; no catsort
        local label = '|DraftCat:Articles with ' .. mmSize .. '&nbsp;mm' .. ' gauge'
        cat = '[[:'.. cat .. label .. ']]'
    else
        if (catSort or '') ~= '' then
            catSort = '|' .. catSort -- todo: add pagename
        else
            catSort = ''
        end
        cat = '[['.. cat .. catSort .. ']]'
    end

    return cat
end

-----------------------------------------------------------------------------------
-- catMentionsSizeParent, parent of all 'Cat:Gauge size mentioned x mm'
-----------------------------------------------------------------------------------
local function catMentionsSizeParent( show )
    local cat

    -- Category page name.
    cat = 'Category:Articles that mention a specific rail gauge'
    if show then
    	-- uses colon and wikilabel; no catsort
        cat = '[[:'.. cat .. '|Cat:Articles with RailGauge]]'
    else
        cat = '[['.. cat .. ']]'
    end

    return cat
end

-----------------------------------------------------------------------------------
-- prepareArgs, arguments coming from an #invoke or from a module
-----------------------------------------------------------------------------------
local function prepareArgs( 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
    -- searchKey is the cleaned value of [1]. [1] is kept as rawInput for error message
    local args = {}
    args["searchKey"] = ''
    args["rawInput"] = origArgs[1] or ''

    for k, v in pairs( origArgs ) do
        v = mw.text.trim( v )
        if k == 1 then
            -- Into lowercase, remove all whitespace, commas to create the search key
            v = mw.ustring.lower(mw.ustring.gsub( v, '[%s%,]', '' ))
            args[ 1 ] = v
            args["searchKey"] = v
        else
            args[ k ] = mw.ustring.lower( v )
        end
    end

    return args
end

-----------------------------------------------------------------------------------
-- getGaugeDataSet, find dataset for a single gauge
-----------------------------------------------------------------------------------
local function getGaugeDataSet( searchKey, units )
    if gaugeDataAll == nil then
        gaugeDataAll = mw.loadData( 'Module:RailGauge/data/sandbox' ) --SANDBOX
    end

    local gData = nil

    for i, gD in ipairs( gaugeDataAll ) do
        for j, alias in ipairs( gD.aliases ) do
            if alias == searchKey then
-- A hit by alias
if units then
    -- unit specific requested
    if gD.def1 == units then
        gData = gD
    end
else
    gData = gD
end
            end
            if gData then break end
        end
        if gData then
            break
        elseif tonumber(searchKey) ~= nil then -- seach by id
--todo: preferred return metric by default, when present
--todo: don't break, an alias could be OK further down the line
            if gD.id == searchKey then
                if units ~= nil then
                    if gD.def1 == units then
                        gData = gD
                    end
                else
                    gData = gD
                end
            end
            if gData then break end
        end
    end

    return gData
end

-----------------------------------------------------------------------------------
-- noWrap, adds span tags to prevent a string from wrapping.
-----------------------------------------------------------------------------------
local function noWrap( s )
    return mw.ustring.format( '<span class="nowrap">%s</span>', s )
end

-----------------------------------------------------------------------------------
-- frac, a slimmed-down version of the {{frac}} template (a nowrap to be added with the unit)
-----------------------------------------------------------------------------------
local function frac( whole, num, den )
    return mw.ustring.format(
        '<span class="frac">%s%s<sup>%s</sup>&frasl;<sub>%s</sub></span>',
        whole or '', whole and '<span class="visualhide">&nbsp;</span>' or '', num, den
    )
end

-----------------------------------------------------------------------------------
-- formatImp, formats imperial measurements into a single element
-----------------------------------------------------------------------------------
local function formatImp( gData, ulink, articleLink, pageName )
    local ret = {}
    local ft = gData.ft

    if ft then
        local ftlink = ulink and not articleLink and '[[Foot (unit)|ft]]' or 'ft'
        table.insert( ret, mw.ustring.format( '%s&nbsp;%s', ft, ftlink ) )
    end
    local inches = gData['in']
    local num = gData.num
    local den = gData.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, '&nbsp;' ) )

    if articleLink then
    	return '[[' .. pageName .. '|' .. gaugeSize .. ']]'
    else
        return gaugeSize
    end
end

-----------------------------------------------------------------------------------
-- formatMet, formats metric measurements into a single formatted element
-----------------------------------------------------------------------------------
local function formatMet( gData, ulink, articleLink, pageName )
    local m = gData.m
    local gaugeSize

    if m then
        local munit = ulink and not articleLink and '[[metre|m]]' or 'm'
        gaugeSize = noWrap( mw.ustring.format( '%s&nbsp;%s', m, munit ) )
    else
        local mm = gData.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&nbsp;%s', mm, mmunit ) )
    end

    if articleLink then
        return '[[' .. pageName .. '|' .. gaugeSize .. ']]'
    else
        return gaugeSize
    end
end

-----------------------------------------------------------------------------------
-- compose, puts together the two metric, imperial measures into an output string.
-----------------------------------------------------------------------------------
local function compose( args, gData )
    local definition = gData.def1
    local pageName = gData.pagename or nil
    local imp = formatImp( gData, args.unitlink == 'on', args.lk=='on' and definition=='imp' and pageName, pageName)
    local met = formatMet( gData, args.unitlink == 'on', args.lk=='on' and definition=='met' and pageName, pageName)
    local first = args.first or gData.def1

    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' or disp == '/' then
            formatText = '/&#x200b;%s'
        elseif disp == 'or' then
            formatText = ' or %s'
        elseif disp == '=' then
            formatText = ' = %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

-----------------------------------------------------------------------------------
-- main, the basic flow of the module.
-----------------------------------------------------------------------------------
function p.main( frame )
    local args = nil
    local gData = nil
    local title = mw.title.getCurrentTitle()

    args = prepareArgs( frame )
    -- Get the data set for this gauge from /data subpage
    gData = getGaugeDataSet ( args.searchKey )

    -- Categorise the page if no gauge information was found.
    if gData == nil then
        local category = ''
        if title:inNamespaces(0, 14) then --main=0,templ=10,cat=14--SANDBOXcolon:cat
            category = mw.ustring.format(
               '[[:Category:Articles with template RailGauge with unrecognized input|%s, %s]]',
               args["rawInput"] or ' ', title.text
            )
        end
        return ( args["rawInput"] or '' ) .. category
    end

    -- Assemble the output.
    local ret = {}
    table.insert( ret, compose( args, gData ) )
    local gaugeName = gData.name
    local gaugeLink = gData.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 maintCatSortDepr = gData.maintcatsort --category to be DEPRECATED, see 'Categorize all' topic (3 April 2014)
    if maintCatSortDepr ~= nil then
        local addMaintCat = args.addcat
        if addMaintCat ~= 'off' and addMaintCat ~= 'no' then
            if title.namespace == 0 or title.namespace == 14 then
                local category = ''
                category = mw.ustring.format(
                    '[[Category:Articles with template RailGauge that may need attention|%s, %s]]',
                    maintCatSortDepr, title.text)
                table.insert( ret, ' ' .. category )
            end
        end
    end

    return table.concat( ret )
end

-----------------------------------------------------------------------------------
-- checkData, 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 def1 values.
    for ti, t in ipairs( data ) do
        local def1 = t.def1
        if def1 ~= 'imp' and def1 ~= 'met' then
            table.insert( ret, mw.ustring.format( 'Invalid def1 value "%s" found for gauge %d (id: <code>%s</code>).', def1 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
    -- Added April 2014: alias should not double with another id (imp and mm not ambiguous)
    local self_id = ''
    local self_def1 = ''
    for ti, t in ipairs( data ) do
        self_id = t.id
        self_def1 = t.def1
        for iC, aliasCheck in ipairs( t.aliases ) do
            if tonumber( aliasCheck ) ~= nil then
                if self_id ~= aliasCheck then
                    for iTwo, tTwo in ipairs( data ) do
                        if aliasCheck == tTwo.id then
table.insert( ret, mw.ustring.format( 'Input alias %s (%s) from <code>id=%s mm</code> ambiguous with gauge id=<code>%s mm</code> (%s)', aliasCheck, self_def1, self_id, tTwo.id, tTwo.def1 ) )
                        end
                    end
                end
            end
        end
    end

    -- temp check
if false then
    local strC = ''
    local self_id = ''
    local self_def1 = ''
    for ti, t in ipairs( data ) do
        self_id = t.id
        self_def1 = t.def1
        for iC, aliasCheck in ipairs( t.aliases ) do
-- strC = ' || ' .. self_id .. ' || ' .. self_def1 .. '|| ' .. aliasCheck
-- table.insert( ret, strC )
        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

-----------------------------------------------------------------------------------
-- documentHeader
-----------------------------------------------------------------------------------
local function documentHeader( sTitle, numberOfEntries, collapsibleState )
    catMparent = catMentionsSizeParent( true )
    local sortColHeaders = ''
    local sortClass = ''

if collapsibleState == nil then
    collapsibleState = 'collapsible uncollapsed' --dev: autocollapse?
else
    collapsibleState = 'collapsible ' .. collapsibleState
end

    if ( numberOfEntries or 0 ) > 1 then
        sortColHeaders = '' ..
        '\n|-' ..
        '\n! &nbsp; || || || || || ||'
        sortClass = 'sortable'
    end

    -- 7 columns
    return '' ..
    '\n{| class="wikitable ' .. sortClass .. ' '  .. collapsibleState .. '" ' ..
    'style="text-align:right; width:100%; background:#B3FFD1;" ' ..
    '\n|+ Rail gauge' .. (sTitle or '') ..
    '\n|- style="background:orange;" |' ..
    '\n! Table !! 2 !! 3 !! 4 !! 5 !! 6 <br>'  .. catMparent .. '!! note' ..
    sortColHeaders

--    '\n|-' ..    '\n! &nbsp; || || || || || ||'
end

-----------------------------------------------------------------------------------
-- documentFooter
-----------------------------------------------------------------------------------
local function documentFooter()
    return '\n|}'
end

-----------------------------------------------------------------------------------
-- DEPR documentOneGauge, create documentation for a gauge; single tablerow
-----------------------------------------------------------------------------------
function p.documentOneGauge( frame ) --, gData )
    local args = {}

    if gData == nil then
        args = prepareArgs( frame )
        gData = getGaugeDataSet ( args.searchKey )
    end

    if gData == nil then
        return ''
    else
        return documentHeader() .. documentGaugeRow( gData ) .. documentFooter()
    end
end

-----------------------------------------------------------------------------------
-- DEPR documentAll, list all defined gauges
-----------------------------------------------------------------------------------
function p.documentAll()
    if gaugeDataAll == nil then
        gaugeDataAll = mw.loadData( 'Module:RailGauge/data/sandbox' ) --SANDBOX
    end
    local ret = {}
    local dDoc = ''

    for i, v in ipairs( gaugeDataAll ) do
        -- v now has gData for a single gauge
        dDoc = documentGaugeRow( v )
        table.insert ( ret, dDoc )
        if i > 12 then break end
    end
    table.sort( ret )

    return documentHeader() .. table.concat( ret, ' ') .. documentFooter()
end

-----------------------------------------------------------------------------------
-- DEPR documentGaugeRow: create documentation row for a gauge
-----------------------------------------------------------------------------------
function documentGaugeRow( gData )
    local gD1 = nil
    local gD2 = nil
    local html = require('Module:HtmlBuilder')
    local htmlString = ''
    local rowsplit = '<div style="border-top:1px solid #ddd; height:1px;"/>'

    if gData == nil then
        return ''
    end

    -- Values independent of met/imp def1:
    local id = gData.id
    local title = mw.title.getCurrentTitle()
    local anchors = anchors( gData )
    local catSort = title.text --?
    local sortKey = format_mm( id )
    htmlString = html.create()
        .tag('div').addClass('sortkey').wikitext( sortKey )
    sortKey = tostring( htmlString )

    local catM = catMentionsSize( id, catSort, title, true )
    htmlString = html.create()
        .tag('div').css('text-align', 'left').wikitext(catM)
    catM = tostring( htmlString )

    -- def1
local def1 = {}
    table.insert( def1, 1, gD1.def1 )

    -- pagename (link-to page)
local linkPage = {}
    linkPage[1] = gD1.pagename or ''
    linkPage['sep'] = ''

    if gD2 then
        if gD1.id ~= gD2.id then
            devNote = gD1.id .. '<=>' .. gD2.id
        end
        table.insert( def1, 2, gD2.def1 )
        linkPage[2] = gD2.pagename or ''
    	if linkPage[1] == linkPage[2] then -- could be two blanks
           linkPage[2] = nil -- nullify
    	else -- even when one is blank
            --linkPage[2] = nil
    	end
    else
    	--D1 only, could be met or imp
    end

    -- linkPage fmt
    for i = 1, 2 do
    	if  linkPage[i] and ( linkPage[i] ~= '' ) then
            if string.len(linkPage[i]) > 25 then
                linkPage[i] = '[[' .. linkPage[i] .. ']]'
            else
                local label
            	htmlString = html.create()
               		.tag('span').wikitext(linkPage[i]).addClass('nowrap')
            	label = tostring(htmlString)
            	linkPage[i] = '[[' .. linkPage[i] .. '|' .. label .. ']]'
            end
            htmlString = html.create()
                .tag('div').css('text-align', 'left').wikitext(linkPage[i])
            linkPage[i] = tostring(htmlString)
        end
    end

local measure = {}
    measure[1] = formatMet( gD1 )
    measure[2] = formatImp( gD1 ) -- from gD1 both
    if def1[1] == 'met' then
        htmlString = html.create()
            .tag('div').wikitext(measure[1]).css('font-weight', 'bold')
        measure[1] = tostring(htmlString)
    end
    if ( def1[1] == 'imp' ) or ( def1[2] == 'imp' ) then
        htmlString = html.create()
            .tag('div').wikitext(measure[2]).css('font-weight', 'bold')
        measure[2] = tostring(htmlString)
    end

local aliases = {}
    aliases[1] = {}
    aliases[2] = {}
    for i, v in ipairs( gD1.aliases ) do
        if tonumber(v) == nil then
            table.insert(aliases[1], v)
        end
    end
    table.sort( aliases[1] )
    if gD2 then
        for i, v in ipairs( gD2.aliases ) do
            if tonumber(v) == nil then
                table.insert(aliases[2], v)
            end
        end
        table.sort( aliases[2] )
        aliases.sep = rowsplit
    end
    local aliasString  = table.concat(aliases[1], '; ') ..
        (aliases.sep or '' ) .. table.concat( aliases[2], '; ' )

--createcat
local createCat = createCatMentionsSize( id )

    local row = {}
    table.insert( row, anchors .. sortKey .. measure[1]) --1
    table.insert( row, sortKey .. measure[2] ) --2
    table.insert( row, table.concat(def1, rowsplit) ) --3
    table.insert( row, aliasString ) --4
    table.insert( row, table.concat( linkPage, rowsplit ) ) --5
    table.insert( row, catM ) --6
    table.insert( row, '[' .. createCat .. ' create]') --7

    return '' ..
    '\n|-' ..
    '\n| ' .. table.concat( row, ' || ' )
end

-----------------------------------------------------------------------------------
-- createCatMentionsSize, creatte and preload control
-----------------------------------------------------------------------------------
function createCatMentionsSize( id )
local catM
    --catMentionsSize( mmSize, catSort, pagename, show, plaintext )
    catM = catMentionsSize( id, nil, nil, nil, true )

    local qry = mw.uri.parseQueryString( 'action=edit&preload=Template:RailGauge/preload-categorypage-railgauge-mentionings' )

    return tostring( mw.uri.fullUrl( catM, qry ) )
end

--===================================================
-- dev: INPUT TO ID
--===================================================
local function fromInputToId( input1 )

    if gaugeDataAll == nil then
        gaugeDataAll = mw.loadData( 'Module:RailGauge/data/sandbox' )
    end

    local id = ''
    local searchKey = mw.ustring.lower(mw.ustring.gsub( input1, '[%s%,]', '' )) --searchkey cleanup

    for i, gD in ipairs( gaugeDataAll ) do
        for j, alias in ipairs( gD.aliases ) do
            if alias == searchKey then
id = gD["id"]
            end
            if id ~= '' then break end
        end
        if id ~= '' then break end
    end

    return id
end

--===================================================
-- dev: ID to rowset -- idtorow SET = 3 or four entries in a row
--===================================================
local function fromIDtoSet( id )
local TableTools = require('Module:TableTools')
local set = {}
local row = {}
local rowsplit = '<div style="border-top:1px solid green; height:1px;"/>'
local html = require('Module:HtmlBuilder')
local htmlString = ''
local note = '' 

    note = note .. 'SET: ' .. id .. ': '

    for i, rgD in ipairs( gaugeDataAll ) do
        if id == rgD["id"] then
            note = note .. i .. ' HIT: ' .. id .. '='.. rgD["def1"]

            if rgD["def1"] == 'met' and set[1] == nil then
                set[1] = rgD
                note = note .. '-metA'
            elseif rgD["def1"] == 'imp' and set[2] == nil then
                set[2] = rgD
                note = note .. '-impB'
            else
                note = note .. 3+TableTools.size( set ) .. '-variC'
                set[3+TableTools.size( set )] = rgD
            end
            set = TableTools.compressSparseArray( set )
            -- result: the set (table) with entries present in data, in sequence:
            -- 1. met, 2. imp, 2, 3, 4, ... any & all second met/imp entries
        end
    end

    note = 'SSS' .. note .. ' = ' .. #set .. 'TTT<br>'
    ---set complete & clean
--= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
if set[1] == nil then
    -- row structure with colspan
    return '' ..
    '\n|-' ..
    '\n| colspan=7 style="color:red; text-align:left;" |' ..
    'Error using [[Template:RailGauge/autodocumentation|RailGauge/autodocumentation]]:' ..
    ' No rail gauge defined for ' .. id .. ' mm.'

end

    -- Values independent of met/imp def1:
    local title = mw.title.getCurrentTitle()
    local anchors = anchors( set[1] )
    local catSort = title.text --?
    local sortKey = format_mm( id )
    htmlString = html.create()
        .tag('div').addClass('sortkey').wikitext( sortKey )
    sortKey = tostring( htmlString )

    local catM = catMentionsSize( id, catSort, title, true )
    htmlString = html.create()
        .tag('div').css('text-align', 'left').wikitext(catM)
    catM = tostring( htmlString )

    -- def1
local def1 = {}
    table.insert( def1, 1, set[1].def1 )

    -- pagename (link-to page)
local linkPage = {}
    linkPage[1] = set[1].pagename or ''
    linkPage['sep'] = ''

    if set[2] then
        table.insert( def1, 2, set[2].def1 )
        linkPage[2] = set[2].pagename or ''
    	if linkPage[1] == linkPage[2] then -- could be two blanks
           linkPage[2] = nil -- nullify
    	else -- even when one is blank
            --linkPage[2] = nil
    	end
    else
    	--D1 only, could be met or imp
    end

    -- linkPage fmt
    for i = 1, 2 do
    	if  linkPage[i] and ( linkPage[i] ~= '' ) then
            if string.len(linkPage[i]) > 25 then
                linkPage[i] = '[[' .. linkPage[i] .. ']]'
            else
                local label
            	htmlString = html.create()
               		.tag('span').wikitext(linkPage[i]).addClass('nowrap')
            	label = tostring(htmlString)
            	linkPage[i] = '[[' .. linkPage[i] .. '|' .. label .. ']]'
            end
            htmlString = html.create()
                .tag('div').css('text-align', 'left').wikitext(linkPage[i])
            linkPage[i] = tostring(htmlString)
        end
    end

local measure = {}
    measure[1] = formatMet( set[1] )
    measure[2] = formatImp( set[1] ) -- from gD 1 both
    if def1[1] == 'met' then
        htmlString = html.create()
            .tag('div').wikitext(measure[1]).css('font-weight', 'bold')
        measure[1] = tostring(htmlString)
    end
    if ( def1[1] == 'imp' ) or ( def1[2] == 'imp' ) then
        htmlString = html.create()
            .tag('div').wikitext(measure[2]).css('font-weight', 'bold')
        measure[2] = tostring(htmlString)
    end

local aliases = {}
    aliases[1] = {}
    aliases[2] = {}
    for i, v in ipairs( set[1].aliases ) do
        if tonumber(v) == nil then
            table.insert(aliases[1], v)
        end
    end
    table.sort( aliases[1] )
    if gD2 then
        for i, v in ipairs( set[2].aliases ) do
            if tonumber(v) == nil then
                table.insert(aliases[2], v)
            end
        end
        table.sort( aliases[2] )
        aliases.sep = rowsplit
    end
    local aliasString  = table.concat(aliases[1], '; ') ..
        (aliases.sep or '' ) .. table.concat( aliases[2], '; ' )

--createcat
local createCat = createCatMentionsSize( id )

    table.insert( row, anchors .. sortKey .. measure[1]) --1
    table.insert( row, sortKey .. measure[2] ) --2
    table.insert( row, table.concat(def1, rowsplit) ) --3
    table.insert( row, aliasString ) --4
    table.insert( row, table.concat( linkPage, rowsplit ) ) --5
table.insert( row, '6' ) --6
table.insert( row, '7' ) --7
    --table.insert( row, catM ) --6
    --table.insert( row, '[' .. createCat .. ' create]') --7

    return '' ..
    '\n|-' ..
    '\n| ' .. table.concat( row, ' || ' )

end

-----------------------------------------------------------------------------------
-- dev:CONTROL doc. To be: single #invoke call for all selfdoc requests (single, multiple, all)
-----------------------------------------------------------------------------------
function p.documentGaugeControl( frame )
---local note = ''
    local args = {}
    local RGlist = {}

    args = prepareArgs( frame )

    if gaugeDataAll == nil then
        gaugeDataAll = mw.loadData( 'Module:RailGauge/data/sandbox' )
    end

    for i, RG in ipairs( gaugeDataAll ) do
       table.insert( RGlist, tonumber( RG["id"] ) )
       if i >= 1 then break end
    end

    ---note = note .. '#before' .. #RGlist .. ' '

    local TableTools = require('Module:TableTools')
    RGlist = TableTools.removeDuplicates( RGlist )
    table.sort( RGlist )

    ---note = note .. ' #after' .. #RGlist .. '; ' .. table.concat(RGlist, '; ')

local entryRow = {}
    table.insert( entryRow, fromIDtoSet( tostring(4321) ) )
    for i, id in ipairs( RGlist ) do
        table.insert( entryRow, fromIDtoSet( tostring(id) ) )
    end
--rm dev: note
    return documentHeader() .. table.concat( entryRow, '' ) .. documentFooter()

end

return p