Module:Ahnentafel
--
 -- implements [[Template:ahnentafel]]
 --
 local p = {}
 -- templatestyles
 local templatestyles = 'Ahnentafel/styles.css'
 -- table of row pointers
 local rows = {}
 -- first and last indices in rows table
 local rowbegin, rowend = -1,-1
 -- tracking
 local tcats = ''
 
 local function checkparameters(k)
 	if (k == 'align' or k == 'collapsed' or k == 'collapsible' or
 		k == 'title' or k == 'float' or k == 'clear' or k == 'ref' or
 		k == 'headnotes' or k == 'headnotes_align' or 
 		k == 'footnotes' or k == 'footnotes_align' or k == 'rtl' or
 		k == 'width' or k == 'min-width' or k == 'text-align') then
 		return
 	end
 	if (k == 'boxstyle' or k == 'style' or k == 'border') then
 		tcats = tcats .. '[[Category:Pages using ahnentafel with ' .. k .. ']]'
 		return
 	end
 	if k:find('^boxstyle_[1-8]$') then
 		return
 	end
 	if k:find('^border_[1-8]$') then
 		return
 	end
 	k = mw.ustring.gsub(k, '[^%w\-_ ]', '?')
 	tcats = tcats .. '[[Category:Pages using ahnentafel with unknown parameters|' .. k .. ' ]]'
 end
 
 local function addcell(r, rspan, cspan, t, s, c)
 	if ((r + rspan - 1) &lt; rowbegin) or (r &gt; rowend) then
 		-- completely out of range; do nothing
 		return
 	elseif r &lt; rowbegin then
 		-- partially out of range, adjust
 		rspan = rspan - (rowbegin - r)
 		r = rowbegin
 	elseif (r + rspan - 1) &gt; rowend then
 		-- partially out of range, adjust
 		rspan = rowend + 1 - r
 	end
 	if rspan &gt; 0 then
 		rows[r]:tag('td')
 			:attr('rowspan', (rspan &gt; 1) and rspan or nil)
 			:attr('colspan', (cspan &gt; 1) and cspan or nil)
 			:cssText(s)
 			:addClass(c)
 			:wikitext(t)
 	end
 end
 
 function p.chart( frame )
 	local getArgs = require('Module:Arguments').getArgs
 	local args = getArgs(frame)
 	local align = (args['align'] or ''):lower()
 	local style = args['style'] or ''
 	local topbranch = 'ahnentafel-t'
 	local botbranch = 'ahnentafel-b'
 
 	local yesno = require('Module:Yesno')
 	
 	if args['collapsed'] and args['collapsed'] ~= '' then
 		args['collapsible'] = 'yes'
 	end
 	
 	if args['title'] and args['title'] ~= '' then
 		args['collapsible'] = 'yes'
 		if yesno(args['collapsed'] or 'no') then
 			args['collapsed'] = 'yes'
 		else
 			args['collapsed'] = 'no'
 		end
 	end
 	
 	-- style for floating
 	if (align == 'right') then
 		style = 'float:right;' .. style
 	elseif (align == 'left') then
 		style = 'float:left;' .. style
 	elseif (align == 'center') then
 		style = 'margin-left:auto; margin-right:auto;' .. style
 	end
 	
 	-- compute the number of levels and track unsupported parameters
 	local maxnum = 0
 	for k, v in pairs( args ) do
 		if (k and type(k) == 'number' or 
 			(type(k) == 'string' and (tonumber(k) or 0) &gt; 0)) then
 			if tonumber(k) &gt; maxnum then
 				maxnum = k
 			end
 		else
 			if (k and type(k) == 'string') then
 				checkparameters(k)
 			end
 		end
 	end
 
 	-- limit the number of levels
 	maxnum = (maxnum &gt; 511) and 511 or maxnum
 	
 	local levels = math.ceil(math.log(maxnum+1)/math.log(2))
 	local cells  = math.pow(2, levels) - 1
 	
 	-- &quot;fill in&quot; missing boxes
 	for k=cells,2,-1 do
 		local j = math.floor(k/2)
 		if args[k] and args[k] ~= '' then
 			if args[j] == nil or args[j] == '' then
 				args[j] = ' ' -- single space
 			end
 		end
 	end
 	
 	-- compute the first and last row number
 	rowbegin = 2*cells+1
 	rowend   = 2*cells+2
 	local cellnum = 0
 	for l = 1,levels do
 		local cellsk = math.pow(2,l-1)
 		local offset = 1
 		for k = 1,cellsk do
 			cellnum = cellnum + 1
 			offset = offset + 2*(math.pow(2,levels-l+1)-1)
 			if args[cellnum] and args[cellnum] ~= '' then
 				rowbegin = (offset &lt; rowbegin) and offset or rowbegin
 				rowend = ((offset+1) &gt; rowend) and (offset+1) or rowend
 			end
 			if args[cellnum] and args[cellnum] == '' then
 				args[cellnum] = nil
 			end
 			offset = offset + 2*(math.pow(2,levels-l+1)-1) + 4
 		end
 	end
 	
 	-- add a collapsing outer container if required
 	local res = mw.html.create('')
 	local innercell = res
 	local innerfs = '88%'
 	if yesno(args['collapsible'] or 'no') then
 		local r = res:tag('table')
 		local t = args['title'] or ('முன்னோர்கள்: ' .. mw.title.getCurrentTitle().text)
 		r:addClass('collapsible')
 		if yesno(args['collapsed'] or 'yes') then
 			r:addClass('collapsed')
 		end
 		local f = args['float'] or ''
 		if f == 'left' then
 			r:css('margin', '0.3em 1em 0.3em 0')
 			r:css('float', 'left')
 			r:css('clear', args['clear'] or 'left')
 			r:css('min-width', args['min-width'] or args['width'] or '33em')
 		elseif f == 'right' then
 			r:css('margin', '0.3em 0 0.3em 1em')
 			r:css('float', 'right')
 			r:css('clear', args['clear'] or 'right')
 			r:css('min-width', args['min-width'] or args['width'] or '33em')
 		elseif f == 'none' then
 			r:css('margin', '0.3em 0')
 			r:css('min-width', args['min-width'] or args['width'] or '60em')
 		else
 			r:css('margin', '0.3em auto')
 			r:css('clear', args['clear'] or 'none')
 			r:css('min-width', args['min-width'] or args['width'] or '60em')
 		end
 		r:css('width', args['width'] or 'auto')
 		r:css('font-size', '88%')
 		r:css('border', '1px solid #aaa')
 		r:tag('tr'):tag('th')
 				:css('padding', '0.2em 0.3em 0.2em 4.3em')
 				:css('background', 'none')
 				:css('width', args['width'] or 'auto')
 				:wikitext(t .. (args['ref'] or ''))
 		innercell = r:tag('tr'):tag('td')
 				:css('text-align', args['text-align'] or 'center')
 		innerfs = nil
 		args['ref'] = nil
 	end
 
 	-- add content before the table if required
 	if args['headnotes'] then
 		if args['headnotes_align'] then
 			innercell:tag('div')
 				:css('width','100%')
 				:css('text-align',args['headnotes_align'])
 				:wikitext(args['headnotes'])
 		else
 			innercell:wikitext(args['headnotes'])
 		end
 	end
 
 	-- build the inner table
 	local root = innercell:tag('table')
 	root:addClass('ahnentafel')
 		:css('font-size', innerfs)
 		:cssText(style)
 
 	-- initialize the rows with 1 by 1 blank cells
 	for k = rowbegin, (rowend+1) do
 		rows[k] = root:tag('tr')
 		rows[k]:tag('td'):wikitext(' ')
 	end
 	-- add a blank row of cells to assist with alignment
 	for k = 1,(3*levels + 1) do
 		rows[rowend+1]:tag('td'):wikitext(' ')
 	end
 	
 	local cellnum = 0
 	for l = 1,levels do
 		local levelstyle = args['boxstyle_' .. l] or ''
 		if args['boxstyle'] and args['boxstyle'] ~= '' then
 			levelstyle = args['boxstyle'] .. ';' .. levelstyle
 		end
 		levelstyle = 'height:0.5em; padding:0 0.2em;' .. levelstyle
 		levelstyle = 'border:' .. (args['border_' .. l] or args['border'] or '1') .. 'px solid black;' .. levelstyle
 		
 		local cellsk = math.pow(2,l-1)
 		local offset = 1
 		for k = 1,cellsk do
 			cellnum = cellnum + 1
 			-- top padding
 			addcell(offset, 2*(math.pow(2,levels-l+1)-1), (l &lt; levels) and 2 or 4, ' ', nil, nil)
 			-- top branch
 			if l &lt; levels then
 				addcell(offset, math.pow(2,levels-l+1)-1, 1, ' ', nil, nil)
 				addcell(offset + math.pow(2,levels-l+1)-1, math.pow(2,levels-l+1)-1, 1, ' ', nil,
 					args[2*cellnum] and topbranch or nil)
 			end
 			offset = offset + 2*(math.pow(2,levels-l+1)-1)
 			-- cell
 			addcell(offset, 2, 4, args[cellnum] or ' ', args[cellnum] and levelstyle or nil, nil)
 			if l &lt; levels then
 				addcell(offset, 2, 3 + 4*(levels - l - 1), ' ', nil, nil)
 			end
 			offset = offset + 2
 			-- bottom padding
 			addcell(offset, 2*(math.pow(2,levels-l+1)-1), (l &lt; levels) and 2 or 4, ' ', nil, nil)
 			-- bottom branch
 			if l &lt; levels then
 				addcell(offset, math.pow(2,levels-l+1)-1, 1, ' ', nil,
 					args[2*cellnum+1] and botbranch or nil)
 				addcell(offset + math.pow(2,levels-l+1)-1, math.pow(2,levels-l+1)-1, 1, ' ', nil, nil)
 			end
 			offset = offset + 2*(math.pow(2,levels-l+1)-1) + 2
 		end
 	end
 	
 	-- add content after the table if required
 	if args['footnotes'] or args['ref'] then
 		if args['footnotes_align'] then
 			innercell:tag('div')
 				:css('width','100%')
 				:css('text-align',args['footnotes_align'])
 				:wikitext(args['footnotes'])
 		else
 			innercell:wikitext(args['ref'])
 			innercell:wikitext(args['footnotes'])
 		end
 	end
 	
 	return frame:extensionTag{ name = 'templatestyles', args = { src = templatestyles} } 
 		.. '&lt;div class=&quot;noresize&quot;&gt;' .. tostring(res) .. '&lt;/div&gt;' .. tcats
 end