Changes

Jump to navigation Jump to search
3,089 bytes added ,  00:42, 21 July 2018
m
1 revision imported
Line 259: Line 259:  
end
 
end
   −
local function wanted_category(cat)
+
local function wanted_category(catkey, catsort, want_warning)
-- Return cat if it is wanted in current namespace, otherwise return nil.
+
-- Return message category if it is wanted in current namespace,
-- This is so tracking categories only include pages that need correction.
+
-- otherwise return ''.
 +
local cat
 
local title = mw.title.getCurrentTitle()
 
local title = mw.title.getCurrentTitle()
 
if title then
 
if title then
Line 268: Line 269:  
for _, v in ipairs(split(config.nscat or nsdefault, ',')) do
 
for _, v in ipairs(split(config.nscat or nsdefault, ',')) do
 
if namespace == tonumber(v) then
 
if namespace == tonumber(v) then
return cat
+
cat = text_code.all_categories[want_warning and 'warning' or catkey]
 +
if catsort and catsort ~= '' and cat:sub(-2) == ']]' then
 +
cat = cat:sub(1, -3) .. '|' .. mw.text.nowiki(usub(catsort, 1, 20)) .. ']]'
 +
end
 +
break
 
end
 
end
 
end
 
end
 
end
 
end
 +
return cat or ''
 
end
 
end
   −
local function message(parms, mcode)
+
local function message(parms, mcode, is_warning)
 
-- Return wikitext for an error message, including category if specified
 
-- Return wikitext for an error message, including category if specified
 
-- for the message type.
 
-- for the message type.
Line 341: Line 347:  
return success and (revid == '')
 
return success and (revid == '')
 
end
 
end
 +
local want_warning = is_warning and
 +
not config.warnings and  -- show unobtrusive warnings if config.warnings not configured
 +
not msg.nowarn          -- but use msg settings, not standard warning, if specified
 
local title = string.gsub(msg[1] or 'Missing message', '$%d+', parts)
 
local title = string.gsub(msg[1] or 'Missing message', '$%d+', parts)
local text = msg[2] or 'Missing message'
+
local text = want_warning and '*' or msg[2] or 'Missing message'
local cat = wanted_category(text_code.all_categories[msg[3]]) or ''
+
local cat = wanted_category(msg[3], mcode[2], want_warning)
 
local anchor = msg[4] or ''
 
local anchor = msg[4] or ''
local fmtkey = ispreview() and 'cvt_format_preview' or (msg.format or 'cvt_format')
+
local fmtkey = ispreview() and 'cvt_format_preview' or
 +
(want_warning and 'cvt_format2' or msg.format or 'cvt_format')
 
local fmt = text_code.all_messages[fmtkey] or 'convert: bug'
 
local fmt = text_code.all_messages[fmtkey] or 'convert: bug'
 
return subparm(fmt, title:gsub('"', '"'), text, cat, anchor)
 
return subparm(fmt, title:gsub('"', '"'), text, cat, anchor)
Line 354: Line 364:  
function add_warning(parms, level, key, text1, text2)  -- for forward declaration above
 
function add_warning(parms, level, key, text1, text2)  -- for forward declaration above
 
-- If enabled, add a warning that will be displayed after the convert result.
 
-- If enabled, add a warning that will be displayed after the convert result.
 +
-- A higher level is more verbose: more kinds of warnings are displayed.
 
-- To reduce output noise, only the first warning is displayed.
 
-- To reduce output noise, only the first warning is displayed.
if config.warnings or level < 0 then
+
if level <= (tonumber(config.warnings) or 1) then
if level <= (tonumber(config.warnings) or 1) then
+
if parms.warnings == nil then
if parms.warnings == nil then
+
parms.warnings = message(parms, { key, text1, text2 }, true)
parms.warnings = message(parms, { key, text1, text2 })
  −
end
   
end
 
end
 
end
 
end
Line 380: Line 389:  
success, speller = pcall(get_speller, spell_module)
 
success, speller = pcall(get_speller, spell_module)
 
if not success or type(speller) ~= 'function' then
 
if not success or type(speller) ~= 'function' then
add_warning(parms, 1, 'cvt_no_spell')
+
add_warning(parms, 1, 'cvt_no_spell', 'spell')
 
return nil
 
return nil
 
end
 
end
Line 581: Line 590:  
-- This is never called to determine a unit name or link because per units
 
-- This is never called to determine a unit name or link because per units
 
-- are handled as a special case.
 
-- are handled as a special case.
-- Similarly, the default output is handled elsewhere.
+
-- Similarly, the default output is handled elsewhere, and for a symbol
 +
-- this is only called from get_default() for default_exceptions.
 
__index = function (self, key)
 
__index = function (self, key)
 
local value
 
local value
Line 606: Line 616:  
}
 
}
   −
local function make_per(unit_table, ulookup)
+
local function make_per(unitcode, unit_table, ulookup)
 
-- Return true, t where t is a per unit with unit codes expanded to unit tables,
 
-- Return true, t where t is a per unit with unit codes expanded to unit tables,
 
-- or return false, t where t is an error message table.
 
-- or return false, t where t is an error message table.
local result = { utype = unit_table.utype, per = {} }
+
local result = {
 +
unitcode = unitcode,
 +
utype = unit_table.utype,
 +
per = {}
 +
}
 
override_from(result, unit_table, { 'invert', 'iscomplex', 'default', 'link', 'symbol', 'symlink' })
 
override_from(result, unit_table, { 'invert', 'iscomplex', 'default', 'link', 'symbol', 'symlink' })
 
result.symbol_raw = (result.symbol or false)  -- to distinguish between a defined exception and a metatable calculation
 
result.symbol_raw = (result.symbol or false)  -- to distinguish between a defined exception and a metatable calculation
Line 675: Line 689:  
end
 
end
 
unitcode = unitcode:gsub('_', ' '):gsub('&nbsp;', ' '):gsub('  +', ' ')
 
unitcode = unitcode:gsub('_', ' '):gsub('&nbsp;', ' '):gsub('  +', ' ')
 +
local function call_make_per(t)
 +
return make_per(unitcode, t,
 +
function (ucode) return lookup(parms, ucode, 'no_combination', utable, fails, depth) end
 +
)
 +
end
 
local t = utable[unitcode]
 
local t = utable[unitcode]
 
if t then
 
if t then
Line 696: Line 715:  
end
 
end
 
if t.per then
 
if t.per then
return make_per(t, function (ucode) return lookup(parms, ucode, 'no_combination', utable, fails, depth) end)
+
return call_make_per(t)
 
end
 
end
 
local combo = t.combination  -- nil or a table of unitcodes
 
local combo = t.combination  -- nil or a table of unitcodes
Line 716: Line 735:  
end
 
end
 
local result = shallow_copy(t)
 
local result = shallow_copy(t)
 +
result.unitcode = unitcode
 
if result.prefixes then
 
if result.prefixes then
 
result.si_name = ''
 
result.si_name = ''
Line 734: Line 754:  
if t and t.prefixes then
 
if t and t.prefixes then
 
local result = shallow_copy(t)
 
local result = shallow_copy(t)
 +
result.unitcode = unitcode
 
result.si_name = parms.opt_sp_us and si.name_us or si.name
 
result.si_name = parms.opt_sp_us and si.name_us or si.name
 
result.si_prefix = si.prefix or prefix
 
result.si_prefix = si.prefix or prefix
Line 754: Line 775:  
local success, result = lookup(parms, baseunit, 'no_combination', utable, fails, depth)
 
local success, result = lookup(parms, baseunit, 'no_combination', utable, fails, depth)
 
if success and not (result.offset or result.builtin or result.engscale) then
 
if success and not (result.offset or result.builtin or result.engscale) then
 +
result.unitcode = unitcode  -- 'e6cuft' not 'cuft'
 
result.defkey = unitcode  -- key to lookup default exception
 
result.defkey = unitcode  -- key to lookup default exception
 
result.engscale = engscale
 
result.engscale = engscale
Line 814: Line 836:  
-- Engineering notation (apart from at start and which has been stripped before here),
 
-- Engineering notation (apart from at start and which has been stripped before here),
 
-- is not supported so do not make a per unit if find text like 'e3' in unitcode.
 
-- is not supported so do not make a per unit if find text like 'e3' in unitcode.
local success, result = make_per({ per = {top, bottom} }, function (ucode) return lookup(parms, ucode, 'no_combination', utable, fails, depth) end)
+
local success, result = call_make_per({ per = {top, bottom} })
 
if success then
 
if success then
 
return true, result
 
return true, result
Line 1,591: Line 1,613:  
--    p2 is text to insert before the output unit
 
--    p2 is text to insert before the output unit
 
--    p1 or p2 may be nil to mean "no preunit"
 
--    p1 or p2 may be nil to mean "no preunit"
-- Using '+ ' gives output like "5+ feet" (no preceding space).
+
-- Using '+' gives output like "5+ feet" (no space before, but space after).
local function withspace(text, i)
+
local function withspace(text, wantboth)
-- Insert space at beginning if i == 1, or at end if i == -1.
+
-- Return text with space before and, if wantboth, after.
-- However, no space is inserted if there is a space or '&nbsp;'
+
-- However, no space is added if there is a space or '&nbsp;' or '-'
-- or '-' at that position ('-' is for adjectival text).
+
-- at that position ('-' is for adjectival text).
local current = text:sub(i, i)
+
-- There is also no space if text starts with '&'
if current == ' ' or current == '-' then
+
-- (e.g. '&deg;' would display a degree symbol with no preceding space).
return text
+
local char = text:sub(1, 1)
end
+
if char == '&' then
if i == 1 then
+
return text -- an html entity can be used to specify the exact display
current = text:sub(1, 6)
  −
else
  −
current = text:sub(-6, -1)
   
end
 
end
if current == '&nbsp;' then
+
if not (char == ' ' or char == '-' or char == '+') then
return text
+
text = ' ' .. text
 
end
 
end
if i == 1 then
+
if wantboth then
return ' ' .. text
+
char = text:sub(-1, -1)
 +
if not (char == ' ' or char == '-' or text:sub(-6, -1) == '&nbsp;') then
 +
text = text .. ' '
 +
end
 
end
 
end
return text .. ' '
+
return text
 
end
 
end
 +
local PLUS = '+ '
 
preunit1 = preunit1 or ''
 
preunit1 = preunit1 or ''
 
local trim1 = strip(preunit1)
 
local trim1 = strip(preunit1)
Line 1,619: Line 1,642:  
return nil
 
return nil
 
end
 
end
return withspace(withspace(preunit1, 1), -1)
+
if trim1 == '+' then
 +
return PLUS
 +
end
 +
return withspace(preunit1, true)
 
end
 
end
 +
preunit1 = withspace(preunit1)
 
preunit2 = preunit2 or ''
 
preunit2 = preunit2 or ''
 
local trim2 = strip(preunit2)
 
local trim2 = strip(preunit2)
if trim1 == '' and trim2 == '' then
+
if trim1 == '+' then
return nil, nil
+
if trim2 == '' or trim2 == '+' then
 +
return PLUS, PLUS
 +
end
 +
preunit1 = PLUS
 
end
 
end
if trim1 ~= '+' then
+
if trim2 == '' then
preunit1 = withspace(preunit1, 1)
+
if trim1 == '' then
end
+
return nil, nil
if trim2 == '&#32;' then  -- trick to make preunit2 empty
+
end
 +
preunit2 = preunit1
 +
elseif trim2 == '+' then
 +
preunit2 = PLUS
 +
elseif trim2 == '&#32;' then  -- trick to make preunit2 empty
 
preunit2 = nil
 
preunit2 = nil
elseif trim2 == '' then
+
else
preunit2 = preunit1
+
preunit2 = withspace(preunit2)
elseif trim2 ~= '+' then
  −
preunit2 = withspace(preunit2, 1)
   
end
 
end
 
return preunit1, preunit2
 
return preunit1, preunit2
Line 1,666: Line 1,698:     
local function get_composite(parms, iparm, in_unit_table)
 
local function get_composite(parms, iparm, in_unit_table)
-- Look for a composite input unit. For example, "{{convert|1|yd|2|ft|3|in}}"
+
-- Look for a composite input unit. For example, {{convert|1|yd|2|ft|3|in}}
 
-- would result in a call to this function with
 
-- would result in a call to this function with
 
--  iparm = 3 (parms[iparm] = "2", just after the first unit)
 
--  iparm = 3 (parms[iparm] = "2", just after the first unit)
Line 1,737: Line 1,769:  
-- Return true if successful or return false, t where t is an error message table.
 
-- Return true if successful or return false, t where t is an error message table.
 
currency_text = nil  -- local testing can hold module in memory; must clear globals
 
currency_text = nil  -- local testing can hold module in memory; must clear globals
 +
local accept_any_text = {
 +
input = true,
 +
qid = true,
 +
qual = true,
 +
stylein = true,
 +
styleout = true,
 +
tracking = true,
 +
}
 
if kv_pairs.adj and kv_pairs.sing then
 
if kv_pairs.adj and kv_pairs.sing then
 
-- For enwiki (before translation), warn if attempt to use adj and sing
 
-- For enwiki (before translation), warn if attempt to use adj and sing
Line 1,770: Line 1,810:  
en_value = number
 
en_value = number
 
else
 
else
add_warning(parms, 1, (en_name == 'frac' and 'cvt_bad_frac' or 'cvt_bad_sigfig'), loc_value)
+
add_warning(parms, 1, (en_name == 'frac' and 'cvt_bad_frac' or 'cvt_bad_sigfig'), loc_name .. '=' .. loc_value)
 
end
 
end
 
end
 
end
elseif en_name == 'stylein' or en_name == 'styleout' or en_name == 'qid' or en_name == 'input' then
+
elseif accept_any_text[en_name] then
 
en_value = loc_value ~= '' and loc_value or nil  -- accept non-empty user text with no validation
 
en_value = loc_value ~= '' and loc_value or nil  -- accept non-empty user text with no validation
 
if en_name == 'input' then
 
if en_name == 'input' then
Line 1,828: Line 1,868:  
end
 
end
 
if parms.abbr then
 
if parms.abbr then
 +
if parms.abbr == 'unit' then
 +
parms.abbr = 'on'
 +
parms.number_word = true
 +
end
 
parms.abbr_org = parms.abbr  -- original abbr, before any flip
 
parms.abbr_org = parms.abbr  -- original abbr, before any flip
 
elseif parms.opt_hand_hh then
 
elseif parms.opt_hand_hh then
Line 1,834: Line 1,878:  
else
 
else
 
parms.abbr = 'out'  -- default is to abbreviate output only (use symbol, not name)
 
parms.abbr = 'out'  -- default is to abbreviate output only (use symbol, not name)
 +
end
 +
if parms.opt_order_out then
 +
-- Disable options that do not work in a useful way with order=out.
 +
parms.opt_flip = nil  -- override adj=flip
 +
parms.opt_spell_in = nil
 +
parms.opt_spell_out = nil
 +
parms.opt_spell_upper = nil
 
end
 
end
 
if parms.opt_spell_out and not abbr_entered then
 
if parms.opt_spell_out and not abbr_entered then
Line 1,999: Line 2,050:  
-- The valid_value is not negative and does not use a fraction, and
 
-- The valid_value is not negative and does not use a fraction, and
 
-- no options requiring further processing of the input are used.
 
-- no options requiring further processing of the input are used.
-- Otherwise, return nothing and caller will reparse the input.
+
-- Otherwise, return nothing or return false, parm1 for caller to interpret.
 
-- Testing shows this function is successful for 96% of converts in articles,
 
-- Testing shows this function is successful for 96% of converts in articles,
 
-- and that on average it speeds up converts by 8%.
 
-- and that on average it speeds up converts by 8%.
 
if parms.opt_ri or parms.opt_spell_in then return end
 
if parms.opt_ri or parms.opt_spell_in then return end
 
local clean = to_en(strip(parms[1] or ''), parms)
 
local clean = to_en(strip(parms[1] or ''), parms)
if #clean > 10 or not clean:match('^[0-9.]+$') then return end
+
if #clean > 10 or not clean:match('^[0-9.]+$') then
 +
return false, clean
 +
end
 
local value = tonumber(clean)
 
local value = tonumber(clean)
 
if not value then return end
 
if not value then return end
Line 2,021: Line 2,074:  
end
 
end
   −
local function wikidata_call(operation, ...)
+
local function wikidata_call(parms, operation, ...)
 
-- Return true, s where s is the result of a Wikidata operation,
 
-- Return true, s where s is the result of a Wikidata operation,
 
-- or return false, t where t is an error message table.
 
-- or return false, t where t is an error message table.
Line 2,032: Line 2,085:  
if success then
 
if success then
 
return status, result
 
return status, result
 +
end
 +
if parms.opt_sortable_debug then
 +
-- Use debug=yes to crash if an error while accessing Wikidata.
 +
error('Error accessing Wikidata: ' .. status, 0)
 
end
 
end
 
return false, { 'cvt_wd_fail' }
 
return false, { 'cvt_wd_fail' }
Line 2,060: Line 2,117:  
if parms.test == 'wikidata' then
 
if parms.test == 'wikidata' then
 
local ulookup = function (ucode)
 
local ulookup = function (ucode)
-- Use empty table for parms so it does not accummulate results when used repeatedly.
+
-- Use empty table for parms so it does not accumulate results when used repeatedly.
 
return lookup({}, ucode, 'no_combination')
 
return lookup({}, ucode, 'no_combination')
 
end
 
end
return wikidata_call('_listunits', ulookup)
+
return wikidata_call(parms, '_listunits', ulookup)
 
end
 
end
 
local success, msg = translate_parms(parms, kv_pairs)
 
local success, msg = translate_parms(parms, kv_pairs)
 
if not success then return false, msg end
 
if not success then return false, msg end
 
if parms.input then
 
if parms.input then
success, msg = wikidata_call('_adjustparameters', parms, 1)
+
success, msg = wikidata_call(parms, '_adjustparameters', parms, 1)
 
if not success then return false, msg end
 
if not success then return false, msg end
 
end
 
end
 
local success, i, in_unit, in_unit_table = simple_get_values(parms)
 
local success, i, in_unit, in_unit_table = simple_get_values(parms)
 
if not success then
 
if not success then
 +
if type(i) == 'string' and i:match('^NNN+$') then
 +
-- Some infoboxes have examples like {{convert|NNN|m}} (3 or more "N").
 +
-- Output an empty string for these.
 +
return false, { 'cvt_no_output' }
 +
end
 
local valinfo
 
local valinfo
 
success, valinfo, i = get_values(parms)
 
success, valinfo, i = get_values(parms)
Line 2,137: Line 2,199:  
end
 
end
 
end
 
end
if not set_precision(word) then
+
if word and not set_precision(word) then
parms.out_unit = word
+
parms.out_unit = parms.out_unit or word
 
if set_precision(strip(parms[i])) then
 
if set_precision(strip(parms[i])) then
 
i = i + 1
 
i = i + 1
Line 2,189: Line 2,251:  
else
 
else
 
parms.precision = precision
 
parms.precision = precision
 +
end
 +
for j = i, i + 3 do
 +
local parm = parms[j]  -- warn if find a non-empty extraneous parameter
 +
if parm and parm:match('%S') then
 +
add_warning(parms, 1, 'cvt_unknown_option', parm)
 +
break
 +
end
 
end
 
end
 
return true, in_unit_table
 
return true, in_unit_table
Line 2,490: Line 2,559:  
--      is "1", or like "1.00", or is a fraction with value < 1;
 
--      is "1", or like "1.00", or is a fraction with value < 1;
 
--  (and more fields shown below, and a calculated 'absvalue' field).
 
--  (and more fields shown below, and a calculated 'absvalue' field).
-- or return true, nil if no value specified;
   
-- or return false, t where t is an error message table.
 
-- or return false, t where t is an error message table.
 
-- Input info.clean uses en digits (it has been translated, if necessary).
 
-- Input info.clean uses en digits (it has been translated, if necessary).
 
-- Output show uses en or non-en digits as appropriate, or can be spelled.
 
-- Output show uses en or non-en digits as appropriate, or can be spelled.
local invalue
  −
if info then
  −
invalue = info.value
  −
if in_current.builtin == 'hand' then
  −
invalue = info.altvalue
  −
end
  −
end
  −
if invalue == nil or invalue == '' then
  −
return true, nil
  −
end
   
if out_current.builtin == 'hand' then
 
if out_current.builtin == 'hand' then
 
return cvt_to_hand(parms, info, in_current, out_current)
 
return cvt_to_hand(parms, info, in_current, out_current)
 
end
 
end
 +
local invalue = in_current.builtin == 'hand' and info.altvalue or info.value
 
local outvalue, extra = convert(parms, invalue, info, in_current, out_current)
 
local outvalue, extra = convert(parms, invalue, info, in_current, out_current)
 
if parms.need_table_or_sort then
 
if parms.need_table_or_sort then
Line 2,777: Line 2,836:  
local linked_pages  -- to record linked pages so will not link to the same page more than once
 
local linked_pages  -- to record linked pages so will not link to the same page more than once
   −
local function make_link(link, id, link_key)
+
local function unlink(unit_table)
 +
-- Forget that the given unit has previously been linked (if it has).
 +
-- That is needed when processing a range of inputs or outputs when an id
 +
-- for the first range value may have been evaluated, but only an id for
 +
-- the last value is displayed, and that id may need to be linked.
 +
linked_pages[unit_table.unitcode or unit_table] = nil
 +
end
 +
 
 +
local function make_link(link, id, unit_table)
 
-- Return wikilink "[[link|id]]", possibly abbreviated as in examples:
 
-- Return wikilink "[[link|id]]", possibly abbreviated as in examples:
 
--  [[Mile|mile]]  --> [[mile]]
 
--  [[Mile|mile]]  --> [[mile]]
Line 2,784: Line 2,851:  
-- * no link given (so caller does not need to check if a link was defined); or
 
-- * no link given (so caller does not need to check if a link was defined); or
 
-- * link has previously been used during the current convert (to avoid overlinking).
 
-- * link has previously been used during the current convert (to avoid overlinking).
-- Linking with a unit uses the unit table as the link key, which fails to detect
+
local link_key
-- overlinking for conversions like the following (each links "mile" twice):
+
if unit_table then
--  {{convert|1|impgal/mi|USgal/mi|lk=on}}
+
link_key = unit_table.unitcode or unit_table
--  {{convert|1|l/km|impgal/mi USgal/mi|lk=on}}
+
else
link_key = link_key or link -- use key if given (the key, but not the link, may be known when need to cancel a link record)
+
link_key = link
 +
end
 
if not link or link == '' or linked_pages[link_key] then
 
if not link or link == '' or linked_pages[link_key] then
 
return id
 
return id
Line 2,832: Line 2,900:  
else
 
else
 
i = 3
 
i = 3
 +
end
 +
if i > 1 and varname == 'pl' then
 +
i = i - 1
 
end
 
end
 
vname = split(unit_table.varname, '!')[i]
 
vname = split(unit_table.varname, '!')[i]
Line 2,862: Line 2,933:  
local per = unit_table.per
 
local per = unit_table.per
 
if per then
 
if per then
 +
local paren1, paren2 = '', ''  -- possible parentheses around bottom unit
 
local unit1 = per[1]  -- top unit_table, or nil
 
local unit1 = per[1]  -- top unit_table, or nil
 
local unit2 = per[2]  -- bottom unit_table
 
local unit2 = per[2]  -- bottom unit_table
Line 2,873: Line 2,945:  
return symbol  -- for exceptions that have the symbol built-in
 
return symbol  -- for exceptions that have the symbol built-in
 
end
 
end
 +
end
 +
if (unit2.symbol):find('⋅', 1, true) then
 +
paren1, paren2 = '(', ')'
 
end
 
end
 
end
 
end
Line 2,912: Line 2,987:  
unit_table.sep = ''
 
unit_table.sep = ''
 
end
 
end
return result .. linked_id(parms, unit2, key_id2, want_link, '1')
+
return result .. paren1 .. linked_id(parms, unit2, key_id2, want_link, '1') .. paren2
 
end
 
end
 
if multiplier then
 
if multiplier then
Line 3,077: Line 3,152:  
local inout = unit_table.inout
 
local inout = unit_table.inout
 
local abbr = parms.abbr
 
local abbr = parms.abbr
if abbr == 'on' or abbr == inout then
+
if (abbr == 'on' or abbr == inout) and not parms.number_word then
 
info.show = info.show ..
 
info.show = info.show ..
 
'<span style="margin-left:0.2em">×<span style="margin-left:0.1em">' ..
 
'<span style="margin-left:0.2em">×<span style="margin-left:0.1em">' ..
Line 3,139: Line 3,214:  
return  preunit .. id1
 
return  preunit .. id1
 
end
 
end
if parms.opt_also_symbol and not composite then
+
if parms.opt_also_symbol and not composite and not parms.opt_flip then
 
local join1 = parms.joins[1]
 
local join1 = parms.joins[1]
 
if join1 == ' (' or join1 == ' [' then
 
if join1 == ' (' or join1 == ' [' then
Line 3,184: Line 3,259:  
local range = parms.range
 
local range = parms.range
 
if range and not add_unit then
 
if range and not add_unit then
linked_pages[first_unit] = nil  -- so the final and only id will be linked, if wanted
+
unlink(first_unit)
 
end
 
end
 
local id = range and make_id(parms, range.n + 1, first_unit) or id1
 
local id = range and make_id(parms, range.n + 1, first_unit) or id1
Line 3,221: Line 3,296:  
-- Processing required for each output unit.
 
-- Processing required for each output unit.
 
-- Return block of text to represent output (value/unit).
 
-- Return block of text to represent output (value/unit).
 +
local inout = out_current.inout  -- normally 'out' but can be 'in' for order=out
 
local id1, want_name = make_id(parms, 1, out_current)
 
local id1, want_name = make_id(parms, 1, out_current)
 
local sep = out_current.sep  -- set by make_id
 
local sep = out_current.sep  -- set by make_id
Line 3,242: Line 3,318:  
if range then
 
if range then
 
-- For simplicity and because more not needed, handle one range item only.
 
-- For simplicity and because more not needed, handle one range item only.
result = range_text(range[1], want_name, parms, result, prefix .. valinfo[2].show, 'out')
+
result = range_text(range[1], want_name, parms, result, prefix .. valinfo[2].show, inout)
 
end
 
end
 
return preunit .. result
 
return preunit .. result
Line 3,251: Line 3,327:  
local range = parms.range
 
local range = parms.range
 
if range and not add_unit then
 
if range and not add_unit then
linked_pages[out_current] = nil  -- so the final and only id will be linked, if wanted
+
unlink(out_current)
 
end
 
end
 
local id = range and make_id(parms, range.n + 1, out_current) or id1
 
local id = range and make_id(parms, range.n + 1, out_current) or id1
local extra, was_hyphenated = hyphenated_maybe(parms, want_name, sep, id, 'out')
+
local extra, was_hyphenated = hyphenated_maybe(parms, want_name, sep, id, inout)
 
if was_hyphenated then
 
if was_hyphenated then
 
add_unit = false
 
add_unit = false
Line 3,275: Line 3,351:  
result = show
 
result = show
 
else
 
else
result = range_text(range[i], want_name, parms, result, show, 'out')
+
result = range_text(range[i], want_name, parms, result, show, inout)
 
end
 
end
 
end
 
end
Line 3,292: Line 3,368:  
-- for a single output (which is not a combination or a multiple);
 
-- for a single output (which is not a combination or a multiple);
 
-- or return false, t where t is an error message table.
 
-- or return false, t where t is an error message table.
out_unit_table.valinfo = collection()
+
if parms.opt_order_out and in_unit_table.unitcode == out_unit_table.unitcode then
local range = parms.range
+
out_unit_table.valinfo = in_unit_table.valinfo
for i = 1, (range and (range.n + 1) or 1) do
+
else
local success, info = cvtround(parms, in_unit_table.valinfo[i], in_unit_table, out_unit_table)
+
out_unit_table.valinfo = collection()
if not success then return false, info end
+
for _, v in ipairs(in_unit_table.valinfo) do
out_unit_table.valinfo:add(info)
+
local success, info = cvtround(parms, v, in_unit_table, out_unit_table)
 +
if not success then return false, info end
 +
out_unit_table.valinfo:add(info)
 +
end
 
end
 
end
 
return true, process_one_output(parms, out_unit_table)
 
return true, process_one_output(parms, out_unit_table)
Line 3,306: Line 3,385:  
-- for an output which is a multiple (like 'ftin');
 
-- for an output which is a multiple (like 'ftin');
 
-- or return false, t where t is an error message table.
 
-- or return false, t where t is an error message table.
 +
local inout = out_unit_table.inout  -- normally 'out' but can be 'in' for order=out
 
local multiple = out_unit_table.multiple  -- table of scaling factors (will not be nil)
 
local multiple = out_unit_table.multiple  -- table of scaling factors (will not be nil)
 
local combos = out_unit_table.combination  -- table of unit tables (will not be nil)
 
local combos = out_unit_table.combination  -- table of unit tables (will not be nil)
Line 3,312: Line 3,392:  
local disp = parms.disp
 
local disp = parms.disp
 
local want_name = (abbr_org == nil and (disp == 'or' or disp == 'slash')) or
 
local want_name = (abbr_org == nil and (disp == 'or' or disp == 'slash')) or
not (abbr == 'on' or abbr == 'out' or abbr == 'mos')
+
not (abbr == 'on' or abbr == inout or abbr == 'mos')
local want_link = (parms.lk == 'on' or parms.lk == 'out')
+
local want_link = (parms.lk == 'on' or parms.lk == inout)
 
local mid = parms.opt_flip and parms.mid or ''
 
local mid = parms.opt_flip and parms.mid or ''
 
local sep1 = '&nbsp;'
 
local sep1 = '&nbsp;'
Line 3,329: Line 3,409:  
local tfrac, thisvalue, strforce
 
local tfrac, thisvalue, strforce
 
local out_current = combos[i]
 
local out_current = combos[i]
out_current.inout = 'out'
+
out_current.inout = inout
 
local scale = multiple[i]
 
local scale = multiple[i]
 
if i == 1 then  -- least significant unit ('in' from 'ftin')
 
if i == 1 then  -- least significant unit ('in' from 'ftin')
Line 3,411: Line 3,491:  
end
 
end
 
local strval
 
local strval
local inout = (i == #combos or outvalue == 0) and 'out' or ''  -- trick so the last value processed (first displayed) has uppercase, if requested
+
local spell_inout = (i == #combos or outvalue == 0) and inout or ''  -- trick so the last value processed (first displayed) has uppercase, if requested
 
if strforce and outvalue == 0 then
 
if strforce and outvalue == 0 then
 
sign = ''  -- any sign is in strforce
 
sign = ''  -- any sign is in strforce
Line 3,417: Line 3,497:  
elseif tfrac then
 
elseif tfrac then
 
local wholestr = (thisvalue > 0) and tostring(thisvalue) or nil
 
local wholestr = (thisvalue > 0) and tostring(thisvalue) or nil
strval = format_fraction(parms, inout, false, wholestr, tfrac.numstr, tfrac.denstr, do_spell)
+
strval = format_fraction(parms, spell_inout, false, wholestr, tfrac.numstr, tfrac.denstr, do_spell)
 
else
 
else
 
strval = (thisvalue == 0) and from_en('0') or with_separator(parms, format(fmt, thisvalue))
 
strval = (thisvalue == 0) and from_en('0') or with_separator(parms, format(fmt, thisvalue))
 
if do_spell then
 
if do_spell then
strval = spell_number(parms, inout, strval) or strval
+
strval = spell_number(parms, spell_inout, strval) or strval
 
end
 
end
 
end
 
end
Line 3,444: Line 3,524:  
local success, result2 = make_result(valinfo[i+1])
 
local success, result2 = make_result(valinfo[i+1])
 
if not success then return false, result2 end
 
if not success then return false, result2 end
result = range_text(range[i], want_name, parms, result, result2, 'out')
+
result = range_text(range[i], want_name, parms, result, result2, inout)
 
end
 
end
 
end
 
end
Line 3,456: Line 3,536:  
local success, bad_output
 
local success, bad_output
 
local bad_input_mcode = in_unit_table.bad_mcode  -- nil if input unit is a valid convert unit
 
local bad_input_mcode = in_unit_table.bad_mcode  -- nil if input unit is a valid convert unit
local invalue1 = in_unit_table.valinfo[1].value
   
local out_unit = parms.out_unit
 
local out_unit = parms.out_unit
if out_unit == nil or out_unit == '' then
+
if out_unit == nil or out_unit == '' or type(out_unit) == 'function' then
 
if bad_input_mcode or parms.opt_input_unit_only then
 
if bad_input_mcode or parms.opt_input_unit_only then
 
bad_output = ''
 
bad_output = ''
 
else
 
else
success, out_unit = get_default(invalue1, in_unit_table)
+
local getdef = type(out_unit) == 'function' and out_unit or get_default
 +
success, out_unit = getdef(in_unit_table.valinfo[1].value, in_unit_table)
 
parms.out_unit = out_unit
 
parms.out_unit = out_unit
 
if not success then
 
if not success then
Line 3,480: Line 3,560:  
end
 
end
 
end
 
end
 +
local lhs, rhs
 
local flipped = parms.opt_flip and not bad_input_mcode
 
local flipped = parms.opt_flip and not bad_input_mcode
local parts = {}
+
if bad_output then
for part = 1, 2 do
+
rhs = (bad_output == '') and '' or message(parms, bad_output)
-- The LHS (parts[1]) is normally the input, but is the output if flipped.
+
elseif parms.opt_input_unit_only then
-- Process LHS first so it will be linked, if wanted.
+
rhs = ''
-- Linking to the same item is suppressed in the RHS to avoid overlinking.
+
else
if (part == 1 and not flipped) or (part == 2 and flipped) then
+
local combos  -- nil (for 'ft' or 'ftin'), or table of unit tables (for 'm ft')
parts[part] = process_input(parms, in_unit_table)
+
if not out_unit_table.multiple then  -- nil/false ('ft' or 'm ft'), or table of factors ('ftin')
elseif bad_output then
+
combos = out_unit_table.combination
parts[part] = (bad_output == '') and '' or message(parms, bad_output)
+
end
else
+
local frac = parms.frac  -- nil or denominator of fraction for output values
local outputs = {}
+
if frac then
local combos  -- nil (for 'ft' or 'ftin'), or table of unit tables (for 'm ft')
+
-- Apply fraction to the unit (if only one), or to non-SI units (if a combination),
if not out_unit_table.multiple then  -- nil/false ('ft' or 'm ft'), or table of factors ('ftin')
+
-- except that if a precision is also specified, the fraction only applies to
combos = out_unit_table.combination
+
-- the hand unit; that allows the following result:
end
+
-- {{convert|156|cm|in hand|1|frac=2}} → 156 centimetres (61.4 in; 15.1½ hands)
local frac = parms.frac  -- nil or denominator of fraction for output values
+
-- However, the following is handled elsewhere as a special case:
if frac then
+
-- {{convert|156|cm|hand in|1|frac=2}} → 156 centimetres (15.1½ hands; 61½ in)
-- Apply fraction to the unit (if only one), or to non-SI units (if a combination),
+
if combos then
-- except that if a precision is also specified, the fraction only applies to
+
local precision = parms.precision
-- the hand unit; that allows the following result:
+
for _, unit in ipairs(combos) do
-- {{convert|156|cm|in hand|1|frac=2}} → 156 centimetres (61.4 in; 15.1½ hands)
+
if unit.builtin == 'hand' or (not precision and not unit.prefixes) then
-- However, the following is handled elsewhere as a special case:
+
unit.frac = frac
-- {{convert|156|cm|hand in|1|frac=2}} → 156 centimetres (15.1½ hands; 61½ in)
  −
if combos then
  −
local precision = parms.precision
  −
for _, unit in ipairs(combos) do
  −
if unit.builtin == 'hand' or (not precision and not unit.prefixes) then
  −
unit.frac = frac
  −
end
   
end
 
end
else
  −
out_unit_table.frac = frac
   
end
 
end
 +
else
 +
out_unit_table.frac = frac
 
end
 
end
local out_first
+
end
local imax = combos and #combos or 1  -- 1 (single unit) or number of unit tables
+
local outputs = {}
for i = 1, imax do
+
local imax = combos and #combos or 1  -- 1 (single unit) or number of unit tables
local success, item
+
if imax == 1 then
local out_current = combos and combos[i] or out_unit_table
+
parms.opt_order_out = nil  -- only useful with an output combination
out_current.inout = 'out'
+
end
if i == 1 then
+
if not flipped and not parms.opt_order_out then
out_first = out_current
+
-- Process left side first so any duplicate links (from lk=on) are suppressed
if imax > 1 and out_current.builtin == 'hand' then
+
-- on right. Example: {{convert|28|e9pc|e9ly|abbr=off|lk=on}}
out_current.out_next = combos[2]  -- built-in hand can influence next unit in a combination
+
lhs = process_input(parms, in_unit_table)
end
+
end
 +
for i = 1, imax do
 +
local success, item
 +
local out_current = combos and combos[i] or out_unit_table
 +
out_current.inout = 'out'
 +
if i == 1 then
 +
if imax > 1 and out_current.builtin == 'hand' then
 +
out_current.out_next = combos[2]  -- built-in hand can influence next unit in a combination
 
end
 
end
if out_current.multiple then
+
if parms.opt_order_out then
success, item = make_output_multiple(parms, in_unit_table, out_current)
+
out_current.inout = 'in'
else
  −
success, item = make_output_single(parms, in_unit_table, out_current)
   
end
 
end
if not success then return false, item end
  −
table.insert(outputs, item)
   
end
 
end
if parms.opt_input_unit_only then
+
if out_current.multiple then
parts[part] = ''
+
success, item = make_output_multiple(parms, in_unit_table, out_current)
 
else
 
else
local sep = parms.table_joins and parms.table_joins[2] or parms.join_between
+
success, item = make_output_single(parms, in_unit_table, out_current)
parts[part] = table.concat(outputs, sep)
   
end
 
end
 +
if not success then return false, item end
 +
outputs[i] = item
 +
end
 +
if parms.opt_order_out then
 +
lhs = outputs[1]
 +
table.remove(outputs, 1)
 +
end
 +
local sep = parms.table_joins and parms.table_joins[2] or parms.join_between
 +
rhs = table.concat(outputs, sep)
 +
end
 +
if flipped or not lhs then
 +
local input = process_input(parms, in_unit_table)
 +
if flipped then
 +
lhs = rhs
 +
rhs = input
 +
else
 +
lhs = input
 
end
 
end
 
end
 
end
 
if parms.join_before then
 
if parms.join_before then
parts[1] = parms.join_before .. parts[1]
+
lhs = parms.join_before .. lhs
 
end
 
end
 
local wikitext
 
local wikitext
 
if bad_input_mcode then
 
if bad_input_mcode then
 
if bad_input_mcode == '' then
 
if bad_input_mcode == '' then
wikitext = parts[1]
+
wikitext = lhs
 
else
 
else
wikitext = parts[1] .. message(parms, bad_input_mcode)
+
wikitext = lhs .. message(parms, bad_input_mcode)
 
end
 
end
 
elseif parms.table_joins then
 
elseif parms.table_joins then
wikitext = parms.table_joins[1] .. parts[1] .. parms.table_joins[2] .. parts[2]
+
wikitext = parms.table_joins[1] .. lhs .. parms.table_joins[2] .. rhs
 
else
 
else
wikitext = parts[1] .. parms.joins[1] .. parts[2] .. parms.joins[2]
+
wikitext = lhs .. parms.joins[1] .. rhs .. parms.joins[2]
 
end
 
end
 
if parms.warnings and not bad_input_mcode then
 
if parms.warnings and not bad_input_mcode then
Line 3,588: Line 3,681:  
-- An unknown input unit would display the input and an error message
 
-- An unknown input unit would display the input and an error message
 
-- with success == true at this point.
 
-- with success == true at this point.
-- Also, can have success == false with a message to output an empty string.
+
-- Also, can have success == false with a message that outputs an empty string.
if success then
+
if parms.input_text then
if parms.have_problem and parms.input_text then
+
if success and not parms.have_problem then
return parms.input_text
+
return result
 +
end
 +
local cat
 +
if parms.tracking then
 +
-- Add a tracking category using the given text as the category sort key.
 +
-- There is currently only one type of tracking, but in principle multiple
 +
-- items could be tracked, using different sort keys for convenience.
 +
cat = wanted_category('tracking', parms.tracking)
 
end
 
end
return result
+
return parms.input_text .. (cat or '')
 
end
 
end
return parms.input_text and parms.input_text or message(parms, result)
+
return success and result or message(parms, result)
 
end
 
end
  
Bureaucrats, private-view, public-view, Administrators
97,692

edits

Navigation menu