Module:Track gauge/autodocument/sandbox

-- This module documents the track gauges

-- as defined in module:Track gauge/data.

-- General note: "id" is the size-id (in mm). With this id, definitions can vary (mm, ft/in, name)

-- Alias (the normalised input value) is the primary search term

local p = {}

local getArgs = require('Module:Arguments').getArgs

local modMath = require('Module:Math')

local modTrackGauge = require('Module:Track gauge') -- sandbox here

local dataPageName = 'Module:Track gauge/data/sandbox' -- sandbox here

local gaugeDataAll = nil

local tableTools = require('Module:tableTools')

-- global counters (to keep between the id-row building calls)

local ttlSizeClassCount = {}

local ttlAliasCount = 0

local ttlEntries = 0

local ttlUnitCount = {}

local ttlAltNameCount = 0

local ttlAltName = {}

local ttlLinkCount = 0

local ttlContentCatsCount = 0

local ttlMentioningCatsCount = 0

local ttlMentioningPageCount = 0

local ttlListedRange = {}

-----------------------------------------------------------------------------------

-- prepareArgs -- Arguments coming from an #invoke or from a module

-----------------------------------------------------------------------------------

local function prepareArgs(frame)

local origArgs = getArgs(frame)

-- Trim whitespace, make lower-case and remove blank arguments for all arguments

-- searchAlias is the cleaned value of [1]. [1] is kept as rawInput for error message

local args = {}

args['searchAlias'] = ''

args['rawInput'] = origArgs[1] or ''

for k, v in pairs(origArgs) do

if tonumber(k) == nil then

-- Named argument

if k == 'docsortlabel' then -- not in TG

args[k] = v

else

args[k] = mw.ustring.lower(v)

end

else

-- Unnamed argument, alias to be searched

args[k] = modTrackGauge.normaliseAliasInput(v)

if k == 1 then

args['searchAlias'] = args[1]

end

end

end

return args

end

-----------------------------------------------------------------------------------

-- formatUnitPlaintext

-- Pattern '00016.5 mm' for table.sort and catsort.

-----------------------------------------------------------------------------------

local function formatUnitPlaintext(tgEntry, unit, fmtZeroPadding, toFracChar)

-- Returns plaintext (ASCII) only. No css.

if tgEntry == nil then

return ''

end

if (unit or tgEntry.def) == 'imp' then

-- imperial

local ft = ''

local inch = ''

local frac = ''

if tgEntry.ft then

ft = tgEntry.ft .. ' ft'

end

if tgEntry.num then

frac = ' ' .. tgEntry.num .. '/' .. tgEntry.den

if toFracChar then

-- as used in contentCat pagenames

if frac == ' 1/8' then frac = '⅛'

elseif frac == ' 1/4' then frac = '¼'

elseif frac == ' 3/8' then frac = '⅜'

elseif frac == ' 1/2' then frac = '½'

elseif frac == ' 3/4' then frac = '¾'

elseif frac == ' 7/8' then frac = '⅞'

else

frac = frac .. ' (error: fraction character missing in module:Track gauge)'

end

end

if tgEntry['in'] then

frac = ' ' .. tgEntry['in'] .. frac .. ' in'

else

frac = ' ' .. frac .. ' in'

end

else

if tgEntry['in'] then

inch = ' ' .. tgEntry['in'] .. ' in'

end

end

return mw.text.trim(ft .. inch .. frac)

else

-- metric (mm)

if fmtZeroPadding == nil or tonumber(fmtZeroPadding) <= 0 then

return tgEntry.id .. ' mm'

else

return string.rep('0', fmtZeroPadding - math.floor(math.log10(tonumber(tgEntry.id))) - 1)

.. tgEntry.id .. ' mm'

end

end

end

-----------------------------------------------------------------------------------

-- document data-sort-value

-----------------------------------------------------------------------------------

local function documentdatasortvalue(tgEntry)

local s = formatUnitPlaintext(tgEntry, 'met', 5)

return tostring(mw.html.create():tag('span'):attr('data-sort-value', s))

end

-----------------------------------------------------------------------------------

-- catSortFromTitle

-- Currently finds "600 mm" when at end of title, then returns "0600 mm" (for catSort).

-- Blank when not found. Used for cat:mentions category page.

-----------------------------------------------------------------------------------

function p.catSortFromTitle()

local title = mw.title.getCurrentTitle()

local catSort = string.match(title.text, '%s(%d+%.?%d*)%smm$') or ''

if catSort ~= '' then

catSort = string.rep('0', 4 - math.floor(math.log10(tonumber(catSort))) - 1)

.. catSort .. ' mm'

end

if catSort == '' then

return '*'

else

return catSort

end

end

-----------------------------------------------------------------------------------

-- documentGaugeClass

-----------------------------------------------------------------------------------

local function documentGaugeClass(tgEntry, countMentionings)

local size = tonumber(tgEntry.id or 0)

local j

if size > 1435 then

j = 5

elseif size == 1435 then

j = 4

elseif size > 500 then

j = 3

elseif size >= 100 then

j = 2

elseif size > 0 then

j = 1

else

j = 6

end

ttlSizeClassCount[j][2] = ttlSizeClassCount[j][2] +1

ttlSizeClassCount[j][4] = ttlSizeClassCount[j][4] + (countMentionings or 0)

return '' .. ttlSizeClassCount[j][1] .. ''

end

-----------------------------------------------------------------------------------

-- anchor -- Anchor text *here* is: ; anchor *there* should be: #1000 mm.

-----------------------------------------------------------------------------------

local function anchor(tgEntry, unit, herethere)

if tgEntry == nil then

return ''

end

unit = unit or tgEntry.def1

local anch = formatUnitPlaintext(tgEntry, unit, 0)

if herethere == 'there' then -- Untested, April 2014

anch = '#' .. anch

else

anch = mw.html.create():tag('span'):attr('id', anch)

end

return tostring(anch)

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 {{frac}} template (a single nowrap 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

-----------------------------------------------------------------------------------

-- debugReturnArgs

-----------------------------------------------------------------------------------

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

-----------------------------------------------------------------------------------

-- checkData -- Public. Performs various checks on the /data subpage.

-- not maintained since ca. 2015

-----------------------------------------------------------------------------------

function p.checkData(frame)

--To be allowed: entry.link empty; then use entry.name.

local dataPage = frame and frame.args and frame.args[1] or dataPageName

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: %s)', 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: %s).', 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: %s).', 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: %s).', 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: %s).', 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 %s measurement ("%s") found for gauge %d (id: %s).', 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('%s', alias))

end

aliases = mw.ustring.format(' (aliases: %s)', mw.text.listToText(aliases))

table.insert(ret, mw.ustring.format('No id found for track 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: %s).', 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: %s).', ti, t.id or ''))

end

end

end

-- Check for named gauges with no links and gauges with links but no names.

-- 20140520: no link? could be acceptable. Code falls back to the unlinked name (in test now).

if false then -- skipped 2014-05-25

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: %s).', 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: %s).', t.link, ti, t.id or ''))

end

end

end

-- Check for invalid def1 values.

for ti, t in ipairs(data) do

local def = t.def1

if def ~= 'imp' and def ~= 'met' then

table.insert(ret, mw.ustring.format('Invalid def1 value "%s" found for gauge %d (id: %s).', def 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: %s).', ti, ai, alias, t.id or ''))

end

end

elseif tkey == 'name' or tkey == 'link' or tkey == 'pagename' or tkey == 'contentcat' then

if tval ~= mw.text.trim(tval) then

table.insert(ret, mw.ustring.format('Unwanted whitespace detected in %s field of gauge %d ("%s", gauge id: %s).', tkey, ti, tval, t.id or ''))

end

elseif mw.ustring.find(tval, '%s') then

table.insert(ret, mw.ustring.format('Unwanted whitespace detected in %s field of gauge %d ("%s", gauge id: %s).', 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_def = ''

for ti, t in ipairs(data) do

self_id = t.id

self_def = 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 id=%s mm ambiguous with gauge id=%s mm (%s)'

, aliasCheck, self_def, self_id, tTwo.id, tTwo.def1)

)

end

end

end

end

end

end

-- Return any errors found.

for i, msg in ipairs(ret) do

ret[i] = mw.ustring.format('%s', 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

-----------------------------------------------------------------------------------

-- catContent -- content category for the gauge

-----------------------------------------------------------------------------------

function p.catContent(frame)

-- catContent (content category for this alias)

-- can be hardcoded in the data, or build by size (pattern)

local args = prepareArgs(frame)

local tgEntry = modTrackGauge.getTrackGaugeEntry(args.searchAlias)

if tgEntry == nil then

return args['displaynotfound'] or 'No gauge entry found for ' .. (args[1] or '""')

end

local catTitle

local label

local catC

local docsortlabel = ''

if args.docsortlabel ~= nil then

docsortlabel = '|' .. args.docsortlabel

end

if tgEntry.contentcat == '' then

catC = ''

elseif tgEntry.contentcat ~= nil then

catC = ':Category:' .. tgEntry.contentcat .. docsortlabel .. ''

else -- no name given, try default name:

local catCsuffix = ' gauge railways'

if tgEntry.def1 == 'met' then

label = formatUnitPlaintext(tgEntry, 'met')

catTitle = mw.title.makeTitle(14, label .. catCsuffix)

if catTitle.exists then

catC = ':' .. catTitle.fullText .. docsortlabel .. ''

end

elseif tgEntry.def1 == 'imp' then

label = formatUnitPlaintext(tgEntry, 'imp', nil, true)

catTitle = mw.title.makeTitle(14, label .. catCsuffix)

if catTitle.exists then

catC = ':' .. catTitle.fullText .. docsortlabel .. ''

end

end

end

return catC

end

-----------------------------------------------------------------------------------

-- catMentions -- maintenance only

-----------------------------------------------------------------------------------

function p.catMentions(frame)

local args = prepareArgs(frame)

local tgEntry = modTrackGauge.getTrackGaugeEntry(args.searchAlias)

if tgEntry == nil then

return args['displaynotfound'] or 'No gauge entry found for ' .. (args[1] or '""')

end

local catM = modTrackGauge.catMentions(tgEntry, args.docsortlabel, 'show')

return catM

end

-----------------------------------------------------------------------------------

-- fromInputToId -- Used cleaned Alias as searchkey

-----------------------------------------------------------------------------------

local function fromInputToId(searchAlias)

gaugeDataAll = mw.loadData(dataPageName)

for i, tgEntry in ipairs(gaugeDataAll) do

for j, alias in ipairs(tgEntry.aliases) do

if alias == searchAlias then

return tgEntry.id

end

end

end

-- Next search: by id (autodocument only, not in main RG)

if tonumber(searchAlias) ~= nil then

for i, tgEntry in ipairs(gaugeDataAll) do

if tgEntry.id == searchAlias then

return tgEntry.id

end

end

end

end

-----------------------------------------------------------------------------------

-- documentInchCount -- Number of inches in decimals.

-----------------------------------------------------------------------------------

local function documentInchCount(tgEntry)

local inches = 0

if tgEntry['num'] ~= nil then

inches = modMath._round(tonumber((tgEntry['num'] or 0) / (tgEntry['den'] or 1)), 4)

end

inches = tostring((tonumber(tgEntry['ft'] or 0) * 12)

+ tonumber(tgEntry['in'] or 0) + inches)

return inches

end

-----------------------------------------------------------------------------------

-- documentInchToMm -- Not used lately

-----------------------------------------------------------------------------------

local function documentInchToMm(inchCount)

return tonumber(inchCount or 0) * 25.4

end

-----------------------------------------------------------------------------------

-- documentGaugeSizeFromTitle -- Currently finds "1620 mm" when at end of title,

-- then returns "1620". Blank when not found.

-----------------------------------------------------------------------------------

function p.documentGaugeSizeFromTitle()

local title = mw.title.getCurrentTitle()

return string.match(title.text, '%s(%d+%.?%d*)%smm$') or ''

end

-----------------------------------------------------------------------------------

-- documentBuildTgList -- The table of id's to fill the table

-----------------------------------------------------------------------------------

function documentBuildTgList(args)

-- Build series from the list. idFrom and idTo are numerical

local tgList = {}

local idFrom = -1

local idTo = -1

for i, v in ipairs(args) do

if v == 'all' then

idFrom = -math.huge

idTo = math.huge

break

end

end

if args.docfrom ~= nil then

idFrom = tonumber(fromInputToId(args.docfrom)

or mw.ustring.gsub(args.docfrom, 'mm', ''))

idTo = math.huge

end

if args.docto ~= nil then

idTo = tonumber(fromInputToId(args.docto)

or mw.ustring.gsub(args.docto, 'mm', ''))

end

if idTo > 0 then -- Some subset is requested from the whole data set

if idFrom > idTo then

local dummy = idFrom

idFrom = idTo

idTo = dummy

end

for i, tgEntry in ipairs(gaugeDataAll) do

if (tonumber(tgEntry.id) >= idFrom) and (tonumber(tgEntry.id) <= idTo) then

table.insert(tgList, tonumber(tgEntry.id))

end

end

tgList = tableTools.removeDuplicates(tgList)

table.sort(tgList)

if #tgList > 1 then

ttlListedRange[1] = tgList[1] .. ' mm – ' .. tgList[#tgList] .. ' mm '

end

end

-- Individual entries can be mentioned in args (all unnamed = numbered params)

-- Need a straight table.to keep sequence right

local id

local argsAliasesIn = tableTools.compressSparseArray(args)

for i, argsAlias in ipairs(argsAliasesIn) do

id = fromInputToId(argsAlias)

if id ~= nil then

table.insert(tgList, i, tonumber(id))

table.insert(ttlListedRange, i, id .. ' mm; ')

end

end

ttlListedRange = tableTools.compressSparseArray(ttlListedRange)

ttlListedRange = tableTools.removeDuplicates(ttlListedRange)

tgList = tableTools.compressSparseArray(tgList)

tgList = tableTools.removeDuplicates(tgList)

return tgList

end

-----------------------------------------------------------------------------------

-- documentPostListStats -- build footer table, after list only

-----------------------------------------------------------------------------------

local function documentPostListStats(countTgList)

-- Report data counters

-- Data

local retFoot = {}

table.insert(retFoot, '\n*Sources')

table.insert(retFoot, ':Data pages: :' .. dataPageName .. '')

table.insert(retFoot, '*Data')

table.insert(retFoot, ':Listed: ' .. table.concat(ttlListedRange, '') .. ' (' .. countTgList .. ' rows)')

table.insert(retFoot, ":Entries (defined gauges, per unit): " .. ttlEntries)

table.insert(retFoot, ":Gauges (defined gauges, per size): " .. countTgList)

for i, stat in ipairs (ttlUnitCount) do

table.insert(retFoot, ':' .. stat[2] .. ': ' .. stat[1])

end

table.insert(retFoot, ':Aliases (input options): ' .. ttlAliasCount)

table.insert(retFoot, ':Named definitions (as output link; ' .. ttlAltNameCount .. '): ' .. table.concat(ttlAltName, '; '))

table.insert(retFoot, ':Entries with an article link: ' .. ttlLinkCount)

-- TODO table.insert(retFoot, '*Named gauges (named input)') -- todo

-- Categories (content, maintenance)

table.insert(retFoot, '*Categories')

table.insert(retFoot, ':Content categories: ' .. ttlContentCatsCount)

table.insert(retFoot, ':"Article mentions track gauge" categories: ' .. ttlMentioningCatsCount)

table.insert(retFoot, ':Articles listed in "mentions" categories: ' .. ttlMentioningPageCount .. ' (not unique)')

-- Size classes (narrow, broad, ..)

table.insert(retFoot, '*Size classes')

for i, stat in ipairs (ttlSizeClassCount) do

if stat[2] ~= 0 then

table.insert(retFoot, ':' .. stat[2] .. ' ' .. stat[3] .. ' (' .. stat[4] .. ' mentionings)')

end

end

local anchor = tostring(mw.html.create():tag('span'):attr('id', 'Statistics'))

-- help:using colors. Hue=190 (blue)

local statTable = anchor .. '\n

class="wikitable collapsible collapsed" style="background:#e6fbff; font-size:85%; width:100%;"'

.. '\n

'

.. '\n! style="background:#ceecf2; width:100%;" | Track gauge data statistics'

.. '\n

'

.. '\n|' .. table.concat(retFoot, '\n')

.. '\n

'

return statTable

end

-----------------------------------------------------------------------------------

-- documentHeader

-----------------------------------------------------------------------------------

local function documentHeader(numberOfEntries, docTitle, docState)

local docBgHeader = '#cef2e0' -- Green. See template:documentation

-- Header row 1 (title)

local pagetitle = mw.title.getCurrentTitle()

urlPurgePage = pagetitle:fullUrl({action = 'purge'})

urlPurgePage = '[' .. urlPurgePage .. ' (purge)]'

if docTitle == '' then

docTitle = 'Track gauges' -- (' .. dataPageName .. ')' -- optional, sandbox here

end

docTitle = docTitle .. ' ' .. urlPurgePage

if docState == '' then

docState = 'uncollapsed'

end

-- Header row 2 (sort buttons, blank cells)

local sortColHeaders = ''

local sortClass = ''

if (numberOfEntries or 0) > 1 then

sortClass = 'sortable'

local sortCell = '! style="background:' .. docBgHeader .. ';"'

-- todo: 10 cols with bg color

sortColHeaders = '\n|- style="background:' .. docBgHeader .. '; line-height:90%;"'

.. '\n!   || || || || || || || || ||'

end

-- Header row 3 (column headers)

local catMparent = modTrackGauge.catMentions(nil, 'Mentionings', 'show')

--10 columns:

local tableStyle = 'style="text-align:right; width:100%; font-size:85%;" '

local retHdr = {}

table.insert(retHdr, '\n

class="wikitable collapsible ' .. docState .. ' ' .. sortClass .. '" ' .. tableStyle)

table.insert(retHdr, '

')

table.insert(retHdr, '! colspan=10 style="background:' .. docBgHeader .. ';" | ' .. docTitle)

table.insert(retHdr, '

')

table.insert(retHdr, '! style="background:' .. docBgHeader .. ';" | Gauge
(mm)')

table.insert(retHdr, '! style="background:' .. docBgHeader .. ';" | Gauge
(ft, in)')

table.insert(retHdr, '! style="background:' .. docBgHeader .. ';" | Alt
name')

table.insert(retHdr, '! style="background:' .. docBgHeader .. ';" | Gauge
(inch)')

table.insert(retHdr, '! style="background:' .. docBgHeader .. ';" | Def
unit')

table.insert(retHdr, '! style="background:' .. docBgHeader .. '; width:8em;" | Aliases
(input options)')

table.insert(retHdr, '! style="background:' .. docBgHeader .. ';" | Class
 ')

table.insert(retHdr, '! style="background:' .. docBgHeader .. '; min-width:5em;" | Source
article')

table.insert(retHdr, '! style="background:' .. docBgHeader .. ';" | Category
(content)')

table.insert(retHdr, '! style="background:' .. docBgHeader .. ';" | ' .. catMparent .. '
(maintenance)')

return table.concat(retHdr, '\n') .. sortColHeaders

end

-----------------------------------------------------------------------------------

-- documentFooter

-----------------------------------------------------------------------------------

local function documentFooter()

return {'\n

'}

end

-----------------------------------------------------------------------------------

-- documentFromIdToEntrySet -- from fromIdToEntrySet

-- From one id, make the set with all one-two-three-more entries (met, inp, variants)

-----------------------------------------------------------------------------------

local function documentFromIdToEntrySet(id, searchedAlias)

local docBgColor = '#e6fff2' -- Green. See header bg color

local rowSplit = '

'

-- From the size-id, build the set of existing entries (met, imp, and variants)

local entry = {}

local defType = 0

-- data

for i, tgEntry in ipairs(gaugeDataAll) do

if id == tgEntry.id then

if tgEntry.def1 == 'met' and entry[1] == nil then

entry[1] = tgEntry

defType = defType + 1

elseif tgEntry.def1 == 'imp' and entry[2] == nil then

entry[2] = tgEntry

defType = defType + 2

else

entry[3 + tableTools.size(entry)] = tgEntry

end

end

end

entry = tableTools.compressSparseArray(entry)

-- Entry set is now complete & clean

-- Result: the entry table with entries present in /data,

-- in sequence if present (1. met, 2. imp, any extra)

-- (to build into a single row, maybe with split cells)

--Build cell elements, then string row together.

local inchCount = documentInchCount(entry[1])

local datasortvalue = documentdatasortvalue(entry[1], 'met', 5)

local aliasList = {}

local tempEntryAltName = {}

local entryAltName = {}

local hasAltName = false

for i, e in ipairs(entry) do

local alis = {}

for j, v in ipairs(e.aliases) do

if tonumber(v) == nil then -- (plain numbers are not shown)

table.insert(alis, tostring(v))

end

end

for j, v in ipairs(alis) do

if string.match(v, '^%d') == nil then -- textual so to italic.

alis[j] = tostring(mw.html.create():tag('span'):wikitext(v):css('font-style', 'italic'))

end

end

table.insert (aliasList, table.concat(alis, '; '))

ttlAliasCount = ttlAliasCount + #alis

-- process Alt name links

if e.name or ~= then

tempEntryAltName[i] = tostring(mw.html.create():tag('span'):wikitext(e.link):css('font-weight', 'bold'))

table.insert(ttlAltName, e.id .. ': ' .. e.link)

ttlAltNameCount = ttlAltNameCount + 1

hasAltName = true

end

end

if hasAltName then

local text

for i, v in ipairs(entry) do

table.insert(entryAltName, i, tempEntryAltName[i] or ' ')

end

end

local def = {} -- Definition unit code: 'met' or 'imp'

local defText = {}

for i, v in ipairs (entry) do

table.insert(def, v.def1)

if v.def1 == 'imp' then

table.insert(defText, 'imp')

ttlUnitCount[2][1] = ttlUnitCount[2][1] + 1

elseif v.def1 == 'met' then

table.insert(defText, 'met')

ttlUnitCount[1][1] = ttlUnitCount[1][1] + 1

end

end

if #entry >= 2 then

if #entry == 2 and entry[1].def1 ~= entry[2].def1 then -- Regular pair: def in met and in imp

ttlUnitCount[3][1] = ttlUnitCount[3][1] + 1

else -- More than 2, or a double unit definition

ttlUnitCount[4][1] = ttlUnitCount[4][1] .. ' ' .. id .. ' mm (' .. #entry ..');'

end

end

-- mm; ft in -- Measurement (number & unit; met and imp; anchor to here)

local measure = {}

local unitanchor = { , }

measure[1] = modTrackGauge.formatMet(entry[1])

measure[2] = modTrackGauge.formatImp(entry[1]) -- both met and imp from entry[1]

if modMath._mod(defType, 2) == 1 then

measure[1] = tostring(mw.html.create():tag('span'):wikitext(measure[1]):css('font-weight', 'bold'))

unitanchor[1] = anchor(entry[1], 'met')

end

if defType >= 2 then

measure[2] = tostring(mw.html.create():tag('span'):wikitext(measure[2]):css('font-weight', 'bold'))

unitanchor[2] = anchor(entry[1], 'imp')

end

-- Linked article

local linkArticle = {}

for i, e in ipairs(entry) do

table.insert(linkArticle, e.pagename)

end

ttlLinkCount = ttlLinkCount + #linkArticle

local eq

if #linkArticle >= 2 then

eq = true

for i, v in ipairs(linkArticle) do

if v ~= linkArticle[1] then

eq = false

break

end

end

if eq == true then

for i, v in ipairs(linkArticle) do

if i > 1 then

linkArticle[i] = nil

end

end

end

end

for i, lp in ipairs(linkArticle) do

local fmtLp = ''

fmtLp = '' .. lp .. ''

linkArticle[i] = tostring(mw.html.create():tag('span'):css('text-align', 'left'):wikitext(fmtLp))

end

-- catContent (content category for this alias). note: function p.catContent is a reduced code of this.

-- can be hardcoded in the data, or build by size (pattern)

local catContent = {}

local catTitle

local label

local skipCheck = false

for i, e in ipairs(entry) do

if e.contentcat == '' then

-- no cat; option to prevent expensive calls

skipCheck = true

elseif e.contentcat ~= nil then

label = string.match(e.contentcat, '([%S]*)') or 'nomatch'

table.insert(catContent,

'cat:' .. label .. ' ...')

end

end

if #catContent >= 2 then

eq = true

for i, v in ipairs(catContent) do

if v ~= catContent[1] then

eq = false

break

end

end

if eq == true then

for i, v in ipairs(catContent) do

if i > 1 then

catContent[i] = nil

end

end

end

end

if #catContent == 0 and not skipCheck then

local catCsuffix = ' gauge railways'

if modMath._mod(defType, 2) == 1 then

label = formatUnitPlaintext(entry[1], 'met')

catTitle = mw.title.makeTitle(14, label .. catCsuffix)

if catTitle.exists then

table.insert(catContent,

'cat:' .. noWrap(label) .. '')

end

end

if defType >= 2 then

label = formatUnitPlaintext(entry[1], 'imp', nil, true)

catTitle = mw.title.makeTitle(14, label .. catCsuffix)

if catTitle.exists then

table.insert(catContent, 'cat:' .. noWrap(label) .. '')

end

end

end

ttlContentCatsCount = ttlContentCatsCount + #catContent

-- Mentions category

local catMentions = modTrackGauge.catMentions(entry[1], "cat:mnt", 'show')

local catCount = mw.site.stats.pagesInCategory(

modTrackGauge.catMentions(entry[1], nil, 'pagename'), pages)

ttlMentioningCatsCount = ttlMentioningCatsCount + 1 -- Exists

ttlMentioningPageCount = ttlMentioningPageCount + catCount

-- class: Counter SizeClass (narrow, broad, ...)

local rgSizeClass = documentGaugeClass(entry[1], catCount)

ttlEntries = ttlEntries + #entry

sortCount = mw.text.truncate('00000' .. tostring(catCount), -5, '')

sortCount = ''

catCount = sortCount .. catCount .. ' P'

-- Compose the size-id row with all cell values (10 columns)

local row = {}

table.insert(row, datasortvalue .. unitanchor[1] .. measure[1])

table.insert(row, datasortvalue .. unitanchor[2] .. measure[2])

table.insert(row, table.concat(entryAltName, rowSplit))

table.insert(row, datasortvalue .. inchCount)

table.insert(row, table.concat(defText, rowSplit))

table.insert(row, table.concat(aliasList, rowSplit))

table.insert(row, rgSizeClass)

table.insert(row, table.concat(linkArticle, rowSplit))

table.insert(row, table.concat(catContent, rowSplit))

table.insert(row, catCount .. ' ' .. catMentions)

return '\n|- style="background:' .. docBgColor .. '; border-top:2px solid #aaa;" |'

.. '\n|'

.. table.concat(row, ' || ')

end

-----------------------------------------------------------------------------------

-- documentGauge -- Selfdocument gauge data (one, multiple, range, all)

-----------------------------------------------------------------------------------

function p.documentGauge(frame)

local args = prepareArgs(frame)

gaugeDataAll = mw.loadData(dataPageName)

-- Init glolbal counters by table:

ttlUnitCount =

{

[1] = {0, 'Entries defined metric'},

[2] = {0, 'Entries defined imperial'},

[3] = {0, 'Gauge sizes defined both metric and imperial'},

[4] = {'', 'Gauge sizes with multiple entries in one unit'}

}

ttlSizeClassCount =

{

[1] = {'scaled', 0, 'scaled or model gauges', 0},

[2] = {'min', 0, 'minimum gauges', 0},

[3] = {'narrow', 0, 'narrow gauges', 0},

[4] = {'s.g.', 0, 'standard gauge', 0},

[5] = {'broad', 0, 'broad gauges', 0},

[6] = {'unk', 0, 'unknown', 0}

}

local tgList = documentBuildTgList(args)

-- Now loop through the prepared tgList[id] and add rows to result le

-- One row contains all available entries for the id (met, imp, a third variant)

local rowTGid = {}

for i, numId in ipairs(tgList) do

table.insert(rowTGid, documentFromIdToEntrySet(tostring(numId)))

end

-- Return args

local retArgs = ''

if args.docreturnargs == 'on' then

retArgs = '\n' .. p.debugReturnArgs(frame)

end

-- Build statistics footer

local retStats = ''

if args.docstats == 'on' then

retStats = documentPostListStats(#tgList)

end

-- Build up

return documentHeader(#tgList, args.doctitle or , args.docstate or )

.. table.concat(rowTGid, '')

.. table.concat(documentFooter(), '')

.. retStats

.. retArgs

end

--------------------------------------------------------

-- doc

--------------------------------------------------------

function p.docFracAliases(frame)

local args = prepareArgs(frame)

gaugeDataAll = mw.loadData(dataPageName)

local tgList = documentBuildTgList(args)

local ttlHitCount =0

local rowIMP = {}

local fracAlias =''

for i, id in ipairs(tgList) do

for j, tgEntry in pairs(gaugeDataAll) do

if nil and tostring(id) == '53.975' and tostring(id) == tgEntry.id then

fracAlias = anchor(tgEntry, 'imp', 'there')

fracAlias = mw.ustring.lower(mw.ustring.gsub(fracAlias, '[%s%,%#]', ''))

table.insert(rowIMP, fracAlias .. '||' .. i .. '||' .. id ..

'||' .. modTrackGauge.formatImp(tgEntry) .. ' || plus ' .. tgEntry.num)

end

if tostring(id) == tgEntry.id and tgEntry.def1 == 'imp' and tgEntry.num ~= nil then

if tgEntry.ft ~= nil then

ttlHitCount = ttlHitCount + 1

fracAlias = anchor(tgEntry, 'imp', 'there')

fracAlias = mw.ustring.lower(mw.ustring.gsub(fracAlias, '[%s%,%#]', ''))

fracAlias = mw.ustring.gsub(fracAlias, '⁄', '/')

table.insert(rowIMP, fracAlias .. '||' .. i .. '||' .. id ..

'||' .. modTrackGauge.formatImp(tgEntry))

fracAlias = tostring((tonumber((tgEntry.ft) or 0) * 12) + tonumber(tgEntry["in"] or 0)) .. tgEntry.num .. '/' .. tgEntry.den .. 'in'

table.insert(rowIMP, fracAlias .. '||' .. i .. '||' .. id ..

'||' .. modTrackGauge.formatImp(tgEntry))

end

end

end

end

-- return '\n|' .. ttlHitCount .. ' hits. ' .. #rowIMP

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

end

return p