Пређи на садржај

Модул:Val — разлика између измена

С Википедије, слободне енциклопедије
Садржај обрисан Садржај додат
Нема описа измене
+
Ред 7: Ред 7:
-- Return formatted message text for an error or warning.
-- Return formatted message text for an error or warning.
-- Can append "#FormattingError" to URL of a page with a problem to find it.
-- Can append "#FormattingError" to URL of a page with a problem to find it.
local anchor = '<span id="FormattingError" />'
local anchor = '<span id="FormattingError"></span>'
local body, category
local body, category
if nocat or mw.title.getCurrentTitle():inNamespaces(1, 2, 3, 5) then
if nocat or mw.title.getCurrentTitle():inNamespaces(1, 2, 3, 5) then
Ред 15: Ред 15:
category = '[[Category:Pages with incorrect formatting templates use]]'
category = '[[Category:Pages with incorrect formatting templates use]]'
end
end
iswarning = false -- problems are infrequent so try showing large error so editor will notice
if iswarning then
if iswarning then
body = '<sup class="noprint Inline-Template" style="white-space:nowrap;">' ..
body = '<sup class="noprint Inline-Template" style="white-space:nowrap;">' ..
'[[Template:Val|<span title="' ..
'[[Шаблон:Val|<span title="' ..
msg:gsub('"', '&quot;') ..
msg:gsub('"', '&quot;') ..
'">warning</span>]]</sup>'
'">упозорење</span>]]</sup>'
else
else
body = '<strong class="error">' ..
body = '<strong class="error">' ..
'Error in &#123;&#123;[[Template:val|val]]&#125;&#125;: ' ..
'Грешка у шаблону &#123;&#123;[[Шаблон:val|val]]&#125;&#125;: ' ..
msg ..
msg ..
'</strong>'
'</strong>'
Ред 32: Ред 33:
-- No need for '&nbsp;' because nowrap applies to all output.
-- No need for '&nbsp;' because nowrap applies to all output.
[","] = ", ",
[","] = ", ",
["by"] = " by ",
["by"] = " пута ",
["-"] = "–",
["-"] = "–",
["–"] = "",
["–"] = "",
["and"] = " and ",
["and"] = " и ",
["or"] = " or " ,
["or"] = " или " ,
["to"] = " to " ,
["to"] = " до " ,
["x"] = " × ",
["x"] = " × ",
["×"] = " × ",
["×"] = " × ",
Ред 62: Ред 63:
local description
local description
if which == 'e' then
if which == 'e' then
description = 'exponent parameter (<b>e</b>)'
description = 'параметар експонента (<b>e</b>)'
else
else
description = 'parameter ' .. which
description = 'параметар ' .. which
end
end
return description .. ' ' .. (msg or 'is not a valid number') .. '.'
return description .. ' ' .. (msg or 'није валидан број') .. '.'
end
end
local result = {}
local result = {}
Ред 74: Ред 75:
if index == 2 then
if index == 2 then
if numbers[1] and numbers[1].exp then
if numbers[1] and numbers[1].exp then
return fail('cannot use a range if the first parameter includes "e"')
return fail('не може се користити опсег ако први параметар укључује "e"')
end
end
numbers.has_ranges = true
numbers.has_ranges = true
else
else
if not numbers.has_ranges then
if not numbers.has_ranges then
return fail('needs a range in parameter 2')
return fail('неопходан опсег у параметру 2')
end
end
end
end
Ред 89: Ред 90:
return nil
return nil
end
end
return fail('does not accept a range')
return fail('не прихвата се опсег')
end
end
if numbers.has_ranges and type(index) == 'number' and (index % 2 == 0) then
if numbers.has_ranges and type(index) == 'number' and (index % 2 == 0) then
return fail('should be a range')
return fail('треба да буде опсег')
end
end
if index == 'e' then
if index == 'e' then
Ред 98: Ред 99:
if e then
if e then
if arg then
if arg then
return fail('cannot be used if the first parameter includes "e"')
return fail('не може да се користи ако први параметар укључује "e"')
end
end
arg = e
arg = e
Ред 116: Ред 117:
result.exp = b
result.exp = b
else
else
return fail('cannot use e notation')
return fail('не може да се користи e нотација')
end
end
end
end
Ред 170: Ред 171:
end
end
if index > 19 then
if index > 19 then
return 'too many parameters'
return 'превише параметара'
end
end
end
end
if numbers.has_ranges and (#numbers % 2 == 0) then
if numbers.has_ranges and (#numbers % 2 == 0) then
return 'неопходан број након последњег параметра јер је у питању опсег'
return 'need a number after the last parameter because it is a range.'
end
end
end
end
Ред 204: Ред 205:
end
end
end
end
error('Unit "' .. ucode .. '" has invalid scale "' .. text .. '"')
error('Јединица "' .. ucode .. '" има невалидну величину "' .. text .. '"')
end
end


Ред 348: Ред 349:
sortkey = perunit.sortkey
sortkey = perunit.sortkey
end
end
if not unit.nospace then
if not (unit.nospace or options.nospace) then
text = '&nbsp;' .. text
text = '&nbsp;' .. text
end
end
Ред 501: Ред 502:
add(ucode .. ' = ' .. text .. '<br />')
add(ucode .. ' = ' .. text .. '<br />')
else
else
add(line .. ' ◆ <b>invalid definition</b><br />')
add(line .. ' ◆ <b>невалидна дефиниција</b><br />')
end
end
else
else
Ред 525: Ред 526:
result = sign .. table.concat(ipart, ',')
result = sign .. table.concat(ipart, ',')
if dpart then
if dpart then
result = result .. '.' .. table.concat(dpart)
result = result .. ',' .. table.concat(dpart)
end
end
else
else
-- Delimit with a small gap by default.
-- Delimit with a comma by default.
local groups = {}
local groups = {}
groups[1] = table.remove(ipart, 1)
groups[1] = table.remove(ipart, 1)
for _, v in ipairs(ipart) do
for _, v in ipairs(ipart) do
table.insert(groups, '<span style="margin-left:.25em">' .. v .. '</span>')
table.insert(groups, ',' .. v)
end
end
if dpart then
if dpart then
table.insert(groups, '.' .. (table.remove(dpart, 1) or ''))
table.insert(groups, ',' .. (table.remove(dpart, 1) or ''))
for _, v in ipairs(dpart) do
for _, v in ipairs(dpart) do
table.insert(groups, '<span style="margin-left:.25em">' .. v .. '</span>')
table.insert(groups, ',' .. v )
end
end
end
end
Ред 555: Ред 556:
end
end
return '<span style="display:inline-block;margin-bottom:-0.3em;vertical-align:-0.4em;line-height:1.2em;font-size:85%;text-align:' ..
return '<span style="display:inline-block;margin-bottom:-0.3em;vertical-align:-0.4em;line-height:1.2em;font-size:85%;text-align:' ..
align .. '">' .. sup .. '<br />' .. sub .. '</span>'
align .. ';">' .. sup .. '<br />' .. sub .. '</span>'
end
end


Ред 602: Ред 603:
else
else
text = (angle or '') ..
text = (angle or '') ..
'<span style="margin-left:0.3em;margin-right:0.15em">±</span>' ..
'<span style="margin-left:0.3em;margin-right:0.15em;">±</span>' ..
delimit('', uncU, fmt)
delimit('', uncU, fmt)
need_parens = true
need_parens = true
Ред 629: Ред 630:
return list_units(options.align)
return list_units(options.align)
end
end
return valerror('invalid action "' .. action .. '".', options.nocat)
return valerror('невалидна акција "' .. action .. '".', options.nocat)
end
end
local number = values.number or (values.numbers and values.numbers[1]) or {}
local number = values.number or (values.numbers and values.numbers[1]) or {}
local e_10 = options.e or {}
local e_10 = options.e or {}
local novalue = (number.value == nil and e_10.clean == nil)
local fmt = options.fmt
local fmt = options.fmt
local want_sort = true
local want_sort = true
local sortable = options.sortable
local sortable = options.sortable
if sortable == 'off' then
if sortable == 'off' or (sortable == nil and novalue) then
want_sort = false
want_sort = false
elseif sortable == 'debug' then
elseif sortable == 'debug' then
Ред 656: Ред 658:
per = unit_spec.per,
per = unit_spec.per,
want_per_link = unit_spec.want_per_link,
want_per_link = unit_spec.want_per_link,
nospace = novalue,
want_longscale = unit_spec.want_longscale,
want_longscale = unit_spec.want_longscale,
sortable = sortable,
sortable = sortable,
Ред 683: Ред 686:
else
else
n_text = ''
n_text = ''
if not e_10.clean then
e_10.clean = '0'
e_10.sign = ''
end
end
end
else
else
Ред 701: Ред 700:
e_text = '10<sup>' .. delimit(e_10.sign, e_10.clean, fmt) .. '</sup>'
e_text = '10<sup>' .. delimit(e_10.sign, e_10.clean, fmt) .. '</sup>'
if number.clean then
if number.clean then
e_text = '<span style="margin-left:0.25em;margin-right:0.15em">×</span>' .. e_text
e_text = '<span style="margin-left:0.25em;margin-right:0.15em;">×</span>' .. e_text
end
end
else
else
e_text = ''
e_text = ''
end
end
local result =
return
'<span class="nowrap">' ..
(sortkey or '') ..
(sortkey or '') ..
(options.prefix or '') ..
(options.prefix or '') ..
Ред 713: Ред 711:
e_text ..
e_text ..
final_unit ..
final_unit ..
(options.suffix or '') ..
(options.suffix or '')
if result ~= '' then
'</span>' ..
result = '<span class="nowrap">' .. result .. '</span>'
(options.warning or '')
end
return result .. (options.warning or '')
end
end


Ред 744: Ред 744:
for k, v in pairs(args) do
for k, v in pairs(args) do
if type(k) == 'string' and not whitelist[k] then
if type(k) == 'string' and not whitelist[k] then
local warning = 'Val parameter "' .. k .. '=' .. v .. '" is not supported'
local warning = 'Вредносни параметар "' .. k .. '=' .. v .. '" није подржан'
return valerror(warning, nocat, true)
return valerror(warning, nocat, true)
end
end
end
end
if not has_ranges and args[4] then
if not has_ranges and args[4] then
return valerror('Val parameter 4 ignored', nocat, true)
return valerror('Вредносни параметар 4 игнорисан', nocat, true)
end
end
end
end
Ред 763: Ред 763:
end
end
if args.u and args.ul then
if args.u and args.ul then
return valerror('unit (<b>u</b>) and unit with link (<b>ul</b>) are both specified, only one is allowed.', nocat)
return valerror('Јединица (<b>u</b>) и јединица с линком (<b>ul</b>) су обоје одређени; само је једно дозвољено.', nocat)
end
end
if args.up and args.upl then
if args.up and args.upl then
return valerror('unit per (<b>up</b>) and unit per with link (<b>upl</b>) are both specified, only one is allowed.', nocat)
return valerror('Јединица по (<b>up</b>) и јединица по с линком (<b>upl</b>) су обоје одређени; само је једно дозвољено.', nocat)
end
end
local values
local values
Ред 814: Ред 814:
prefix = args.p,
prefix = args.p,
sandbox = string.find(frame:getTitle(), 'sandbox', 1, true) ~= nil,
sandbox = string.find(frame:getTitle(), 'sandbox', 1, true) ~= nil,
sortable = args.sortable or (args.debug == 'yes' and 'debug' or 'on'),
sortable = args.sortable or (args.debug == 'yes' and 'debug' or nil),
suffix = args.s,
suffix = args.s,
warning = check_parameters(args, numbers.has_ranges, nocat),
warning = check_parameters(args, numbers.has_ranges, nocat),

Верзија на датум 29. март 2018. у 09:16

Документација модула[прикажи] [уреди] [историја] [освежи]
  • Имплементира шаблон {{Val}}.

-- For Template:Val, output a number and optional unit.
-- Format options include scientific and uncertainty notations.

local data_module  -- name of module defining units

local function valerror(msg, nocat, iswarning)
	-- Return formatted message text for an error or warning.
	-- Can append "#FormattingError" to URL of a page with a problem to find it.
	local anchor = '<span id="FormattingError"></span>'
	local body, category
	if nocat or mw.title.getCurrentTitle():inNamespaces(1, 2, 3, 5) then
		-- No category in Talk, User, User_talk, or Wikipedia_talk.
		category = ''
	else
		category = '[[Category:Pages with incorrect formatting templates use]]'
	end
	iswarning = false  -- problems are infrequent so try showing large error so editor will notice
	if iswarning then
		body = '<sup class="noprint Inline-Template" style="white-space:nowrap;">' ..
			'[[Шаблон:Val|<span title="' ..
			msg:gsub('"', '&quot;') ..
			'">упозорење</span>]]</sup>'
	else
		body = '<strong class="error">' ..
			'Грешка у шаблону &#123;&#123;[[Шаблон:val|val]]&#125;&#125;: ' ..
			msg ..
			'</strong>'
	end
	return anchor .. body .. category
end

local range_types = {
	-- No need for '&nbsp;' because nowrap applies to all output.
	[","]   = ", ",
	["by"]  = " пута ",
	["-"]   = "–",
	["–"]   = "—",
	["and"] = " и ",
	["or"]  = " или " ,
	["to"]  = " до " ,
	["x"]   = " × ",
	["×"]   = " × ",
	["/"]   = "/",
}
local range_repeat_unit = {
	-- WP:UNIT wants unit repeated when a "multiply" range is used.
	["x"]   = true,
	["×"]   = true,
}

local function extract_item(index, numbers, arg)
	-- Extract an item from arg and store the result in numbers[index].
	-- If no argument or if argument is valid, return nil (no error);
	-- otherwise, return an error message.
	-- The stored result is:
	-- * a table for a number (empty if there was no specified number); or
	-- * a string for range text
	-- Input like 1e3 is regarded as invalid for all except argument 1
	-- which accepts e notation as an alternative to the 'e' argument.
	-- Input commas are removed so 1,234 is the same as 1234.
	local which = index
	local function fail(msg)
		local description
		if which == 'e' then
			description = 'параметар експонента (<b>e</b>)'
		else
			description = 'параметар ' .. which
		end
		return description .. ' ' .. (msg or 'није валидан број') .. '.'
	end
	local result = {}
	local range = range_types[arg]
	if range then
		if type(index) == 'number' and (index % 2 == 0) then
			if index == 2 then
				if numbers[1] and numbers[1].exp then
					return fail('не може се користити опсег ако први параметар укључује "e"')
				end
				numbers.has_ranges = true
			else
				if not numbers.has_ranges then
					return fail('неопходан опсег у параметру 2')
				end
			end
			numbers[index] = range
			if range_repeat_unit[arg] then
				-- Any "repeat" range forces unit (if any) to be repeated for all items.
				numbers.isrepeat = true
			end
			return nil
		end
		return fail('не прихвата се опсег')
	end
	if numbers.has_ranges and type(index) == 'number' and (index % 2 == 0) then
		return fail('треба да буде опсег')
	end
	if index == 'e' then
		local e = numbers[1] and numbers[1].exp
		if e then
			if arg then
				return fail('не може да се користи ако први параметар укључује "e"')
			end
			arg = e
			which = 1
		end
	end
	if arg and arg ~= '' then
		arg = arg:gsub(',', '')
		if arg:sub(1, 1) == '(' and arg:sub(-1) == ')' then
			result.parens = true
			arg = arg:sub(2, -2)
		end
		local a, b = arg:match('^(.+)[Ee](.+)$')
		if a then
			if index == 1 then
				arg = a
				result.exp = b
			else
				return fail('не може да се користи e нотација')
			end
		end
		local isnegative, propersign, prefix
		local minus = '−'
		prefix, arg = arg:match('^(.-)([%d.]+)$')
		local value = tonumber(arg)
		if not value then
			return fail()
		end
		if arg:sub(1, 1) == '.' then
			arg = '0' .. arg
		end
		if prefix == '' then
			-- Ignore.
		elseif prefix == '±' then
			-- Display for first number, ignore for others.
			if index == 1 then
				propersign = '±'
			end
		elseif prefix == '+' then
			propersign = '+'
		elseif prefix == '-' or prefix == minus then
			propersign = minus
			isnegative = true
		else
			return fail()
		end
		result.clean = arg
		result.sign = propersign or ''
		result.value = isnegative and -value or value
	end
	numbers[index] = result
	return nil  -- no error
end

local function get_args(numbers, args)
	-- Extract arguments and store the results in numbers.
	-- Return nothing (no error) if ok; otherwise, return an error message.
	for index = 1, 99 do
		local which = index
		local arg = args[which]  -- has been trimmed
		if not arg then
			which = 'e'
			arg = args[which]
		end
		local msg = extract_item(which, numbers, arg)
		if msg then
			return msg
		end
		if which == 'e' then
			break
		end
		if index > 19 then
			return 'превише параметара'
		end
	end
	if numbers.has_ranges and (#numbers % 2 == 0) then
		return 'неопходан број након последњег параметра јер је у питању опсег'
	end
end

local function get_scale(text, ucode)
	-- Return the value of text as a number, or throw an error.
	-- This supports extremely basic expressions of the form:
	--   a / b
	--   a ^ b
	-- where a and b are numbers or 'pi'.
	local n = tonumber(text)
	if n then
		return n
	end
	n = text:gsub('pi', math.pi)
	for _, op in ipairs({ '/', '^' }) do
		local a, b = n:match('^(.-)' .. op .. '(.*)$')
		if a then
			a = tonumber(a)
			b = tonumber(b)
			if a and b then
				if op == '/' then
					return a / b
				elseif op == '^' then
					return a ^ b
				end
			end
			break
		end
	end
	error('Јединица "' .. ucode .. '" има невалидну величину "' .. text .. '"')
end

local function get_builtin_unit(ucode, definitions)
	-- Return table of information for the specified built-in unit, or nil if not known.
	-- Each defined unit code must be followed by two spaces (not tab characters).
	local _, pos = definitions:find('\n' .. ucode .. '  ', 1, true)
	if pos then
		local endline = definitions:find('%s*\n', pos)
		if endline then
			local result = {}
			local n = 0
			local text = definitions:sub(pos + 1, endline - 1):gsub('%s%s+', '\t')
			for item in (text .. '\t'):gmatch('(%S.-)\t') do
				if item == 'ALIAS' then
					result.alias = true
				elseif item == 'ANGLE' then
					result.isangle = true
					result.nospace = true
				elseif item == 'NOSPACE' then
					result.nospace = true
				elseif item == 'SI' then
					result.si = true
				else
					n = n + 1
					if n == 1 then
						local link, symbol = item:match('^%[%[([^|]+)|(.+)%]%]$')
						if link then
							result.symbol = symbol
							result.link = link
							n = 2
						else
							result.symbol = item
						end
					elseif n == 2 then
						result.link = item
					elseif n == 3 then
						result.scale_text = item
						result.scale = get_scale(item, ucode)
					else
						result.more_ignored = item
						break
					end
				end
			end
			if result.si then
				local s = result.symbol
				if ucode == 'mc' .. s or ucode == 'mu' .. s then
					result.ucode = 'µ' .. s  -- unit code for convert should be this
				end
			end
			if n >= 2 or (n >= 1 and result.alias) then
				return result
			end
			-- Ignore invalid definition, treating it as a comment.
		end
	end
end

local function convert_lookup(ucode, value, scaled_top, want_link, si, options)
	local lookup = require('Module:Convert')._unit
	return lookup(ucode, {
			value = value,
			scaled_top = scaled_top,
			link = want_link,
			si = si,
			sort = options.sortable,
		})
end

local function get_unit(ucode, value, scaled_top, options)
	local want_link = options.want_link
	if scaled_top then
		want_link = options.want_per_link
	end
	local data = mw.loadData(data_module)
	local result = options.want_longscale and
		get_builtin_unit(ucode, data.builtin_units_long_scale) or
		get_builtin_unit(ucode, data.builtin_units)
	local si, use_convert
	if result then
		if result.alias then
			ucode = result.symbol
			use_convert = true
		end
		if result.scale then
			-- Setting si means convert will use the unit as given, and the sort key
			-- will be calculated from the value without any extra scaling that may
			-- occur if convert found the unit code. For example, if val defines the
			-- unit 'year' with a scale and if si were not set, convert would also apply
			-- its own scale because convert knows that a year is 31,557,600 seconds.
			si = { result.symbol, result.link }
			value = value * result.scale
		end
		if result.si then
			ucode = result.ucode or ucode
			si = { result.symbol, result.link }
			use_convert = true
		end
	else
		result = {}
		use_convert = true
	end
	local convert_unit = convert_lookup(ucode, value, scaled_top, want_link, si, options)
	result.sortkey = convert_unit.sortspan
	if use_convert then
		result.text = convert_unit.text
		result.scaled_top = convert_unit.scaled_value
	else
		if want_link then
			result.text = '[[' .. result.link .. '|' .. result.symbol .. ']]'
		else
			result.text = result.symbol
		end
		result.scaled_top = value
	end
	return result
end

local function makeunit(value, options)
	-- Return table of information for the requested unit and options, or
	-- return nil if no unit.
	options = options or {}
	local unit
	local ucode = options.u
	local percode = options.per
	if ucode then
		unit = get_unit(ucode, value, nil, options)
	elseif percode then
		unit = { nospace = true, scaled_top = value }
	else
		return nil
	end
	local text = unit.text or ''
	local sortkey = unit.sortkey
	if percode then
		local function bracketed(code, text)
			return code:find('[*./]') and '(' .. text .. ')' or text
		end
		local perunit = get_unit(percode, 1, unit.scaled_top, options)
		text = (ucode and bracketed(ucode, text) or '') ..
				'/' .. bracketed(percode, perunit.text)
		sortkey = perunit.sortkey
	end
	if not (unit.nospace or options.nospace) then
		text = '&nbsp;' .. text
	end
	return { text = text, isangle = unit.isangle, sortkey = sortkey }
end

local function list_units(mode)
	-- Return wikitext to list the built-in units.
	-- A unit code should not contain wikimarkup so don't bother escaping.
	local data = mw.loadData(data_module)
	local definitions = data.builtin_units .. data.builtin_units_long_scale
	local last_was_blank = true
	local n = 0
	local result = {}
	local function add(line)
		if line == '' then
			last_was_blank = true
		else
			if last_was_blank and n > 0 then
				n = n + 1
				result[n] = ''
			end
			last_was_blank = false
			n = n + 1
			result[n] = line
		end
	end
	local si_prefixes = {
		-- These are the prefixes recognized by convert; u is accepted for micro.
		y = 'y',
		z = 'z',
		a = 'a',
		f = 'f',
		p = 'p',
		n = 'n',
		u = 'µ',
		['µ'] = 'µ',
		m = 'm',
		c = 'c',
		d = 'd',
		da = 'da',
		h = 'h',
		k = 'k',
		M = 'M',
		G = 'G',
		T = 'T',
		P = 'P',
		E = 'E',
		Z = 'Z',
		Y = 'Y',
	}
	local function is_valid(ucode, unit)
		if unit and not unit.more_ignored then
			assert(type(unit.symbol) == 'string' and unit.symbol ~= '')
			if unit.alias then
				if unit.link or unit.scale_text or unit.si then
					return false
				end
			end
			if unit.si then
				if unit.scale_text then
					return false
				end
				ucode = unit.ucode or ucode
				local base = unit.symbol
				if ucode == base then
					unit.display = base
					return true
				end
				local plen = #ucode - #base
				if plen > 0 then
					local prefix = si_prefixes[ucode:sub(1, plen)]
					if prefix and ucode:sub(plen + 1) == base then
						unit.display = prefix .. base
						return true
					end
				end
			else
				unit.display = unit.symbol
				return true
			end
		end
		return false
	end
	local lookup = require('Module:Convert')._unit
	local function show_convert(ucode, unit)
		-- If a built-in unit defines a scale or sets the SI flag, any unit defined in
		-- convert is not used (the scale or SI prefix's scale is used for a sort key).
		-- If there is no scale or SI flag, and the unit is not defined in convert,
		-- the sort key may not be correct; this allows such units to be identified.
		if not (unit.si or unit.scale_text) then
			if mode == 'convert' then
				unit.show = not lookup(unit.alias and unit.symbol or ucode).unknown
				unit.show_text = 'CONVERT'
			elseif mode == 'unknown' then
				unit.show = lookup(unit.alias and unit.symbol or ucode).unknown
				unit.show_text = 'UNKNOWN'
			elseif not unit.alias then
				-- Show convert's scale in square brackets ('[1]' for an unknown unit).
				-- Don't show scale for an alias because it's misleading for temperature
				-- and an alias is probably not useful for anything else.
				local scale = lookup(ucode, {value=1, sort='on'}).scaled_value
				if type(scale) == 'number' then
					scale = string.format('%.5g', scale):gsub('e%+?(%-?)0*(%d+)', 'e%1%2')
				else
					scale = '?'
				end
				unit.show = true
				unit.show_text = '[' .. scale .. ']'
			end
		end
	end
	for line in definitions:gmatch('([^\n]*)\n') do
		local pos, _ = line:find('  ', 1, true)
		if pos then
			local ucode = line:sub(1, pos - 1)
			local unit = get_builtin_unit(ucode, '\n' .. line .. '\n')
			if is_valid(ucode, unit) then
				show_convert(ucode, unit)
				local flags, text
				if unit.alias then
					text = unit.symbol
				else
					text = '[[' .. unit.link .. '|' .. unit.display .. ']]'
				end
				if unit.isangle then
					unit.nospace = nil  -- don't show redundant flag
				end
				for _, f in ipairs({
						{ 'alias', 'ALIAS' },
						{ 'isangle', 'ANGLE' },
						{ 'nospace', 'NOSPACE' },
						{ 'si', 'SI' },
						{ 'scale_text', unit.scale_text },
						{ 'show', unit.show_text },
					}) do
					if unit[f[1]] then
						local t = f[2]
						if t:match('^%u+$') then
							t = '<small>' .. t .. '</small>'
						end
						if flags then
							flags = flags .. ' ' .. t
						else
							flags = t
						end
					end
				end
				if flags then
					text = text .. ' • ' .. flags
				end
				add(ucode .. ' = ' .. text .. '<br />')
			else
				add(line .. ' ◆ <b>невалидна дефиниција</b><br />')
			end
		else
			add(line)
		end
	end
	return table.concat(result, '\n')
end

local delimit_groups = require('Module:Gapnum').groups
local function delimit(sign, numstr, fmt)
	-- Return sign and numstr (unsigned digits or '.' only) after formatting.
	-- Four-digit integers are not formatted with gaps.
	fmt = (fmt or ''):lower()
	if fmt == 'none' or (fmt == '' and #numstr == 4 and numstr:match('^%d+$')) then
		return sign .. numstr
	end
	-- Group number by integer and decimal parts.
	-- If there is no decimal part, delimit_groups returns only one table.
	local ipart, dpart = delimit_groups(numstr)
	local result
	if fmt == 'commas' then
		result = sign .. table.concat(ipart, ',')
		if dpart then
			result = result .. ',' .. table.concat(dpart)
		end
	else
		-- Delimit with a comma by default.
		local groups = {}
		groups[1] = table.remove(ipart, 1)
		for _, v in ipairs(ipart) do
			table.insert(groups, ',' .. v)
		end
		if dpart then
			table.insert(groups, ',' .. (table.remove(dpart, 1) or ''))
			for _, v in ipairs(dpart) do
				table.insert(groups, ',' .. v )
			end
		end
		result = sign .. table.concat(groups)
	end
	return result
end

local function sup_sub(sup, sub, align)
	-- Return the same result as Module:Su except val defaults to align=right.
	if align == 'l' or align == 'left' then
		align = 'left'
	elseif align == 'c' or align == 'center' then
		align = 'center'
	else
		align = 'right'
	end
	return '<span style="display:inline-block;margin-bottom:-0.3em;vertical-align:-0.4em;line-height:1.2em;font-size:85%;text-align:' ..
		align .. ';">' .. sup .. '<br />' .. sub .. '</span>'
end

local function range_text(items, unit_table, options)
	local fmt = options.fmt
	local nend = items.nend or ''
	if items.isrepeat or unit_table.isangle then
		nend = nend .. unit_table.text
	end
	local text = ''
	for i = 1, #items do
		if i % 2 == 0 then
			text = text .. items[i]
		else
			text = text .. delimit(items[i].sign, items[i].clean, fmt) .. nend
		end
	end
	return text
end

local function uncertainty_text(uncertainty, unit_table, options)
	local angle, text, need_parens
	if unit_table.isangle then
		angle = unit_table.text
	end
	local upper = uncertainty.upper or {}
	local lower = uncertainty.lower or {}
	local uncU = upper.clean
	if uncU then
		local fmt = options.fmt
		local uncL = lower.clean
		if uncL then
			uncU = delimit('+', uncU, fmt) .. (upper.errend or '')
			uncL = delimit('−', uncL, fmt) .. (lower.errend or '')
			if angle then
				uncU = uncU .. angle
				uncL = uncL .. angle
			end
			text = (angle or '') ..
				'<span style="margin-left:0.3em;">' ..
				sup_sub(uncU, uncL, options.align) ..
				'</span>'
		else
			if upper.parens then
				text = '(' .. uncU .. ')'  -- old template did not delimit
			else
				text = (angle or '') ..
					'<span style="margin-left:0.3em;margin-right:0.15em;">±</span>' ..
					delimit('', uncU, fmt)
				need_parens = true
			end
			if uncertainty.errend then
				text = text .. uncertainty.errend
			end
			if angle then
				text = text .. angle
			end
		end
	else
		if angle then
			text = angle
		end
	end
	return text, need_parens
end

local function _main(values, unit_spec, options)
	data_module = 'Module:Val/units' .. (options.sandbox and '/sandbox' or '')
	local action = options.action
	if action then
		if action == 'list' then
			-- Kludge: am using the align parameter (a=xxx) for type of list.
			return list_units(options.align)
		end
		return valerror('невалидна акција "' .. action .. '".', options.nocat)
	end
	local number = values.number or (values.numbers and values.numbers[1]) or {}
	local e_10 = options.e or {}
	local novalue = (number.value == nil and e_10.clean == nil)
	local fmt = options.fmt
	local want_sort = true
	local sortable = options.sortable
	if sortable == 'off' or (sortable == nil and novalue) then
		want_sort = false
	elseif sortable == 'debug' then
		-- Same as sortable = 'on' but the sort key is displayed.
	else
		sortable = 'on'
	end
	local sort_value = 1
	if want_sort then
		sort_value = number.value or 1
		if e_10.value and sort_value ~= 0 then
			-- The 'if' avoids {{val|0|e=1234}} giving an invalid sort_value due to overflow.
			sort_value = sort_value * 10^e_10.value
		end
	end
	local unit_table = makeunit(sort_value, {
						u = unit_spec.u,
						want_link = unit_spec.want_link,
						per = unit_spec.per,
						want_per_link = unit_spec.want_per_link,
						nospace = novalue,
						want_longscale = unit_spec.want_longscale,
						sortable = sortable,
					})
	local sortkey
	if unit_table then
		if want_sort then
			sortkey = unit_table.sortkey
		end
	else
		unit_table = { text = '' }
		if want_sort then
			sortkey = convert_lookup('dummy', sort_value, nil, nil, nil, { sortable = sortable }).sortspan
		end
	end
	local final_unit = unit_table.isangle and '' or unit_table.text
	local e_text, n_text, need_parens
	local uncertainty = values.uncertainty
	if uncertainty then
		if number.clean then
			n_text = delimit(number.sign, number.clean, fmt) .. (number.nend or '')
			local text
			text, need_parens = uncertainty_text(uncertainty, unit_table, options)
			if text then
				n_text = n_text .. text
			end
		else
			n_text = ''
		end
	else
		if values.numbers.isrepeat then
			final_unit = ''
		end
		n_text = range_text(values.numbers, unit_table, options)
		need_parens = true
	end
	if e_10.clean then
		if need_parens then
			n_text = '(' .. n_text .. ')'
		end
		e_text = '10<sup>' .. delimit(e_10.sign, e_10.clean, fmt) .. '</sup>'
		if number.clean then
			e_text = '<span style="margin-left:0.25em;margin-right:0.15em;">×</span>' .. e_text
		end
	else
		e_text = ''
	end
	local result =
		(sortkey or '') ..
		(options.prefix or '') ..
		n_text ..
		e_text ..
		final_unit ..
		(options.suffix or '')
	if result ~= '' then
		result = '<span class="nowrap">' .. result .. '</span>'
	end
	return result .. (options.warning or '')
end

local function check_parameters(args, has_ranges, nocat)
	-- Return warning text for the first problem parameter found, or nothing if ok.
	local whitelist = {
		a = true,
		action = true,
		debug = true,
		e = true,
		['end'] = true,
		errend = true,
		['+errend'] = true,
		['-errend'] = true,
		fmt = true,
		['long scale'] = true,
		long_scale = true,
		longscale = true,
		nocategory = true,
		p = true,
		s = true,
		sortable = true,
		u = true,
		ul = true,
		up = true,
		upl = true,
	}
	for k, v in pairs(args) do
		if type(k) == 'string' and not whitelist[k] then
			local warning = 'Вредносни параметар "' .. k .. '=' .. v .. '" није подржан'
			return valerror(warning, nocat, true)
		end
	end
	if not has_ranges and args[4] then
		return valerror('Вредносни параметар 4 игнорисан', nocat, true)
	end
end

local function main(frame)
	local getArgs = require('Module:Arguments').getArgs
	local args = getArgs(frame, {wrappers = { 'Template:Val', 'Template:Val/sandboxlua' }})
	local nocat = args.nocategory
	local numbers = {}  -- table of number tables, perhaps with range text
	local msg = get_args(numbers, args)
	if msg then
		return valerror(msg, nocat)
	end
	if args.u and args.ul then
		return valerror('Јединица (<b>u</b>) и јединица с линком (<b>ul</b>) су обоје одређени; само је једно дозвољено.', nocat)
	end
	if args.up and args.upl then
		return valerror('Јединица по (<b>up</b>) и јединица по с линком (<b>upl</b>) су обоје одређени; само је једно дозвољено.', nocat)
	end
	local values
	if numbers.has_ranges then
		-- Multiple values with range separators but no uncertainty.
		numbers.nend = args['end']
		values = {
			numbers = numbers,
		}
	else
		-- A single value with optional uncertainty.
		local function setfield(i, dst, src)
			local v = args[src]
			if v then
				if numbers[i] then
					numbers[i][dst] = v
				else
					numbers[i] = { [dst] = v }
				end
			end
		end
		setfield(1, 'nend', 'end')
		setfield(2, 'errend', '+errend')
		setfield(3, 'errend', '-errend')
		values = {
			number = numbers[1],
			uncertainty = {
				upper = numbers[2],
				lower = numbers[3],
				errend = args.errend,
			}
		}
	end
	local unit_spec = {
			u = args.ul or args.u,
			want_link = args.ul ~= nil,
			per = args.upl or args.up,
			want_per_link = args.upl ~= nil,
			want_longscale = (args.longscale or args.long_scale or args['long scale']) == 'on',
		}
	local options = {
			action = args.action,
			align = args.a,
			e = numbers.e,
			fmt = args.fmt,
			nocat = nocat,
			prefix = args.p,
			sandbox = string.find(frame:getTitle(), 'sandbox', 1, true) ~= nil,
			sortable = args.sortable or (args.debug == 'yes' and 'debug' or nil),
			suffix = args.s,
			warning = check_parameters(args, numbers.has_ranges, nocat),
		}
	return _main(values, unit_spec, options)
end

return { main = main, _main = _main }