Difference between revisions of "Module:Clade"

From Summertime Saga Wiki
Jump to: navigation, search
(Created page with "--[[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;...")
 
m
 
(2 intermediate revisions by the same user not shown)
Line 1: Line 1:
--[[NOTE: this module contains functions for generating the table structure of the clade tree:  
+
--NOTE: this module contains functions for generating the table structure of the clade tree in two ways:
 
+
-- (1) the original method was generated by a number of function calls from the template using {{invoke}}
The main function is called by the template using the {{invoke}} instruction; the three main functions are:
+
--        function p.openTable(frame) - creates wikitext code to open the HTML table
        p.main(frame) - opens and closes table, loops through the children of node, main is invoked once and controls the rest, calling ...
+
--        function p.node(frame)  - deals with the first node (|1,|label1) and creates wikitext for top row of the table
        p.addTaxon(childNumber, nodeLeaf) - the nuts and bolts; code dealing with each child node
+
--        function p.nodeN(frame) - deals with a sister nodes (|2-17), adding a row each time it is called
        p.addLabel(childNumber) - adds the label text
+
--        function p.closeTable() - closes the HTML table
       
+
-- (2) the revised method is called by the template with one {{invoke}} instruction; it has three functions:
        now uses templatestyles
+
--        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
  
 
local p = {}
 
local p = {}
Line 13: Line 14:
  
  
--[[============================== main function ===========================
+
--============================== main function (for Method 2) ===========================
-- main function, which will generate the table structure of the tree
+
-- main function, which will  
 +
--[[Main function to generate the table structure of the tree
  
Test version:
+
Usage: {{#invoke:Module:Sandbox/Jts1882/CladeN|main|style={{{STYLE}}} }}
Usage: {{#invoke:Module:Sandbox/Jts1882/CladeN|main|style={{{STYLE|}}} }}
+
Template: User:Jts1882/sandbox/templates/CladeN
Template:CladeN
+
]]
  
Release version:
 
Usage: {{#invoke:Clade|main|style={{{STYLE|}}} }}
 
Template:Clade
 
]]
 
  
 
function p.main(frame)
 
function p.main(frame)
Line 30: Line 28:
 
local maxChildren = 20 -- currently 17 in the clade/cladex templates
 
local maxChildren = 20 -- currently 17 in the clade/cladex templates
 
local childNumber = 0
 
local childNumber = 0
    lastNode = 0 -- make this global for now (declaring here prevents lua error for nil value in template)
+
local lastNode = 0
--local nodeCount = 0 -- total leafs plus new clade branches
+
local nodeCount = 0 -- total leafs plus new clade branches
--local leafCount = 0 -- just the terminal leaves
+
local leafCount = 0 -- just the terminal leaves
--local cladeCount = 0 -- new clade calls (each with a table)
+
local cladeCount = 0 -- new clade calls (each with a table)
 
local childCount = 0 -- number of leaves in the clade (can use to set bottom of bracket in addTaxon()
 
local childCount = 0 -- number of leaves in the clade (can use to set bottom of bracket in addTaxon()
 
local totalCount = 0
 
local totalCount = 0
 
 
    infoOutput = p.getCladeTreeInfo() -- get info about clade structure, e.g. lastNode (last |N= child number)
+
--[[Note: 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 doesn’t affect the template depths;
    --[[ add the templatestyles tag 
+
it would probably be better to store the transcluded nodes in a table (a job for later)
          add to every clade table >> 137 times in Passeriformes test page
+
]]
          cladeCount==1 >> 43 insertions
+
--[[ disable proprocessing loop
          nodeCount==2 >> 5 insertions (once for the style css, one link in each cladegram)
+
while childNumber < maxChildren do -- preprocessing loop
                            2 in Neosuchia (one for style css, one link for second cladogram)
+
childNumber = childNumber + 1 -- so we start with 1
                            this is because it counts "dummy" clades and outer clades with one leaf
+
local nodeLeaf,data = mw.getCurrentFrame():getParent().args[tostring(childNumber)] or "" -- get data from |N=
          cladeCount==1 AND nodeCount==2 >>  1 insertions
+
 
              but none in Neosuchia
+
if nodeLeaf ~= "" then
    --]]
+
childCount = childCount + 1 -- this counts child elements in this clade
     -- worked for wrong reasons if nodeCount==2  then
+
   
     if cladeCount==then  
+
    for i in string.gmatch(nodeLeaf, "|| rowspan") do -- count number of rows started (transclusion)
     local src = "Template:Clade/styles.css"
+
  nodeCount = nodeCount + 1
     cladeString = cladeString .. p.templateStyle( frame, src ) .. '\n'
+
    end
     end
+
    for i in string.gmatch(nodeLeaf, "{| cellspacing") do -- count number of tables started (transclusion)
   
+
  cladeCount = cladeCount + 1
   
+
    end
 +
          -- _, cladeCount = string.gsub(nodeLeaf, "{| cellspacing", "")
 +
            --_, nodeCount = string.gsub(nodeLeaf, "|| rowspan", "")
 +
           
 +
            --totalCount = totalCount + nodeCount
 +
lastNode = childNumber
 +
-- if data ~= nil then totalCount = totalCount + tonumber(data) else totalCount = totalCount + 1 end
 +
end
 +
 
 +
 
 +
--cladeString = cladeString .. '\n' .. ' count= ' .. totalCount
 +
 
 +
end
 +
]]
 +
     nodeCount = nodeCount + childCount --(elements passed down by transduction plus current child elements)
 +
    cladeCount = cladeCount + 1        --(elements passed down by transduction plus current clade)
 +
    leafCount = nodeCount-cladeCount+1
 +
     totalCount =leafCount-- cladeCount --nodeCount
 +
 
 +
 
 +
 
 +
    local testing = false
 +
    --testing = true                  -- COMMENT OUT TO GET MODULE RUNNING WITHOUT THE TEST
 +
    if testing then  
 +
     local dataString = p.test(1)
 +
     cladeString = cladeString .. dataString
 +
    return cladeString
 +
     end
 +
 
 
local tableStyle = frame.args.style or ""
 
local tableStyle = frame.args.style or ""
--if tableStyle == '{{{style}}}' then tableStyle = "" end -- no longer needed as pipe added to template to suppress passing of {{{style}} when no value
+
if tableStyle == '{{{style}}}' then tableStyle = "" end
 
if tableStyle ~= "" then
 
tableStyle = ' style="' .. tableStyle .. '"' -- include style= in string to suppress empty style elements
 
end
 
   
 
    reverseClade =frame.args.reverse or false -- a global
 
    --ENFORCE GLOBAL FOR DEVELOPMENT
 
    --reverseClade = true
 
  
 
local captionName =mw.getCurrentFrame():getParent().args['caption'] or ""
 
local captionName =mw.getCurrentFrame():getParent().args['caption'] or ""
 
local captionStyle = mw.getCurrentFrame():getParent().args['captionstyle'] or ""
 
local captionStyle = mw.getCurrentFrame():getParent().args['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 .. '"'
+
--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 .. '{| style="border-spacing:0;margin:0;' .. tableStyle .. '"'
    cladeString = cladeString .. '{|class="clade"' .. tableStyle
 
  
 
     -- add caption
 
     -- add caption
Line 90: Line 104:
 
local moreNeeded = true
 
local moreNeeded = true
 
childNumber = 0
 
childNumber = 0
--lastNode = 0
+
lastNode = 0
  
 
--[[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)
Line 99: Line 113:
 
]]
 
]]
 
 
-- main loop
+
while childNumber < maxChildren do -- disable moreNeeded
while childNumber < lastNode do -- use the last number determined in the preprocessing
+
                                        -- while childNumber < 17 and moreNeeded == true do
 
 
 
childNumber = childNumber + 1 -- so we start with 1
 
childNumber = childNumber + 1 -- so we start with 1
 
local nodeLeaf = mw.getCurrentFrame():getParent().args[tostring(childNumber)] or ""  -- get data from |N=
 
local nodeLeaf = mw.getCurrentFrame():getParent().args[tostring(childNumber)] or ""  -- get data from |N=
Line 112: Line 125:
 
nodeLabel = p.getNewickOuterterm(newickString) -- need to use terminal part of newick string for label
 
nodeLabel = p.getNewickOuterterm(newickString) -- need to use terminal part of newick string for label
 
end
 
end
cladeString = cladeString .. '\n' .. p.addTaxon(childNumber, p.newick(0, newickString), nodeLabel, lastNode)
+
cladeString = cladeString .. '\n' .. p.addTaxon(childNumber, p.newick(0, newickString), nodeLabel)
--lastNode=lastNode+1 -- there is a counting problem with the newickstring
+
lastNode = childNumber
 
elseif nodeLeaf ~= "" then -- if the node contains a leaf name or clade structue
 
elseif nodeLeaf ~= "" then -- if the node contains a leaf name or clade structue
--if reverseClade2 then
+
cladeString = cladeString .. '\n' .. p.addTaxon(childNumber, nodeLeaf, nodeLabel)
-- cladeString = cladeString .. '\n' .. p.addTaxonReverse(childNumber, nodeLeaf, nodeLabel, lastNode)
+
lastNode = childNumber
    --else
+
else
cladeString = cladeString .. '\n' .. p.addTaxon(childNumber, nodeLeaf, nodeLabel, lastNode)
+
moreNeeded = false -- no |N= so don’t look for more
--end
+
-- Note: this changes the behaviour. If there is no |2, it won’t look for |3 or |4, which clade does (is this desirable?)
 +
-- this has been disabled to allow consistent behaviour with clade/cladex (it will check to the limit set)
 
end
 
end
 
end
 
end
  
 +
--[[finish last row by adding cell with no left border
 +
        note: the row was started in addTaxon(), but the cell not added as left border yet to be determined;
 +
            here the cell is added to the last child node with no left border as it is below the bracket
 +
            this will be moved to addTaxon() when the number of the last child is known
 +
  ]]
 +
  local subLabel = mw.getCurrentFrame():getParent().args['sublabel'..tostring(lastNode)] or ""  -- request in addLabel
 +
 +
--use subLabel for annotating the clade structues to use structure information (DEBUGGIING ONLY)
 +
--subLabel= '(N=' .. totalCount .. ')'
 +
 +
--cladeString = cladeString ..  '\n' .. '| style="border: 0; padding: 0; vertical-align: top;" | <br/> '
 +
cladeString = cladeString ..  '\n' .. '|style="border:0;vertical-align:top;text-align:center;"|' .. p.addLabel(lastNode-1,subLabel)
 +
 +
    --[[ footer description (add addition row spanning two columns at bottom of table)
 +
    this is designed to provide the description below the whole clade structure (mimicking cladogram);
 +
    however, it can also be used to add labels and spacing to the whole clade structre
 +
]]
 
local footerText  = mw.getCurrentFrame():getParent().args['footer'] or ""
 
local footerText  = mw.getCurrentFrame():getParent().args['footer'] or ""
 
local footerStyle = mw.getCurrentFrame():getParent().args['footerstyle'] or ""
 
local footerStyle = mw.getCurrentFrame():getParent().args['footerstyle'] or ""
  
 
if footerText ~= "" then
 
if footerText ~= "" then
 +
  --cladeString = cladeString ..  '<tr><td>Test <br/><br/><br/><br/><br/></td></tr>'
 
  cladeString = cladeString ..  '\n|-style="' .. footerStyle .. '"\n|colspan="2"|<p>' .. footerText .. '</p>||'
 
  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
 
end
  
Line 140: Line 166:
 
 
 
return cladeString
 
return cladeString
--return '<div style="width:auto;">\n' .. cladeString .. '</div>'
 
 
end
 
end
  
Line 149: Line 174:
 
     note that the first and last child nodes need to be handled differently from the middle elements
 
     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 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 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)
+
    the last child doesn’t use a left border for the first cell in the second row (as it is above the bracket)
 +
    a complication is that the number of the last child node is not known;
 +
    as a result the cells will be added to the row on the next iteration or after the main loop finishes
 
]]
 
]]
function p.addTaxon(childNumber, nodeLeaf, nodeLabel, lastNode)
+
function p.addTaxon(childNumber, nodeLeaf, nodeLabel)
  
-- get border formating parameters for branch (default to global nodeParameters)
+
-- get leaf and labels
 +
--local nodeLeaf  = mw.getCurrentFrame():getParent().args[tostring(childNumber)] or ""    -- used passed variable to avoid redundancy
 +
--local nodeLabel = mw.getCurrentFrame():getParent().args['label'..tostring(childNumber)] or ""  -- request in addLabel
 +
 +
-- get formating parameters for branch (default to global nodeParameters)
 
--    - the branch parameters have a number, e.g. |colorN, |thicknessN, |stateN
 
--    - the branch parameters have a number, e.g. |colorN, |thicknessN, |stateN
 
--    - the node parameters have no number, e.g. |color, |thickness, |state
 
--    - the node parameters have no number, e.g. |color, |thickness, |state
Line 164: Line 195:
  
 
     -- the left border takes node parameters, the bottom border takes branch parameters
 
     -- 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 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
 
-- variables for right hand bar or bracket
Line 193: Line 213:
 
if barTop    ~= "0" then barTop    = "2px solid " .. barTop    end
 
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  
 
 
 
 
-- now construct wikitext  
 
-- now construct wikitext  
 +
local styleString = ''
 
local cladeString = ''
 
local cladeString = ''
local styleString = ''
 
    local borderStyle = '' -- will be used if border color, thickness or state is to be changed
 
    local classString = ''
 
    local reverseClass = ''
 
   
 
    -- 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
+
--[[ first add cell to the spacing row (for 2-17); it completes the previous row
   
+
    why is this here rather than at the end of each row?
if useInlineStyle then
+
To allow left border (here while extending bracket) or not (end of bracket)
if childNumber == 1 then
+
]]
        borderStyle = 'border-left:none;border-right:none;border-bottom:' .. bottomBorder .. ';'
+
if childNumber ~= 1 then
        --borderStyle = 'border-bottom:' .. bottomBorder .. ';'
+
  local subLabel = mw.getCurrentFrame():getParent().args['sublabel'..tostring(childNumber-1)] or "" -- request in addLabel
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 ~= '') then
+
--styleString = ' style="border-left:' .. leftBorder ..';border-top:0;border-right:0;border-bottom:0;vertical-align:top;text-align:center;"'
  styleString = 'style="' .. borderStyle .. branchStyle .. '"'
+
styleString = ' style="border:0;border-left:' .. leftBorder ..';vertical-align:top;text-align:center;"'
    end
+
--cladeString = cladeString .. '| ' .. styleString .. ' | <br/> ' -- for sublabel ' | y<br/> '
 +
cladeString = cladeString .. '|' .. styleString .. '|' ..  p.addLabel(childNumber-1,subLabel)
 +
end  
 +
 
 +
  -- new row
 +
  cladeString = cladeString .. '\n|-'
  
 +
-- now add cell with label
 +
styleString = ''
 
if childNumber == 1 then
 
if childNumber == 1 then
classString= 'class="clade-label first" '   -- add class first for top row
+
-- the width gives minimum spacing when all labels are empty (was 1.5em)
    else
+
--styleString = ' style="width:1em;border-bottom:' .. bottomBorder .. ';border-left:0;border-top:0;border-right:0;padding:0 0.2em;vertical-align:bottom;text-align:center;' .. branchStyle .. '"'
     classString = 'class="clade-label' .. reverseClass .. '" ' -- add "reverse" class if ltr cladogram
+
styleString = ' style="width:1em;border:0;padding:0 0.2em;border-bottom:' .. bottomBorder .. ';vertical-align:bottom;text-align:center;' .. branchStyle .. '"'
     end
+
else -- for 2-17
 +
     styleString = ' style="border:0;padding:0 0.2em;border-left:' .. leftBorder .. ';border-bottom:' .. bottomBorder .. ';vertical-align:bottom;text-align:center;' .. branchStyle .. '"'
 +
end
 +
     cladeString = cladeString .. '\n|' .. styleString  .. '|' .. p.addLabel(childNumber,nodeLabel) -- p.addLabel(nodeLabel)
  
    -- wikitext for cell with label
+
-- add cell with leaf (which may be a table with transluded content)
     local labelCellString = '\n|' .. classString .. styleString  .. '|' .. p.addLabel(childNumber,nodeLabel) -- p.addLabel(nodeLabel)
+
     --cladeString = cladeString .. '|| rowspan=2 style="border: 0; padding: 0; border-right: ' .. barRight .. ';border-bottom: ' .. barBottom .. ';border-top: ' .. barTop .. ';' .. branchStyle .. ' " | '
   
+
styleString = '' 
    --cladeString = cladeString .. labelCellString
 
 
 
---------------------------------------------------------------------------------
 
-- (3) add cell with leaf (which may be a table with transluded clade content)
 
   
 
 
     if barRight  ~= "0"  then  
 
     if barRight  ~= "0"  then  
     if reverseClade then -- we want the bar on the left
+
     styleString = 'style="border:0;padding:0;border-right:' .. barRight .. ';border-bottom:' .. barBottom .. ';border-top:' .. barTop .. ';' .. branchStyle .. '"'
    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
 
     else
     if (branchStyle ~= '') then
+
     styleString = 'style="border:0;padding:0;' .. branchStyle .. '"'
    styleString = ' style="' .. branchStyle .. '"'
 
        else
 
        styleString = '' -- use defaults in styles.css
 
        end
 
 
     end
 
     end
  
     -- wikitext for leaf cell (note: nodeLeaf needs to be on newline if new wikitable)
+
     cladeString = cladeString .. '||rowspan=2 ' .. styleString .. '|'
 
+
     -- note: this is where leading spaces can be added to labels and leaves. This was commented out in the clade template. Most trees seem to have added spaces, to would need to check for existing spaces; however, cladex uses it
    classString = 'class="clade-leaf' .. reverseClass .. '"'
+
    -- or could use padding:0 0.2em in cell style as for labels
 
+
     cladeString = cladeString .. '\n' .. nodeLeaf
    local leafCellString = '\n|rowspan=2 ' .. classString  .. styleString .. ' |\n' .. nodeLeaf
 
 
 
    --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 = mw.getCurrentFrame():getParent().args['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;'
 
    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 ~= '' then       
 
      styleString = ' style="' .. borderStyle .. branchStyle .. '"'
 
     end
 
 
 
    --local sublabel = p.addLabel(childNumber,subLabel)
 
 
 
    if childNumber == lastNode then
 
    classString = 'class="clade-slabel last" '
 
    else
 
        classString = 'class="clade-slabel' .. reverseClass .. '" '
 
    end
 
    local sublabelCellString = '\n|' .. classString .. styleString .. '|' ..  p.addLabel(childNumber,subLabel)
 
 
      
 
      
     --cladeString = cladeString .. sublabelCellString
+
     -- stuff for right-hand bar-bracket
 
 
    -- 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
 
 
 
---------------------------------------------------------------------------------------------
 
--[[ reverse version;
 
        currently duplicates the code changing order of elements (bars, leaf, label, sublabel)
 
        variables border-right
 
--]]
 
function p.addTaxonReverse(childNumber, nodeLeaf, nodeLabel, lastNode)
 
 
 
-- (1) get formating parameters for branch (default to global nodeParameters)
 
--    - the branch parameters have a number, e.g. |colorN, |thicknessN, |stateN
 
--    - the node parameters have no number, e.g. |color, |thickness, |state
 
local branchThickness = tonumber(mw.getCurrentFrame():getParent().args['thickness'..tostring(childNumber)]) or nodeThickness
 
local branchColor = mw.getCurrentFrame():getParent().args['color'..tostring(childNumber)] or nodeColor
 
local branchStyle = mw.getCurrentFrame():getParent().args['style'..tostring(childNumber)] or ""
 
local branchState = mw.getCurrentFrame():getParent().args['state'..tostring(childNumber)] or nodeState -- "solid"
 
if branchState == 'double' then if branchThickness < 2 then branchThickness = 3 end end  -- need thick line for double
 
 
 
    -- the left border takes node parameters, the bottom border takes branch parameters
 
  local bottomBorder =  tostring(branchThickness) ..'px ' .. branchState  .. (branchColor~="" and ' ' .. branchColor or '')
 
  local leftBorder  =  tostring(nodeThickness)  ..'px ' .. nodeState  .. (nodeColor~="" and ' ' .. nodeColor or '')
 
 
-- variables for right hand bar or bracket
 
--local barColor  = ""
 
local barRight  = mw.getCurrentFrame():getParent().args['bar'..tostring(childNumber)] or "0"
 
local barBottom = mw.getCurrentFrame():getParent().args['barend'..tostring(childNumber)] or "0"
 
local barTop    = mw.getCurrentFrame():getParent().args['barbegin'..tostring(childNumber)] or "0"
 
local barLabel  = mw.getCurrentFrame():getParent().args['barlabel'..tostring(childNumber)] or ""
 
local groupLabel  = mw.getCurrentFrame():getParent().args['grouplabel'..tostring(childNumber)] or ""
 
local groupLabelStyle  = mw.getCurrentFrame():getParent().args['labelstyle'..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 styleString = ''
 
local cladeString = ''
 
 
 
    -- (1) wikitext for new row
 
    cladeString = cladeString .. '\n|-'
 
 
 
    -- (4) stuff for right-hand brackets (or left hand brackets in reverse version)
 
 
 
-- note: it might be useful to add rowspan=2 to allow full range of positions
 
if groupLabel ~= ""then
 
cladeString = cladeString .. '\n|rowspan=2 class="clade-bar" style="'.. groupLabelStyle .. '" |' .. groupLabel 
 
end
 
    --[[ don't use bar label in the reverse version (probably best to get rid altogether)
 
 
if barRight  ~= "0"  then  
 
if barRight  ~= "0"  then  
 
     cladeString = cladeString .. '&nbsp;&nbsp;'            -- add spaces between leaf text and bar
 
     cladeString = cladeString .. '&nbsp;&nbsp;'            -- add spaces between leaf text and bar
 
if barLabel ~= "" then  
 
if barLabel ~= "" then  
cladeString = cladeString .. '\n|rowspan=2 style="vertical-align:middle;" |' .. barLabel  
+
-- cladeString = cladeString .. '\n| ' .. barLabel end  -- experiment for labeling the bar
 +
cladeString = cladeString .. '<td style="vertical-align:middle;">' .. barLabel ..'</td>'
 
end
 
end
 
end  
 
end  
    --]]
+
if groupLabel ~= ""then  
 
+
     --cladeString = cladeString .. '&nbsp;&nbsp;'           -- add spaces between leaf text and bar (could use padding)
 
+
cladeString = cladeString .. '<td style="'.. groupLabelStyle .. '">' .. groupLabel ..'</td>'  
-- (3) add cell with leaf (which may be a table with transluded clade content)
+
end
    --cladeString = cladeString .. '|| rowspan=2 style="border: 0; padding: 0; border-right: ' .. barRight .. ';border-bottom: ' .. barBottom .. ';border-top: ' .. barTop .. ';' .. branchStyle .. ' " | '
 
styleString = '' 
 
    if barRight  ~= "0" then  
 
     --(before CSS styling) -- styleString = 'style="border:0;padding:0;border-right:' .. barRight .. ';border-bottom:' .. barBottom .. ';border-top:' .. barTop .. ';' .. branchStyle .. '"'
 
    styleString = ' style="border-left:' .. barRight .. ';border-bottom:' .. barBottom .. ';border-top:' .. barTop .. ';' .. branchStyle .. '"'
 
    else
 
    --(before CSS styling) -- styleString = 'style="border:0;padding:0;' .. branchStyle .. '"'
 
    if (branchStyle ~= '') then
 
    styleString = ' style="' .. branchStyle .. '"'
 
        end
 
    end
 
   
 
    -- add wikitext for leaf cell (note: nodeLeaf needs to be on newline if new wikitable)
 
    cladeString = cladeString .. '\n|rowspan=2 class="clade-leafR"' .. styleString .. '|\n' .. nodeLeaf
 
    --cladeString = cladeString .. '\n' .. nodeLeaf  -- needs to be on newline if new wikitable
 
 
 
-- (2) now add cell with label
 
styleString = ''
 
if childNumber == 1 then
 
-- the width gives minimum spacing when all labels are empty (was 1.5em)
 
--(before CSS styling) -- styleString = ' style="width:1em;border:0;padding:0 0.2em;border-bottom:' .. bottomBorder .. ';vertical-align:bottom;text-align:center;' .. branchStyle .. '"'
 
        styleString = 'style="border-bottom:' .. bottomBorder .. ';' .. branchStyle .. '"'
 
else -- for 2-17
 
    --(before CSS styling) -- styleString = ' style="border:0;padding:0 0.2em;border-left:' .. leftBorder .. ';border-bottom:' .. bottomBorder .. ';vertical-align:bottom;text-align:center;' .. branchStyle .. '"'
 
    styleString = 'style="border-right:' .. leftBorder .. ';border-bottom:' .. bottomBorder .. ';' .. branchStyle .. '"'
 
end
 
    --  add wikitext for cell with label
 
    cladeString = cladeString .. '\n|class="clade-label" ' .. styleString  .. '|' .. p.addLabel(childNumber,nodeLabel) -- p.addLabel(nodeLabel)
 
  
   
+
-- add row (needed because of rowspan=2);
+
    -- note: the cell will be added on the next iteration of addTaxon (with left border) or after the main loop (without left border)
 
 
-- (5) add second row (only one cell needed for sublabel because of rowspan=2)  
 
 
 
     local branchStyleString = 'style="' .. branchStyle .. '"  '  
 
     local branchStyleString = 'style="' .. branchStyle .. '"  '  
 
     if branchStyle == '' then branchStyleString = ''  end -- avoid empty style elements
 
     if branchStyle == '' then branchStyleString = ''  end -- avoid empty style elements
 
cladeString = cladeString .. '\n|-' .. branchStyleString
 
cladeString = cladeString .. '\n|-' .. branchStyleString
 
    local subLabel = mw.getCurrentFrame():getParent().args['sublabel'..tostring(childNumber)] or ""  -- request in addLabel
 
 
 
-- FOR TESTING: use subLabel for annotating the clade structues to use structure information (DEBUGGIING ONLY)
+
-- TODO code to put whole row here instead of completing on next call to addTaxon() or after main loop
--subLabel= '(N=' .. infoOutput .. ')'
+
 
-- END TESTING
 
 
 
-- (6) add cell containing sublabel
 
if childNumber~=lastNode then -- if childNumber==lastNode we don't want left border, otherwise we do
 
styleString = ' style="border-right:' .. leftBorder ..';"'
 
    else
 
    --cladeString = cladeString ..  '\n' .. '| style="border: 0; padding: 0; vertical-align: top;" | <br/> '
 
    --(before CSS styling) -- styleString = 'style="border:0;vertical-align:top;text-align:center;"'
 
styleString = ''
 
    end
 
   
 
    local sublabel = p.addLabel(childNumber,subLabel)
 
    local classString = '' -- class only needed if there is a label to display
 
    if sublabel ~= '<br/>' then classString = 'class="clade-slabel" ' end
 
   
 
    cladeString = cladeString .. '\n|' .. classString .. styleString .. '|' ..  sublabel
 
   
 
 
return cladeString
 
return cladeString
 
end
 
end
 
  
 
--[[ adds text for label or sublabel to a cell
 
--[[ adds text for label or sublabel to a cell
Line 477: Line 290:
 
--local nodeLabel = mw.getCurrentFrame():getParent().args['label'..tostring(childNumber)] or ""
 
--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?)
+
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 ~= "{{{label1}}}" then
if nodeLabel == "" then
+
if firstChars == "{{{" or nodeLabel == "" then
--return '<br/>' --'&nbsp;<br/>'  -- remove space to reduce post-expand include size (the width=1.5em handles spacing)
+
return '<br/>' --'&nbsp;<br/>'  -- remove space to reduce post-expand include size (the width=1.5em handles spacing)
--return '<br/>' -- must return something; this is critical for clade structure
 
return '&#8239;' -- &nbsp; &thinsp; &#8239;(thin nbsp)
 
 
else
 
else
 
-- spaces can cause  wrapping and can break tree structure, hence use span with nowrap class
 
-- spaces can cause  wrapping and can break tree structure, hence use span with nowrap class
Line 497: Line 308:
 
     else
 
     else
 
     local nowrapString = string.gsub(nodeLabel," ", "&nbsp;") -- replace spaces with non-breaking space
 
     local nowrapString = string.gsub(nodeLabel," ", "&nbsp;") -- replace spaces with non-breaking space
                      -- could replace hyphen with non-breaking hyphen (&#8209;)
 
 
return nowrapString
 
return nowrapString
 
end
 
end
Line 510: Line 320:
 
return string.gsub(newickString, "%b()", "")  -- delete parenthetic term
 
return string.gsub(newickString, "%b()", "")  -- delete parenthetic term
 
end
 
end
 
 
function p.newick(count,newickString)
 
function p.newick(count,newickString)
 
 
local cladeString = ""
 
local cladeString = ""
 
count = count+1
 
count = count+1
 
--start table
 
--start table
--cladeString = cladeString .. '{| style="border-collapse:collapse;border-spacing:0;border:0;margin:0;'
+
cladeString = cladeString .. '{| style="border-collapse:collapse;border-spacing:0;border:0;margin:0;'
cladeString = cladeString .. '{| class="clade" '
 
 
 
 
local j,k
 
local j,k
Line 533: Line 340:
 
--cladeString = cladeString .. '\n' .. p.addTaxon(1, innerTerm2, "")
 
--cladeString = cladeString .. '\n' .. p.addTaxon(1, innerTerm2, "")
  
    -- this needs a lastNode variable
 
 
local s = strsplit(innerTerm2, ",")
 
local s = strsplit(innerTerm2, ",")
--oldLastNode=lastNode
 
local lastNode=table.getn(s) -- number of child branches
 
 
local i=1
 
local i=1
 
while s[i] do
 
while s[i] do
Line 544: Line 348:
 
if string.find(restoredString, '%(.*%)') then
 
if string.find(restoredString, '%(.*%)') then
 
--cladeString = cladeString .. '\n' .. p.addTaxon(i, restoredString, "x")
 
--cladeString = cladeString .. '\n' .. p.addTaxon(i, restoredString, "x")
cladeString = cladeString .. '\n' .. p.addTaxon(i, p.newick(count,restoredString), outerTerm, lastNode)
+
cladeString = cladeString .. '\n' .. p.addTaxon(i, p.newick(count,restoredString), outerTerm)
 
-- p.addTaxon(2, p.newick(count,newickString2), "root")
 
-- p.addTaxon(2, p.newick(count,newickString2), "root")
 
else
 
else
cladeString = cladeString .. '\n' .. p.addTaxon(i, restoredString, "", lastNode) --count)
+
cladeString = cladeString .. '\n' .. p.addTaxon(i, restoredString, "") --count)
 
end
 
end
 
i=i+1
 
i=i+1
 
end
 
end
  -- 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|}'
--cladeString = cladeString ..  '\n| <br/> \n|}' -- is this legacy for extra sublabel?
+
cladeString = cladeString ..  '\n|}'
 
 
return cladeString
 
return cladeString
 
end
 
end
Line 572: Line 374:
 
end
 
end
  
 +
 +
-- Bits from Peter Coxhead’s module
 +
--    https://en.wikipedia.org/w/index.php?title=Module:Autotaxobox&action=edit
 +
 +
--[[
 +
Utility function primarily intended for use in checking and debugging.
 +
Returns number of levels in a taxonomic hierarchy, starting from
 +
the supplied taxon as level 1.
 +
Usage: {{#invoke:Autotaxobox|nLevels|TAXON}}
 +
]]
 +
function p.nLevels(frame)
 +
local currTaxon = frame.args[1]
 +
    local i = 1
 +
local maxN = 2
 +
local searching = true
 +
while searching and i < maxN  do
 +
--local parent = frame:expandTemplate{ title = 'Template:Taxonomy/' .. currTaxon, args = {['machine code'] = 'parent' } }
 +
local parent = frame:expandTemplate{ title =  currTaxon, args = {['machine code'] = 'parent' } }
 +
if parent ~= '' then
 +
currTaxon = parent
 +
i = i + 1
 +
else
 +
searching = false
 +
end
 +
end
 +
if searching then return currTaxon --maxN .. '+'
 +
else return i
 +
end
 +
end
 +
 +
--[[
 +
Utility function primarily intended for use in checking and debugging.
 +
Returns a comma separated list of a taxonomic hierarchy, starting from
 +
the supplied taxon.
 +
Usage: {{#invoke:Autotaxobox|listAll|TAXON}}
 +
]]
 +
function p.listAll(frame)
 +
local currTaxon = frame.args[1]
 +
local i = 1
 +
local searching = true
 +
local maxN = 100
 +
local lst = currTaxon
 +
while i < maxN and searching do
 +
local currCall = 'Template:Taxonomy/' .. currTaxon
 +
local parent = frame:expandTemplate{ title = currCall, args = {['machine code'] = 'parent' } }
 +
if parent ~= '' then
 +
currTaxon = parent
 +
lst = lst .. ', ' .. currTaxon
 +
i = i + 1
 +
else
 +
searching = false
 +
end
 +
end
 +
if searching then lst = lst .. '...' end
 +
return lst .. ' (' .. i .. ')'
 +
end
  
 
-- =================== experimental Newick to clade parser function =============================
 
-- =================== experimental Newick to clade parser function =============================
Line 612: Line 470:
 
end
 
end
  
--[[ Parse one level of Newick string
+
--[[ Parse one level of Newick sting
     This function receives a Newick string, which has two components
+
     This function recieves a Newick string, which has two components
 
       1. the right hand term is a clade label: |labelN=labelname
 
       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
 
       2. the left hand term in parenthesis has common delimited child nodes, each of which can be
Line 686: Line 544:
 
------------------------------------------------------------------------------------------
 
------------------------------------------------------------------------------------------
  
 +
function p.test(dummy)
 +
local dataString = ""
 +
    dataString = "NEWCLADE:"
 +
   
 +
    local childNumber =0
 +
    local lastNode = 0
 +
while childNumber < 17 do -- disable moreNeeded
 +
 +
childNumber = childNumber + 1 -- so we start with 1
 +
local nodeLeaf = mw.getCurrentFrame():getParent().args[tostring(childNumber)]  or "" -- get data from |N=
 +
--local nodeLabel = mw.getCurrentFrame():getParent().args['label'..tostring(childNumber)] or ""  -- get data from |labelN=
 +
        if nodeLeaf ~= "" then
 +
dataString = dataString .. tostring(childNumber)
  
 +
local firstChars = string.sub(nodeLeaf, 1,2) -- get first two characters
 +
--if nodeLabel ~= "{{{label1}}}" then
 +
if firstChars == "{{"  then
 +
                dataString = dataString .. 'XXX' .. nodeLeaf
 +
end
 +
dataString = dataString .. 'LEAF:' .. nodeLeaf
 +
 +
lastNode = childNumber
 +
end
 +
end
 +
    local numClades=0
 +
    local numBranches=0
 +
    -- _, count = string.gsub(dataString, "NEWCLADE", "NEXT")
 +
 
 +
    for i in string.gmatch(dataString, "NEWCLADE") do
 +
  numClades = numClades + 1
 +
    end
 +
    dataString =  dataString .. '<br/>(numClades=' .. numClades .. ')'
 +
    for i in string.gmatch(dataString, "LEAF") do
 +
  numBranches = numBranches + 1
 +
    end
 +
    dataString =  dataString .. '<br/>(numClades=' .. numClades .. ')'
 +
    dataString =  dataString .. '<br/>(numBranches=' .. numBranches .. ')'
 +
   
 +
mw:log(numClades)
 +
 +
--return 'numClades=' .. numClades .. '; numBranches=' .. numBranches
 +
return  dataString
 +
end
 
function p.test2(target)
 
function p.test2(target)
 
local target ="User:Jts1882/sandbox/templates/Template:Passeroidea"
 
local target ="User:Jts1882/sandbox/templates/Template:Passeroidea"
Line 692: Line 592:
 
return result
 
return result
 
end
 
end
-------------------------------------------------------------------------------------------
 
  
function p.toggle(frame)
 
 
if 1==2 then return 'some text' end
 
 
--local toggleSymbol = 'toggle all'
 
local toggleSymbol = mw.getCurrentFrame():getParent().args['button'] or ""
 
 
local toggleString = '<div class="'
 
               
 
    local i=0
 
    while i < 20 do  -- limit on number of toggle elements controlled by the trigger button
 
    i = i + 1 -- so we start with 1
 
local target = mw.getCurrentFrame():getParent().args['id'..tostring(i)]
 
   
 
    -- add classes for the three elements of each target: expand symbol, collapse symbol and contents
 
    if target ~= nil then
 
            toggleString = toggleString .. ' mw-customtoggle-myClade' .. target
 
                ..            ' mw-customtoggle-collapseSymbol' .. target
 
                ..            ' mw-customtoggle-expandSymbol' .. target
 
        end
 
    end
 
 
 
 
toggleString = toggleString  ..  '">' .. toggleSymbol .. '</div>'
 
 
  return toggleString
 
end
 
-----------------------------------------------------------------------------------------------
 
 
function p.hidden(frame)
 
function p.hidden(frame)
 
      
 
      
Line 754: Line 625:
 
     local contentString = '<td style="padding:0;">'
 
     local contentString = '<td style="padding:0;">'
 
                 .. '<div class="mw-collapsible' .. contentState .. '" id="mw-customcollapsible-myClade' .. id .. '>'
 
                 .. '<div class="mw-collapsible' .. contentState .. '" id="mw-customcollapsible-myClade' .. id .. '>'
                 .. '<div class="mw-collapsible-content mw-customtoggle-NOT_ON_CONTENT" >' -- don't toggle on the content
+
                 .. '<div class="mw-collapsible-content mw-customtoggle-NOT_ON_CONTENT" >' -- don’t toggle on the content
 
                  
 
                  
 
                 .. '\n' .. p.main(frame)  -- important to start wikitext tables on new line
 
                 .. '\n' .. p.main(frame)  -- important to start wikitext tables on new line
Line 787: Line 658:
 
return cladeString
 
return cladeString
 
end
 
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 = mw.getCurrentFrame():getParent().args[tostring(childNumber)] or ""  -- get data from |N=
 
        local newickString = mw.getCurrentFrame():getParent().args['newick'..tostring(childNumber)] or ""  -- get data from |labelN=
 
if newickString ~= "" or nodeLeaf ~= "" 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
 
  
--[[ code for placing TemplateStyles from the module
+
-- this must be at end
    source: Anomie (CC-0)  https://phabricator.wikimedia.org/T200442
 
]]
 
 
 
function p.templateStyle( frame, src )
 
  return frame:extensionTag( 'templatestyles', '', { src = src } );
 
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.testTable(frame)
 
 
-- build HTML table
 
local galleryTable = mw.html.create('table')
 
galleryTable:addClass('wikitable')
 
galleryTable:addClass('sortable')
 
galleryTable:css('text-align', 'center')
 
 
    galleryTable:tag('caption'):wikitext('Table created with mw.html')
 
   
 
    local row = galleryTable:tag('tr')
 
row:tag('th'):wikitext('Header 1')
 
row:tag('th'):wikitext('Header 2')
 
row:tag('th'):wikitext('Header 3')
 
--:attr('rowspan', 2)
 
 
row = galleryTable:tag('tr')
 
row:tag('td'):css('text-align', 'left'):wikitext('A1')
 
row:tag('td'):css('text-align', 'left'):wikitext('B1')
 
row:tag('td'):css('text-align', 'left'):wikitext('C1')
 
 
 
local row2 = mw.html.create('tr')                        -- create an independent row element
 
row2:tag('td'):css('text-align', 'left'):wikitext('A3')
 
row2:tag('td'):css('text-align', 'left'):wikitext('B3')
 
row2:tag('td'):css('text-align', 'left'):wikitext('C3')
 
 
row = galleryTable:tag('tr')
 
row:tag('td'):css('text-align', 'left'):wikitext('A2')
 
row:tag('td'):css('text-align', 'left'):wikitext('B2')
 
row:tag('td'):css('text-align', 'left'):wikitext('C2')
 
 
galleryTable:node(row2)                                  -- now add row2 as node
 
 
return tostring(galleryTable)
 
end
 
 
 
function p.example(frame)
 
 
local params = mw.getCurrentFrame():getParent().args
 
-- build HTML table
 
local exampleTable = mw.html.create('table')
 
exampleTable:addClass('wikitable')
 
local align = params['align'] or 'center'
 
if align == 'center' then
 
exampleTable:css('margin-left', 'auto')
 
exampleTable:css('margin-right','auto')
 
elseif align == 'right' then
 
exampleTable:css('float',align)
 
end
 
 
local columns = {'description','code','output','comment'}
 
--local headers = {'Description','Code','Output','Comment'}
 
    -- create header row and add requested headers
 
    local row = exampleTable:tag('tr')
 
    local headerText
 
    for k,v in pairs(columns) do
 
if params[v] then
 
if params[v]~='' then headerText=params[v]  else headerText = firstToUpper(v) end
 
row:tag('th'):wikitext(headerText)
 
    end
 
    end
 
 
 
-- now deal with the data rows
 
local i=0
 
while i<10 do
 
i=i+1
 
local moreRows = false
 
for k,v in pairs(columns) do
 
if params[v..i] then moreRows = true end
 
end
 
if not moreRows then break end
 
 
row = exampleTable:tag('tr')
 
for k,v in pairs(columns) do
 
if params[v] then
 
if params[v] then
 
row:tag('td'):css('text-align', 'left'):wikitext(params[v..i])
 
else
 
row:tag('td')
 
end
 
end
 
end
 
    end
 
 
 
return tostring(exampleTable)
 
end
 
function firstToUpper(str)
 
    return (str:gsub("^%l", string.upper))
 
end
 
 
 
-- display a gallery of cladograms using floating div elements
 
 
 
function p.gallery(frame)
 
 
local params = mw.getCurrentFrame():getParent().args
 
 
 
    local outerBorders = false
 
    if params['caption'] or params['footer'] then outerBorders = true end
 
   
 
-- the gallery container
 
local gallery = mw.html.create('div'):addClass('clade-gallery'):css('float',params['align'])
 
 
 
-- main caption for gallery if there is one
 
 
if params['main-caption'] then
 
gallery:tag('div'):wikitext(params['main-caption']):addClass('main-caption')
 
end
 
 
 
-- a gallery of floating divs with content
 
  
    local shading = ''
 
--if params['shading'] then shading = 'shading' end -- class for shading
 
if params['shading'] then shading = 'wikitable' end -- class for shading
 
 
local vertical = '' -- defaults to horizontal
 
if params['vertical'] then vertical = 'vertical' end -- class for vertical display
 
 
local sizeStyling = ''
 
if params['width'] then sizeStyling = sizeStyling .. 'width:' .. params['width'] .. ';' end
 
if params['height'] then sizeStyling = sizeStyling .. 'height:' .. params['height'] .. ';' end
 
 
    local noborder = ''
 
    if not params['main-borders'] then noborder = 'no-border' end
 
   
 
    -- the gallery contents
 
    local content = gallery:tag('div'):addClass('main-content'):addClass(noborder)
 
                                      :css('height',params['height']) 
 
  local i=0
 
while i<15 do
 
i=i+1
 
local caption = params['caption'..i] --or ''
 
local header = params['header'..i] --or ''
 
local footer = params['footer'..i] --or ''
 
 
-- for each gallery element containing  a cladogram
 
        if params['cladogram'..i] then
 
    local cladogramTable = content:tag('table')
 
                  :addClass(shading):addClass('gallery-element'):addClass(vertical)
 
                  :css('width',params['width'])   
 
                  :css('height',params['height'])   
 
            if caption then
 
        cladogramTable:tag('caption'):wikitext(caption):addClass('caption')
 
        end
 
        if header then
 
        local row = cladogramTable:tag('tr'):addClass('header')
 
        row:tag('th'):wikitext(header)
 
        end
 
        -- if params['cladogram'..i] already checked
 
        local row = cladogramTable:tag('tr')
 
        row:tag('td'):wikitext(params['cladogram'..i])
 
                      :addClass('cladogram')
 
                      --:css("padding", "20px")
 
                  :css('width',params['cladogram-width'])   
 
                  :css('height',params['cladogram-height'])   
 
       
 
        if footer then
 
        local row = cladogramTable:tag('tr')
 
        row:tag('td'):wikitext(footer):addClass('footer'):addClass(shading)
 
        end
 
   
 
    --[[DIV VERSION - different browsers behave differently
 
    -- the div containing each element in the gallery
 
    local div = content:tag('div'):addClass('gallery-element'):addClass(direction)
 
                  :css('width',params['width'])   
 
                  :css('height',params['height'])   
 
   
 
    -- the caption, header, content and footer for each element
 
    if caption then div:tag('div'):wikitext(caption):addClass('caption'):addClass(shading) end
 
   
 
    if header then div:tag('div'):wikitext(header):addClass('header'):addClass(shading) end
 
   
 
    div:tag('div'):wikitext(params['cladogram'..i]):addClass('cladogram'):addClass(shading)
 
                  :css('width',params['cladogram-width'])   
 
                  :css('height',params['cladogram-height'])   
 
   
 
    if footer then div:tag('div'):wikitext(footer):addClass('footer'):addClass(shading) end
 
    END DIV VERSION--]]
 
    else
 
      break
 
    end
 
end
 
 
-- main footer
 
if params['main-footer'] then
 
gallery:tag('div'):wikitext(params['main-footer']):addClass('main-footer'):addClass(noborder)
 
end
 
-- TemplateStyles file
 
local src = "Template:Clade gallery/styles.css"
 
   
 
return p.templateStyle( frame, src ) ..  tostring(gallery)
 
end
 
 
-- this must be at end
 
 
return p
 
return p

Latest revision as of 02:52, 11 July 2020

This module is designed to be used with the template {{clade}} to draw phylogenetic trees or cladograms. The new template‐module combination extends the feature available with {{clade}} while replicating the behavior of the older templates written with the template language.

Additionally, the module has the code for a prototype template, which converts Newick strings into clade structure. This is not to be used in Wikipedia articles, but is a utility 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

{{#invoke:Clade|main|style={{{style}}}}}


--NOTE: this module contains functions for generating the table structure of the clade tree in two ways:
-- (1) the original method was generated by a number of function calls from the template using {{invoke}}
--        function p.openTable(frame) - creates wikitext code to open the HTML table
--        function p.node(frame)  - deals with the first node (|1,|label1) and creates wikitext for top row of the table
--        function p.nodeN(frame) - deals with a sister nodes (|2-17), adding a row each time it is called
--        function p.closeTable() - closes the HTML table
-- (2) the revised method is called by the template with one {{invoke}} instruction; it has three functions:
--        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

local p = {}



--============================== main function (for Method 2) ===========================
-- main function, which will 
--[[Main function to generate the table structure of the tree

Usage: {{#invoke:Module:Sandbox/Jts1882/CladeN|main|style={{{STYLE}}} }}
Template: User:Jts1882/sandbox/templates/CladeN
]]


function p.main(frame)

	local cladeString = ""	
	local maxChildren = 20 -- currently 17 in the clade/cladex templates
	local childNumber = 0
	local lastNode = 0
	local nodeCount = 0 -- total leafs plus new clade branches
	local leafCount = 0 -- just the terminal leaves
	local cladeCount = 0 -- new clade calls (each with a table)
	local childCount = 0 -- number of leaves in the clade (can use to set bottom of bracket in addTaxon()
	local totalCount = 0
	
	--[[Note: 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 doesn’t affect the template depths; 
		it would probably be better to store the transcluded nodes in a table (a job for later)
	]]
--[[ disable proprocessing loop
	while 	childNumber < maxChildren do -- preprocessing loop
		childNumber = childNumber + 1 -- so we start with 1
		local nodeLeaf,data = mw.getCurrentFrame():getParent().args[tostring(childNumber)] or ""  -- get data from |N=
  
		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, "{| cellspacing") do -- count number of tables started (transclusion)
   				cladeCount = cladeCount + 1
     		end
           -- _, cladeCount = string.gsub(nodeLeaf, "{| cellspacing", "")
            --_, nodeCount = string.gsub(nodeLeaf, "|| rowspan", "")
            
            --totalCount = totalCount + nodeCount
			lastNode = childNumber
		--	if data ~= nil then totalCount = totalCount + tonumber(data) else totalCount = totalCount + 1 end
		end


		--cladeString = cladeString .. '\n' .. ' count= ' .. totalCount

	end
]]	
    nodeCount = nodeCount + childCount --(elements passed down by transduction plus current child elements)
    cladeCount = cladeCount + 1        --(elements passed down by transduction plus current clade)
    leafCount = nodeCount-cladeCount+1
    totalCount =leafCount-- cladeCount --nodeCount 



    local testing = false
    --testing = true                  -- COMMENT OUT TO GET MODULE RUNNING WITHOUT THE TEST
    if testing then 
    	local dataString = p.test(1)
    	cladeString = cladeString .. dataString
    	return cladeString 
    end

	local tableStyle = frame.args.style or ""
	if tableStyle == '{{{style}}}' then tableStyle = "" end

	local captionName =mw.getCurrentFrame():getParent().args['caption'] or ""
	local captionStyle = mw.getCurrentFrame():getParent().args['captionstyle'] or ""

	-- open table	
	--cladeString = cladeString .. '{| style="border-collapse:collapse;border-spacing:0;margin:0;' .. tableStyle .. '"'
    cladeString = cladeString .. '{| style="border-spacing:0;margin:0;' .. tableStyle .. '"'

    -- add caption
	if captionName ~= "" then
		cladeString = cladeString .. '\n|+ style="' .. captionStyle .. '"|' .. captionName
	end
	
	-- global nodeParameters (unnumber, i.e. color, thickness, state) apply to whole node bracket, 
	--    but can be overrriden by branchParameters (numbered, e.g. color2, thickness2, state2)
	nodeColor = mw.getCurrentFrame():getParent().args['color'] or ""
	nodeThickness = tonumber(mw.getCurrentFrame():getParent().args['thickness']) or 1
	nodeState = mw.getCurrentFrame():getParent().args['state'] or "solid"
	
	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
	]]
	
	while 	childNumber < maxChildren do -- disable moreNeeded 
	                                        -- while childNumber < 17 and moreNeeded == true do
		childNumber = childNumber + 1 -- so we start with 1
		local nodeLeaf = mw.getCurrentFrame():getParent().args[tostring(childNumber)] or ""  -- get data from |N=
		local nodeLabel = mw.getCurrentFrame():getParent().args['label'..tostring(childNumber)] or ""  -- get data from |labelN=
		
		
		local newickString = mw.getCurrentFrame():getParent().args['newick'..tostring(childNumber)] or ""  -- get data from |labelN=
		if newickString ~= "" then -- if using a newick string instead of a clade structure
			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 = childNumber
		elseif nodeLeaf ~= "" then -- if the node contains a leaf name or clade structue
			cladeString = cladeString .. '\n' .. p.addTaxon(childNumber, nodeLeaf, nodeLabel)
			lastNode = childNumber
		else
			moreNeeded = false -- no |N= so don’t look for more
			-- Note: this changes the behaviour. If there is no |2, it won’t look for |3 or |4, which clade does (is this desirable?)
			-- this has been disabled to allow consistent behaviour with clade/cladex (it will check to the limit set)
		end
	end

	--[[finish last row by adding cell with no left border 
        note: the row was started in addTaxon(), but the cell not added as left border yet to be determined; 
             here the cell is added to the last child node with no left border as it is below the bracket
             this will be moved to addTaxon() when the number of the last child is known 
   	]]
   local subLabel = mw.getCurrentFrame():getParent().args['sublabel'..tostring(lastNode)] or ""  -- request in addLabel
	
	--use subLabel for annotating the clade structues to use structure information (DEBUGGIING ONLY)
	--subLabel= '(N=' .. totalCount .. ')'
	
	--cladeString = cladeString ..  '\n' .. '| style="border: 0; padding: 0; vertical-align: top;" | <br/> '
	cladeString = cladeString ..  '\n' .. '|style="border:0;vertical-align:top;text-align:center;"|' .. p.addLabel(lastNode-1,subLabel) 

    --[[ footer description (add addition row spanning two columns at bottom of table)
    	this is designed to provide the description below the whole clade structure (mimicking cladogram);
    	however, it can also be used to add labels and spacing to the whole clade structre
	]]
	local footerText  = mw.getCurrentFrame():getParent().args['footer'] or ""
	local footerStyle = mw.getCurrentFrame():getParent().args['footerstyle'] or ""

	if footerText ~= "" then
	   --cladeString = cladeString ..  '<tr><td>Test <br/><br/><br/><br/><br/></td></tr>' 
	   cladeString = cladeString ..  '\n|-style="' .. footerStyle .. '"\n|colspan="2"|<p>' .. footerText .. '</p>||'
	end

	-- close table (wikitext to close table)
	cladeString = cladeString ..  '\n|}'
	
	return cladeString
end

--[[ 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 above the bracket)
	     a complication is that the number of the last child node is not known;
	     	as a result the cells will be added to the row on the next iteration or after the main loop finishes
]]
function p.addTaxon(childNumber, nodeLeaf, nodeLabel)

	-- get leaf and labels
	--local nodeLeaf  = mw.getCurrentFrame():getParent().args[tostring(childNumber)] or ""    -- used passed variable to avoid redundancy
	--local nodeLabel = mw.getCurrentFrame():getParent().args['label'..tostring(childNumber)] or ""  -- request in addLabel
	
	-- get formating parameters for branch (default to global nodeParameters)
	--    - the branch parameters have a number, e.g. |colorN, |thicknessN, |stateN
	--    - the node parameters have no number, e.g. |color, |thickness, |state
	local branchThickness = tonumber(mw.getCurrentFrame():getParent().args['thickness'..tostring(childNumber)]) or nodeThickness
	local branchColor = mw.getCurrentFrame():getParent().args['color'..tostring(childNumber)] or nodeColor
	local branchStyle = mw.getCurrentFrame():getParent().args['style'..tostring(childNumber)] or ""
	local branchState = mw.getCurrentFrame():getParent().args['state'..tostring(childNumber)] or nodeState -- "solid" 
	if branchState == 'double' then if branchThickness < 2 then branchThickness = 3 end end  -- need thick line for double

    -- the left border takes node parameters, the bottom border takes branch parameters
   local bottomBorder =  tostring(branchThickness) ..'px ' .. branchState  .. (branchColor~="" and ' ' .. branchColor or '')
   local leftBorder   =  tostring(nodeThickness)   ..'px ' .. nodeState  .. (nodeColor~="" and ' ' .. nodeColor or '')
	
	-- variables for right hand bar or bracket
	--local barColor  = "" 
	local barRight  = mw.getCurrentFrame():getParent().args['bar'..tostring(childNumber)] or "0"
	local barBottom = mw.getCurrentFrame():getParent().args['barend'..tostring(childNumber)] or "0"
	local barTop    = mw.getCurrentFrame():getParent().args['barbegin'..tostring(childNumber)] or "0"
	local barLabel  = mw.getCurrentFrame():getParent().args['barlabel'..tostring(childNumber)] or ""
	local groupLabel  = mw.getCurrentFrame():getParent().args['grouplabel'..tostring(childNumber)] or ""
	local groupLabelStyle  = mw.getCurrentFrame():getParent().args['labelstyle'..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 styleString = ''
	local cladeString = ''

	--[[ first add cell to the spacing row (for 2-17); it completes the previous row 
	    why is this here rather than at the end of each row? 
		To allow left border (here while extending bracket) or not (end of bracket)
	]]
	if childNumber  ~= 1 then
  		local subLabel = mw.getCurrentFrame():getParent().args['sublabel'..tostring(childNumber-1)] or ""  -- request in addLabel

		--styleString = ' style="border-left:' .. leftBorder ..';border-top:0;border-right:0;border-bottom:0;vertical-align:top;text-align:center;"'
		styleString = ' style="border:0;border-left:' .. leftBorder ..';vertical-align:top;text-align:center;"'
		--cladeString = cladeString .. '| ' .. styleString .. ' | <br/> ' -- for sublabel ' | y<br/> '
		cladeString = cladeString .. '|' .. styleString .. '|' ..  p.addLabel(childNumber-1,subLabel)
	end 
   
   -- new row
   cladeString = cladeString .. '\n|-'

	-- now add cell with label
	styleString = ''
	if childNumber == 1 then
		-- the width gives minimum spacing when all labels are empty (was 1.5em)
		--styleString = ' style="width:1em;border-bottom:' .. bottomBorder .. ';border-left:0;border-top:0;border-right:0;padding:0 0.2em;vertical-align:bottom;text-align:center;' .. branchStyle .. '"'
		styleString = ' style="width:1em;border:0;padding:0 0.2em;border-bottom:' .. bottomBorder .. ';vertical-align:bottom;text-align:center;' .. branchStyle .. '"'
 	else -- for 2-17
    	styleString = ' style="border:0;padding:0 0.2em;border-left:' .. leftBorder .. ';border-bottom:' .. bottomBorder .. ';vertical-align:bottom;text-align:center;' .. branchStyle .. '"'
	end
    cladeString = cladeString .. '\n|' .. styleString  .. '|' .. p.addLabel(childNumber,nodeLabel) -- p.addLabel(nodeLabel)

	-- add cell with leaf (which may be a table with transluded content)
    --cladeString = cladeString .. '|| rowspan=2 style="border: 0; padding: 0; border-right: ' .. barRight .. ';border-bottom: ' .. barBottom .. ';border-top: ' .. barTop .. ';' .. branchStyle .. ' " | '
	styleString = ''  
    if barRight  ~= "0"  then 
    	styleString = 'style="border:0;padding:0;border-right:' .. barRight .. ';border-bottom:' .. barBottom .. ';border-top:' .. barTop .. ';' .. branchStyle .. '"'
    else
    	styleString = 'style="border:0;padding:0;' .. branchStyle .. '"'
    end

    cladeString = cladeString .. '||rowspan=2 ' .. styleString .. '|'
    -- note: this is where leading spaces can be added to labels and leaves. This was commented out in the clade template. Most trees seem to have added spaces, to would need to check for existing spaces; however, cladex uses it
    -- or could use padding:0 0.2em in cell style as for labels
    cladeString = cladeString .. '\n' .. nodeLeaf
    
    -- stuff for right-hand bar-bracket
	if barRight  ~= "0"  then 
    	cladeString = cladeString .. '&nbsp;&nbsp;'            -- add spaces between leaf text and bar
		if barLabel ~= "" then 
			-- cladeString = cladeString ..  '\n| ' .. barLabel end  -- experiment for labeling the bar
			cladeString = cladeString .. '<td style="vertical-align:middle;">' .. barLabel ..'</td>' 
		end
	end 
	if groupLabel ~= ""then 
    	--cladeString = cladeString .. '&nbsp;&nbsp;'            -- add spaces between leaf text and bar (could use padding)
		cladeString = cladeString .. '<td style="'.. groupLabelStyle .. '">' .. groupLabel ..'</td>' 
	end 	

	-- add row	(needed because of rowspan=2); 
    -- note: the cell will be added on the next iteration of addTaxon (with left border) or after the main loop (without left border)
    local branchStyleString = 'style="' .. branchStyle .. '"  ' 
    if branchStyle == '' then branchStyleString = ''  end -- avoid empty style elements
	cladeString = cladeString .. '\n|-' .. branchStyleString
	
	-- TODO code to put whole row here instead of completing on next call to addTaxon() or after main loop

	
	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 nodeLabel ~= "{{{label1}}}" then
	if firstChars == "{{{" or nodeLabel == "" then
		return '<br/>' --'&nbsp;<br/>'  -- remove space to reduce post-expand include size (the width=1.5em handles spacing)
	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 string.find(nodeLabel, "span ") ~= nil  then  stylingElementDetected = true end
		if string.find(nodeLabel, " style") ~= nil then stylingElementDetected = true end 
		
		if stylingElementDetected == true then 
			return '<span class="nowrap">' .. nodeLabel .. '</span>'
    	else	
    		local nowrapString = string.gsub(nodeLabel," ", "&nbsp;") -- replace spaces with non-breaking space
			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 .. '{| style="border-collapse:collapse;border-spacing:0;border:0;margin:0;'  
	
	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, "")

	local s = strsplit(innerTerm2, ",")
	local i=1	
	while s[i] do	
		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)
			-- p.addTaxon(2, p.newick(count,newickString2), "root")
		else
			cladeString = cladeString .. '\n' .. p.addTaxon(i, restoredString, "") --count)
		end
		i=i+1
	end

	-- close table
	cladeString = cladeString ..  '\n' .. '| style="border: 0; padding: 0; vertical-align: top;" | <br/> \n|}'
	
	return cladeString
end
-- emulate a standard split string function
function strsplit(inputstr, sep) 
        if sep == nil then
                sep = "%s"
        end
        local t={} ; i=1
        for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
                t[i] = str
                i = i + 1
        end
        return t
end


-- Bits from Peter Coxhead’s module
--    https://en.wikipedia.org/w/index.php?title=Module:Autotaxobox&action=edit

--[[
Utility function primarily intended for use in checking and debugging.
Returns number of levels in a taxonomic hierarchy, starting from
the supplied taxon as level 1.
Usage: {{#invoke:Autotaxobox|nLevels|TAXON}}
]]
function p.nLevels(frame)
	local currTaxon = frame.args[1]
    local i = 1
	local maxN = 2
	local searching = true
	while searching and i < maxN  do
		--local parent = frame:expandTemplate{ title = 'Template:Taxonomy/' .. currTaxon, args = {['machine code'] = 'parent' } }
		local parent = frame:expandTemplate{ title =  currTaxon, args = {['machine code'] = 'parent' } }
		if parent ~= '' then
			currTaxon = parent
			i = i + 1
		else
			searching = false
		end
	end
	if searching then return currTaxon --maxN .. '+'
	else return i
	end
end

--[[
Utility function primarily intended for use in checking and debugging.
Returns a comma separated list of a taxonomic hierarchy, starting from
the supplied taxon.
Usage: {{#invoke:Autotaxobox|listAll|TAXON}}
]]
function p.listAll(frame)
	local currTaxon = frame.args[1]
	local i = 1
	local searching = true
	local maxN = 100
	local lst = currTaxon
	while i < maxN and searching do
		local currCall = 'Template:Taxonomy/' .. currTaxon
		local parent = frame:expandTemplate{ title = currCall, args = {['machine code'] = 'parent' } }
		if parent ~= '' then
			currTaxon = parent
			lst = lst .. ', ' .. currTaxon
			i = i + 1
		else
			searching = false
		end
	end
	if searching then lst = lst .. '...' end
	return lst .. ' (' .. i .. ')'
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']
	
	
	if newickString == '{{{newickstring}}}' then return newickString  end

	-- 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 = mw.getCurrentFrame():getParent().args['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 sting
     This function recieves 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 = strsplit(innerTerm2, ",")
		local i=1	
		while s[i] do	
			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 = mw.getCurrentFrame():getParent().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.test(dummy)
	local dataString = ""
    dataString =	"NEWCLADE:"
    
    local childNumber =0
    local lastNode = 0
	while 	childNumber < 17 do -- disable moreNeeded 
	
		childNumber = childNumber + 1 -- so we start with 1
		local nodeLeaf = mw.getCurrentFrame():getParent().args[tostring(childNumber)]  or "" -- get data from |N=
		--local nodeLabel = mw.getCurrentFrame():getParent().args['label'..tostring(childNumber)] or ""  -- get data from |labelN=
        if nodeLeaf ~= "" then
			dataString = dataString .. tostring(childNumber)

			local firstChars = string.sub(nodeLeaf, 1,2) -- get first two characters
			--if nodeLabel ~= "{{{label1}}}" then
			if firstChars == "{{"  then
                dataString = dataString .. 'XXX' .. nodeLeaf
			end
			dataString = dataString .. 'LEAF:' .. nodeLeaf
			
			lastNode = childNumber
		end
	end
    local numClades=0
    local numBranches=0
    -- _, count = string.gsub(dataString, "NEWCLADE", "NEXT")
   
    for i in string.gmatch(dataString, "NEWCLADE") do
   		numClades = numClades + 1
     end
    dataString =   dataString .. '<br/>(numClades=' .. numClades .. ')'
    for i in string.gmatch(dataString, "LEAF") do
   		numBranches = numBranches + 1
     end
    dataString =   dataString .. '<br/>(numClades=' .. numClades .. ')'
    dataString =   dataString .. '<br/>(numBranches=' .. numBranches .. ')'
    
	mw:log(numClades)
	
	--return 'numClades=' .. numClades .. '; numBranches=' .. numBranches
	return  dataString 
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 p.hidden(frame)
    
    local id = mw.getCurrentFrame():getParent().args['id'] or ""
    local mode = mw.getCurrentFrame():getParent().args['mode'] or "right"
    local expandSymbol = mw.getCurrentFrame():getParent().args['expand-symbol'] or "&#8862;"
    local collapseSymbol = mw.getCurrentFrame():getParent().args['collapse-symbol'] or "&#8863;"
    local initialState = mw.getCurrentFrame():getParent().args['expanded']
    
    -- default is content collapsed
    local contentState = " mw-collapsed" -- class to collapse content at start
    local collapseSymbolState = " mw-collapsed"
    local expandSymbolState = ""
    if initialState then
       contentState = ""
       collapseSymbolState =  ""
       expandSymbolState = " mw-collapsed" 
    end
    	
	
    -- collapsible element containing the expand sympol and/or text
    local expandSymbolString = '<td style="padding:0 0 0.25em 0;">' 
                .. '<div class="mw-collapsible' .. expandSymbolState .. '" id="mw-customcollapsible-expandSymbol' .. id .. '">'
                .. '<div class="mw-collapsible-content mw-customtoggle-expandSymbol' .. id .. '">'
                .. '<span class="mw-customtoggle-myClade' .. id 
                ..             ' mw-customtoggle-collapseSymbol' .. id 
                ..             ' mw-customtoggle-expandSymbol' .. id 
                ..    '" style="font-size:100%;">' .. expandSymbol .. '</span>'
                .. '</div></div></td>'
    
    -- collapsible element containing the clade content 
    local contentString = '<td style="padding:0;">'
                .. '<div class="mw-collapsible' .. contentState .. '" id="mw-customcollapsible-myClade' .. id .. '>'
                .. '<div class="mw-collapsible-content mw-customtoggle-NOT_ON_CONTENT" >' -- don’t toggle on the content
                
                .. '\n' .. p.main(frame)  -- important to start wikitext tables on new line
                .. '</div></div></td>'
    
    -- collapsible element containing the collapse sympol and/or text
    local collapseSymbolString = '<td style="padding:0 0 0.4em 0;">'
                .. '<div class="mw-collapsible' .. collapseSymbolState .. '" id="mw-customcollapsible-collapseSymbol' .. id .. '">'
                .. '<div class="mw-collapsible-content mw-customtoggle-collapseSymbol' .. id .. '" >'
                .. '<span class="mw-customtoggle-expandSymbol' .. id 
                            .. ' mw-customtoggle-myClade' .. id 
                            .. ' mw-customtoggle-collapseSymbol' .. id 
                            .. ' " style="font-size:100%;" >' .. collapseSymbol .. '</span>'
                            .. '</div></div></td>'


	local tableStyle = frame.args.style or ""
	if tableStyle == '{{{style}}}' then tableStyle = "" end
 	local cladeString = '<table style="border-spacing:0;margin:0;'..tableStyle ..'"><tr>'
    cladeString = cladeString .. expandSymbolString 
    if mode == "left" then
    	cladeString = cladeString .. collapseSymbolString
    end
    cladeString = cladeString .. contentString 
    if mode == "right" then
    	cladeString = cladeString .. collapseSymbolString
    end
    -- Note: if we want collapse string left and right it needs an extra element with a different id
    cladeString = cladeString ..  '</tr></table>'


return cladeString
end



-- this must be at end

return p