Mô đun:Clade

--NOTE: this module contains functions for generating the table structure of the clade tree:

The main function is called by the template using the 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: } }}
Template:CladeN

Release version:
Usage: } }}
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 cladeCount1 condition, this is reduced to 34
however cladeCount1 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
local src = "Template:Clade/styles.css"
cladeString = cladeString .. p.templateStyle( frame, src ) .. '\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 = '\n'

-- open table
-- (border-collapse causes problems (see talk) -- cladeString = cladeString ..

cladeString = p.addSubTrees(cladeString) -- add subtrees

return cladeString
--return '\n' .. cladeString ..
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", [-1]="-1", [0]="0"}
for i=27, 500, 1 do
suffix[i] = tostring(i)
end
for i=-100, 0, 1 do
suffix[i]= tostring(i)
end
for i=-100, 500, 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
--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)

--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 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
else
if (branchStyle ~= ) then
styleString = ' style="' .. branchStyle .. '"'
else
styleString = -- use defaults in styles.css
end
end

classString = 'class="clade-leaf' .. reverseClass .. '"'

--note: the \n causes plain leaf elements get wrapped in 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'
-- note: the classes interfere with the node counter, so try simpler version with style
content = '\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 with vertical padding (see above)

--local leafCellString = '\n|rowspan=2 ' .. classString .. styleString .. ' |\n' .. content -- the new line causes 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 childNumberlastNode then -- if childNumberlastNode we don't want left border, otherwise we do
borderStyle = 'border-right:none;border-left:none;'
else
if reverseClade then
borderStyle = 'border-left:none;border-right:' .. leftBorder .. ';'
else
borderStyle = 'border-right:none;border-left:' .. leftBorder .. ';'
end
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
--return --'&nbsp;' -- remove space to reduce post-expand include size (the width=1.5em handles spacing)
--return -- must return something; this is critical for clade structure
return '&8239;' -- &nbsp; &thinsp; &8239;(thin nbsp)
else
-- spaces can cause wrapping and can break tree structure, hence use span with nowrap class
--return .. nodeLabel ..

-- 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. ") then stylingElementDetected = true end

if stylingElementDetected == true then
return .. nodeLabel ..
else
local nowrapString = string.gsub(nodeLabel," ", "&nbsp;") -- 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,"-", "&8209;") -- replace hyphen with non-breaking hyphen (&8209;)
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)

local cladeString = ""
count = count+1
--start table
--cladeString = cladeString ..
--cladeString = cladeString .. '\n| \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: } }}

function p.newickConverter(frame)

local newickString = frame.args['newickstring'] or pargs['newick']

--if 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 ..

local resultString =
local option = pargs['option'] or
if option == 'tree' then
--show the transcluded clade diagram
resultString = cladeString
else
-- show the Newick string
resultString = ..newickString..
-- show the converted clade structure
resultString = resultString .. .. cladeString ..
end
--resultString = frame:expandTemplate{ title = 'clade', frame:preprocess(cladeString) }

return resultString
end

--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..'=' ..
return cladeString
end

function p.getIndent(levelNumber)
local indent = "\r"
local extraIndent = pargs['indent'] or mw.getCurrentFrame().args['indent'] or 0

while tonumber(extraIndent) > 0 do
indent = indent .. " " -- an extra indent to make aligining compound trees easier
extraIndent = extraIndent - 1
end

while levelNumber > 1 do
indent = indent .. " "
levelNumber = levelNumber-1
end
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 '..listString..
-- show the converted clade structure
resultString = resultString .. .. cladeString ..
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 list = mw.text.split(listString, "\n")
local i=1
local child=1
local lastNode=0

while list[i] do
list[i]=list[i]:gsub("^@", "") -- strip the first @

if not string.match( list[i], "^@", 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], "^@", 1 ) then

local label=list[i]
i=i+1
local recombined = list[i]
while list[i+1] and string.match( list[i+1], "^@", 1 ) do
recombined = recombined .. "\n" .. list[i+1]
i=i+1
end
cladeString = cladeString .. indent .. '|label' .. child ..'=' .. label
cladeString = cladeString .. indent .. '|' .. child ..'=' ..
return cladeString
end

-- this must be at end
return p