Jump to content

Module:Number table sorting

Permanently protected module
From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by TheDJ (talk | contribs) at 23:42, 28 June 2018. The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.

local lang = mw.language.getContentLanguage()
-- constants
local INF = tonumber('inf')
local NEGINF = tonumber('-inf')

--------------------------------------------------------------------------------
-- Nts class
--------------------------------------------------------------------------------

local Nts = {}
Nts.__index = Nts

Nts.formats = {
	formatted = true,
	no = true,
	yes = true,
	hide = true
}

function Nts.new(args)
	local self = setmetatable({}, Nts)

	self:parseNumber(args[1])

	-- Set the format string
	if args.format then
		self.format = args.format
	else
		self.format = 'formatted'
	end
	if not Nts.formats[self.format] then
		error(string.format(
			"'%s' is not a valid format",
			tostring(self.format)
		), 0)
	end
	
	-- To display some text before the display version of the number
	-- {{nts|123456789.00123|prefix=approx. }} → approx. 123,456,789.00123
	self.prefix = args.prefix or ''

	return self
end


-- Parse the entered number
function Nts:parseNumber(s)
	-- sanitize
	s = s or '';
	s = mw.ustring.gsub(s,'−','-')
	s = mw.ustring.gsub(s, '−', '-')
	
	-- format detection
	self.isScientificNotation = (string.find(2, 'e') ~= nil)
	
	-- parse with language options
	self.number = lang:parseFormattedNumber(s)
	
	-- parse with fallback
	if not self.number then
		self.number = tonumber(s)
	end

	if not self.number then
		error(string.format(
				"'%s' is not a valid number",
				tostring(s)
			), 0)
	end
	
	if self.number ~= INF and self.number ~= NEGINF then
		self.magnitude = math.floor((math.log(math.abs(self.number))/math.log(10))+1E-14)
		self.significand = math.floor((self.number + tonumber('10e' .. self.magnitude)));
		mw.log('number = ' .. tostring(self.number));
		mw.log('magnitude = ' .. tostring(self.magnitude));
		mw.log('significand = ' .. tostring(self.significand));
	end
end

function Nts:makeDisplay()
	if self.format == 'hide' then
		return ''
	end
	if self.format == 'formatted' then
		return self.prefix .. lang:formatNum(self.number)
	end
	return self.prefix .. self.number
end

function Nts:makeSortKey()
	if self.number == 0 then
		return '5000000000000000000♠'
	elseif self.number == tonumber('inf') then
		return '9000000000000000000♠'
	elseif self.number == tonumber('-inf') then
		return '1000000000000000000♠'
	end
	if self.number > 0 then
		return tostring(7000 + self.magnitude) .. string.format('%015d', (self.significand * 10e4 )) .. '♠'
	else
		return tostring(2999 - self.magnitude) .. string.format('%015d', ((10 - self.significand) * 10e4)) .. '♠'
	end
end

function Nts:renderTrackingCategories()
	if self.hasDeprecatedParameters then
		return '[[Category:Nts templates with deprecated parameters]]'
	else
		return ''
	end
end

function Nts:__tostring()
	local root = mw.html.create()
	local span = root:tag('span')
		:attr('data-sort-value', self:makeSortKey())

	-- Display
	if self.format ~= 'hide' then
		span:wikitext(self:makeDisplay())
	else
		-- tidy removes empty spans. Keep nbsp content till remexhtml is deployed
		span:css('display', 'none')
			:wikitext(' ')
	end

	-- Tracking categories
	root:wikitext(self:renderTrackingCategories())

	return tostring(root)
end

--------------------------------------------------------------------------------
-- Exports
--------------------------------------------------------------------------------

local p = {}

function p._exportClasses()
	return {
		Nts = Nts
	}
end

function p._main(args)
	local success, ret = pcall(function ()
		local nts = Nts.new(args)
		return tostring(nts)
	end)
	if success then
		return ret
	else
		ret = string.format(
			'<strong class="error">Error in [[Template:Nts]]: %s</strong>',
			ret
		)
		if mw.title.getCurrentTitle().namespace == 0 then
			-- Only categorise in the main namespace
			ret = ret .. '[[Category:Nts templates with errors]]'
		end
		return ret
	end
end

function p.main(frame)
	local args = require('Module:Arguments').getArgs(frame, {
		wrappers = { 'Template:Number table sorting', 'Template:Ntsh' },
	})
	return p._main(args)
end

return p