Module:Convert/show

-- Prepare tables of wikitext to display simple documentation about

-- specified units. Data is obtained by calling Module:Convert.

-- Also provides a function to show convert usage examples.

local Collection -- a table to hold items

Collection = {

add = function (self, item)

if item ~= nil then

self.n = self.n + 1

self[self.n] = item

end

end,

join = function (self, sep)

return table.concat(self, sep)

end,

remove = function (self, pos)

if self.n > 0 and (pos == nil or (0 < pos and pos <= self.n)) then

self.n = self.n - 1

return table.remove(self, pos)

end

end,

sort = function (self, comp)

table.sort(self, comp)

end,

new = function ()

return setmetatable({n = 0}, Collection)

end

}

Collection.__index = Collection

local function strip(text)

-- Return text with no leading/trailing whitespace.

return text:match("^%s*(.-)%s*$")

end

local function fakeFrame(selfArgs, parentArgs)

-- Simulate enough of a MediaWiki module frame for convert.

-- This is a cheap way to call convert with specified arguments.

return {

args = selfArgs,

parent = parentArgs and fakeFrame(parentArgs, nil),

getParent = function (self) return self.parent end,

}

end

local cvtFunction

local function callConvert(args)

if not cvtFunction then

cvtFunction = require('Module:Convert').convert

end

return cvtFunction(fakeFrame({}, args))

end

local function makeTable(frame, results, units)

results:add('

class="wikitable"')

results:add('! Unit code !! Unit symbol !! Unit name !! US name, if different')

for i, ucode in ipairs(units) do

local row = Collection.new()

row:add(ucode)

local args = { '1', ucode, abbr = 'on', disp = 'unit' }

row:add(callConvert(args))

args.abbr = 'off'

local name1 = callConvert(args)

row:add(name1)

args.sp = 'us'

local name1_us = callConvert(args)

if name1_us == name1 then

row:add('')

else

row:add(name1_us)

end

results:add('

')

results:add(strip('| ' .. row:join('

')))

end

results:add('

')

results:add('')

end

-- Commonly used units for main documentation.

-- Can only be input units (not combinations or multiples).

local commonUnits = {

["Area"] = {

heading = "Area",

examples = { "1.5|sqmi|km2", "1.5|sqmi|km2|abbr=off", "1.5|sqmi|km2|abbr=on" },

"acre",

"ha",

"m2",

"cm2",

"km2",

"sqin",

"sqft",

"sqyd",

"sqmi",

},

["Fuel efficiency"] = {

heading = "Fuel efficiency",

examples = { "12|mpgus|km/L", "12|mpgus|km/L|abbr=off", "12|mpgus|km/L|abbr=off|sp=us", "12|mpgus|km/L|abbr=on" },

"km/L",

"mpgimp",

"mpgus",

"L/km",

"L/100 km",

},

["Length"] = {

heading = "Length",

examples = { "123|cm|in", "123|cm|in|abbr=off|sp=us", "123|cm|in|abbr=on" },

"uin",

"in",

"ft",

"yd",

"mi",

"nmi",

"m",

"cm",

"mm",

"km",

"angstrom",

},

["Mass"] = {

heading = "Mass",

examples = { "72.3|kg|lb", "72.3|kg|lb|abbr=off", "72.3|kg|lb|abbr=on" },

"g",

"kg",

"oz",

"lb",

"st",

"LT",

"MT",

"ST",

},

["Pressure"] = {

heading = "Pressure",

examples = { "28|psi|Pa", "28|psi|Pa|abbr=off", "28|psi|Pa|abbr=on" },

"atm",

"mbar",

"psi",

"Pa",

},

["Speed"] = {

heading = "Speed",

examples = { "60|mph|km/h", "60|mph|km/h|abbr=off", "60|mph|km/h|abbr=on" },

"km/h",

"km/s",

"kn",

"mph",

},

["Temperature"] = {

heading = "Temperature",

examples = { "100|C|F", "100|C|F|abbr=off", "100|C-change|F-change", "100|C-change|F-change|abbr=out" },

"C",

"F",

"K",

"C-change",

"F-change",

"K-change",

},

["Torque"] = {

heading = "Torque",

examples = { "12.5|Nm|lb.in", "12.5|Nm|lb.in|abbr=off", "12.5|Nm|lb.in|abbr=on|lk=on" },

"lb.in",

"lb.ft",

"Nm",

},

["Volume"] = {

heading = "Volume",

examples = { "125|cuin|l", "125|cuin|l|abbr=off", "125|cuin|l|abbr=on" },

"cuin",

"cuft",

"cuyd",

"cumi",

"impgal",

"impoz",

"usgal",

"usoz",

"L",

"l",

"m3",

"cc",

"mm3",

},

}

-- Order in which sections are wanted when doing all common units.

local commonSections = {

"Area",

"Fuel efficiency",

"Length",

"Mass",

"Pressure",

"Speed",

"Temperature",

"Torque",

"Volume",

}

local function _showExamples(frame, results, examples, wantTable)

local fmt

if wantTable then

results:add('

')

fmt = '|%s

%s'

else

fmt = '*%s → %s'

end

for i, item in ipairs(examples) do

if wantTable and i > 1 then

results:add('

')

end

item = item:gsub('!', '|')

item = '{{convert' .. (item:sub(1, 1) == '|' and '' or '|') .. item .. '}}'

results:add(fmt:format(mw.text.nowiki(item), frame:preprocess(item)))

end

if wantTable then

results:add('

')

end

end

local function _showLinks(frame, results, args)

local sandbox = args[1] == 'sandbox' and '/sandbox' or ''

local dataModule = 'Module:Convert/data' .. sandbox

local textModule = 'Module:Convert/text' .. sandbox

local dataCode = require(dataModule)

local textCode = require(textModule)

local uniqueLinks = {}

local links = Collection.new()

local function addLink(link)

if link and link ~= '' then

-- Some items (alias symlink, chainlk symbol) are already linked.

-- Therefore, add link syntax if not present, before testing for uniqueness.

-- There will be some duplicate targets such as chain + ch.

if link:sub(1, 2) ~= '[[' then

link = '' .. link .. ''

end

if not uniqueLinks[link] then

uniqueLinks[link] = true

links:add(link)

end

end

end

for _, v in ipairs(textCode.customary_units) do

addLink(v.link)

end

for _, v in pairs(dataCode.all_units) do

-- This does not add anything for automatic per units (assuming they do not define a link).

-- That is ok because per unit x/y has link LINK(x)/LINK(y).

if v.symbol and v.symbol:sub(1, 2) == '[[' then

addLink(v.symbol)

end

if v.name1 and v.name1:sub(1, 2) == '[[' then

addLink(v.name1)

end

addLink(v.symlink)

addLink(v.link or v.name1 or (not v.per and not v.target) and v.symbol)

end

for _, v in pairs(dataCode.link_exceptions) do

addLink(v)

end

for _, v in pairs(dataCode.per_unit_fixups) do

if type(v) == 'table' then

addLink(v.link)

end

end

local function comp(a, b)

local la = a:lower(a)

local lb = b:lower(b)

if la == lb then

return a < b

end

return la < lb

end

links:sort(comp)

for _, v in ipairs(links) do

results:add('*' .. v)

end

end

local function _showUnits(frame, results, args)

local doFull

if args[1] == nil then

doFull = true

args = commonSections

end

local group = Collection.new()

for _, item in ipairs(args) do

local units = commonUnits[item] or commonUnits[item:sub(1, 1):upper() .. item:sub(2)]

if units then

if group.n > 0 then

makeTable(frame, results, group)

group = Collection.new()

end

if doFull then

if units.heading then

results:add('===' .. units.heading .. '===')

end

if units.examples then

results:add('Examples:')

_showExamples(frame, results, units.examples)

end

end

makeTable(frame, results, units)

else

group:add(item)

end

end

if group.n > 0 then

makeTable(frame, results, group)

end

end

local function showExamples(frame, wantTable)

local results = Collection.new()

local ok, msg = pcall(_showExamples, frame, results, frame.args, wantTable)

if ok then

return results:join('\n')

end

return 'Error\n' .. msg

end

local function showLinks(frame)

local results = Collection.new()

local ok, msg = pcall(_showLinks, frame, results, frame.args)

if ok then

return results:join('\n')

end

return 'Error\n' .. msg

end

local function showUnits(frame)

local results = Collection.new()

local ok, msg = pcall(_showUnits, frame, results, frame.args)

if ok then

return results:join('\n')

end

return 'Error\n' .. msg

end

return {

links = showLinks,

unit = showUnits,

units = showUnits,

['list'] = function (frame) return showExamples(frame, false) end,

['table'] = function (frame) return showExamples(frame, true) end,

}