Difference between revisions of "Module:Ahnentafel/sandbox"

From blackwiki
Jump to navigation Jump to search
blackwiki>Frietjes
blackwiki>Frietjes
Line 3: Line 3:
 
--
 
--
 
local p = {}
 
local p = {}
 +
 
-- templatestyles
 
-- templatestyles
 
local templatestyles = 'Ahnentafel/styles.css'
 
local templatestyles = 'Ahnentafel/styles.css'
Line 11: Line 12:
 
-- tracking
 
-- tracking
 
local tcats = ''
 
local tcats = ''
 +
 +
-- args
 +
local args = {}
 +
 +
-- inner cell
 +
local innerfs = '88%'
 +
local innercell = nil
 +
 +
-- right to left?
 +
local rtl = false
  
 
local function checkparameters(k)
 
local function checkparameters(k)
 
if (k == 'align' or k == 'collapsed' or k == 'collapsible' or
 
if (k == 'align' or k == 'collapsed' or k == 'collapsible' or
k == 'title' or k == 'float' or k == 'clear' or k == 'ref' or
+
k == 'title' or k == 'float' or k == 'clear' or k == 'ref' or  
k == 'headnotes' or k == 'headnotes_align' or  
+
k == 'headnotes' or k == 'headnotes_align' or k == 'rtl' or
k == 'footnotes' or k == 'footnotes_align' or k == 'rtl' or
+
k == 'footnotes' or k == 'footnotes_align' or
 
k == 'width' or k == 'min-width' or k == 'text-align') then
 
k == 'width' or k == 'min-width' or k == 'text-align') then
 
return
 
return
Line 25: Line 36:
 
end
 
end
 
if k:find('^boxstyle_[1-8]$') then
 
if k:find('^boxstyle_[1-8]$') then
 +
return
 +
end
 +
if k:find('^color_[1-8]$') then
 
return
 
return
 
end
 
end
 
if k:find('^border_[1-8]$') then
 
if k:find('^border_[1-8]$') then
 +
return
 +
end
 +
if k:find('^r_[1-9][0-9]*$') then
 
return
 
return
 
end
 
end
 
k = mw.ustring.gsub(k, '[^%w\-_ ]', '?')
 
k = mw.ustring.gsub(k, '[^%w\-_ ]', '?')
 
tcats = tcats .. '[[Category:Pages using ahnentafel with unknown parameters|' .. k .. ' ]]'
 
tcats = tcats .. '[[Category:Pages using ahnentafel with unknown parameters|' .. k .. ' ]]'
end
 
 
local function float_style()
 
-- style for floating
 
local align = (args['align'] or ''):lower()
 
if (align == 'right') then
 
return 'float:right;'
 
elseif (align == 'left') then
 
return 'float:left;'
 
elseif (align == 'center') then
 
return 'margin-left:auto; margin-right:auto;'
 
else
 
return ''
 
end
 
 
end
 
end
  
Line 61: Line 64:
 
end
 
end
 
if rspan > 0 then
 
if rspan > 0 then
rows[r]:tag('td')
+
local c = mw.html.create('td')
 
:attr('rowspan', (rspan > 1) and rspan or nil)
 
:attr('rowspan', (rspan > 1) and rspan or nil)
 
:attr('colspan', (cspan > 1) and cspan or nil)
 
:attr('colspan', (cspan > 1) and cspan or nil)
 +
:css('background', 'cornsilk')
 
:cssText(s)
 
:cssText(s)
 
:addClass(c)
 
:addClass(c)
 
:wikitext(t)
 
:wikitext(t)
 +
if rtl then
 +
table.insert(rows[r], 1, tostring(c))
 +
else
 +
table.insert(rows[r], tostring(c))
 +
end
 
end
 
end
 
end
 
end
  
function p.chart( frame )
+
local function float_style()
local getArgs = require('Module:Arguments').getArgs
+
-- style for floating
local args = getArgs(frame)
 
 
local align = (args['align'] or ''):lower()
 
local align = (args['align'] or ''):lower()
local style = args['style'] or ''
+
if (align == 'right') then
local topbranch = 'ahnentafel-t'
+
return 'float:right;'
local botbranch = 'ahnentafel-b'
+
elseif (align == 'left') then
 +
return 'float:left;'
 +
elseif (align == 'center') then
 +
return 'margin-left:auto; margin-right:auto;'
 +
else
 +
return ''
 +
end
 +
end
  
 +
local function add_outer_container(res)
 +
-- inner = outer (default)
 +
innercell = res
 +
 
local yesno = require('Module:Yesno')
 
local yesno = require('Module:Yesno')
+
 
 
if args['collapsed'] and args['collapsed'] ~= '' then
 
if args['collapsed'] and args['collapsed'] ~= '' then
 
args['collapsible'] = 'yes'
 
args['collapsible'] = 'yes'
Line 93: Line 112:
 
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) > 0)) then
 
if tonumber(k) > 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 > 511) and 511 or maxnum
 
 
local levels = math.ceil(math.log(maxnum+1)/math.log(2))
 
local cells  = math.pow(2, levels) - 1
 
 
-- "fill in" 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 < rowbegin) and offset or rowbegin
 
rowend = ((offset+1) > 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
 
if yesno(args['collapsible'] or 'no') then
 
local r = res:tag('table')
 
local r = res:tag('table')
Line 197: Line 151:
 
args['ref'] = nil
 
args['ref'] = nil
 
end
 
end
 +
end
  
-- add content before the table if required
+
local function add_headnotes()
 
if args['headnotes'] then
 
if args['headnotes'] then
 
if args['headnotes_align'] then
 
if args['headnotes_align'] then
Line 209: Line 164:
 
end
 
end
 
end
 
end
 +
end
  
-- build the inner table
+
local function add_footnotes()
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 < levels) and 2 or 4, ' ', nil, nil)
 
-- top branch
 
if l < 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 < 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 < levels) and 2 or 4, ' ', nil, nil)
 
-- bottom branch
 
if l < 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'] or args['ref'] then
 
if args['footnotes_align'] then
 
if args['footnotes_align'] then
Line 274: Line 174:
 
:wikitext(args['footnotes'])
 
:wikitext(args['footnotes'])
 
else
 
else
innercell:wikitext(args['ref'])
+
innercell:wikitext(args['ref'])
 
innercell:wikitext(args['footnotes'])
 
innercell:wikitext(args['footnotes'])
 
end
 
end
 
end
 
end
 +
end
 +
 +
local function process_args()
 +
-- 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) > 0)) then
 +
if tonumber(k) > maxnum then
 +
maxnum = k
 +
end
 +
elseif (k and type(k) == 'string') then
 +
if k:find('^r_[1-9][0-9]*$') then
 +
k = mw.ustring.gsub(k, '^r_([1-9][0-9]*)$', '%1')
 +
k = tonumber(k)
 +
if k > maxnum then
 +
maxnum = k
 +
end
 +
args[k] = v
 +
else
 +
checkparameters(k)
 +
end
 +
end
 +
end
 +
 +
-- limit the number of levels
 +
maxnum = (maxnum > 1023) and 1023 or maxnum
 +
 +
local levels = math.ceil(math.log(maxnum+1)/math.log(2))
 +
local cells  = math.pow(2, levels) - 1
 
 
return frame:extensionTag{ name = 'templatestyles', args = { src = templatestyles} }
+
-- "fill in" missing boxes
.. '<div class="noresize">' .. tostring(res) .. '</div>' .. tcats
+
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
 +
 +
return cells, levels
 
end
 
end
  
Line 343: Line 282:
 
rows = crows
 
rows = crows
 
crows = {}
 
crows = {}
 +
end
 +
 +
-- add content after the table if required
 +
add_footnotes()
 +
 +
return tostring(res) .. tcats
 +
end
 +
 +
function p.chart( frame )
 +
local getArgs = require('Module:Arguments').getArgs
 +
args = getArgs(frame)
 +
 +
-- right to left?
 +
rtl = (args['rtl'] and args['rtl'] ~= '') and true or false
 +
 +
-- inner style
 +
local style = float_style() .. (args['style'] or '')
 +
 +
-- compute number of levels and cells and add empty cells
 +
local cells, levels = process_args()
 +
 +
-- add a collapsing outer container if required
 +
local res = mw.html.create('')
 +
add_outer_container(res)
 +
 +
-- add content before the table if required
 +
add_headnotes()
 +
 +
local topbranch = 'border-top:#000 solid 1px; border-' .. (rtl and 'right' or 'left') .. ':#000 solid 1px;'
 +
local botbranch = 'border-bottom:#000 solid 1px; border-' .. (rtl and 'right' or 'left') .. ':#000 solid 1px;'
 +
 +
-- 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 < rowbegin) and offset or rowbegin
 +
rowend = ((offset+1) > 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
 +
 +
-- build the inner table
 +
local root = innercell:tag('table')
 +
root:css('border-collapse', 'separate')
 +
:css('border-spacing', '0')
 +
:css('line-height', '130%')
 +
:css('font-size', innerfs)
 +
:cssText(style)
 +
 +
-- initialize the rows with 1 by 1 blank cells
 +
for k = rowbegin, (rowend+1) do
 +
rows[k] = {}
 +
table.insert(rows[k], tostring(mw.html.create('td'):wikitext(' ')))
 +
end
 +
-- add a blank row of cells to assist with alignment
 +
for k = 1,(3*levels + 1) do
 +
table.insert(rows[rowend+1], tostring(mw.html.create('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, math.pow(2,levels-l+1)-1, (l < levels) and 2 or 4, ' ', 'background-color:silver')
 +
addcell(offset + math.pow(2,levels-l+1)-1, math.pow(2,levels-l+1)-1, (l < levels) and 2 or 4, ' ', 'background-color:pink')
 +
-- top branch
 +
if l < levels then
 +
addcell(offset, math.pow(2,levels-l+1)-2, 1, ' ', 'background-color:lightblue')
 +
addcell(offset + math.pow(2,levels-l+1)-2, 1, 1, ' ', 'background-color:lightblue')
 +
addcell(offset + math.pow(2,levels-l+1)-1, math.pow(2,levels-l+1)-1, 1, ' ',
 +
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)
 +
if l < levels then
 +
addcell(offset, 2, 3 + 4*(levels - l - 1), ' ', 'background-color:yellow')
 +
end
 +
offset = offset + 2
 +
-- bottom padding
 +
addcell(offset + math.pow(2,levels-l+1)-1, math.pow(2,levels-l+1)-1, (l < levels) and 2 or 4, ' ', 'background-color:lime')
 +
addcell(offset, math.pow(2,levels-l+1)-1, (l < levels) and 2 or 4, ' ', 'background-color:magenta')
 +
-- bottom branch
 +
if l < levels then
 +
addcell(offset, math.pow(2,levels-l+1)-1, 1, ' ',
 +
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, ' ', 'background-color:steelblue')
 +
end
 +
offset = offset + 2*(math.pow(2,levels-l+1)-1) + 2
 +
end
 +
end
 +
 +
-- finalize the inner table
 +
for k = rowbegin, (rowend+1) do
 +
root:tag('tr'):css('text-align', 'center'):wikitext(table.concat(rows[k], ''))
 
end
 
end
 
 

Revision as of 23:38, 24 October 2018

Documentation for this module may be created at Module:Ahnentafel/sandbox/doc

--
-- 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 = ''

-- args
local args = {}

-- inner cell
local innerfs = '88%'
local innercell = nil

-- right to left?
local rtl = false

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 == 'rtl' or
		k == 'footnotes' or k == 'footnotes_align' 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('^color_[1-8]$') then
		return
	end
	if k:find('^border_[1-8]$') then
		return
	end
	if k:find('^r_[1-9][0-9]*$') 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) < rowbegin) or (r > rowend) then
		-- completely out of range; do nothing
		return
	elseif r < rowbegin then
		-- partially out of range, adjust
		rspan = rspan - (rowbegin - r)
		r = rowbegin
	elseif (r + rspan - 1) > rowend then
		-- partially out of range, adjust
		rspan = rowend + 1 - r
	end
	if rspan > 0 then
		local c = mw.html.create('td')
			:attr('rowspan', (rspan > 1) and rspan or nil)
			:attr('colspan', (cspan > 1) and cspan or nil)
			:css('background', 'cornsilk')
			:cssText(s)
			:addClass(c)
			:wikitext(t)
		if rtl then
			table.insert(rows[r], 1, tostring(c))
		else
			table.insert(rows[r], tostring(c))
		end
	end
end

local function float_style()
	-- style for floating
	local align = (args['align'] or ''):lower()
	if (align == 'right') then
		return 'float:right;'
	elseif (align == 'left') then
		return 'float:left;'
	elseif (align == 'center') then
		return 'margin-left:auto; margin-right:auto;' 
	else
		return ''
	end
end

local function add_outer_container(res)
	-- inner = outer (default)
	innercell = res
	
	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
	
	if yesno(args['collapsible'] or 'no') then
		local r = res:tag('table')
		local t = args['title'] or ('Ancestors of ' .. 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
end

local function add_headnotes()
	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
end

local function add_footnotes()
	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
end

local function process_args()
	-- 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) > 0)) then
			if tonumber(k) > maxnum then
				maxnum = k
			end
		elseif (k and type(k) == 'string') then
			if k:find('^r_[1-9][0-9]*$') then
				k = mw.ustring.gsub(k, '^r_([1-9][0-9]*)$', '%1')
				k = tonumber(k)
				if k > maxnum then
					maxnum = k
				end
				args[k] = v
			else
				checkparameters(k)
			end
		end
	end

	-- limit the number of levels
	maxnum = (maxnum > 1023) and 1023 or maxnum
	
	local levels = math.ceil(math.log(maxnum+1)/math.log(2))
	local cells  = math.pow(2, levels) - 1
	
	-- "fill in" 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
	
	return cells, levels
end

function p.list( frame )
	local colors = {'#fcc', 'silver', '#ffc', '#bfc', '#9fe', '#ffc', '#bfc', '#9fe'}
	local getArgs = require('Module:Arguments').getArgs
	args = getArgs(frame)

	-- inner style
	local style = float_style() .. (args['style'] or '') 
	
	-- compute number of levels and cells and add empty cells
	local cells, levels = process_args()
	
	-- add a collapsing outer container if required
	local res = mw.html.create('')
	add_outer_container(res)

	-- add content before the table if required
	add_headnotes()
	
	-- build the inner div
	local root = innercell:tag('div')
	root:addClass('treeview')
		:css('font-size', innerfs)
		:css('text-align', 'left')
		:cssText(style)

	rows[1] = root:tag('ul'):tag('li')
	crows = {}
	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
		
		local cellsk = math.pow(2,l-1)
		local offset = 1
		for k = 1,cellsk do
			cellnum = cellnum + 1
			
			-- cell
			if args[cellnum] then
				rows[k]
					:tag('span')
					:css('background-color', colors[l])
					:cssText(levelstyle)
					:wikitext(cellnum .. '. ' .. args[cellnum])
				local ul
				if args[2*cellnum] or args[2*cellnum + 1] then
					ul = rows[k]:tag('ul')
				end
				if args[2*cellnum] then
					crows[2*(k-1) + 1] = ul:tag('li')
				end
				if args[2*cellnum + 1] then
					crows[2*(k-1) + 2] = ul:tag('li'):addClass('lastline')
				end
			end
		end
		rows = crows
		crows = {}
	end
	
	-- add content after the table if required
	add_footnotes()
	
	return tostring(res) .. tcats
end

function p.chart( frame )
	local getArgs = require('Module:Arguments').getArgs
	args = getArgs(frame)

	-- right to left?	
	rtl = (args['rtl'] and args['rtl'] ~= '') and true or false

	-- inner style
	local style = float_style() .. (args['style'] or '') 
	
	-- compute number of levels and cells and add empty cells
	local cells, levels = process_args()
	
	-- add a collapsing outer container if required
	local res = mw.html.create('')
	add_outer_container(res)

	-- add content before the table if required
	add_headnotes()
	
	local topbranch = 'border-top:#000 solid 1px; border-' .. (rtl and 'right' or 'left') .. ':#000 solid 1px;'
	local botbranch = 'border-bottom:#000 solid 1px; border-' .. (rtl and 'right' or 'left') .. ':#000 solid 1px;'
	
	-- 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 < rowbegin) and offset or rowbegin
				rowend = ((offset+1) > 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

	-- build the inner table
	local root = innercell:tag('table')
	root:css('border-collapse', 'separate')
		:css('border-spacing', '0')
		:css('line-height', '130%')
		:css('font-size', innerfs)
		:cssText(style)

	-- initialize the rows with 1 by 1 blank cells
	for k = rowbegin, (rowend+1) do
		rows[k] = {}
		table.insert(rows[k], tostring(mw.html.create('td'):wikitext(' ')))
	end
	-- add a blank row of cells to assist with alignment
	for k = 1,(3*levels + 1) do
		table.insert(rows[rowend+1], tostring(mw.html.create('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, math.pow(2,levels-l+1)-1, (l < levels) and 2 or 4, ' ', 'background-color:silver')
			addcell(offset + math.pow(2,levels-l+1)-1, math.pow(2,levels-l+1)-1, (l < levels) and 2 or 4, ' ', 'background-color:pink')
			-- top branch
			if l < levels then
				addcell(offset, math.pow(2,levels-l+1)-2, 1, ' ', 'background-color:lightblue')
				addcell(offset + math.pow(2,levels-l+1)-2, 1, 1, ' ', 'background-color:lightblue')
				addcell(offset + math.pow(2,levels-l+1)-1, math.pow(2,levels-l+1)-1, 1, ' ',
					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)
			if l < levels then
				addcell(offset, 2, 3 + 4*(levels - l - 1), ' ', 'background-color:yellow')
			end
			offset = offset + 2
			-- bottom padding
			addcell(offset + math.pow(2,levels-l+1)-1, math.pow(2,levels-l+1)-1, (l < levels) and 2 or 4, ' ', 'background-color:lime')
			addcell(offset, math.pow(2,levels-l+1)-1, (l < levels) and 2 or 4, ' ', 'background-color:magenta')
			-- bottom branch
			if l < levels then
				addcell(offset, math.pow(2,levels-l+1)-1, 1, ' ', 
					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, ' ', 'background-color:steelblue')
			end
			offset = offset + 2*(math.pow(2,levels-l+1)-1) + 2
		end
	end

	-- finalize the inner table
	for k = rowbegin, (rowend+1) do
		root:tag('tr'):css('text-align', 'center'):wikitext(table.concat(rows[k], ''))
	end
	
	-- add content after the table if required
	add_footnotes()
	
	return tostring(res) .. tcats
end

return p