|
| 1 | +-- |
| 2 | +-- implements [[Template:ahnentafel]] |
| 3 | +-- |
| 4 | +local p = {} |
| 5 | +-- templatestyles |
| 6 | +local templatestyles = 'Ahnentafel/styles.css' |
| 7 | +-- table of row pointers |
| 8 | +local rows = {} |
| 9 | +-- first and last indices in rows table |
| 10 | +local rowbegin, rowend = -1,-1 |
| 11 | +-- tracking |
| 12 | +local tcats = '' |
| 13 | + |
| 14 | +local function checkparameters(k) |
| 15 | + if (k == 'align' or k == 'collapsed' or k == 'collapsible' or |
| 16 | + k == 'title' or k == 'float' or k == 'clear' or k == 'ref' or |
| 17 | + k == 'headnotes' or k == 'headnotes_align' or |
| 18 | + k == 'footnotes' or k == 'footnotes_align' or k == 'rtl' or |
| 19 | + k == 'width' or k == 'min-width' or k == 'text-align') then |
| 20 | + return |
| 21 | + elseif (k == 'boxstyle' or k == 'style' or k == 'border') then |
| 22 | + tcats = tcats .. '[[Category:Pages using ahnentafel with ' .. k .. ']]' |
| 23 | + return |
| 24 | + elseif k:find('^boxstyle_[1-8]$') then |
| 25 | + return |
| 26 | + elseif k:find('^border_[1-8]$') then |
| 27 | + return |
| 28 | + end |
| 29 | + k = mw.ustring.gsub(k, '[^%w%-_ ]', '?') |
| 30 | + tcats = tcats .. '[[Category:Pages using ahnentafel with unknown parameters|' .. k .. ' ]]' |
| 31 | +end |
| 32 | + |
| 33 | +local function addcell(r, rspan, cspan, t, s, c) |
| 34 | + if ((r + rspan - 1) < rowbegin) or (r > rowend) then |
| 35 | + -- completely out of range; do nothing |
| 36 | + return |
| 37 | + elseif r < rowbegin then |
| 38 | + -- partially out of range, adjust |
| 39 | + rspan = rspan - (rowbegin - r) |
| 40 | + r = rowbegin |
| 41 | + elseif (r + rspan - 1) > rowend then |
| 42 | + -- partially out of range, adjust |
| 43 | + rspan = rowend + 1 - r |
| 44 | + end |
| 45 | + if rspan > 0 then |
| 46 | + rows[r]:tag('td') |
| 47 | + :attr('rowspan', (rspan > 1) and rspan or nil) |
| 48 | + :attr('colspan', (cspan > 1) and cspan or nil) |
| 49 | + :cssText(s) |
| 50 | + :addClass(c) |
| 51 | + :wikitext(t) |
| 52 | + end |
| 53 | +end |
| 54 | + |
| 55 | +function p.chart( frame ) |
| 56 | + local getArgs = require('Module:Arguments').getArgs |
| 57 | + local args = getArgs(frame) |
| 58 | + local align = (args['align'] or ''):lower() |
| 59 | + local style = args['style'] or '' |
| 60 | + local topbranch = 'ahnentafel-t' |
| 61 | + local botbranch = 'ahnentafel-b' |
| 62 | + |
| 63 | + local yesno = require('Module:Yesno') |
| 64 | + |
| 65 | + if args['collapsed'] and args['collapsed'] ~= '' then |
| 66 | + args['collapsible'] = 'yes' |
| 67 | + end |
| 68 | + |
| 69 | + if args['title'] and args['title'] ~= '' then |
| 70 | + args['collapsible'] = 'yes' |
| 71 | + if yesno(args['collapsed'] or 'no') then |
| 72 | + args['collapsed'] = 'yes' |
| 73 | + else |
| 74 | + args['collapsed'] = 'no' |
| 75 | + end |
| 76 | + end |
| 77 | + |
| 78 | + -- style for floating |
| 79 | + if (align == 'right') then |
| 80 | + style = 'float:right;' .. style |
| 81 | + elseif (align == 'left') then |
| 82 | + style = 'float:left;' .. style |
| 83 | + elseif (align == 'center') then |
| 84 | + style = 'margin-left:auto; margin-right:auto;' .. style |
| 85 | + end |
| 86 | + |
| 87 | + -- compute the number of levels and track unsupported parameters |
| 88 | + local maxnum = 0 |
| 89 | + for k, v in pairs(args) do |
| 90 | + if (k and type(k) == 'number' or |
| 91 | + (type(k) == 'string' and (tonumber(k) or 0) > 0)) then |
| 92 | + if tonumber(k) > maxnum then |
| 93 | + maxnum = k |
| 94 | + end |
| 95 | + elseif (k and type(k) == 'string') then |
| 96 | + checkparameters(k) |
| 97 | + end |
| 98 | + end |
| 99 | + |
| 100 | + -- limit the number of levels |
| 101 | + maxnum = (maxnum > 511) and 511 or maxnum |
| 102 | + |
| 103 | + local levels = math.ceil(math.log(maxnum+1)/math.log(2)) |
| 104 | + local cells = math.pow(2, levels) - 1 |
| 105 | + |
| 106 | + -- "fill in" missing boxes |
| 107 | + for k = cells, 2, -1 do |
| 108 | + local j = math.floor(k/2) |
| 109 | + if args[k] and args[k] ~= '' and |
| 110 | + (args[j] == nil or args[j] == '') then |
| 111 | + args[j] = ' ' -- single space |
| 112 | + end |
| 113 | + end |
| 114 | + |
| 115 | + -- compute the first and last row number |
| 116 | + rowbegin = 2*cells+1 |
| 117 | + rowend = 2*cells+2 |
| 118 | + local cellnum = 0 |
| 119 | + for l = 1,levels do |
| 120 | + local cellsk = math.pow(2,l-1) |
| 121 | + local offset = 1 |
| 122 | + for k = 1,cellsk do |
| 123 | + cellnum = cellnum + 1 |
| 124 | + offset = offset + 2 * (math.pow(2, levels-l + 1) - 1) |
| 125 | + if args[cellnum] and args[cellnum] ~= '' then |
| 126 | + rowbegin = (offset < rowbegin) and offset or rowbegin |
| 127 | + rowend = ((offset+1) > rowend) and (offset+1) or rowend |
| 128 | + end |
| 129 | + if args[cellnum] and args[cellnum] == '' then |
| 130 | + args[cellnum] = nil |
| 131 | + end |
| 132 | + offset = offset + 2*(math.pow(2,levels-l+1)-1) + 4 |
| 133 | + end |
| 134 | + end |
| 135 | + |
| 136 | + -- add a collapsing outer container if required |
| 137 | + local res = mw.html.create('') |
| 138 | + local innercell = res |
| 139 | + local innerfs = '88%' |
| 140 | + if yesno(args['collapsible'] or 'no') then |
| 141 | + local r = res:tag('table') |
| 142 | + local t = args['title'] or ('Ancestors of ' .. mw.title.getCurrentTitle().text) |
| 143 | + r:addClass('collapsible') |
| 144 | + local f = args['float'] or '' |
| 145 | + if f == 'left' then |
| 146 | + r:css('margin', '0.3em 1em 0.3em 0') |
| 147 | + r:css('float', 'left') |
| 148 | + r:css('clear', args['clear'] or 'left') |
| 149 | + r:css('min-width', args['min-width'] or args['width'] or '33em') |
| 150 | + elseif f == 'right' then |
| 151 | + r:css('margin', '0.3em 0 0.3em 1em') |
| 152 | + r:css('float', 'right') |
| 153 | + r:css('clear', args['clear'] or 'right') |
| 154 | + r:css('min-width', args['min-width'] or args['width'] or '33em') |
| 155 | + elseif align == 'center' then |
| 156 | + r:css('margin', '0.3em auto') |
| 157 | + r:css('clear', args['clear'] or 'none') |
| 158 | + r:css('min-width', args['min-width'] or args['width'] or '60em') |
| 159 | + else |
| 160 | + r:css('margin', '0.3em 0') |
| 161 | + r:css('min-width', args['min-width'] or args['width'] or '60em') |
| 162 | + end |
| 163 | + r:css('width', args['width'] or 'auto') |
| 164 | + r:css('font-size', '88%') |
| 165 | + r:css('border', '1px solid #aaa') |
| 166 | + r:tag('tr'):tag('th') |
| 167 | + :css('padding', '0.2em 0.3em 0.2em 4.3em') |
| 168 | + :css('background', 'none') |
| 169 | + :css('color', 'inherit') |
| 170 | + :css('width', args['width'] or 'auto') |
| 171 | + :wikitext(t .. (args['ref'] or '')) |
| 172 | + innercell = r:tag('tr'):tag('td') |
| 173 | + :css('text-align', args['text-align'] or 'center') |
| 174 | + innerfs = nil |
| 175 | + args['ref'] = nil |
| 176 | + end |
| 177 | + |
| 178 | + -- add content before the table if required |
| 179 | + if args['headnotes'] then |
| 180 | + if args['headnotes_align'] then |
| 181 | + innercell:tag('div') |
| 182 | + :css('width','100%') |
| 183 | + :css('text-align',args['headnotes_align']) |
| 184 | + :wikitext(args['headnotes']) |
| 185 | + else |
| 186 | + innercell:wikitext(args['headnotes']) |
| 187 | + end |
| 188 | + end |
| 189 | + |
| 190 | + -- build the inner table |
| 191 | + local root = innercell:tag('table') |
| 192 | + root:addClass('ahnentafel') |
| 193 | + :css('font-size', innerfs) |
| 194 | + :cssText(style) |
| 195 | + |
| 196 | + -- initialize the rows with 1 by 1 blank cells |
| 197 | + for k = rowbegin, (rowend+1) do |
| 198 | + rows[k] = root:tag('tr') |
| 199 | + rows[k]:tag('td'):wikitext(' ') |
| 200 | + end |
| 201 | + -- add a blank row of cells to assist with alignment |
| 202 | + for k = 1,(3*levels + 1) do |
| 203 | + rows[rowend+1]:tag('td'):wikitext(' ') |
| 204 | + end |
| 205 | + |
| 206 | + local cellnum = 0 |
| 207 | + for l = 1,levels do |
| 208 | + local levelstyle = args['boxstyle_' .. l] or '' |
| 209 | + if args['boxstyle'] and args['boxstyle'] ~= '' then |
| 210 | + levelstyle = args['boxstyle'] .. ';' .. levelstyle |
| 211 | + end |
| 212 | + levelstyle = 'height:0.5em; padding: 2px 4px 2px 4px;' .. levelstyle |
| 213 | + levelstyle = 'border:' .. (args['border_' .. l] or args['border'] or '1') .. 'px solid black;' .. levelstyle |
| 214 | + |
| 215 | + local cellsk = math.pow(2,l-1) |
| 216 | + local offset = 1 |
| 217 | + for k = 1,cellsk do |
| 218 | + cellnum = cellnum + 1 |
| 219 | + -- top padding |
| 220 | + addcell(offset, 2*(math.pow(2,levels-l+1)-1), (l < levels) and 2 or 4, ' ', nil, nil) |
| 221 | + -- top branch |
| 222 | + if l < levels then |
| 223 | + addcell(offset, math.pow(2,levels-l+1)-1, 1, ' ', nil, nil) |
| 224 | + addcell(offset + math.pow(2,levels-l+1)-1, math.pow(2,levels-l+1)-1, 1, ' ', nil, |
| 225 | + args[2*cellnum] and topbranch or nil) |
| 226 | + end |
| 227 | + offset = offset + 2*(math.pow(2,levels-l+1)-1) |
| 228 | + -- cell |
| 229 | + addcell(offset, 2, 4, args[cellnum] or ' ', args[cellnum] and levelstyle or nil, nil) |
| 230 | + if l < levels then |
| 231 | + addcell(offset, 2, 3 + 4*(levels - l - 1), ' ', nil, nil) |
| 232 | + end |
| 233 | + offset = offset + 2 |
| 234 | + -- bottom padding |
| 235 | + addcell(offset, 2*(math.pow(2,levels-l+1)-1), (l < levels) and 2 or 4, ' ', nil, nil) |
| 236 | + -- bottom branch |
| 237 | + if l < levels then |
| 238 | + addcell(offset, math.pow(2,levels-l+1)-1, 1, ' ', nil, |
| 239 | + args[2*cellnum+1] and botbranch or nil) |
| 240 | + addcell(offset + math.pow(2,levels-l+1)-1, math.pow(2,levels-l+1)-1, 1, ' ', nil, nil) |
| 241 | + end |
| 242 | + offset = offset + 2*(math.pow(2,levels-l+1)-1) + 2 |
| 243 | + end |
| 244 | + end |
| 245 | + |
| 246 | + -- add content after the table if required |
| 247 | + if args['footnotes'] or args['ref'] then |
| 248 | + if args['footnotes_align'] then |
| 249 | + innercell:tag('div') |
| 250 | + :css('width','100%') |
| 251 | + :css('text-align',args['footnotes_align']) |
| 252 | + :wikitext(args['footnotes']) |
| 253 | + else |
| 254 | + innercell:wikitext(args['ref']) |
| 255 | + innercell:wikitext(args['footnotes']) |
| 256 | + end |
| 257 | + end |
| 258 | + |
| 259 | + return frame:extensionTag{ name = 'templatestyles', args = { src = templatestyles} } |
| 260 | + .. '<div class="noresize">' .. tostring(res) .. '</div>' .. tcats |
| 261 | +end |
| 262 | + |
| 263 | +return p |
0 commit comments