Module:Clade/sandbox: Difference between revisions
Update |
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') |
||
Line 41: | Line 41: | ||
pargs = frame:getParent().args -- parent arguments |
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.style or "" |
local tableStyle = frame.args.style or "" |
||
Line 63: | Line 63: | ||
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 90: | 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 119: | 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 132: | 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 154: | 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 164: | 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 199: | 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 218: | 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 236: | 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 271: | 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 = ' style="' .. branchStyle .. '"' |
|||
⚫ | |||
else |
|||
⚫ | |||
⚫ | |||
end |
|||
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 359: | 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 385: | 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;' |
||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
else |
|||
⚫ | |||
⚫ | |||
end |
|||
⚫ | |||
end |
|||
end |
|||
⚫ | |||
local branchLengthStyle = "" |
local branchLengthStyle = "" |
||
if branchLength ~= "" then |
if branchLength ~= "" then |
||
Line 402: | 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 459: | 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 |
|||
⚫ | |||
--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 466: | 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 491: | 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) |
|||
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 520: | 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 533: | 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 558: | 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 572: | 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 603: | 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 614: | 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) |
|||
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 630: | 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 642: | Line 639: | ||
local extraIndent = pargs['indent'] or mw.getCurrentFrame().args['indent'] or 0 |
local extraIndent = pargs['indent'] or mw.getCurrentFrame().args['indent'] or 0 |
||
⚫ | |||
while tonumber(extraIndent) > 0 do |
|||
⚫ | |||
extraIndent = extraIndent - 1 |
|||
⚫ | |||
while levelNumber > 1 do |
|||
indent = indent .. " " |
|||
levelNumber = levelNumber-1 |
|||
⚫ | |||
return indent |
return indent |
||
end |
end |
||
Line 663: | 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 = string.gsub(newickString, pattern, replace) |
||
end |
end |
||
pattern = nil; replace = nil |
|||
end |
end |
||
newickString = string.gsub |
newickString = string.gsub(newickString, "_", " ") -- replace underscore with space |
||
return newickString |
return newickString |
||
end |
end |
||
Line 692: | 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 698: | 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 |
|||
nodeCount=0 |
|||
cladeCount = 0 |
|||
cladeCount=0 |
|||
leafCount = 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 751: | 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 762: | Line 752: | ||
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 768: | 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 777: | 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 847: | Line 837: | ||
pargs = frame:getParent().args |
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 863: | 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 872: | 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 891: | 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( |
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 |
Latest revision as of 20:48, 8 April 2023
![]() | This is the module sandbox page for Module:Clade (diff). |
![]() | This Lua module is used on approximately 9,200 pages and changes may be widely noticed. Test changes in the module's /sandbox or /testcases subpages, or in your own module sandbox. Consider discussing changes on the talk page before implementing them. |
![]() | This module is rated as ready for general use. It has reached a mature form and is thought to be relatively bug-free and ready for use wherever appropriate. It is ready to mention on help pages and other Wikipedia resources as an option for new users to learn. To reduce server load and bad output, it should be improved by sandbox testing rather than repeated trial-and-error editing. |
![]() | This module is subject to page protection. It is a highly visible module in use by a very large number of pages, or is substituted very frequently. Because vandalism or mistakes would affect many pages, and even trivial editing might cause substantial load on the servers, it is protected from editing. |
![]() | This module uses TemplateStyles: |
This module is designed to be used with the clade template to draw phylogenetic trees or cladograms. The new template-module combination extends the feature available with the clade and cladex templates, while replicating the behaviour of the older templates written with the template language. This module is copied from the test version Module:Sandbox/Jts1882/CladeN, which was used with the template Template:CladeN to test the features during development.
Additionally, the module has the code for template {{clade newick converter}}, which is a basic utility to convert Newick strings into nested clade structures. This is not to be used in wikipedia articles, but is a tool to help construct cladograms for inclusion in articles.
The diagram below gives an overview of the features. See the template documentation for a more detailed description of how to use the templates.
Usage
[edit]{{#invoke:Clade|main|style={{{style}}}}}
Examples
[edit]Simple examples mimicking the clade template:
- User:Jts1882/sandbox/templates/Template:Passeroidea
- User:Jts1882/sandbox/templates/Template:Passerida
Example demonstrating more advanced features of cladeN:
Example demonstrating features available:
Node structure |
|
|label1=Node structure |sublabel1=(brackets)
|style1=background-color:#ffdddd;
|1={{Clade
|label1=label1
|1=leaf 1
|label2=label2
|grouplabel2= leaf 2 is a nested clade structure
|2={{Clade |style=background-color:#ffaaaa;
|label1=label A
|1=leaf A
|label2=label B
|2=leaf B
|bar1=purple |bar2=purple
}}
|label3=label3 |sublabel3=sublabel3
|3=leaf 3
}}
| |||||||||||||||||||||||||||||||||||||||||||||||
(brackets) | |||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||
Leaf styling |
|
|label3=Leaf styling |sublabel3=(branches)
|style3=background-color:#eeeeee;
|3={{Clade
|label1=thicknessN
|1={{Clade
|1=1 (default) |thickness1=1 |barbegin1=blue
|2=2 |thickness2=2 |bar2=blue |barlabel2= line thickness
|3=3 |thickness3=3 |barend3=blue
}}
|label2=colorN
|2={{Clade
|1=black (default) |color1=black
|2=red |color2=red
|3=blue |color3=blue
|4= #00ff00 |color4=#00ff00
}}
|label3=stateN
|3={{Clade
|1=solid (default) |state1=solid |barbegin1=purple
|2=dotted |state2=dotted |bar2=purple
|3=dashed |state3=dashed |bar3=purple |barlabel3= line styles
|4=none |state4=none |bar4=purple
|5=double |state5=double |barend5=purple
}}
}}
| |||||||||||||||||||||||||||||||||||||||||||||||
(branches) | |||||||||||||||||||||||||||||||||||||||||||||||||
node styling |
|
|label4=node styling |sublabel4=(brackets)
|style4=background-color:#ffffee;
|4={{Clade
|label1=thickness
|1={{Clade |thickness=3
|1=I
|2=J
|3=K
}}
|label2=color
|2={{Clade |color=red
|1=A
|2=B
|3=C
}}
|label3=state
|3={{Clade |state=dashed
|1=X
|2=Y
|3=Z
}} }}
| |||||||||||||||||||||||||||||||||||||||||||||||
(brackets) | |||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||
newick |
|
|label6=newick
|style6=background-color:#ddffdd;
|6={{Clade
|label1=string
|1=((lion,jaguar,leopard),((siberian,bengal)tiger,snow leopard))panthera
|label2=subtree
|grouplabel2= expanded Newick string
|grouplabelstyle2=vertical-align: middle;
|2={{Clade |style=background-color:#ccffcc;
|newick1=((lion,jaguar,leopard),((siberian,bengal)tiger,snow leopard))panthera
|1=Leaf1 (redundant if newick1 set)
}}
}}
| |||||||||||||||||||||||||||||||||||||||||||||||
paraphyly example |
|
|label7=paraphyly example |style7=background-color:#ffeeff;
|7={{Clade
|1={{Clade
|1=[[Geraniales]]
|2=[[Myrtales]]
}}
|grouplabel2= label a paraphyletic group within a clade
|label2=clade
|style2=background-color:#ffddff;
|2={{Clade
|1=[[Crossosomatales]] |barbegin1=purple
|2={{Clade
|1=[[Picramniales]] |bar1=purple
|2={{Clade
|1=[[Sapindales]] |bar1=purple
|2={{Clade
|1=[[Huerteales]] |bar1=purple
|2={{Clade
|1=[[Brassicales]] |barend1=purple
|2=[[Malvales]] |style2=background-color:#ff99ff;
}} }} }} }} }} }}
| |||||||||||||||||||||||||||||||||||||||||||||||
A cladogram illustrating clade features. |
Subpages
[edit]--[[NOTE: this module contains functions for generating the table structure of the clade tree:
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')
local p = {}
local pargs = {} -- parent arguments
local lastNode=0
local nodeCount=0
local cladeCount=0
local leafCount=0
local templateStylesCount=0
local infoOutput
local reverseClade = false
--[[============================== main function ===========================
-- main function, which will generate the table structure of the tree
Test version:
Usage: {{#invoke:Module:Sandbox/Jts1882/CladeN|main|style={{{STYLE|}}} }}
Template:CladeN
Release version:
Usage: {{#invoke:Clade|main|style={{{STYLE|}}} }}
Template:Clade
]]
function p.main(frame)
local cladeString = ""
local maxChildren = 20 -- was 17 in the clade/cladex templates
local childNumber = 0
local childCount = 0 -- number of leaves in the clade (can use to set bottom of bracket in addTaxon()
local totalCount = 0
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.style or ""
if tableStyle ~= "" then
tableStyle = ' style="' .. tableStyle .. '"' -- include style= in string to suppress empty style elements
end
reverseClade =frame.args.reverse or pargs.reverse or false -- a global
--ENFORCE GLOBAL FOR DEVELOPMENT
--reverseClade = true
local captionName = pargs['caption'] 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
-- (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
cladeString = cladeString .. '\n|+ style="' .. captionStyle .. '"|' .. captionName
end
local moreNeeded = true
childNumber = 0
--lastNode = 0
--[[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
]]
-- main loop
while childNumber < lastNode do -- use the last number determined in the preprocessing
childNumber = childNumber + 1 -- so we start with 1
local nodeLeaf = pargs[tostring(childNumber)] or "" -- get data from |N=
local nodeLabel = pargs['label'..tostring(childNumber)] or "" -- get data from |labelN=
local newickString = pargs['newick'..tostring(childNumber)] or "" -- get data from |labelN=
local listString = pargs['list'..tostring(childNumber)] or ""
if listString ~= "" then
cladeString = cladeString .. '\n' .. p.addTaxon(childNumber, p.list(0, listString), nodeLabel, lastNode)
elseif newickString ~= "" then -- if using a newick string instead of a clade structure
newickString = p.processNewickString(newickString,childNumber)
if nodeLabel == "" then -- use labelN by default, otherwise use root name from Newick string
nodeLabel = p.getNewickOuterterm(newickString) -- need to use terminal part of newick string for label
end
cladeString = cladeString .. '\n' .. p.addTaxon(childNumber, p.newick(0, newickString), nodeLabel, lastNode)
--lastNode=lastNode+1 -- there is a counting problem with the newickstring
elseif nodeLeaf ~= "" then -- if the node contains a leaf name or clade structue
--if reverseClade2 then
-- cladeString = cladeString .. '\n' .. p.addTaxonReverse(childNumber, nodeLeaf, nodeLabel, lastNode)
--else
cladeString = cladeString .. '\n' .. p.addTaxon(childNumber, nodeLeaf, nodeLabel, lastNode)
--end
end
end
local footerText = pargs['footer'] or ""
local footerStyle = pargs['footerstyle'] or ""
if footerText ~= "" then
cladeString = cladeString .. '\n|-style="' .. footerStyle .. '"\n|colspan="2"|<p>' .. footerText .. '</p>||'
-- note the footer causes a problem with tr:last-child so need either
-- (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
-- (3) if footer= set the style inline for the last sublabel row (more a temp fix)
-- (4) set class for first and last element (DONE. Also works well with reverse class)
end
-- close table (wikitext to close table)
cladeString = cladeString .. '\n|}'
cladeString = p.addSubTrees(cladeString) -- add subtrees
return cladeString
--return '<div style="width:auto;">\n' .. cladeString .. '</div>'
end
--[[ =============================function to add subtrees ========================================== ]]
function p.addSubTrees(cladeString)
--local pargs = mw.getCurrentFrame():getParent().args
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
local subclade = pargs['subclade'..suffix[i]]
local target = pargs['target'..suffix[i]] or "SUBCLADE_" .. suffix[i]
if subclade then
if string.find(cladeString, target) then
cladeString = string.gsub(cladeString,target,subclade)
end
end
end
return cladeString
end
--[[ -------------------------------------- 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)
--[[ 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
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 nodeState = pargs['state'] or "solid"
-- get border formating parameters for branch (default to global nodeParameters)
local branchColor = pargs['color'..tostring(childNumber)] or nodeColor
local branchThickness = tonumber(pargs['thickness'..tostring(childNumber)]) or nodeThickness
local branchState = pargs['state'..tostring(childNumber)] or nodeState
if branchState == 'double' then
if branchThickness < 2 then branchThickness = 3 end -- need thick line for double
end
local branchStyle = pargs['style'..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 leftBorder = tostring(nodeThickness) ..'px ' .. nodeState .. (nodeColor~="" and ' ' .. nodeColor or '')
--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
-- use inline styling non-default color, line thickness or state have been set
if branchColor ~= "" or branchThickness ~= 1 or branchState ~= "solid" then
useInlineStyle = true
end
-- variables for right hand bar or bracket
--local barColor = ""
local barRight = pargs['bar'..tostring(childNumber)] or "0"
local barBottom = pargs['barend'..tostring(childNumber)] or "0"
local barTop = pargs['barbegin'..tostring(childNumber)] or "0"
local barLabel = pargs['barlabel'..tostring(childNumber)] or ""
local groupLabel = pargs['grouplabel'..tostring(childNumber)] or ""
local groupLabelStyle = pargs['grouplabelstyle'..tostring(childNumber)] or ""
local labelStyle = pargs['labelstyle'..tostring(childNumber)] or ""
local sublabelStyle = pargs['sublabelstyle'..tostring(childNumber)] or ""
--replace colours with format string; need right bar for all three options
if barRight ~= "0" then barRight = "2px solid " .. barRight end
if barTop ~= "0" then barRight = "2px solid " .. barTop end
if barBottom ~= "0" then barRight = "2px solid " .. barBottom end
if barTop ~= "0" then barTop = "2px solid " .. barTop end
if barBottom ~= "0" then barBottom = "2px solid " .. barBottom end
-- now construct wikitext
local cladeString = ''
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
if useInlineStyle then
if childNumber == 1 then
borderStyle = 'border-left:none;border-right:none;border-bottom:' .. bottomBorder .. ';'
--borderStyle = 'border-bottom:' .. bottomBorder .. ';'
else -- for 2-17
if reverseClade then
borderStyle = 'border-left:none;border-right:' .. leftBorder .. ';border-bottom:' .. bottomBorder .. ';'
else
borderStyle = 'border-left:' .. leftBorder .. ';border-bottom:' .. bottomBorder .. ';'
end
end
end
if useInlineStyle or branchStyle ~= '' or branchLength ~= "" or labelStyle ~= "" then
local branchLengthStyle = ""
if branchLength ~= "" then
if childNumber == 1 then
branchLengthStyle = 'width:' .. branchLength .. ';' -- add width to first element
end
--if childNumber > 1 then prefix = 'max-' end
branchLengthStyle = branchLengthStyle --= prefix .. 'width:' .. branchLength .. ';'
.. 'max-width:' .. branchLength ..';'
.. 'padding:0em;' -- remove padding to make calculation easier
-- following moved to styles.css
-- .. 'white-space:nowrap'
-- .. 'overflow:hidden;' -- clip labels longer than the max-width
-- .. 'text-overflow:clip;' -- ellipsis;'
widthClass = " clade-fixed-width"
end
styleString = 'style="' .. borderStyle .. branchLengthStyle .. branchStyle .. labelStyle .. '"'
end
if childNumber == 1 then
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)
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 .. '"'
local barLabelCellString = ''
if barRight ~= "0" and barLabel ~= "" then
barLabelCellString = '\n|rowspan=2 ' .. classString .. ' |' .. barLabel
else -- uncomment following line to see the cell structure
--barLabelCellString = '\n|rowspan=2 ' .. classString .. ' |' .. 'BL'
end
if groupLabel ~= "" then
barLabelCellString = barLabelCellString .. '\n|rowspan=2 ' .. classString .. ' style="'.. groupLabelStyle .. '" |' .. groupLabel
else -- uncomment following line to see the cell structure
--barLabelCellString = barLabelCellString .. '\n|rowspan=2 ' .. classString .. '" |' .. 'GL'
end
--cladeString = cladeString .. barLabelCellString
-------------------------------------------------------------------------------------
-- (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|-'
-----------------------------------
-- (6) add cell containing sublabel
local subLabel = pargs['sublabel'..tostring(childNumber)] or "" -- request in addLabel
-- FOR TESTING: use subLabel for annotating the clade structues to use structure information (DEBUGGIING ONLY)
--if childNumber==lastNode then subLabel= infoOutput end
-- END TESTING
borderStyle = ''
styleString = ''
if useInlineStyle then
if childNumber==lastNode then -- if childNumber==lastNode we don't want left border, otherwise we do
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 = ""
if branchLength ~= "" then
if childNumber == 1 then
branchLengthStyle = 'width:' .. branchLength .. ';' -- add width to first element
end
--if childNumber > 1 then prefix = 'max-' end
branchLengthStyle = branchLengthStyle --= prefix .. 'width:' .. branchLength .. ';'
.. 'max-width:' .. branchLength ..';'
.. 'padding:0em;' -- remove padding to make calculation easier
end
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 .. sublabelCellString
else
cladeString = cladeString .. '\n|-'
cladeString = cladeString .. labelCellString
cladeString = cladeString .. leafCellString
cladeString = cladeString .. barLabelCellString
cladeString = cladeString .. '\n|-' -- add second row (only one cell needed for sublabel because of rowspan=2);
cladeString = cladeString .. sublabelCellString
end
return cladeString
end
--[[ adds text for label or sublabel to a cell
]]
function p.addLabel(childNumber,nodeLabel)
--local nodeLabel = mw.getCurrentFrame():getParent().args['label'..tostring(childNumber)] or ""
--local firstChars = string.sub(nodeLabel, 1,2) -- get first two characters; will be {{ if no parameter (for Old method?)
--if firstChars == "{{{" or nodeLabel == "" then
if nodeLabel == "" then
-- Has to be some content for layout, but can use CSS to add it instead.
return ''
else
-- spaces can cause wrapping and can break tree structure, hence use span with nowrap class
--return '<span class="nowrap">' .. nodeLabel .. '</span>'
-- a better method for template expansion size is to replace spaces with nonbreaking spaces
-- however, there is a problem if labels have a styling element (e.g. <span style= ..., <span title= ...)
local stylingElementDetected = false
if nodeLabel:find("span ") or nodeLabel:find(" style") then
stylingElementDetected = true
end
--TODO test following alternative
--if nodeLabel:find( "%b<>") then stylingElementDetected = true end
if stylingElementDetected == true then
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
end
end
end
--[[=================== Newick string handling function =============================
]]
function p.getNewickOuterterm(newickString)
return string.gsub(newickString, "%b()", "") -- delete parenthetic term
end
function p.newick(count,newickString)
--start table
--'{| style="border-collapse:collapse;border-spacing:0;border:0;margin:0;'
local cladeString = '{|class=clade '
count = count + 1
local j,k
j,k = string.find(newickString, '%(.*%)') -- find location of outer parenthesised term
local innerTerm = string.sub(newickString, j+1, k-1) -- select content in parenthesis
local outerTerm = string.gsub(newickString, "%b()", "") -- delete parenthetic term
if outerTerm == 'panthera' then outerTerm = "x" end -- how is this set in local variable for inner nodes?
outerTerm = tostring(count)
-- 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, "")
-- this needs a lastNode variable
local s = p.strsplit(innerTerm2, ",")
--oldLastNode=lastNode
local lastNode=table.getn(s) -- number of child branches
local i = 1
while s[i] do
local restoredString = string.gsub(s[i],"XXX", ",") -- convert back to commas
--restoredString = s[i]
local outerTerm = string.gsub(restoredString, "%b()", "")
if string.find(restoredString, '%(.*%)') then
--cladeString = cladeString .. '\n' .. p.addTaxon(i, restoredString, "x")
cladeString = cladeString .. '\n' .. p.addTaxon(i, p.newick(count,restoredString), outerTerm, lastNode)
-- p.addTaxon(2, p.newick(count,newickString2), "root")
else
cladeString = cladeString .. '\n' .. p.addTaxon(i, restoredString, "", lastNode) --count)
end
i = i + 1
end
-- lastNode=oldLastNode
-- close table
--cladeString = cladeString .. '\n' .. '| style="border: 0; padding: 0; vertical-align: top;" | <br/> \n|}'
--cladeString = cladeString .. '\n| <br/> \n|}' -- is this legacy for extra sublabel?
cladeString = cladeString .. '\n|}'
return cladeString
end
-- emulate a standard split string function
-- why not use mw.text.split(s, 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
-- =================== experimental Newick to clade parser function =============================
--[[Function of convert Newick strings to clade format
Usage: {{#invoke:Module:Sandbox/Jts1882/CladeN|newickConverter|newickstring={{{NEWICK_STRING}}} }}
]]
function p.newickConverter(frame)
local newickString = frame.args['newickstring'] or frame.args['newick'] or pargs['newickstring'] or pargs['newick']
--if newickString == '{{{newickstring}}}' then return newickString end
newickString = p.processNewickString(newickString,"") -- "childNumber")
-- show the Newick string
local cladeString = ''
local levelNumber = 1 -- for depth of iteration
local childNumber = 1 -- number of sister elements on node (always one for root)
-- converted the newick string to the clade structure
cladeString = cladeString .. '{{clade'
cladeString = cladeString .. p.newickParseLevel(newickString, levelNumber, childNumber)
cladeString = cladeString .. '\r}}'
local resultString = ''
local option = pargs['option'] or ''
if option == 'tree' then
--show the transcluded clade diagram
resultString = cladeString
else
-- show the Newick string
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
--[[ 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)
local cladeString = ""
local indent = p.getIndent(levelNumber)
--levelNumber=levelNumber+1
local j=0
local k=0
j,k = string.find(newickString, '%(.*%)') -- find location of outer parenthesised term
local innerTerm = string.sub(newickString, j+1, k-1) -- select content in parenthesis
local outerTerm = string.gsub(newickString, "%b()", "") -- delete parenthetic term
cladeString = cladeString .. indent .. '|label'..childNumber..'=' .. outerTerm
cladeString = cladeString .. indent .. '|' .. childNumber..'=' .. '{{clade'
levelNumber=levelNumber+1
indent = p.getIndent(levelNumber)
-- 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 i = 1
while s[i] do
local restoredString = string.gsub(s[i],"XXX", ",") -- convert back to commas
local outerTerm = string.gsub(restoredString, "%b()", "")
if string.find(restoredString, '%(.*%)') then
--cladeString = cladeString .. indent .. '|y' .. i .. '=' .. p.newickParseLevel(restoredString,levelNumber+1,i)
cladeString = cladeString .. p.newickParseLevel(restoredString,levelNumber,i)
else
cladeString = cladeString .. indent .. '|' .. i .. '=' .. restoredString --.. '(level=' .. levelNumber .. ')'
end
i = i + 1
end
-- end -- end splitting of strings
cladeString = cladeString .. indent .. '}}'
return cladeString
end
function p.getIndent(levelNumber)
local indent = "\r"
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
end
function p.newickstuff(newickString)
end
function p.processNewickString(newickString,childNumber)
local maxPatterns = 5
local i = 0
local pargs = pargs
local pattern = pargs['newick'..tostring(childNumber)..'-pattern'] -- unnumbered option for i = 1
local replace = pargs['newick'..tostring(childNumber)..'-replace']
while i < maxPatterns do
i = i + 1
pattern = pattern or pargs['newick'..tostring(childNumber)..'-pattern'..tostring(i)]
replace = replace or pargs['newick'..tostring(childNumber)..'-replace'..tostring(i)] or ""
if pattern then
newickString = string.gsub(newickString, pattern, replace)
end
pattern = nil; replace = nil
end
newickString = string.gsub(newickString, "_", " ") -- replace underscore with space
return newickString
end
------------------------------------------------------------------------------------------
function p.test2(target)
local target ="User:Jts1882/sandbox/templates/Template:Passeroidea"
local result = mw.getCurrentFrame():expandTemplate{ title = target, args = {['style'] = '' } }
return result
end
-------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------
--[[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 provides the global lastNode that is used to limit the main while loop
--]]
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 childNumber < maxChildren do -- preprocessing loop
childNumber = childNumber + 1 -- so we start with 1
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 nodeLeaf ~= "" then
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
end
for i in string.gmatch(nodeLeaf, '{|class="clade"') do -- count number of tables started (transclusion)
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
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 -- 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)
-- output for testing: number of clades / total nodes / terminal nodes (=leaves)
-- (internal nodes) (cladogram height)
infoOutput = '<small>[' .. cladeCount .. '/' .. nodeCount .. '/' .. leafCount .. ']</small>'
return infoOutput
end
function p.showClade(frame)
--local code = frame.args.code or ""
local code = frame:getParent().args['code2'] or ""
--return code
--return mw.text.unstrip(code)
--local test = "<pre>Hello</pre>"
--return string.sub(test,6,-7)
local o1 =frame:getParent():getArgument('code2')
return o1:expand()
--return string.sub(code,2,-1) -- strip marker \127'"`UNIQ--tagname-8 hex digits-QINU`"'\127
--return frame:preprocess(string.sub(code,3))
end
function p.firstToUpper(str)
return (str:gsub("^%l", string.upper))
end
--[[ 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)
local cladeString = ""
--count = count+1
local i = 1
local child = 1
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" '
while list[i] do
list[i] = list[i]:gsub("^"..listChar, "") -- strip the first @ or *
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 @/*)
lastNode = lastNode+1
end
i = i + 1
end
i = 1
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
child = child + 1
end
cladeString = cladeString .. '\n|}'
mw.addWarning("WARNING. This is a test feature only.")
return cladeString
end
-- =================== experimental wikitext list to clade parser function =============================
--[[Function of convert wikitext lists to clade format
Usage: {{#invoke:Module:Sandbox/Jts1882/CladeN|listConverter|list={{{WIKITEXT_LIST}}} }}
]]
function p.cladeConverter(frame)
pargs = frame:getParent().args
if frame.args['list'] or pargs['list'] then
return p.listConverter(frame)
elseif frame.args['newickstring'] or frame.args['newick'] or pargs['newickstring'] or pargs['newick'] then
return p.newickConverter(frame)
else
local message = "Conversion needs wikitext list or newick string in parameters ''list'' or ''newick'' respectively"
mw.addWarning(message)
return message
end
end
function p.listConverter(frame)
local listString = frame.args['list'] or pargs['list']
-- show the list string
local cladeString = ''
local levelNumber = 1 -- for depth of iteration
local childNumber = 1 -- number of sister elements on node (always one for root)
local indent = p.getIndent(levelNumber)
-- converted the newick string to the clade structure
cladeString = cladeString .. indent .. '{{clade'
cladeString = cladeString .. p.listParseLevel(listString, levelNumber, childNumber)
--cladeString = cladeString .. '\r}}'
local resultString = ''
local option = pargs['option'] or ''
if option == 'tree' then
--show the transcluded clade diagram
resultString = cladeString
else
-- show the wikitext list string
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
function p.listParseLevel(listString,levelNumber,childNumber)
local cladeString = ""
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, "") -- strip the first @
if not string.match(list[i], "^"..listChar, 1) then -- count children at this level (not beginning wiht @)
lastNode = lastNode +1
end
i = i + 1
end
i = 1
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
child=child+1
end
cladeString = cladeString .. indent .. '}}'
if nowiki then
return mw.getCurrentFrame():preprocess( '<nowiki>'.. cladeString .. '</nowiki>') --return wrapped in nowiki
else
return cladeString
end
end
-- this must be at end
return p