Module:Clade and Module:Clade/sandbox: Difference between pages
(Difference between pages)
imported>Jts1882 add div to allow overflow: '<div class="clade"> ... </div>' |
imported>Lemondoge Simplifications and some formatting improvements |
||
| Line 2: | Line 2: | ||
The main function is called by the template using the {{invoke}} instruction; the three main functions are: | The main function is called by the template using the {{invoke}} instruction; the three main functions are: | ||
p.main(frame) - opens and closes table, loops through the children of node, main is invoked once and controls the rest, calling ... | |||
p.addTaxon(childNumber, nodeLeaf) - the nuts and bolts; code dealing with each child node | |||
p.addLabel(childNumber) - adds the label text | |||
now uses templatestyles | |||
]] | ]] | ||
require('strict') | require('strict') | ||
local p = {} | local p = {} | ||
local pargs = {} -- parent arguments | local pargs = {} -- parent arguments | ||
| Line 40: | Line 39: | ||
local totalCount = 0 | local totalCount = 0 | ||
pargs = | pargs = frame:getParent().args -- parent arguments | ||
infoOutput = p.getCladeTreeInfo() -- get info about clade structure, e.g. lastNode (last |N= child number) | |||
--[[ add the templatestyles tag conditionally to reduce expansion size (currently diabled) | |||
when added to every clade table, it increases post‐expand include size significantly | |||
e.g. the Neosuchia page (or test version) is increase by about 8% (672 bytes each) | |||
if template styles added to all pages there are 133 stripmarkers | |||
with cladeCount==1 condition, this is reduced to 34 | |||
however cladeCount==1 condition interfers with fix for additional line due to parser bug T18700 | |||
killing the strip markers also removes backlinks to references using citation templates | |||
--]] | |||
--cladeString =mw.text.killMarkers( cladeString ) -- also kills off strip markers using citation templates | |||
--cladeString = mw.text.unstrip(cladeString) | |||
--if cladeCount==1 then | |||
cladeString = cladeString .. frame:extensionTag('templatestyles', '', | |||
{ src="Template:Clade/sandbox/styles.css" }) .. '\n' | |||
--end | |||
local tableStyle = frame.args | local tableStyle = frame.args.style or "" | ||
if tableStyle ~= "" then | if tableStyle ~= "" then | ||
tableStyle = ' style="' .. tableStyle .. '"' -- include style= in string to suppress empty style elements | tableStyle = ' style="' .. tableStyle .. '"' -- include style= in string to suppress empty style elements | ||
end | end | ||
reverseClade =frame.args.reverse or pargs.reverse or false -- a global | |||
--ENFORCE GLOBAL FOR DEVELOPMENT | |||
--reverseClade = true | |||
local captionName = pargs['caption'] or "" | local captionName = pargs['caption'] or "" | ||
local captionStyle = pargs['captionstyle'] or "" | local captionStyle = pargs['captionstyle'] or "" | ||
-- add an element to mimick nowiki WORKS BUT DISABLE FOR DEMO PURPOSES | |||
--cladeString = '<p class="mw-empty-elt"></p>\n' | |||
-- open table | -- open table | ||
-- (border-collapse causes problems (see talk) -- cladeString = cladeString .. '{| style="border-collapse:collapse;border-spacing:0;margin:0;' .. tableStyle .. '"' | -- (border-collapse causes problems (see talk) -- cladeString = cladeString .. '{| style="border-collapse:collapse;border-spacing:0;margin:0;' .. tableStyle .. '"' | ||
-- (before CSS styling) -- cladeString = cladeString .. '{| style="border-spacing:0;margin:0;' .. tableStyle .. '"' | |||
cladeString = cladeString .. '{|class="clade"' .. tableStyle | |||
-- add caption | |||
if captionName ~= "" then | if captionName ~= "" then | ||
cladeString = cladeString .. '\n|+ style="' .. captionStyle .. '"|' .. captionName | cladeString = cladeString .. '\n|+ style="' .. captionStyle .. '"|' .. captionName | ||
| Line 91: | Line 90: | ||
--[[get child elements (add more rows for each child of node; each child is two rows) | --[[get child elements (add more rows for each child of node; each child is two rows) | ||
the function addTaxon is called to add the rows for each child element; | |||
each child add two rows: the first cell of each row contains the label or sublabel (below the line label), respectively; | |||
the second cell spans both rows and contains the leaf name or a new clade structure | |||
a third cell on the top row is sometimes added to contain a group to the right | |||
]] | ]] | ||
| Line 120: | Line 119: | ||
--if reverseClade2 then | --if reverseClade2 then | ||
-- cladeString = cladeString .. '\n' .. p.addTaxonReverse(childNumber, nodeLeaf, nodeLabel, lastNode) | -- cladeString = cladeString .. '\n' .. p.addTaxonReverse(childNumber, nodeLeaf, nodeLabel, lastNode) | ||
--else | |||
cladeString = cladeString .. '\n' .. p.addTaxon(childNumber, nodeLeaf, nodeLabel, lastNode) | cladeString = cladeString .. '\n' .. p.addTaxon(childNumber, nodeLeaf, nodeLabel, lastNode) | ||
--end | --end | ||
| Line 133: | Line 132: | ||
-- note the footer causes a problem with tr:last-child so need either | -- note the footer causes a problem with tr:last-child so need either | ||
-- (1) use <tfoot> but it is not allowed or incompatable | -- (1) use <tfoot> but it is not allowed or incompatable | ||
-- | -- cladeString = cladeString .. '<tfoot><tr style="' .. footerStyle .. '"><td colspan="2"><p>' .. footerText .. '</p></td></tr></tfoot>' | ||
-- (2) always add footer and use nth:last-child(2) but is this backwards compatible | -- (2) always add footer and use nth:last-child(2) but is this backwards compatible | ||
-- (3) if footer= set the style inline for the last sublabel row (more a temp fix) | -- (3) if footer= set the style inline for the last sublabel row (more a temp fix) | ||
| Line 144: | Line 143: | ||
cladeString = p.addSubTrees(cladeString) -- add subtrees | cladeString = p.addSubTrees(cladeString) -- add subtrees | ||
return cladeString | |||
--return '<div style="width:auto;">\n' .. cladeString .. '</div>' | --return '<div style="width:auto;">\n' .. cladeString .. '</div>' | ||
end | end | ||
| Line 164: | Line 154: | ||
local suffix = { [1]="A", [2]="B", [3]="C", [4]="D", [5]="E", [6]="F", [7]="G", [8]="H", [9]="I", [10]="J", | local suffix = { [1]="A", [2]="B", [3]="C", [4]="D", [5]="E", [6]="F", [7]="G", [8]="H", [9]="I", [10]="J", | ||
[11]="K", [12]="L", [13]="M", [14]="N", [15]="O", [16]="P", [17]="Q", [18]="R", [19]="S", [20]="T", | |||
[21]="U", [22]="V", [23]="W", [24]="X", [25]="Y", [26]="Z"} | |||
for i=1, 26, 1 do | for i = 1, 26, 1 do | ||
local subclade = pargs['subclade'..suffix[i]] | local subclade = pargs['subclade'..suffix[i]] | ||
local target = pargs['target'..suffix[i]] or "SUBCLADE_" .. suffix[i] | local target = pargs['target'..suffix[i]] or "SUBCLADE_" .. suffix[i] | ||
| Line 174: | Line 164: | ||
end | end | ||
end | end | ||
end | |||
return cladeString | return cladeString | ||
end | end | ||
--[[ -------------------------------------- p.addTaxon() ------------------------------------------ | --[[ -------------------------------------- p.addTaxon() ------------------------------------------ | ||
function to add child elements | |||
adds wikitext for two rows of the table for each child node, | |||
the first cell in each is used for the label and sublabel; the bottom border forms the horizonal branch of the bracket | |||
the second cell is used for the leafname or a transcluded clade structure and spans both rows | |||
note that the first and last child nodes need to be handled differently from the middle elements | |||
the middle elements (|2, |3 ...) use a left border to create the vertical line of the bracket | |||
the first child element doesn't use a left border for the first cell in the top row (as it is above the bracket) | |||
the last child doesn't use a left border for the first cell in the second row (as it is below the bracket) | |||
]] | ]] | ||
function p.addTaxon(childNumber, nodeLeaf, nodeLabel, lastNode) | function p.addTaxon(childNumber, nodeLeaf, nodeLabel, lastNode) | ||
--[[ get border formating parameters (i.e. color, thickness, state) | --[[ get border formating parameters (i.e. color, thickness, state) | ||
nodeParameters for whole bracket (unnumbered, i.e. color, thickness, state) apply to whole node bracket, | |||
branchParameters apply to individual branches | branchParameters apply to individual branches | ||
the branch parameters have a number, e.g. |colorN, |thicknessN, |stateN | |||
the node parameters have no number, e.g. |color, |thickness, |state | |||
]] | ]] | ||
local nodeColor = pargs['color'] or "" -- don't set default to allow green on black gadget | |||
local nodeThickness = tonumber(pargs['thickness']) or 1 | local nodeThickness = tonumber(pargs['thickness']) or 1 | ||
local nodeState = pargs['state'] or "solid" | local nodeState = pargs['state'] or "solid" | ||
| Line 209: | Line 199: | ||
local branchLength = pargs['length'] or pargs['length'..tostring(childNumber)] or "" | local branchLength = pargs['length'] or pargs['length'..tostring(childNumber)] or "" | ||
-- the left border takes node parameters, the bottom border takes branch parameters | |||
-- this has coding on the colours for green on black | |||
local bottomBorder = tostring(branchThickness) ..'px ' .. branchState .. (branchColor~="" and ' ' .. branchColor or '') | local bottomBorder = tostring(branchThickness) ..'px ' .. branchState .. (branchColor~="" and ' ' .. branchColor or '') | ||
local leftBorder = tostring(nodeThickness) ..'px ' .. nodeState .. (nodeColor~="" and ' ' .. nodeColor or '') | local leftBorder = tostring(nodeThickness) ..'px ' .. nodeState .. (nodeColor~="" and ' ' .. nodeColor or '') | ||
--The default border styles are in the CSS (styles.css) | --The default border styles are in the CSS (styles.css) | ||
-- | -- the inline styling is applied when thickness, color or state are change | ||
local useInlineStyle = false | local useInlineStyle = false | ||
| Line 228: | Line 218: | ||
local barRight = pargs['bar'..tostring(childNumber)] or "0" | local barRight = pargs['bar'..tostring(childNumber)] or "0" | ||
local barBottom = pargs['barend'..tostring(childNumber)] or "0" | local barBottom = pargs['barend'..tostring(childNumber)] or "0" | ||
local barTop | local barTop = pargs['barbegin'..tostring(childNumber)] or "0" | ||
local barLabel = pargs['barlabel'..tostring(childNumber)] or "" | local barLabel = pargs['barlabel'..tostring(childNumber)] or "" | ||
local groupLabel | local groupLabel = pargs['grouplabel'..tostring(childNumber)] or "" | ||
local groupLabelStyle = pargs['grouplabelstyle'..tostring(childNumber)] or "" | local groupLabelStyle = pargs['grouplabelstyle'..tostring(childNumber)] or "" | ||
local labelStyle | local labelStyle = pargs['labelstyle'..tostring(childNumber)] or "" | ||
local sublabelStyle = pargs['sublabelstyle'..tostring(childNumber)] or "" | local sublabelStyle = pargs['sublabelstyle'..tostring(childNumber)] or "" | ||
--replace colours with format string; need right bar for all three options | --replace colours with format string; need right bar for all three options | ||
if barRight ~= "0" then barRight = "2px solid " .. barRight end | if barRight ~= "0" then barRight = "2px solid " .. barRight end | ||
if barTop | if barTop ~= "0" then barRight = "2px solid " .. barTop end | ||
if barBottom ~= "0" then barRight = "2px solid " .. barBottom end | if barBottom ~= "0" then barRight = "2px solid " .. barBottom end | ||
if barTop | if barTop ~= "0" then barTop = "2px solid " .. barTop end | ||
if barBottom ~= "0" then barBottom = "2px solid " .. barBottom end | if barBottom ~= "0" then barBottom = "2px solid " .. barBottom end | ||
| Line 246: | Line 236: | ||
local cladeString = '' | local cladeString = '' | ||
local styleString = '' | local styleString = '' | ||
local borderStyle = '' -- will be used if border color, thickness or state is to be changed | |||
local classString = '' | |||
local reverseClass = '' | |||
local widthClass = '' | |||
-- class to add if using reverse (rtl) cladogram; | |||
if reverseClade then reverseClass = ' reverse' end | |||
-- (1) wikitext for new row | |||
--cladeString = cladeString .. '\n|-' | |||
-- (2) now add cell with label | -- (2) now add cell with label | ||
if useInlineStyle then | if useInlineStyle then | ||
if childNumber == 1 then | if childNumber == 1 then | ||
borderStyle = 'border-left:none;border-right:none;border-bottom:' .. bottomBorder .. ';' | |||
--borderStyle = 'border-bottom:' .. bottomBorder .. ';' | |||
else -- for 2-17 | else -- for 2-17 | ||
if reverseClade then | if reverseClade then | ||
borderStyle = 'border-left:none;border-right:' .. leftBorder .. ';border-bottom:' .. bottomBorder .. ';' | |||
else | |||
borderStyle = 'border-left:' .. leftBorder .. ';border-bottom:' .. bottomBorder .. ';' | |||
end | |||
end | end | ||
end | end | ||
| Line 281: | Line 271: | ||
branchLengthStyle = branchLengthStyle --= prefix .. 'width:' .. branchLength .. ';' | branchLengthStyle = branchLengthStyle --= prefix .. 'width:' .. branchLength .. ';' | ||
.. 'max-width:' .. branchLength ..';' | .. 'max-width:' .. branchLength ..';' | ||
.. 'padding:0em;' -- remove padding to make calculation easier | |||
-- following moved to styles.css | -- following moved to styles.css | ||
-- .. 'white-space:nowrap' | -- .. 'white-space:nowrap' | ||
-- .. 'overflow:hidden;' | -- .. 'overflow:hidden;' -- clip labels longer than the max-width | ||
-- .. 'text-overflow:clip;' -- ellipsis;' | -- .. 'text-overflow:clip;' -- ellipsis;' | ||
widthClass = " clade-fixed-width" | |||
end | end | ||
styleString = 'style="' .. borderStyle .. branchLengthStyle .. branchStyle .. labelStyle .. '"' | styleString = 'style="' .. borderStyle .. branchLengthStyle .. branchStyle .. labelStyle .. '"' | ||
end | |||
if childNumber == 1 then | if childNumber == 1 then | ||
classString= 'class="clade-label first'.. widthClass .. '" ' | classString= 'class="clade-label first'.. widthClass .. '" ' -- add class "first" for top row | ||
else | |||
classString = 'class="clade-label' .. reverseClass .. widthClass .. '" ' -- add "reverse" class if ltr cladogram | |||
end | |||
-- wikitext for cell with label | |||
local labelCellString = '\n|' .. classString .. styleString .. '|' .. p.addLabel(childNumber,nodeLabel) -- p.addLabel(nodeLabel) | |||
--cladeString = cladeString .. labelCellString | |||
--------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | ||
-- (3) add cell with leaf (which may be a table with transluded clade content) | -- (3) add cell with leaf (which may be a table with transluded clade content) | ||
if barRight ~= "0" then | |||
if reverseClade then -- we want the bar on the left | |||
styleString = ' style="border-left:' .. barRight .. ';border-bottom:' .. barBottom .. ';border-top:' .. barTop .. ';' .. branchStyle .. '"' | |||
else | |||
styleString = ' style="border-right:' .. barRight .. ';border-bottom:' .. barBottom .. ';border-top:' .. barTop .. ';' .. branchStyle .. '"' | |||
end | |||
elseif (branchStyle ~= '') then | |||
styleString = ' style="' .. branchStyle .. '"' | |||
else | |||
styleString = '' -- use defaults in styles.css | |||
end | |||
classString = 'class="clade-leaf' .. reverseClass .. '"' | |||
--[[note: the \n causes plain leaf elements get wrapped in <p> with style="margin:0.4em 0 0.5em 0;" | |||
this adds spacing to rows, but is set by defaults rather than the clade template | |||
it also means there are two newlines when it is a clade structure (which might explain some past issues) | |||
]] | |||
local content = '\n' .. nodeLeaf -- the newline is not necessary, but keep for backward compatibility | |||
-- test using image parameter | |||
local image = pargs['image'..tostring(childNumber)] or "" | |||
if image ~= "" then | |||
--content = content .. image -- basic version | |||
content = '\n{|class=clade style=width:auto' --defaults to width:100% because of class "clade" | |||
.. '\n|class=clade-leaf|\n' .. nodeLeaf | |||
.. '\n|class=clade-leaf|\n' .. image | |||
.. '\n|}' | |||
-- note: the classes interfere with the node counter, so try simpler version with style | |||
content = '\n{|style=width:100%;border-spacing:0' --width:auto is "tight"; 100% needed for image alignment | |||
.. '\n|style=border:0;padding:0|\n' .. nodeLeaf | |||
.. '\n|style=border:0;padding:0|\n' .. image | |||
.. '\n|}' | |||
end | |||
-- wikitext for leaf cell (note: nodeLeaf needs to be on newline if new wikitable) | |||
-- but that is no longer the case (newline is now forced) | |||
-- the newline wraps plain leaf terminals in <P> with vertical padding (see above) | |||
--local leafCellString = '\n|rowspan=2 ' .. classString .. styleString .. ' |\n' .. content -- the new line causes <p> wrapping for plain leaf terminals | |||
local leafCellString = '\n|rowspan=2 ' .. classString .. styleString .. ' |' .. content | |||
--cladeString = cladeString .. leafCellString | |||
------------------------------------------- | |||
-- (4) stuff for right-hand bracket labels | |||
classString='class="clade-bar' .. reverseClass .. '"' | classString='class="clade-bar' .. reverseClass .. '"' | ||
local barLabelCellString = '' | |||
if barRight ~= "0" and barLabel ~= "" then | if barRight ~= "0" and barLabel ~= "" then | ||
barLabelCellString = '\n|rowspan=2 ' .. classString .. ' |' .. barLabel | barLabelCellString = '\n|rowspan=2 ' .. classString .. ' |' .. barLabel | ||
| Line 369: | Line 357: | ||
barLabelCellString = barLabelCellString .. '\n|rowspan=2 ' .. classString .. ' style="'.. groupLabelStyle .. '" |' .. groupLabel | barLabelCellString = barLabelCellString .. '\n|rowspan=2 ' .. classString .. ' style="'.. groupLabelStyle .. '" |' .. groupLabel | ||
else -- uncomment following line to see the cell structure | else -- uncomment following line to see the cell structure | ||
--barLabelCellString = barLabelCellString .. '\n|rowspan=2 ' .. classString .. '" |' .. 'GL' | |||
end | end | ||
--cladeString = cladeString .. barLabelCellString | --cladeString = cladeString .. barLabelCellString | ||
------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | ||
-- (5) add second row (only one cell needed for sublabel because of rowspan=2); | -- (5) add second row (only one cell needed for sublabel because of rowspan=2); | ||
-- | -- note: earlier versions applied branch style to row rather than cell | ||
-- | -- for consistency, it is applied to the sublabel cell as with the label cell | ||
--cladeString = cladeString .. '\n|-' | --cladeString = cladeString .. '\n|-' | ||
| Line 395: | Line 383: | ||
if childNumber==lastNode then -- if childNumber==lastNode we don't want left border, otherwise we do | if childNumber==lastNode then -- if childNumber==lastNode we don't want left border, otherwise we do | ||
borderStyle = 'border-right:none;border-left:none;' | borderStyle = 'border-right:none;border-left:none;' | ||
elseif reverseClade then | |||
borderStyle = 'border-left:none;border-right:' .. leftBorder .. ';' | |||
else | |||
borderStyle = 'border-right:none;border-left:' .. leftBorder .. ';' | |||
end | |||
end | |||
if borderStyle ~= '' or branchStyle ~= '' or branchLength ~= '' or sublabelStyle ~= "" then | |||
local branchLengthStyle = "" | local branchLengthStyle = "" | ||
if branchLength ~= "" then | if branchLength ~= "" then | ||
| Line 412: | Line 398: | ||
branchLengthStyle = branchLengthStyle --= prefix .. 'width:' .. branchLength .. ';' | branchLengthStyle = branchLengthStyle --= prefix .. 'width:' .. branchLength .. ';' | ||
.. 'max-width:' .. branchLength ..';' | .. 'max-width:' .. branchLength ..';' | ||
.. 'padding:0em;' -- remove padding to make calculation easier | |||
end | end | ||
styleString = 'style="' .. borderStyle .. branchLengthStyle .. branchStyle .. sublabelStyle .. '"' | styleString = 'style="' .. borderStyle .. branchLengthStyle .. branchStyle .. sublabelStyle .. '"' | ||
--styleString = ' style="' .. borderStyle .. branchStyle .. sublabelStyle .. '"' | |||
end | |||
--local sublabel = p.addLabel(childNumber,subLabel) | |||
if childNumber == lastNode then | |||
classString = 'class="clade-slabel last' .. widthClass .. '" ' | |||
else | |||
classString = 'class="clade-slabel' .. reverseClass .. widthClass .. '" ' | |||
end | |||
local sublabelCellString = '\n|' .. classString .. styleString .. '|' .. p.addLabel(childNumber,subLabel) | |||
--cladeString = cladeString .. sublabelCellString | |||
-- constuct child element wikitext | |||
if reverseClade then | |||
cladeString = cladeString .. '\n|-' | |||
cladeString = cladeString .. barLabelCellString | |||
cladeString = cladeString .. leafCellString | |||
cladeString = cladeString .. labelCellString | |||
cladeString = cladeString .. '\n|-' | cladeString = cladeString .. '\n|-' | ||
cladeString = cladeString .. sublabelCellString | cladeString = cladeString .. sublabelCellString | ||
else | |||
cladeString = cladeString .. '\n|-' | |||
cladeString = cladeString .. labelCellString | |||
cladeString = cladeString .. leafCellString | |||
cladeString = cladeString .. barLabelCellString | |||
cladeString = cladeString .. '\n|-' | cladeString = cladeString .. '\n|-' -- add second row (only one cell needed for sublabel because of rowspan=2); | ||
cladeString = cladeString .. sublabelCellString | cladeString = cladeString .. sublabelCellString | ||
end | |||
return cladeString | return cladeString | ||
end | end | ||
| Line 469: | Line 455: | ||
-- however, there is a problem if labels have a styling element (e.g. <span style= ..., <span title= ...) | -- however, there is a problem if labels have a styling element (e.g. <span style= ..., <span title= ...) | ||
local stylingElementDetected = false | local stylingElementDetected = false | ||
if | if nodeLabel:find("span ") or nodeLabel:find(" style") then | ||
stylingElementDetected = true | |||
end | |||
--TODO test following alternative | --TODO test following alternative | ||
--if nodeLabel:find( "%b<>") then stylingElementDetected = true end | --if nodeLabel:find( "%b<>") then stylingElementDetected = true end | ||
| Line 476: | Line 463: | ||
if stylingElementDetected == true then | if stylingElementDetected == true then | ||
return '<div style=display:inline class=nowrap>' .. nodeLabel .. '</div>' | return '<div style=display:inline class=nowrap>' .. nodeLabel .. '</div>' | ||
else | |||
local nowrapString = string.gsub(nodeLabel, " ", " ") -- replace spaces with non-breaking space | |||
if not nowrapString:find("UNIQ.-QINU") and not nowrapString:find("%[%[.-%]%]") then -- unless a strip marker | |||
nowrapString = string.gsub(nowrapString, "-", "‑") -- replace hyphen with non-breaking hyphen (‑) | |||
end | |||
return nowrapString | return nowrapString | ||
end | end | ||
| Line 501: | Line 488: | ||
local j,k | local j,k | ||
j,k = string.find(newickString, '%(.*%)') | j,k = string.find(newickString, '%(.*%)') -- find location of outer parenthesised term | ||
local innerTerm = string.sub(newickString, j+1, k-1) | local innerTerm = string.sub(newickString, j+1, k-1) -- select content in parenthesis | ||
local outerTerm = string.gsub(newickString, "%b()", "") -- delete parenthetic term | local outerTerm = string.gsub(newickString, "%b()", "") -- delete parenthetic term | ||
if outerTerm == 'panthera' then outerTerm = "x" end | if outerTerm == 'panthera' then outerTerm = "x" end -- how is this set in local variable for inner nodes? | ||
outerTerm = tostring(count) | outerTerm = tostring(count) | ||
-- need to remove commas in bracket terms before split, so temporarily replace commas between brackets | -- need to remove commas in bracket terms before split, so temporarily replace commas between brackets | ||
local innerTerm2 = string.gsub(innerTerm, "%b()", function (n) | |||
return string.gsub(n, ",%s*", "XXX") -- also strip spaces after commas here | |||
end) | |||
--cladeString = cladeString .. '\n' .. p.addTaxon(1, innerTerm2, "") | --cladeString = cladeString .. '\n' .. p.addTaxon(1, innerTerm2, "") | ||
-- this needs a lastNode variable | |||
local s = p.strsplit(innerTerm2, ",") | local s = p.strsplit(innerTerm2, ",") | ||
--oldLastNode=lastNode | --oldLastNode=lastNode | ||
local lastNode=table.getn(s) -- number of child branches | local lastNode=table.getn(s) -- number of child branches | ||
local i=1 | local i = 1 | ||
while s[i] do | while s[i] do | ||
local restoredString = string.gsub(s[i],"XXX", ",") -- convert back to commas | local restoredString = string.gsub(s[i],"XXX", ",") -- convert back to commas | ||
| Line 530: | Line 517: | ||
cladeString = cladeString .. '\n' .. p.addTaxon(i, restoredString, "", lastNode) --count) | cladeString = cladeString .. '\n' .. p.addTaxon(i, restoredString, "", lastNode) --count) | ||
end | end | ||
i=i+1 | i = i + 1 | ||
end | end | ||
-- lastNode=oldLastNode | -- lastNode=oldLastNode | ||
-- close table | -- close table | ||
--cladeString = cladeString .. '\n' .. '| style="border: 0; padding: 0; vertical-align: top;" | <br/> \n|}' | --cladeString = cladeString .. '\n' .. '| style="border: 0; padding: 0; vertical-align: top;" | <br/> \n|}' | ||
| Line 543: | Line 530: | ||
-- why not use mw.text.split(s, sep)? | -- why not use mw.text.split(s, sep)? | ||
function p.strsplit(inputstr, sep) | function p.strsplit(inputstr, sep) | ||
if sep == nil then | |||
sep = "%s" | |||
end | |||
local t={} | |||
local i = 1 | |||
for str in string.gmatch(inputstr, "([^"..sep.."]+)") do | |||
t[i] = str | |||
i = i + 1 | |||
end | |||
return t | |||
end | end | ||
| Line 568: | Line 555: | ||
--if newickString == '{{{newickstring}}}' then return newickString end | --if newickString == '{{{newickstring}}}' then return newickString end | ||
newickString = p.processNewickString(newickString,"") -- "childNumber") | |||
-- show the Newick string | -- show the Newick string | ||
local cladeString = '' | local cladeString = '' | ||
local levelNumber = 1 | local levelNumber = 1 -- for depth of iteration | ||
local childNumber = 1 | local childNumber = 1 -- number of sister elements on node (always one for root) | ||
-- converted the newick string to the clade structure | -- converted the newick string to the clade structure | ||
| Line 582: | Line 569: | ||
local resultString = '' | local resultString = '' | ||
local option = pargs['option'] or '' | |||
if option == 'tree' then | |||
--show the transcluded clade diagram | --show the transcluded clade diagram | ||
resultString = cladeString | resultString = cladeString | ||
else | |||
-- show the Newick string | |||
resultString = '<pre>'..newickString..'</pre>' | resultString = '<pre>'..newickString..'</pre>' | ||
-- show the converted clade structure | |||
resultString = resultString .. '<pre>'.. cladeString ..'</pre>' | |||
end | |||
--resultString = frame:expandTemplate{ title = 'clade', frame:preprocess(cladeString) } | |||
return resultString | |||
end | end | ||
--[[ Parse one level of Newick string | --[[ Parse one level of Newick string | ||
This function receives a Newick string, which has two components | |||
1. the right hand term is a clade label: |labelN=labelname | |||
2. the left hand term in parenthesis has common delimited child nodes, each of which can be | |||
i. a taxon name which just needs: |N=leafname | |||
ii. a Newick string which needs further processing through reiteration | |||
]] | ]] | ||
function p.newickParseLevel(newickString,levelNumber,childNumber) | function p.newickParseLevel(newickString,levelNumber,childNumber) | ||
local cladeString = "" | local cladeString = "" | ||
local indent = p.getIndent(levelNumber) | local indent = p.getIndent(levelNumber) | ||
| Line 613: | Line 600: | ||
local j=0 | local j=0 | ||
local k=0 | local k=0 | ||
j,k = string.find(newickString, '%(.*%)') | j,k = string.find(newickString, '%(.*%)') -- find location of outer parenthesised term | ||
local innerTerm = string.sub(newickString, j+1, k-1) | local innerTerm = string.sub(newickString, j+1, k-1) -- select content in parenthesis | ||
local outerTerm = string.gsub(newickString, "%b()", "") -- delete parenthetic term | local outerTerm = string.gsub(newickString, "%b()", "") -- delete parenthetic term | ||
| Line 624: | Line 611: | ||
-- protect commas in inner parentheses from split; temporarily replace commas between parentheses | -- protect commas in inner parentheses from split; temporarily replace commas between parentheses | ||
local innerTerm2 = string.gsub(innerTerm, "%b()", function (n) | |||
return string.gsub(n, ",%s*", "XXX") -- also strip spaces after commas here | |||
end) | |||
local s = p.strsplit(innerTerm2, ",") | local s = p.strsplit(innerTerm2, ",") | ||
local i=1 | local i = 1 | ||
while s[i] do | while s[i] do | ||
local restoredString = string.gsub(s[i],"XXX", ",") -- convert back to commas | local restoredString = string.gsub(s[i],"XXX", ",") -- convert back to commas | ||
| Line 640: | Line 627: | ||
cladeString = cladeString .. indent .. '|' .. i .. '=' .. restoredString --.. '(level=' .. levelNumber .. ')' | cladeString = cladeString .. indent .. '|' .. i .. '=' .. restoredString --.. '(level=' .. levelNumber .. ')' | ||
end | end | ||
i=i+1 | i = i + 1 | ||
end | end | ||
-- | -- end -- end splitting of strings | ||
cladeString = cladeString .. indent .. '}}' | cladeString = cladeString .. indent .. '}}' | ||
return cladeString | |||
end | end | ||
| Line 652: | Line 639: | ||
local extraIndent = pargs['indent'] or mw.getCurrentFrame().args['indent'] or 0 | local extraIndent = pargs['indent'] or mw.getCurrentFrame().args['indent'] or 0 | ||
indent = indent .. string.rep(" ", extraIndent) .. string.rep(" ", levelNumber - 1) -- extra indent to make aligning compound trees easier | |||
return indent | return indent | ||
end | end | ||
| Line 673: | Line 653: | ||
local i = 0 | local i = 0 | ||
local pargs = pargs | local pargs = pargs | ||
local pattern = pargs['newick'..tostring(childNumber)..'-pattern'] -- unnumbered option for i=1 | local pattern = pargs['newick'..tostring(childNumber)..'-pattern'] -- unnumbered option for i = 1 | ||
local replace = pargs['newick'..tostring(childNumber)..'-replace'] | |||
while i < maxPatterns do | while i < maxPatterns do | ||
i=i+1 | i = i + 1 | ||
pattern = pattern or pargs['newick'..tostring(childNumber)..'-pattern'..tostring(i)] | pattern = pattern or pargs['newick'..tostring(childNumber)..'-pattern'..tostring(i)] | ||
replace = replace or pargs['newick'..tostring(childNumber)..'-replace'..tostring(i)] or "" | replace = replace or pargs['newick'..tostring(childNumber)..'-replace'..tostring(i)] or "" | ||
if pattern then | if pattern then | ||
newickString = string.gsub (newickString, pattern, replace) | newickString = string.gsub(newickString, pattern, replace) | ||
end | end | ||
pattern = nil; replace = nil | |||
end | end | ||
newickString = string.gsub (newickString, "_", " ") -- replace underscore with space | newickString = string.gsub(newickString, "_", " ") -- replace underscore with space | ||
return newickString | return newickString | ||
end | end | ||
| Line 702: | Line 682: | ||
--[[function getCladeTreeInfo() | --[[function getCladeTreeInfo() | ||
this preprocessing loop gets information about the whole structure (number of nodes, leaves etc) | |||
it makes a redundant calls to the templates through transclusion, but doen't affect the template depths; | it makes a redundant calls to the templates through transclusion, but doen't affect the template depths; | ||
it provides the global lastNode that is used to limit the main while loop | it provides the global lastNode that is used to limit the main while loop | ||
| Line 708: | Line 688: | ||
function p.getCladeTreeInfo() | function p.getCladeTreeInfo() | ||
-- enable proprocessing loop | |||
local childNumber = 0 | |||
local childCount = 0 | |||
local maxChildren = 20 | |||
--info veriables (these are global for now) | |||
nodeCount = 0 | |||
cladeCount = 0 | |||
leafCount = 0 | |||
while | while childNumber < maxChildren do -- preprocessing loop | ||
childNumber = childNumber + 1 -- so we start with 1 | childNumber = childNumber + 1 -- so we start with 1 | ||
local nodeLeaf,data = pargs[tostring(childNumber)] or "" -- get data from |N= | local nodeLeaf,data = pargs[tostring(childNumber)] or "" -- get data from |N= | ||
local newickString = pargs['newick'..tostring(childNumber)] or "" -- get data from |labelN= | |||
local listString = pargs['list'..tostring(childNumber)] or "" -- get data from |labelN= | |||
if newickString ~= "" or nodeLeaf ~= "" or listString ~= "" then | if newickString ~= "" or nodeLeaf ~= "" or listString ~= "" then | ||
--if nodeLeaf ~= "" then | --if nodeLeaf ~= "" then | ||
childCount = childCount + 1 -- this counts child elements in this clade | childCount = childCount + 1 -- this counts child elements in this clade | ||
--[[] | |||
for i in string.gmatch(nodeLeaf, "||rowspan") do -- count number of rows started (transclusion) | |||
nodeCount = nodeCount + 1 | nodeCount = nodeCount + 1 | ||
end | |||
for i in string.gmatch(nodeLeaf, '{|class="clade"') do -- count number of tables started (transclusion) | |||
cladeCount = cladeCount + 1 | cladeCount = cladeCount + 1 | ||
end | |||
]] | |||
-- count occurences of clade structure using number of classes used and add to counters | |||
local _, nClades = string.gsub(nodeLeaf, 'class="clade"', "") | |||
local _, nNodes = string.gsub(nodeLeaf, 'class="clade%-leaf"', "") | |||
cladeCount = cladeCount + nClades | |||
nodeCount = nodeCount + nNodes | |||
lastNode = childNumber -- this gets the last node with a valid entry, even when missing numbers | lastNode = childNumber -- this gets the last node with a valid entry, even when missing numbers | ||
end | end | ||
end | end | ||
--]] | --]] | ||
-- nodes can be either terminal leaves or a clade structure (table) | |||
-- note: should change class clade-leaf to clade-node to reflect this | |||
nodeCount = nodeCount -- number of nodes (class clade-leaf) passed down by transduction | |||
+ childCount + 1 -- plus one for current clade and one for each of its child element | |||
cladeCount = cladeCount + 1 | cladeCount = cladeCount + 1 -- number of clade structure tables passed down by transduction (plus one for current clade) | ||
leafCount = nodeCount-cladeCount -- number of terminal leaves (equals height of cladogram) | leafCount = nodeCount-cladeCount -- number of terminal leaves (equals height of cladogram) | ||
-- output for testing: number of clades / total nodes / terminal nodes (=leaves) | -- output for testing: number of clades / total nodes / terminal nodes (=leaves) | ||
-- | -- (internal nodes) (cladogram height) | ||
infoOutput = '<small>[' .. cladeCount .. '/' .. nodeCount .. '/' .. leafCount .. ']</small>' | infoOutput = '<small>[' .. cladeCount .. '/' .. nodeCount .. '/' .. leafCount .. ']</small>' | ||
| Line 761: | Line 741: | ||
function p.showClade(frame) | function p.showClade(frame) | ||
--local code = frame.args.code or "" | --local code = frame.args.code or "" | ||
local code = frame:getParent().args['code2'] or "" | |||
--return code | --return code | ||
| Line 769: | Line 749: | ||
--return string.sub(test,6,-7) | --return string.sub(test,6,-7) | ||
local o1 =frame:getParent() | local o1 =frame:getParent():getArgument('code2') | ||
return o1:expand() | return o1:expand() | ||
--return string.sub(code,2,-1) | --return string.sub(code,2,-1) -- strip marker \127'"`UNIQ--tagname-8 hex digits-QINU`"'\127 | ||
--return frame:preprocess(string.sub(code,3)) | --return frame:preprocess(string.sub(code,3)) | ||
end | end | ||
| Line 778: | Line 758: | ||
function p.firstToUpper(str) | function p.firstToUpper(str) | ||
return (str:gsub("^%l", string.upper)) | |||
end | end | ||
--[[ function to generate cladogram from a wikitext-like list | --[[ function to generate cladogram from a wikitext-like list | ||
- uses @ instead of * because we don't want wikitext processed and nowiki elements are passed as stripmarkers (?) | |||
]] | ]] | ||
function p.list(count,listString) | function p.list(count,listString) | ||
| Line 787: | Line 767: | ||
local cladeString = "" | local cladeString = "" | ||
--count = count+1 | --count = count+1 | ||
local i = 1 | |||
local child = 1 | |||
local lastNode=0--table.getn(list) -- number of child branches (potential) | local lastNode=0--table.getn(list) -- number of child branches (potential) | ||
local listChar = "@" | |||
if listString:find("UNIQ.-QINU") then -- if wrapped in nowiki | |||
mw.addWarning("Stripping content in nowiki tags") | |||
listString = mw.text.trim( mw.text.unstripNoWiki( listString ) ) | |||
end | |||
if string.match( listString, "^*", 1 ) then | |||
listChar = "*" | |||
end | |||
local list = mw.text.split(listString, "\n") | |||
cladeString = cladeString .. '{| class="clade" ' | cladeString = cladeString .. '{| class="clade" ' | ||
while list[i] | while list[i] do | ||
list[i]=list[i]:gsub("^"..listChar, "") | list[i] = list[i]:gsub("^"..listChar, "") -- strip the first @ or * | ||
list[i]=mw.text.trim(list[i]) | list[i] = mw.text.trim(list[i]) -- trim | ||
if not string.match( list[i], "^"..listChar, 1 ) then -- count children at this level (not beginning wiht @/*) | if not string.match( list[i], "^"..listChar, 1 ) then -- count children at this level (not beginning wiht @/*) | ||
lastNode = lastNode+1 | lastNode = lastNode+1 | ||
end | end | ||
i=i+1 | i = i + 1 | ||
end | end | ||
i=1 | i = 1 | ||
while list[i] do | while list[i] do | ||
--[[ pseudocode: | |||
if next value begins with @ we have a subtree, | |||
which must be recombined and past iteratively | |||
else we have a simple leaf | |||
]] | |||
-- if the next value begins with @, we have a subtree which should be recombined | |||
if list[i+1] and string.match( list[i+1], "^"..listChar, 1 ) then | |||
local label=list[i] | |||
i = i + 1 | |||
local recombined = list[i] | |||
while list[i+1] and string.match( list[i+1], "^"..listChar, 1 ) do | |||
recombined = recombined .. "\n" .. list[i+1] | |||
i = i + 1 | |||
end | |||
--cladeString = cladeString .. '\n' .. p.addTaxon(child, recombined, label, lastNode) | |||
cladeString = cladeString .. '\n' .. p.addTaxon(child, p.list(count,recombined), label, lastNode) | |||
else | |||
cladeString = cladeString .. '\n' .. p.addTaxon(child, list[i], "", lastNode) | |||
end | |||
i=i+1 | i = i + 1 | ||
child=child+1 | child = child + 1 | ||
end | end | ||
| Line 855: | Line 835: | ||
]] | ]] | ||
function p.cladeConverter(frame) | function p.cladeConverter(frame) | ||
pargs = | pargs = frame:getParent().args | ||
if | if frame.args['list'] or pargs['list'] then | ||
return p.listConverter(frame) | return p.listConverter(frame) | ||
elseif frame.args['newickstring'] or frame.args['newick'] or pargs['newickstring'] or pargs['newick'] then | |||
return p.newickConverter(frame) | |||
else | else | ||
local message = "Conversion needs wikitext list or newick string in parameters ''list'' or ''newick'' respectively" | local message = "Conversion needs wikitext list or newick string in parameters ''list'' or ''newick'' respectively" | ||
| Line 873: | Line 853: | ||
-- show the list string | -- show the list string | ||
local cladeString = '' | local cladeString = '' | ||
local levelNumber = 1 | local levelNumber = 1 -- for depth of iteration | ||
local childNumber = 1 | local childNumber = 1 -- number of sister elements on node (always one for root) | ||
local indent = p.getIndent(levelNumber) | local indent = p.getIndent(levelNumber) | ||
-- converted the newick string to the clade structure | -- converted the newick string to the clade structure | ||
| Line 882: | Line 862: | ||
local resultString = '' | local resultString = '' | ||
local option = pargs['option'] or '' | |||
if option == 'tree' then | |||
--show the transcluded clade diagram | --show the transcluded clade diagram | ||
resultString = cladeString | resultString = cladeString | ||
else | |||
-- show the wikitext list string | |||
resultString = '<pre>'..listString..'</pre>' | resultString = '<pre>'..listString..'</pre>' | ||
-- show the converted clade structure | |||
resultString = resultString .. '<pre>'.. cladeString ..'</pre>' | |||
end | |||
--resultString = frame:expandTemplate{ title = 'clade', frame:preprocess(cladeString) } | |||
return resultString | |||
end | end | ||
| Line 901: | Line 881: | ||
local cladeString = "" | local cladeString = "" | ||
local indent = p.getIndent(levelNumber) | local indent = p.getIndent(levelNumber) | ||
levelNumber=levelNumber+1 | |||
local nowiki = false | |||
if listString:find("UNIQ.-QINU") then -- if wrapped in nowiki | |||
mw.addWarning("Stripping content in nowiki tags") | |||
listString = mw.text.trim( mw.text.unstripNoWiki( listString ) ) | |||
nowiki = true | |||
end | |||
local listChar = "@" | |||
if string.match( listString, "^*", 1 ) then | |||
listChar = "*" | |||
end | |||
local list = mw.text.split(listString, "\n") | |||
local i = 1 | |||
local child = 1 | |||
local lastNode = 0 | |||
while list[i] do | |||
list[i]=list[i]:gsub("^"..listChar, "") | list[i] = list[i]:gsub("^"..listChar, "") -- strip the first @ | ||
if not string.match( list[i], "^"..listChar, 1 ) then -- count children at this level (not beginning wiht @) | if not string.match(list[i], "^"..listChar, 1) then -- count children at this level (not beginning wiht @) | ||
lastNode = lastNode+1 | lastNode = lastNode +1 | ||
end | end | ||
i=i+1 | i = i + 1 | ||
end | end | ||
i = 1 | |||
while list[i] do | while list[i] do | ||
--[[ pseudocode: | |||
if next value begins with @ we have a subtree, | |||
which must be recombined and past iteratively | |||
else we have a simple leaf | |||
]] | |||
-- if the next value begins with @, we have a subtree which should be recombined | |||
if list[i+1] and string.match( list[i+1], "^"..listChar, 1 ) then | |||
local label = list[i] | |||
i = i + 1 | |||
local recombined = list[i] | |||
while list[i+1] and string.match( list[i+1], "^"..listChar, 1 ) do | |||
recombined = recombined .. "\n" .. list[i+1] | |||
i = i + 1 | |||
end | |||
cladeString = cladeString .. indent .. '|label' .. child ..'=' .. label | |||
cladeString = cladeString .. indent .. '|' .. child ..'=' .. '{{clade' | |||
.. p.listParseLevel(recombined,levelNumber,i) | |||
else | |||
cladeString = cladeString .. indent .. '|' .. child ..'=' .. list[i] | |||
end | |||
i=i+1 | i = i + 1 | ||
child=child+1 | child=child+1 | ||
end | end | ||