Permanently protected module
From Wikipedia, the free encyclopedia


--

-- This template implements {{Historical populations}}

--

local p = {}

local lang = mw.getContentLanguage()

local Date -- lazy initialization



local function ifexist(page)

    if not page then return false end

    if mw.title.new(page).exists then return true end

    return false

end



local function isempty( s )

	return not s or s:match( '^%s*(.-)%s*$' ) == ''

end



local function splitnumandref( s )

	s = s:match( '^%s*(.-)%s*$' )

	local t1 = mw.text.unstrip(s)

        local t2 = s:match( '^([%d][%d,]*)' )

	if( t1 == t2 ) then

		local t3 = s:match( '^[%d][%d,]*(.-)$' )

		return t1, t3

	else

		return s, ''

	end

end



local function formatnumR(num)

	return tonumber(lang:parseFormattedNumber(num))

end



local function formatnum(num)

	return lang:parseFormattedNumber(num) and lang:formatNum(lang:parseFormattedNumber(num)) or num

end



-- this function creates an array with the {year, population, percent change}

local function getpoprow(year, popstr, pyear, ppopstr, linktype, percentages, current_year)

	local pop, popref = splitnumandref( popstr or '')

	local ppop, ppopref = splitnumandref( ppopstr or '')

	local percent = ''

	local yearnum = formatnumR(mw.ustring.gsub(year or '', '^%s*([%d][%d][%.%d]+).*$', '%1') or '')

	local pyearnum = formatnumR(mw.ustring.gsub(pyear or '', '^%s*([%d][%d][%.%d]+).*$', '%1') or '')

	local popnum = formatnumR(pop)

	local ppopnum = formatnumR(ppop)

	if( linktype == 'US' or linktype == 'USA' ) then

		if( (yearnum or 0) >= 1790 and yearnum <= current_year and math.fmod(math.floor(yearnum), 10) == 0) then

			if( yearnum < current_year ) then

				year = '[[' .. tostring(yearnum) .. ' United States census|' .. year .. ']]'

  			elseif( ifexist(tostring(yearnum) .. ' United States census') ) then

				year = '[[' .. tostring(yearnum) .. ' United States census|' .. year .. ']]'

			end

		end

	end

	if(percentages ~= 'off') then

		local pstr = '—&nbsp;&nbsp;&nbsp;&nbsp;'

		if(popnum ~= nil and ppopnum ~= nil and (ppopnum > 0)) then

			if(percentages == 'pagr') then

				pstr = mw.ustring.format('%.2f', 100*math.abs(math.pow(popnum/ppopnum,1/(yearnum-pyearnum)) - 1))

			elseif(percentages == 'monthly') then

				if Date == nil then Date = require('Module:Date')._Date end

				local date1 = Date(year)

				local date2 = Date(pyear)

				local diff = date1 - date2

				local months = (diff.age_days/(365.25/12))

				pstr = mw.ustring.format('%.2f', 100*math.abs(math.pow(popnum/ppopnum,1/months) - 1))

			else

				pstr = mw.ustring.format('%.1f', 100*math.abs(popnum/ppopnum - 1))

			end

			if( popnum < ppopnum ) then

				pstr = '−' .. pstr .. '%'

			else

				pstr = '+' .. pstr .. '%'

			end

		elseif(popnum ~= nil and ppopnum ~= nil and (ppopnum == popnum)) then

			pstr = mw.ustring.format('%.2f', 0) .. '%'

		end

		percent = pstr

	end

	-- strip the fractional part of the year, if there is one

	year = mw.ustring.gsub(year or '', '^%s*([%d][%d][%d]+)%.[%d]*', '%1')

	

	return {year, formatnum(pop) .. popref, percent }

end



-- this function creates an array with table header labels

local function getheadrow(percentages, popname, yearname, percentname)

	-- year cell	

	if(yearname == '') then

		yearname = 'Year'

	end

	-- population cell

	if(popname == '') then

		popname = '<abbr title="Population">Pop.</abbr>'

	end



	-- percentages cell

	if( percentages ~= 'off' and percentname == '') then

		if( percentages == 'pagr' ) then

			percentname = '<abbr title="Per annum growth rate">±% p.a.</abbr>'

		elseif( percentages == 'monthly' ) then

			percentname = '<abbr title="Per month growth rate">±% p.m.</abbr>'

		else

			percentname = '<abbr title="Percent change">±%</abbr>'

		end

	end

	return {yearname, popname, percentname}

end



-- this function builds the json for the population graph

local function graphjson(data, gwidth, gheight, gtype)

	local yearcount = #data

	local graphargs = {

		'width' = gwidth,

		'height' = gheight,

		'type' = gtype or 'line',

		'yAxisTitle' = 'Population',

		'yAxisMin' = 0,

		'xAxisTitle' = 'Year',

		'xAxisAngle' = '-45',

		'yGrid' = 'y',

		'yAxisFormat' = ',d',

		'x' = '',

		'y' = ''

	}

	local firstpoint = true

	for offset = 1,yearcount do

		local x,y = dataoffset][1], dataoffset][2

		-- delink if necessary

		if x:match('^%s*%[%[[^%[%]]*%|([^%[%]]*)%]%]') then

			x = x:match('^%s*%[%[[^%[%]]*%|([^%[%]]*)%]%]')

		end

		y = formatnumR(y)

		if x and y then

			graphargs'x' = graphargs'x' .. (firstpoint and '' or ', ') .. x

			graphargs'y' = graphargs'y' .. (firstpoint and '' or ', ') .. y

			firstpoint = false

		end

	end

	local Graph = require('Module:Graph')

	

	return Graph.chart({args = graphargs})

end



local function rendergraph(frame, data, gwidth, gheight, gthumb, gtype)

	local graph = frame:extensionTag{name = 'graph', content = graphjson(data, gwidth, gheight, gtype)}



	if(gthumb ~= '') then

		local graphdiv = mw.html.create('div')

			:addClass('thumb')

			:addClass(gthumb == 'right' and 'tright' or 'tleft')

			:css('clear', 'none')

		graphdiv

			:tag('div')

			:addClass('thumbinner')

			:wikitext(graph)

		return tostring(graphdiv)

	end

	

	return '<div class="center">' .. graph .. '</div>'

end



-- this function renders the population table in a vertical format

local function rendervertical(data, head, title, footnote, alignfn, class, style, width, shading, percol, cols, graphpos, graph)

	-- define a couple helper functions

	local function addrowcell(trow, tag, text, align, shading, style)

		cell = trow:tag(tag)

		cell

			:css('text-align', align)

			:css('padding', '1px')

			:wikitext(text)

			:css('border-bottom', shading ~= 'off' and '1px solid #bbbbbb' or nil)

			:cssText(style)

	end

	local function addheadcell(trow, text, align, width, pad)

		cell = trow:tag('th')

		cell

			:css('border-bottom', '1px solid black')

			:css('padding', pad and ('1px ' .. pad) or '1px')

			:css('text-align', align)

			:css('width', width)

			:wikitext(text)

	end



	local colspan = 3

	local yearcount = #data

	local argcount = 2*yearcount

	

	if( isempty(width) ) then

		width = '15em'

	end

	

	-- override the value of cols if percol has been specified

	if( percol > 0 ) then

		cols = math.floor( (yearcount - 1) / percol ) + 1

	end

	

	-- compute the number of rows per col

	local rowspercol = math.floor( (yearcount - 1) / cols ) + 1



	-- specify the colspan for the title and footer lines

	if( cols > 1 ) then

		colspan = cols

	else

		if (head3 == '') then 

			colspan = 2

		else

			colspan = 3

		end

	end



	-- compute outer table width

	local twidth = width

	if( (cols > 1) and width:match('^%s*[%d]+[%w]+%s*$') ) then

		local widthnum = mw.ustring.gsub( width, '^%s*([%d]+)([%w]+)%s*$', '%1' )

		local widthunit = mw.ustring.gsub( width, '^%s*([%d]+)([%w]+)%s*$', '%2' )

		twidth = tostring(widthnum*cols) .. widthunit

	end

	

	-- create the outer table

	local root = mw.html.create('table')

	root

		:addClass(class)

		:css('width', twidth)

		:css('border-top-width', '0')

		:cssText(style'table'])

	-- add title

	local caption = root:tag('caption')

	caption

		:css('border-top', '1px #aaa solid')

		:css('border-left', '1px #aaa solid')

		:css('border-right', '1px #aaa solid')

		:css('background-color', 'lavender')

		:css('padding', '0.25em')

		:css('font-weight', 'bold')

		:wikitext(title)

		

	-- add the graph line (if top graph)

	if((graphpos == 'top' or graphpos == 't') and graph ~= '') then

		row = root:tag('tr')

		cell = row:tag('td')

		cell

			:attr('colspan', colspan)

			:css('border-bottom', '1px solid black')

			:wikitext(graph)

		graph = ''

	end

	

	-- loop over columns and rows within columns

	local offset = 1

	local t = root

	for c = 1,cols do

		-- add inner tables if we are rendering more than one column

		if( cols > 1) then

			if (c == 1) then

				row = root:tag('tr')

				row:attr('valign', 'top')

				cell = row:tag('td')

				cell:css('padding', '0 0.5em')

			else

				cell = row:tag('td')

				cell

					:css('padding', '0 0.5em')

					:css('border-left', 'solid 1px #aaa')

			end

			t = cell:tag('table')

			t

				:css('border-spacing', '0')

				:css('width', width)

		end

		-- start column headers

		local hrow = t:tag('tr')

		hrow:css('font-size', '95%')

		-- year header

		addheadcell(hrow, head1], nil, head3 ~= '' and '3em' or 'auto', nil, nil)

		-- population header

		addheadcell(hrow, head2], 'right', nil, '2px')

		-- percentages header

		if( head3 ~= '' ) then

			addheadcell(hrow, head3], 'right', nil, nil)

		end

		-- end column headers

		-- start population rows

		for r = 1,rowspercol do

			-- generate the row if we have not exceeded the rowcount

			-- shade every fifth row, unless shading = off

			local s = 'off'

			if( math.fmod((c - 1)*rowspercol + r, 5) == 0 and r ~= rowspercol) then

				s = shading

			end

			if(offset <= yearcount) then

				-- start population row

				local prow = t:tag('tr')

				-- year cell

				addrowcell(prow, 'th', dataoffset][1], 'center', s, style'year'])

				-- population cell

				addrowcell(prow, 'td', dataoffset][2], 'right', s, style'pop'])

				-- percentage cell

				if( not isempty(head3]) ) then

					addrowcell(prow, 'td', dataoffset][3], 'right', s, style'pct'])

				end

				-- end population row

				offset = offset + 1

			end

		end

	end



	-- add the graph line (if bottom graph)

	if((graphpos == 'bottom' or graphpos == 'b') and graph ~= '') then

		row = root:tag('tr')

		cell = row:tag('td')

		cell

			:attr('colspan', colspan)

			:css('border-top', '1px solid black')

			:wikitext(graph)

		graph = ''

	end

	-- add the footnote line 	

	if( footnote ~= '') then

		row = root:tag('tr')

		cell = row:tag('td')

		cell

			:attr('colspan', colspan)

			:css('border-top', '1px solid black')

			:css('font-size', '85%')

			:css('text-align', alignfn)

			:wikitext(footnote)

	end

	

	return graph .. tostring(root)

end



-- this function renders the population table in a horizontal format

local function renderhorizontal(data, head, title, footnote, alignfn, class, style, width, shading, perrow, rows, graphpos, graph)

	local row

	local cell

	local yearcount = #data

	local argcount = 2*yearcount

	

	-- override the value of rows if perrow has been specified

	if( perrow > 0 ) then

		rows = math.floor( (yearcount - 1) / perrow ) + 1

	end

	

	-- compute the number of cols per row

	local colsperrow = math.floor( (yearcount - 1) / rows ) + 1



	-- create the outer table

	local root = mw.html.create('table')

	root

		:addClass(class)

		:css('font-size', '90%')

		:cssText(style'table'])

	-- create title row

	row = root:tag('tr')

	cell = row:tag('th')

	cell

		:css('padding', '0.25em')

		:attr('colspan', colsperrow + 1)

		:wikitext(title)



	-- add the graph line (if top graph)

	if((graphpos == 'top' or graphpos == 't') and graph ~= '') then

		row = root:tag('tr')

		cell = row:tag('td')

		cell

			:attr('colspan', colsperrow + 1)

			:css('border-bottom', '1px solid black')

			:wikitext(graph)

		graph = ''

	end



	-- loop over rows and columns within rows

	local offset = 1

	for r = 1,rows do

		local rowoffset = offset

		-- render the years

		row = root:tag('tr')

		cell = row:tag('th')

		cell:wikitext(head1])

			:css('border-top', r > 1 and '2px solid #000' or nil)

		for c = 1,colsperrow do

			cell = row:tag('td')

			if(offset <= yearcount) then

				cell:wikitext(dataoffset][1])

					:css('text-align', 'center')

					:css('border-top', r > 1 and '2px solid #000' or nil)

					:cssText(style'year'])

			else

				cell:css('border-width', r > 1 and '2px 0 0 0' or 0)

					:css('border-top', r > 1 and '2px solid #000' or nil)

			end

			offset = offset + 1

		end

		-- render the pop

		offset = rowoffset

		row = root:tag('tr')

		cell = row:tag('th')

		cell:wikitext(head2])

		for c = 1,colsperrow do

			cell = row:tag('td')

			if(offset <= yearcount) then

				cell:wikitext(dataoffset][2])

					:css('text-align', 'right')

					:css('padding-right', '2px')

					:cssText(style'pop'])

			else

				cell:css('border-width', 0)

			end

			offset = offset + 1

		end

		-- render the percentages

		if(head3 ~= '') then

			offset = rowoffset

			row = root:tag('tr')

			cell = row:tag('th')

			cell:wikitext(head3])

			for c = 1,colsperrow do

				cell = row:tag('td')

				if(offset <= yearcount) then

					cell:wikitext(dataoffset][3])

						:css('text-align', 'right')

						:css('padding-right', '2px')

						:cssText(style'pct'])

				else

					cell:css('border-width', 0)

				end

				offset = offset + 1

			end

		end

	end

	

	-- add the graph line (if bottom graph)

	if((graphpos == 'bottom' or graphpos == 'b') and graph ~= '') then

		row = root:tag('tr')

		cell = row:tag('td')

		cell

			:attr('colspan', colsperrow + 1)

			:css('border-top', '1px solid black')

			:wikitext(graph)

		graph = ''

	end

	

	-- add the footnote line 	

	if( footnote ~= '') then

		row = root:tag('tr')

		cell = row:tag('td')

		cell

			:css('border-top', '2px solid black')

			:css('font-size', '85%')

			:css('text-align', alignfn)

			:attr('colspan', colsperrow + 1)

			:wikitext(footnote)

	end

	

	return graph .. tostring(root)

end



-- this is the main function

function p.poptable(frame)

	local data = {}

	local style = {}

	local args = frame.args1 and frame.args or frame:getParent().args



	local title			= args'title'			or ''

	local align			= args'align'			or ''

	local clear			= args'clear'			or ''

	local direction		= args'direction'		or ''

	local percentages	= args'percentages'	or ''

	local state			= args'state'			or ''

	local linktype		= args'type'			or ''

	local shading		= args'shading'		or 'on'

	local width			= args'width'			or ''

	local subbox		= args'subbox'		or ''

	local popname		= args'pop_name'		or ''

	local yearname		= args'year_name'		or ''

	local percentname   = args'percent_name'  or ''

	local footnote		= args'footnote'		or ''

	local alignfn		= args'align-fn'		or ''

	local source		= args'source'		or ''

	local graphpos      = args'graph-pos'     or ''

	local graphwidth    = args'graph-width'   or ''

	local graphheight   = args'graph-height'  or ''

	local graphtype     = args'graph-type'    or 'line'

	local percol = tonumber(args'percol'])	or 0

	local cols	 = tonumber(args'cols'])	or 1

	local perrow = tonumber(args'perrow'])	or 0

	local rows	 = tonumber(args'rows'])	or 1

	style'year' = args'year_style'

	style'pop'  = args'pop_style'

	style'pct'  = args'pct_style'



	-- setup classes and styling for outer table

	local class = direction == 'horizontal' and 'wikitable' or 'toccolours'

	if( state == 'collapsed' ) then

		class = class .. ' collapsible collapsed'

	end



	if( isempty(title) ) then

		title = 'Historical population'

	end



	if( isempty(align) ) then

		align = direction ~= 'horizontal' and 'right' or 'center'

	end

	

	if( isempty(alignfn) ) then

		alignfn = 'left'

	end

	

	if( isempty(clear) ) then

		clear = align == 'center' and '' or align

	end

	

	local margin = '0.5em 0 1em 0.5em'

	if( align == 'left' ) then

		margin = '0.5em 1em 0.5em 0'

	elseif( align == 'none' ) then

		margin = '0.5em 1em 0.5em 0'

	elseif( align == 'center' ) then

		margin = '0.5em auto'

		align = ''

	end



	if( isempty(subbox) ) then

		style'table' =

			'border-spacing: 0;' ..

			(align ~= '' and 'float:' .. align .. ';' or '') ..

			(clear ~= '' and 'clear:' .. clear .. ';' or '') ..

			'margin:' .. margin .. ';'

	else

		style'table' =

			'margin:0;' ..

			'border-collapse:collapse;' ..

			'border:none;'

	end

	

	style'table' = style'table' .. (args'table_style' or '')

		

	-- setup the footer text

	if( source ~= '' ) then

		source = 'Source: ' .. source

		if( footnote ~= '' ) then

			footnote = footnote .. '<br/>'

		end

	end

	footnote = footnote .. source

	

	-- setup the data header cols/rows

	local head = getheadrow(percentages, popname, yearname, percentname)

	

	-- count the total number of population rows

	local argcount = 0

	local rowcount = 0

	for k, v in pairs( args ) do

		if ( (type( k ) == 'number') and (not isempty(argsk])) ) then

			if( k >= 1 and math.floor(k) == k and k > argcount) then

				argcount = k

			end

			if( math.fmod(k - 1, 2) == 0 ) then

				rowcount = rowcount + 1

			end

		end

	end



	-- here is where we build all the data for the table

	-- loop over columns and rows within columns

	local pyear = ''

	local ppop = ''

	local offset = 1

	local current_year = tonumber(os.date("%Y", os.time()))

	for r = 1,rowcount do

		-- skip blank rows

		while(isempty(argsoffset]) and offset <= argcount) do

			offset = offset + 2

		end

		-- generate the row if we have not exceeded the rowcount

		if(offset <= argcount) then

			table.insert(data, getpoprow(argsoffset], argsoffset + 1 or '', pyear, ppop, 

				linktype, percentages, current_year) )

			pyear = argsoffset

			ppop = argsoffset+1 or ''

			offset = offset + 2

		end

	end



	local graph = ''

	graphpos = graphpos:lower()

	-- now that we have the data for the table, render it in the requested format



	if (direction == 'horizontal') then

		if graphpos ~= '' then

			local gwidth = tonumber(graphwidth) or 200

			local gheight= tonumber(graphheight) or 170

			local gthumb = 

				(graphpos == 'r' or graphpos == 'right' and 'right') or

				(graphpos == 'l' or graphpos == 'left' and 'left') or ''

			graph = rendergraph(frame, data, gwidth, gheight, gthumb, graphtype)

		end

		return renderhorizontal(data, head, title, footnote, alignfn, class, style, width, shading, perrow, rows, graphpos, graph)

	else

		if graphpos ~= '' then

			local gwidth = tonumber(graphwidth) or (170 * cols)

			local gheight= tonumber(graphheight) or 170

			local gthumb = 

				(graphpos == 'r' or graphpos == 'right' and 'right') or

				(graphpos == 'l' or graphpos == 'left' and 'left') or ''

			graph = rendergraph(frame, data, gwidth, gheight, gthumb, graphtype)

		end

		return rendervertical(data, head, title, footnote, alignfn, class, style, width, shading, percol, cols, graphpos, graph)

	end



end



return p