Difference between revisions of "Module:Clade"

From blackwiki
Jump to navigation Jump to search
blackwiki>Jts1882
(don't replace hyphens in labels with strip markers)
m (42 revisions imported)
 
(8 intermediate revisions by 2 users not shown)
Line 8: Line 8:
 
         now uses templatestyles
 
         now uses templatestyles
 
]]
 
]]
 
+
require('Module:No globals')
 
local p = {}
 
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  ===========================
Line 28: Line 34:
  
 
local cladeString = ""
 
local cladeString = ""
local maxChildren = 20 -- currently 17 in the clade/cladex templates
+
local maxChildren = 20 -- was 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 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 childCount = 0 -- number of leaves in the clade (can use to set bottom of bracket in addTaxon()
 
local totalCount = 0
 
local totalCount = 0
 +
 +
pargs = frame:getParent().args -- parent arguments
 
 
 
     infoOutput = p.getCladeTreeInfo() -- get info about clade structure, e.g. lastNode (last |N= child number)
 
     infoOutput = p.getCladeTreeInfo() -- get info about clade structure, e.g. lastNode (last |N= child number)
 
      
 
      
     --[[ add the templatestyles tag if terminal clade template
+
     --[[ add the templatestyles tag conditionally to reduce expansion size (currently diabled)
          add to every clade table >> 137 times in Passeriformes test page
+
        when added to every clade table, it increases post‐expand include size significantly
          cladeCount==1 >>  43 insertions
+
          e.g. the Neosuchia page (or test version) is increase by about 8% (672 bytes each)
        removing conitional addition for now as this reintroduces the extra line due to parser bug T18700
+
                if template styles added to all pages there are 133 stripmarkers
 +
                with cladeCount==1 condition, this is reduced to 34
 +
        however cladeCount==1 condition interfers with fix for additional line due to parser bug T18700
 +
        killing the strip markers also removes backlinks to references using citation templates
 
     --]]
 
     --]]
 +
    --cladeString =mw.text.killMarkers( cladeString ) -- also kills off strip markers using citation templates
 +
    --cladeString = mw.text.unstrip(cladeString)
 
     --if cladeCount==1  then  
 
     --if cladeCount==1  then  
 
     local src = "Template:Clade/styles.css"
 
     local src = "Template:Clade/styles.css"
 
     cladeString = cladeString .. p.templateStyle( frame, src ) .. '\n'
 
     cladeString = cladeString .. p.templateStyle( frame, src ) .. '\n'
 
     --end
 
     --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 ~= "" then
 
if tableStyle ~= "" then
 
tableStyle = ' style="' .. tableStyle .. '"' -- include style= in string to suppress empty style elements
 
tableStyle = ' style="' .. tableStyle .. '"' -- include style= in string to suppress empty style elements
 
end
 
end
 
      
 
      
     reverseClade =frame.args.reverse or false -- a global
+
     reverseClade =frame.args.reverse or pargs.reverse or false -- a global
 
     --ENFORCE GLOBAL FOR DEVELOPMENT
 
     --ENFORCE GLOBAL FOR DEVELOPMENT
 
     --reverseClade = true
 
     --reverseClade = true
  
local captionName =mw.getCurrentFrame():getParent().args['caption'] or ""
+
local captionName = pargs['caption'] or ""
local captionStyle = mw.getCurrentFrame():getParent().args['captionstyle'] or ""
+
local captionStyle = pargs['captionstyle'] or ""
  
 
     -- add an element to mimick nowiki WORKS BUT DISABLE FOR DEMO PURPOSES
 
     -- add an element to mimick nowiki WORKS BUT DISABLE FOR DEMO PURPOSES
Line 77: Line 84:
 
end
 
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
 
local moreNeeded = true
Line 98: Line 100:
  
 
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 = pargs[tostring(childNumber)] or ""  -- get data from |N=
local nodeLabel = mw.getCurrentFrame():getParent().args['label'..tostring(childNumber)] or ""  -- get data from |labelN=
+
local nodeLabel = pargs['label'..tostring(childNumber)] or ""  -- get data from |labelN=
 
 
 
 
local newickString = mw.getCurrentFrame():getParent().args['newick'..tostring(childNumber)] or ""  -- get data from |labelN=
+
local newickString = pargs['newick'..tostring(childNumber)] or ""  -- get data from |labelN=
if newickString ~= "" then -- if using a newick string instead of a clade structure
+
local listString  = pargs['list'..tostring(childNumber)] or ""
 +
 +
if listString ~= "" then
 +
cladeString = cladeString .. '\n' .. p.addTaxon(childNumber, p.list(0, listString), nodeLabel, lastNode)
 +
elseif newickString ~= "" then -- if using a newick string instead of a clade structure
 
newickString = p.processNewickString(newickString,childNumber)
 
newickString = p.processNewickString(newickString,childNumber)
 
if nodeLabel == "" then -- use labelN by default, otherwise use root name from Newick string
 
if nodeLabel == "" then -- use labelN by default, otherwise use root name from Newick string
Line 119: Line 125:
 
end
 
end
  
local footerText  = mw.getCurrentFrame():getParent().args['footer'] or ""
+
local footerText  = pargs['footer'] or ""
local footerStyle = mw.getCurrentFrame():getParent().args['footerstyle'] or ""
+
local footerStyle = pargs['footerstyle'] or ""
  
 
if footerText ~= "" then
 
if footerText ~= "" then
Line 145: Line 151:
 
function p.addSubTrees(cladeString)
 
function p.addSubTrees(cladeString)
 
 
local pargs = mw.getCurrentFrame():getParent().args
+
--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",  
 
local suffix = { [1]="A", [2]="B", [3]="C", [4]="D", [5]="E", [6]="F", [7]="G", [8]="H", [9]="I", [10]="J",  
Line 161: Line 167:
 
return cladeString
 
return cladeString
 
end
 
end
--[[ function to add child elements
+
--[[ -------------------------------------- p.addTaxon() ------------------------------------------
 +
    function to add child elements
 
     adds wikitext for two rows of the table for each child node,  
 
     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 first cell in each is used for the label and sublabel; the bottom border forms the horizonal branch of the bracket
Line 172: Line 179:
 
function p.addTaxon(childNumber, nodeLeaf, nodeLabel, lastNode)
 
function p.addTaxon(childNumber, nodeLeaf, nodeLabel, lastNode)
  
 +
--[[ get border formating parameters (i.e. color, thickness, state)
 +
    nodeParameters for whole bracket (unnumbered, i.e. color, thickness, state) apply to whole node bracket,
 +
branchParameters apply to individual branches
 +
    the branch parameters have a number, e.g. |colorN, |thicknessN, |stateN
 +
    the node parameters have no number, e.g. |color, |thickness, |state
 +
]]
 +
    local nodeColor = pargs['color'] or ""                -- don't set default to allow green on black gadget
 +
local nodeThickness = tonumber(pargs['thickness']) or 1
 +
local nodeState = pargs['state'] or "solid"
 
-- get border formating parameters for branch (default to global nodeParameters)
 
-- get border formating parameters for branch (default to global nodeParameters)
--    - the branch parameters have a number, e.g. |colorN, |thicknessN, |stateN
+
local branchColor = pargs['color'..tostring(childNumber)] or nodeColor
--    - the node parameters have no number, e.g. |color, |thickness, |state
+
local branchThickness = tonumber(pargs['thickness'..tostring(childNumber)]) or nodeThickness
local branchThickness = tonumber(mw.getCurrentFrame():getParent().args['thickness'..tostring(childNumber)]) or nodeThickness
+
local branchState = pargs['state'..tostring(childNumber)] or nodeState
local branchColor = mw.getCurrentFrame():getParent().args['color'..tostring(childNumber)] or nodeColor
+
if branchState == 'double' then
local branchStyle = mw.getCurrentFrame():getParent().args['style'..tostring(childNumber)] or ""
+
if branchThickness < 2 then branchThickness = 3 end -- need thick line for double
local branchLabelStyle = mw.getCurrentFrame():getParent().args['branch'..tostring(childNumber)] or ""
+
end 
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
+
local branchStyle = pargs['style'..tostring(childNumber)] or ""
 +
local branchLength = pargs['length'] or pargs['length'..tostring(childNumber)] or ""
  
 
     -- the left border takes node parameters, the bottom border takes branch parameters
 
     -- the left border takes node parameters, the bottom border takes branch parameters
Line 199: Line 216:
 
-- variables for right hand bar or bracket
 
-- variables for right hand bar or bracket
 
--local barColor  = ""  
 
--local barColor  = ""  
local barRight  = mw.getCurrentFrame():getParent().args['bar'..tostring(childNumber)] or "0"
+
local barRight  = pargs['bar'..tostring(childNumber)] or "0"
local barBottom = mw.getCurrentFrame():getParent().args['barend'..tostring(childNumber)] or "0"
+
local barBottom = pargs['barend'..tostring(childNumber)] or "0"
local barTop    = mw.getCurrentFrame():getParent().args['barbegin'..tostring(childNumber)] or "0"
+
local barTop    = pargs['barbegin'..tostring(childNumber)] or "0"
local barLabel  = mw.getCurrentFrame():getParent().args['barlabel'..tostring(childNumber)] or ""
+
local barLabel  = pargs['barlabel'..tostring(childNumber)] or ""
local groupLabel = mw.getCurrentFrame():getParent().args['grouplabel'..tostring(childNumber)] or ""
+
local groupLabel     = pargs['grouplabel'..tostring(childNumber)] or ""
local groupLabelStyle = mw.getCurrentFrame():getParent().args['labelstyle'..tostring(childNumber)] or ""
+
local groupLabelStyle = pargs['grouplabelstyle'..tostring(childNumber)] or ""
 +
local labelStyle      = pargs['labelstyle'..tostring(childNumber)] or ""
 +
local sublabelStyle  = pargs['sublabelstyle'..tostring(childNumber)] or ""
  
 
--replace colours with format string; need right bar for all three options
 
--replace colours with format string; need right bar for all three options
Line 220: Line 239:
 
     local classString = ''
 
     local classString = ''
 
     local reverseClass = ''
 
     local reverseClass = ''
 +
    local widthClass = ''
 
      
 
      
 
     -- class to add if using reverse (rtl) cladogram;  
 
     -- class to add if using reverse (rtl) cladogram;  
Line 241: Line 261:
 
end
 
end
 
end
 
end
 
+
   if useInlineStyle or (branchStyle ~= '') or (branchLabelStyle ~= '') then
+
   if useInlineStyle or branchStyle ~= '' or branchLength ~= "" or labelStyle ~= "" then
   styleString = 'style="' .. borderStyle .. branchStyle .. branchLabelStyle .. '"'
+
  local branchLengthStyle = ""
 +
  if branchLength ~= "" then
 +
  if childNumber == 1 then
 +
  branchLengthStyle = 'width:' .. branchLength .. ';' -- add width to first element
 +
  end
 +
  --if childNumber > 1 then prefix = 'max-' end
 +
  branchLengthStyle = branchLengthStyle --= prefix  .. 'width:' .. branchLength .. ';'
 +
  .. 'max-width:' .. branchLength ..';'
 +
                              .. 'padding:0em;'        -- remove padding to make calculation easier
 +
  -- following moved to styles.css
 +
  -- .. 'white-space:nowrap'
 +
  -- .. 'overflow:hidden;'    -- clip labels longer than the max-width
 +
  -- .. 'text-overflow:clip;' -- ellipsis;'
 +
      widthClass = " clade-fixed-width"
 +
  end
 +
   styleString = 'style="' .. borderStyle .. branchLengthStyle .. branchStyle .. labelStyle .. '"'
 
     end
 
     end
  
 
if childNumber == 1 then
 
if childNumber == 1 then
classString= 'class="clade-label first" '   -- add class first for top row
+
classString= 'class="clade-label first'.. widthClass .. '" '                 -- add class "first" for top row
 
     else
 
     else
     classString = 'class="clade-label' .. reverseClass .. '" ' -- add "reverse" class if ltr cladogram
+
     classString = 'class="clade-label' .. reverseClass .. widthClass .. '" ' -- add "reverse" class if ltr cladogram
 
     end
 
     end
  
Line 273: Line 308:
 
         end
 
         end
 
     end
 
     end
 +
 
 +
    classString = 'class="clade-leaf' .. reverseClass .. '"'
 +
 +
    --[[note: the \n causes plain leaf elements get wrapped in <p> with style="margin:0.4em 0 0.5em 0;"
 +
          this adds spacing to rows, but is set by defaults rather than the clade template
 +
          it also means there are two newlines when it is a clade structure (which might explain some past issues)
 +
    ]]
 +
    local content = '\n' .. nodeLeaf  -- the newline is not necessary, but keep for backward compatibility
 +
 +
    -- test using image parameter
 +
    local image = pargs['image'..tostring(childNumber)] or "" 
 +
 +
    if image ~= "" then                                 
 +
    --content = content .. image -- basic version
 +
      content = '\n{| class="clade" style="width:auto;"'  --defaults to width:100% because of class "clade"
 +
          .. '\n| class="clade-leaf" ' .. '|\n' .. nodeLeaf
 +
          .. '\n| class="clade-leaf" ' .. '|\n' .. image
 +
          .. '\n|}'
 +
      -- note: the classes interfere with the node counter, so try simpler version with style
 +
        content = '\n{| style="width:100%;border-spacing:0;" '  --width:auto is "tight"; 100% needed for image alignment
 +
          .. '\n| style="border:0;padding:0;" ' .. '|\n' .. nodeLeaf
 +
          .. '\n| style="border:0;padding:0;" ' .. '|\n' .. image
 +
          .. '\n|}'
 +
    end
 +
  
 
     -- wikitext for leaf cell (note: nodeLeaf needs to be on newline if new wikitable)
 
     -- wikitext for leaf cell (note: nodeLeaf needs to be on newline if new wikitable)
 +
    --                        but that is no longer the case (newline is now forced)
 +
    --                        the newline wraps plain leaf terminals in <P> with vertical padding (see above)
  
     classString = 'class="clade-leaf' .. reverseClass .. '"'
+
     --local leafCellString = '\n|rowspan=2 ' .. classString  .. styleString .. ' |\n' .. content -- the new line causes <p> wrapping for plain leaf terminals
 
+
     local leafCellString = '\n|rowspan=2 ' .. classString  .. styleString .. ' |' .. content
     local leafCellString = '\n|rowspan=2 ' .. classString  .. styleString .. ' |\n' .. nodeLeaf
 
 
    
 
    
 
     --cladeString = cladeString .. leafCellString
 
     --cladeString = cladeString .. leafCellString
Line 313: Line 374:
 
-- (6) add cell containing sublabel
 
-- (6) add cell containing sublabel
 
 
local subLabel = mw.getCurrentFrame():getParent().args['sublabel'..tostring(childNumber)] or ""  -- request in addLabel
+
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)
 
-- FOR TESTING: use subLabel for annotating the clade structues to use structure information (DEBUGGIING ONLY)
Line 332: Line 393:
 
    end  
 
    end  
 
     end
 
     end
     if borderStyle ~= '' or  branchStyle ~= '' then         
+
     if borderStyle ~= '' or  branchStyle ~= '' or  branchLength ~= '' or sublabelStyle ~= "" then       
      styleString = ' style="' .. borderStyle .. branchStyle .. '"'
+
  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
 
     end
  
Line 339: Line 411:
  
 
     if childNumber == lastNode then  
 
     if childNumber == lastNode then  
     classString = 'class="clade-slabel last" '  
+
     classString = 'class="clade-slabel last' .. widthClass .. '" '  
 
     else
 
     else
         classString = 'class="clade-slabel' .. reverseClass .. '" '
+
         classString = 'class="clade-slabel' .. reverseClass .. widthClass .. '" '
 
     end
 
     end
 
     local sublabelCellString = '\n|' .. classString .. styleString .. '|' ..  p.addLabel(childNumber,subLabel)
 
     local sublabelCellString = '\n|' .. classString .. styleString .. '|' ..  p.addLabel(childNumber,subLabel)
Line 367: Line 439:
 
end
 
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
 
    cladeString = cladeString .. '&nbsp;&nbsp;'            -- add spaces between leaf text and bar
 
if barLabel ~= "" then
 
cladeString = cladeString .. '\n|rowspan=2 style="vertical-align:middle;" |' .. barLabel
 
end
 
end
 
    --]]
 
 
 
-- (3) add cell with leaf (which may be a table with transluded clade 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
 
    --(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)
 
 
   
 
 
 
-- (5) add second row (only one cell needed for sublabel because of rowspan=2)
 
 
    local branchStyleString = 'style="' .. branchStyle .. '"  '
 
    if branchStyle == '' then branchStyleString = ''  end -- avoid empty style elements
 
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)
 
--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
 
end
 
  
  
Line 511: Line 462:
 
if string.find(nodeLabel, "span ") ~= nil  then  stylingElementDetected = true end
 
if string.find(nodeLabel, "span ") ~= nil  then  stylingElementDetected = true end
 
if string.find(nodeLabel, " style") ~= nil then stylingElementDetected = true end  
 
if string.find(nodeLabel, " style") ~= nil then stylingElementDetected = true end  
 +
--TODO test following alternative
 +
--if nodeLabel:find( "%b<>") then stylingElementDetected = true end
 
 
 
if stylingElementDetected == true then  
 
if stylingElementDetected == true then  
return '<span class="nowrap">' .. nodeLabel .. '</span>'
+
return '<div style="display:inline;" class="nowrap">' .. nodeLabel .. '</div>'
 
     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
    if not nowrapString:find("UNIQ.-QINU") then                -- unless a strip marker
+
    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;)
 
    nowrapString = string.gsub(nowrapString,"-", "&#8209;") -- replace hyphen with non-breaking hyphen (&#8209;)
 
             end
 
             end
Line 555: Line 508:
  
 
     -- this needs a lastNode variable
 
     -- this needs a lastNode variable
local s = strsplit(innerTerm2, ",")
+
local s = p.strsplit(innerTerm2, ",")
 
--oldLastNode=lastNode
 
--oldLastNode=lastNode
 
local lastNode=table.getn(s) -- number of child branches
 
local lastNode=table.getn(s) -- number of child branches
 
local i=1
 
local i=1
 
while s[i] do
 
while s[i] do
restoredString = string.gsub(s[i],"XXX", ",")  -- convert back to commas
+
local restoredString = string.gsub(s[i],"XXX", ",")  -- convert back to commas
 
--restoredString = s[i]
 
--restoredString = s[i]
 
local outerTerm = string.gsub(restoredString, "%b()", "")
 
local outerTerm = string.gsub(restoredString, "%b()", "")
Line 581: Line 534:
 
end
 
end
 
-- emulate a standard split string function
 
-- emulate a standard split string function
function strsplit(inputstr, sep)  
+
-- why not use mw.text.split(s, sep)?
 +
function p.strsplit(inputstr, sep)  
 
         if sep == nil then
 
         if sep == nil then
 
                 sep = "%s"
 
                 sep = "%s"
 
         end
 
         end
         local t={} ; i=1
+
         local t={}  
 +
        local i=1
 
         for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
 
         for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
 
                 t[i] = str
 
                 t[i] = str
Line 602: Line 557:
 
function p.newickConverter(frame)
 
function p.newickConverter(frame)
 
 
local newickString = frame.args['newickstring'] or mw.getCurrentFrame():getParent().args['newick']
+
local newickString = frame.args['newickstring'] or pargs['newick']
 
 
 
--if newickString == '{{{newickstring}}}' then return newickString  end
 
--if newickString == '{{{newickstring}}}' then return newickString  end
Line 620: Line 575:
  
 
local resultString = ''
 
local resultString = ''
     local option = mw.getCurrentFrame():getParent().args['option'] or ''
+
     local option = pargs['option'] or ''
 
     if option == 'tree' then
 
     if option == 'tree' then
 
--show the transcluded clade diagram
 
--show the transcluded clade diagram
Line 666: Line 621:
 
                                            end)
 
                                            end)
 
 
local s = strsplit(innerTerm2, ",")
+
local s = p.strsplit(innerTerm2, ",")
 
local i=1
 
local i=1
 
while s[i] do
 
while s[i] do
restoredString = string.gsub(s[i],"XXX", ",")  -- convert back to commas
+
local restoredString = string.gsub(s[i],"XXX", ",")  -- convert back to commas
 
 
 
local outerTerm = string.gsub(restoredString, "%b()", "")
 
local outerTerm = string.gsub(restoredString, "%b()", "")
Line 688: Line 643:
 
function p.getIndent(levelNumber)
 
function p.getIndent(levelNumber)
 
local indent = "\r"
 
local indent = "\r"
local extraIndent = mw.getCurrentFrame():getParent().args['indent'] or 0
+
local extraIndent = pargs['indent'] or mw.getCurrentFrame().args['indent'] or 0
 
 
 
while tonumber(extraIndent) > 0 do
 
while tonumber(extraIndent) > 0 do
Line 710: Line 665:
 
local maxPatterns = 5
 
local maxPatterns = 5
 
local i = 0
 
local i = 0
local pargs = mw.getCurrentFrame():getParent().args
+
local pargs = pargs
 
local pattern = pargs['newick'..tostring(childNumber)..'-pattern'] -- unnumbered option for i=1
 
local pattern = pargs['newick'..tostring(childNumber)..'-pattern'] -- unnumbered option for i=1
 
     local replace = pargs['newick'..tostring(childNumber)..'-replace']
 
     local replace = pargs['newick'..tostring(childNumber)..'-replace']
Line 737: Line 692:
 
-------------------------------------------------------------------------------------------
 
-------------------------------------------------------------------------------------------
  
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)
 
   
 
    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
 
------------------------------------------------------------------------------------------
 
  
 
--[[function getCladeTreeInfo()
 
--[[function getCladeTreeInfo()
Line 851: Line 713:
 
while childNumber < maxChildren do -- preprocessing loop
 
while childNumber < maxChildren do -- preprocessing loop
 
childNumber = childNumber + 1 -- so we start with 1
 
childNumber = childNumber + 1 -- so we start with 1
local nodeLeaf,data = mw.getCurrentFrame():getParent().args[tostring(childNumber)] or ""  -- get data from |N=
+
local nodeLeaf,data = pargs[tostring(childNumber)] or ""  -- get data from |N=
         local newickString = mw.getCurrentFrame():getParent().args['newick'..tostring(childNumber)] or ""  -- get data from |labelN=
+
         local newickString = pargs['newick'..tostring(childNumber)] or ""  -- get data from |labelN=
if newickString ~= "" or nodeLeaf ~= "" then
+
        local listString = pargs['list'..tostring(childNumber)] or ""  -- get data from |labelN=
 +
if newickString ~= "" or nodeLeaf ~= "" or listString ~= "" then
 
--if nodeLeaf ~= "" then  
 
--if nodeLeaf ~= "" then  
 
childCount = childCount + 1  -- this counts child elements in this clade  
 
childCount = childCount + 1  -- this counts child elements in this clade  
Line 913: Line 776:
 
--return frame:preprocess(string.sub(code,3))
 
--return frame:preprocess(string.sub(code,3))
 
end
 
end
function p.testTable(frame)
+
 
 +
 
 +
function p.firstToUpper(str)
 +
    return (str:gsub("^%l", string.upper))
 +
end
 +
--[[ function to generate cladogram from a wikitext-like list
 +
        - uses @ instead of * because we don't want wikitext processed and nowiki elements are passed as stripmarkers (?)
 +
]]
 +
function p.list(count,listString)
 
 
-- build HTML table
+
local cladeString = ""
local galleryTable = mw.html.create('table')
+
--count = count+1
galleryTable:addClass('wikitable')
+
    local list = mw.text.split(listString, "\n")
galleryTable:addClass('sortable')
+
    local i=1
galleryTable:css('text-align', 'center')
+
    local child=1
 +
local lastNode=0--table.getn(list) -- number of child branches (potential)
 +
 
 +
cladeString = cladeString .. '{| class="clade" '
 
 
    galleryTable:tag('caption'):wikitext('Table created with mw.html')
+
while list[i]  do
   
+
list[i]=list[i]:gsub("^@", "")               -- strip the first @
    local row = galleryTable:tag('tr')
+
row:tag('th'):wikitext('Header 1')
+
if not string.match( list[i], "^@", 1 ) then -- count children at this level (not beginning wiht @)
row:tag('th'):wikitext('Header 2')
+
lastNode = lastNode+1
row:tag('th'):wikitext('Header 3')
+
end
--:attr('rowspan', 2)
+
i=i+1
 +
end
 
 
row = galleryTable:tag('tr')
+
i=1
row:tag('td'):css('text-align', 'left'):wikitext('A1')
+
while list[i]  do
row:tag('td'):css('text-align', 'left'):wikitext('B1')
+
 
row:tag('td'):css('text-align', 'left'):wikitext('C1')
+
    --[[ 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 .. '\n' .. p.addTaxon(child, recombined, label, lastNode)  
 +
    cladeString = cladeString .. '\n' .. p.addTaxon(child, p.list(count,recombined), label, lastNode)  
 +
    else
 +
    cladeString = cladeString .. '\n' .. p.addTaxon(child, list[i], "", lastNode)
 +
    end
 +
i=i+1
 +
child=child+1
 +
end
 +
 
  
local row2 = mw.html.create('tr')                        -- create an independent row element
+
cladeString = cladeString .. '\n|}'
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
+
mw.addWarning("WARNING. This is a test feature only.")
+
return cladeString 
return tostring(galleryTable)
 
 
end
 
end
 +
-- =================== experimental Newick to clade parser function =============================
  
function p.example(frame)
+
--[[Function of convert Newick strings to clade format
 +
 
 +
Usage: {{#invoke:Module:Sandbox/Jts1882/CladeN|newickConverter|newickstring={{{NEWICK_STRING}}} }}
 +
]]
 +
function p.cladeConverter(frame)
 
 
local params = mw.getCurrentFrame():getParent().args
+
end
-- build HTML table
+
function p.listConverter(frame)
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 listString = frame.args['list'] or pargs['list']
--local headers = {'Description','Code','Output','Comment'}
+
 
    -- create header row and add requested headers
+
-- show the list string
    local row = exampleTable:tag('tr')
+
local cladeString = ''
    local headerText
+
local levelNumber = 1          -- for depth of iteration
    for k,v in pairs(columns) do
+
local childNumber = 1          --  number of sister elements on node  (always one for root)
if params[v] then
+
local indent = p.getIndent(levelNumber)
if params[v]~='' then headerText=params[v]  else headerText = firstToUpper(v) end
+
--  converted the newick string to the clade structure
row:tag('th'):wikitext(headerText)
+
cladeString = cladeString .. indent .. '{{clade'
    end
+
cladeString = cladeString .. p.listParseLevel(listString, levelNumber, childNumber)  
    end
+
--cladeString = cladeString .. '\r}}'
  
-- now deal with the data rows
+
local resultString = ''
local i=0
+
    local option = pargs['option'] or ''
while i<10 do
+
    if option == 'tree' then
i=i+1
+
--show the transcluded clade diagram
local moreRows = false
+
resultString =   cladeString   
for k,v in pairs(columns) do
+
    else
if params[v..i] then moreRows = true end
+
    -- show the Newick string
end
+
resultString = '<pre>'..listString..'</pre>'
if not moreRows then break end
+
    -- show the converted clade structure
+
    resultString = resultString .. '<pre>'.. cladeString ..'</pre>'
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
 
     end
 +
    --resultString = frame:expandTemplate{ title = 'clade',  frame:preprocess(cladeString) }
  
return tostring(exampleTable)
+
     return resultString
end
 
function firstToUpper(str)
 
     return (str:gsub("^%l", string.upper))
 
 
end
 
end
  
-- display a gallery of cladograms using floating div elements
+
function p.listParseLevel(listString,levelNumber,childNumber)
  
function p.gallery(frame)
+
local cladeString = ""
+
local indent = p.getIndent(levelNumber)
local params = mw.getCurrentFrame():getParent().args
+
    levelNumber=levelNumber+1
  
     local outerBorders = false
+
     local list = mw.text.split(listString, "\n")
     if params['caption'] or params['footer'] then outerBorders = true end
+
     local i=1
 +
    local child=1
 +
    local lastNode=0
 
      
 
      
-- the gallery container
+
    while list[i]  do
local gallery = mw.html.create('div'):addClass('clade-gallery'):css('float',params['align'])
+
list[i]=list[i]:gsub("^@", "")               -- strip the first @
 
+
-- main caption for gallery if there is one
+
if not string.match( list[i], "^@", 1 ) then -- count children at this level (not beginning wiht @)
+
lastNode = lastNode+1 
if params['main-caption'] then
+
end
gallery:tag('div'):wikitext(params['main-caption']):addClass('main-caption')
+
i=i+1
 
end
 
end
 +
    i=1
  
-- a gallery of floating divs with content
+
while list[i]  do
  
    local shading = ''
+
    --[[ pseudocode:
--if params['shading'] then shading = 'shading' end -- class for shading
+
        if next value begins with @ we have a subtree,
if params['shading'] then shading = 'wikitable' end -- class for shading
+
        which must be recombined and past iteratively
+
        else we have a simple leaf
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 the next value begins with @, we have a subtree which should be recombined
    if not params['main-borders'] then noborder = 'no-border' end
+
    if list[i+1] and string.match( list[i+1], "^@", 1 then
   
+
   
    -- the gallery contents
+
        local label=list[i]
    local content = gallery:tag('div'):addClass('main-content'):addClass(noborder)
+
          i=i+1
                                      :css('height',params['height'])   
+
    local recombined = list[i]
  local i=0
+
    while list[i+1] and string.match( list[i+1], "^@", 1 ) do
while i<15 do
+
    recombined = recombined .. "\n" .. list[i+1]  
i=i+1  
+
    i=i+1
local caption = params['caption'..i] --or ''
+
    end
local header = params['header'..i] --or ''
+
    cladeString = cladeString .. indent .. '|label' .. child ..'=' .. label
local footer = params['footer'..i] --or ''
+
    cladeString = cladeString .. indent .. '|' .. child ..'=' ..  '{{clade'
+
                              .. p.listParseLevel(recombined,levelNumber,i)
-- 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
 
    else
      break
+
    cladeString = cladeString .. indent .. '|' .. child ..'=' ..  list[i]
 
    end
 
    end
 +
i=i+1
 +
child=child+1
 
end
 
end
+
 
-- main footer
+
 
if params['main-footer'] then
+
cladeString = cladeString .. indent .. '}}'  
gallery:tag('div'):wikitext(params['main-footer']):addClass('main-footer'):addClass(noborder)
+
return cladeString
end
 
-- TemplateStyles file
 
local src = "Template:Clade gallery/styles.css"
 
   
 
return p.templateStyle( frame, src ) ..  tostring(gallery)
 
 
end
 
end
 +
 +
  
 
-- this must be at end
 
-- this must be at end
 
return p
 
return p

Latest revision as of 15:37, 26 September 2020

Script error: No such module "Uses TemplateStyles".


This module is designed to be used with the clade template to draw phylogenetic trees or cladograms. The new template-module combination extends the feature available with the clade and cladex templates, while replicating the behaviour of the older templates written with the template language. This module is copied from the test version Module:Sandbox/Jts1882/CladeN, which was used with the template Template:CladeN to test the features during development.

Additionally, the module has the code for template {{clade newick converter}}, which is a basic utility to convert Newick strings into nested clade structures. This is not to be used in wikipedia articles, but is a tool to help construct cladograms for inclusion in articles.

The diagram below gives an overview of the features. See the template documentation for a more detailed description of how to use the templates.

Usage

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

Examples

Simple examples mimicking the clade template:

Example demonstrating more advanced features of cladeN:

Example demonstrating features available:

Template:Clade
Node structure
label1

leaf 1

label2
label A

leaf A

label B

leaf B

 leaf 2 is a nested clade structure
label3

leaf 3

sublabel3
|label1=Node structure  |sublabel1=(brackets)
|style1=background-color:#ffdddd;
|1={{Clade
   |label1=label1  
   |1=leaf 1             
   |label2=label2
   |grouplabel2= leaf 3 is a nested clade structure 
   |2={{Clade |style=background-color:#ffaaaa;
      |label1=label A
      |1=leaf A
      |label2=label B
      |2=leaf B
      |bar1=purple |bar2=purple 
      }}
   |label3=label3 |sublabel3=sublabel3
   |3=leaf 3           
   }}
(brackets)

 

Leaf styling
thicknessN

1 (default)

2

 line thickness

3

colorN

black (default)

red

blue

 #00ff00

stateN

solid (default)

dotted

dashed

 line styles

none

double

|label3=Leaf styling  |sublabel3=(branches)
|style3=background-color:#eeeeee;
|3={{Clade
   |label1=thicknessN
   |1={{Clade
      |1=1 (default) |thickness1=1 |barbegin1=blue 
      |2=2           |thickness2=2 |bar2=blue |barlabel2= line thickness
      |3=3           |thickness3=3 |barend3=blue 
      }}
   |label2=colorN
   |2={{Clade
      |1=black (default) |color1=black          
      |2=red             |color2=red    
      |3=blue            |color3=blue   
      |4= #00ff00   |color4=#00ff00   
      }}
   |label3=stateN
   |3={{Clade
      |1=solid (default) |state1=solid  |barbegin1=purple 
      |2=dotted          |state2=dotted |bar2=purple 
      |3=dashed          |state3=dashed |bar3=purple |barlabel3= line styles
      |4=none            |state4=none   |bar4=purple
      |5=double          |state5=double |barend5=purple 
      }}
   }}
(branches)
node styling
thickness

I

J

K

color

A

B

C

state

X

Y

Z

|label4=node styling  |sublabel4=(brackets)
|style4=background-color:#ffffee;
|4={{Clade
   |label1=thickness
   |1={{Clade |thickness=3
      |1=I  
      |2=J          
      |3=K             
      }}
   |label2=color
   |2={{Clade |color=red 
      |1=A
      |2=B                
      |3=C             
      }}
   |label3=state
   |3={{Clade |state=dashed
      |1=X 
      |2=Y         
      |3=Z        
   }} }}
(brackets)

 

newick
string

((lion,jaguar,leopard),((siberian,bengal)tiger,snow leopard))panthera

subtree
panthera

lion

jaguar

leopard

tiger

siberian

bengal

snow leopard

 expanded Newick string
|label6=newick  
|style6=background-color:#ddffdd;
|6={{Clade
   |label1=string
   |1=((lion,jaguar,leopard),((siberian,bengal)tiger,snow leopard))panthera         
   |label2=subtree 
   |grouplabel2= expanded Newick string    
   |grouplabelstyle2=vertical-align: middle;
   |2={{Clade |style=background-color:#ccffcc;
      |newick1=((lion,jaguar,leopard),((siberian,bengal)tiger,snow leopard))panthera
      |1=Leaf1 (redundant if newick1 set)        
      }}
   }}
paraphyly example 

Geraniales

Myrtales

clade

Crossosomatales

Picramniales

Sapindales

Huerteales

Brassicales

Malvales

 label a paraphyletic group within a clade
|label7=paraphyly example  |style7=background-color:#ffeeff;
|7={{Clade
   |1={{Clade
      |1=[[Geraniales]]          
      |2=[[Myrtales]]            
      }}
   |grouplabel2= label a paraphyletic group within a clade   
   |label2=clade
   |style2=background-color:#ffddff;
   |2={{Clade
      |1=[[Crossosomatales]]         |barbegin1=purple
      |2={{Clade
         |1=[[Picramniales]]         |bar1=purple
         |2={{Clade
            |1=[[Sapindales]]        |bar1=purple 
            |2={{Clade
               |1=[[Huerteales]]     |bar1=purple
               |2={{Clade
                  |1=[[Brassicales]] |barend1=purple
                  |2=[[Malvales]]    |style2=background-color:#ff99ff;
   }} }} }} }} }} }}

A cladogram illustrating clade features.


Subpages



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

The main function is called by the template using the {{invoke}} instruction; the three main functions are:
        p.main(frame) - opens and closes table, loops through the children of node, main is invoked once and controls the rest, calling ...
        p.addTaxon(childNumber, nodeLeaf) - the nuts and bolts; code dealing with each child node
        p.addLabel(childNumber) - adds the label text
        
        now uses templatestyles
]]
require('Module:No globals')
local p = {}
local pargs = {}  -- parent arguments
local lastNode=0
local nodeCount=0
local cladeCount=0
local leafCount=0
local templateStylesCount=0
local infoOutput
local reverseClade = false

--[[============================== main function  ===========================
-- main function, which will generate the table structure of the tree

Test version:
Usage: {{#invoke:Module:Sandbox/Jts1882/CladeN|main|style={{{STYLE|}}} }}
Template:CladeN

Release version:
Usage: {{#invoke:Clade|main|style={{{STYLE|}}} }}
Template:Clade
]]

function p.main(frame)

	local cladeString = ""	
	local maxChildren = 20 -- was 17 in the clade/cladex templates
	local childNumber = 0
	local childCount = 0 -- number of leaves in the clade (can use to set bottom of bracket in addTaxon()
	local totalCount = 0
	
	pargs = frame:getParent().args -- parent arguments
	
    infoOutput = p.getCladeTreeInfo() -- get info about clade structure, e.g. lastNode (last |N= child number)
    
    --[[ add the templatestyles tag  conditionally to reduce expansion size (currently diabled)
        when added to every clade table, it increases post‐expand include size significantly
           e.g. the Neosuchia page (or test version) is increase by about 8% (672 bytes each)
                if template styles added to all pages there are 133 stripmarkers 
                with cladeCount==1 condition, this is reduced to 34
        however cladeCount==1 condition interfers with fix for additional line due to parser bug T18700
        killing the strip markers also removes backlinks to references using citation templates 
    --]]
    --cladeString =mw.text.killMarkers( cladeString ) -- also kills off strip markers using citation templates
    --cladeString = mw.text.unstrip(cladeString)
    --if cladeCount==1  then 
    	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 = '<p class="mw-empty-elt"></p>\n'
    
	-- open table	
	-- (border-collapse causes problems (see talk) -- cladeString = cladeString .. '{| style="border-collapse:collapse;border-spacing:0;margin:0;' .. tableStyle .. '"'
    -- (before CSS styling) -- cladeString = cladeString .. '{| style="border-spacing:0;margin:0;' .. tableStyle .. '"'
    cladeString = cladeString .. '{|class="clade"' .. tableStyle

    -- add caption
	if captionName ~= "" then
		cladeString = cladeString .. '\n|+ style="' .. captionStyle .. '"|' .. captionName
	end
	
	
	local moreNeeded = true
	childNumber = 0
	--lastNode = 0

	--[[get child elements (add more rows for each child of node; each child is two rows)
	    the function addTaxon is called to add the rows for each child element;
	    each child add two rows: the first cell of each row contains the label or sublabel (below the line label), respectively;
	    the second cell spans both rows and contains the leaf name or a new clade structure
	    a third cell on the top row is sometimes added to contain a group  to the right
	]]
	
	-- main loop
	while 	childNumber < lastNode do -- use the last number determined in the preprocessing

		childNumber = childNumber + 1 -- so we start with 1
		local nodeLeaf = pargs[tostring(childNumber)] or ""  -- get data from |N=
		local nodeLabel = pargs['label'..tostring(childNumber)] or ""  -- get data from |labelN=
		
		
		local newickString = pargs['newick'..tostring(childNumber)] or ""  -- get data from |labelN=
		local listString   = pargs['list'..tostring(childNumber)] or "" 
		
		if listString ~= "" then
			cladeString = cladeString .. '\n' .. p.addTaxon(childNumber, p.list(0, listString), nodeLabel, lastNode)
		elseif newickString ~= "" then -- if using a newick string instead of a clade structure
			newickString = p.processNewickString(newickString,childNumber)
			if nodeLabel == "" then -- use labelN by default, otherwise use root name from Newick string
				nodeLabel = p.getNewickOuterterm(newickString) -- need to use terminal part of newick string for label
			end
			cladeString = cladeString .. '\n' .. p.addTaxon(childNumber, p.newick(0, newickString), nodeLabel, lastNode)
			--lastNode=lastNode+1 -- there is a counting problem with the newickstring
		elseif nodeLeaf ~= "" then -- if the node contains a leaf name or clade structue
			--if reverseClade2 then
			--	cladeString = cladeString .. '\n' .. p.addTaxonReverse(childNumber, nodeLeaf, nodeLabel, lastNode)
		    --else
				cladeString = cladeString .. '\n' .. p.addTaxon(childNumber, nodeLeaf, nodeLabel, lastNode)
			--end
		end
	end

	local footerText  = pargs['footer'] or ""
	local footerStyle = pargs['footerstyle'] or ""

	if footerText ~= "" then
	   cladeString = cladeString ..  '\n|-style="' .. footerStyle .. '"\n|colspan="2"|<p>' .. footerText .. '</p>||'
	   -- note the footer causes a problem with tr:last-child so need either
	   -- (1) use <tfoot> but it is not allowed or incompatable
	   --           cladeString = cladeString ..  '<tfoot><tr style="' .. footerStyle .. '"><td colspan="2"><p>' .. footerText .. '</p></td></tr></tfoot>'
	   -- (2) always add footer and use nth:last-child(2) but is this backwards compatible
	   -- (3) if footer= set the style inline for the last sublabel row (more a temp fix)
	   -- (4) set class for first and last element (DONE. Also works well with reverse class)
	end

	-- close table (wikitext to close table)
	cladeString = cladeString ..  '\n|}'
	
	cladeString = p.addSubTrees(cladeString) -- add subtrees
	
	return cladeString
	--return '<div style="width:auto;">\n' .. cladeString .. '</div>'
end

--[[ =============================function to add subtrees ========================================== ]]

function p.addSubTrees(cladeString)
	
	--local pargs = mw.getCurrentFrame():getParent().args 
	
	local suffix = { [1]="A", [2]="B", [3]="C", [4]="D", [5]="E", [6]="F", [7]="G", [8]="H", [9]="I", [10]="J", 
		             [11]="K", [12]="L", [13]="M", [14]="N", [15]="O", [16]="P", [17]="Q", [18]="R", [19]="S", [20]="T", 
		             [21]="U", [22]="V", [23]="W", [24]="X", [25]="Y", [26]="Z"}
	for i=1, 26, 1 do
		local subclade = pargs['subclade'..suffix[i]]
		local target = pargs['target'..suffix[i]] or "SUBCLADE_" .. suffix[i]
		if subclade  then 
			if string.find(cladeString, target) then
				cladeString = string.gsub(cladeString,target,subclade)
			end
		end
    end
	return cladeString
end
--[[ -------------------------------------- p.addTaxon() ------------------------------------------
     function to add child elements
     adds wikitext for two rows of the table for each child node, 
     	the first cell in each is used for the label and sublabel; the bottom border forms the horizonal branch of the bracket
     	the second cell is used for the leafname or a transcluded clade structure and spans both rows
     note that the first and last child nodes need to be handled differently from the middle elements
	     the middle elements (|2, |3 ...) use a left border to create the vertical line of the bracket
	     the first child element doesn't use a left border for the first cell in the top row (as it is above the bracket)
	     the last child doesn't use a left border for the first cell in the second row (as it is below the bracket)
]]
function p.addTaxon(childNumber, nodeLeaf, nodeLabel, lastNode)

	--[[ get border formating parameters (i.e. color, thickness, state)
    		nodeParameters for whole bracket (unnumbered, i.e. color, thickness, state) apply to whole node bracket, 
			branchParameters apply to individual branches
	    	the branch parameters have a number, e.g. |colorN, |thicknessN, |stateN
	    	the node parameters have no number, e.g. |color, |thickness, |state
	]]
    local nodeColor = pargs['color'] or ""                 -- don't set default to allow green on black gadget
	local nodeThickness = tonumber(pargs['thickness']) or 1
	local nodeState = pargs['state'] or "solid"
	-- get border formating parameters for branch (default to global nodeParameters)
	local branchColor = pargs['color'..tostring(childNumber)] or nodeColor 
	local branchThickness = tonumber(pargs['thickness'..tostring(childNumber)]) or nodeThickness
	local branchState = pargs['state'..tostring(childNumber)] or nodeState 
	if branchState == 'double' then 
		if branchThickness < 2 then branchThickness = 3 end -- need thick line for double
	end  
	
	local branchStyle = pargs['style'..tostring(childNumber)] or ""
	local branchLength = pargs['length'] or pargs['length'..tostring(childNumber)] or ""

    -- the left border takes node parameters, the bottom border takes branch parameters
    -- this has coding on the colours for green on black
   local bottomBorder =  tostring(branchThickness) ..'px ' .. branchState  .. (branchColor~="" and ' ' .. branchColor or '')
   local leftBorder   =  tostring(nodeThickness)   ..'px ' .. nodeState  .. (nodeColor~="" and ' ' .. nodeColor or '')
	
	--The default border styles are in the CSS (styles.css)
	--    the inline styling is applied when thickness, color or state are change
	
	local useInlineStyle = false
	-- use inline styling non-default color, line thickness or state have been set
	if branchColor ~= "" or branchThickness ~=  1 or branchState ~= "solid"  then
		useInlineStyle = true
	end
	
	
	-- variables for right hand bar or bracket
	--local barColor  = "" 
	local barRight  = pargs['bar'..tostring(childNumber)] or "0"
	local barBottom = pargs['barend'..tostring(childNumber)] or "0"
	local barTop    = pargs['barbegin'..tostring(childNumber)] or "0"
	local barLabel  = pargs['barlabel'..tostring(childNumber)] or ""
	local groupLabel      = pargs['grouplabel'..tostring(childNumber)] or ""
	local groupLabelStyle = pargs['grouplabelstyle'..tostring(childNumber)] or ""
	local labelStyle      = pargs['labelstyle'..tostring(childNumber)] or ""
	local sublabelStyle   = pargs['sublabelstyle'..tostring(childNumber)] or ""

	--replace colours with format string; need right bar for all three options
	if barRight  ~= "0" then barRight  = "2px solid " .. barRight  end 
	if barTop    ~= "0" then barRight  = "2px solid " .. barTop    end
	if barBottom ~= "0" then barRight  = "2px solid " .. barBottom end 
	if barTop    ~= "0" then barTop    = "2px solid " .. barTop    end
	if barBottom ~= "0" then barBottom = "2px solid " .. barBottom end 
	
	
	-- now construct wikitext 
	local cladeString = ''
	local styleString = ''
    local borderStyle = '' -- will be used if border color, thickness or state is to be changed
    local classString = ''
    local reverseClass = ''
    local widthClass = ''
    
    -- class to add if using reverse (rtl) cladogram; 
    if reverseClade then reverseClass = ' reverse' end 
    
    -- (1) wikitext for new row
    --cladeString = cladeString .. '\n|-'

	-- (2) now add cell with label
    
	if useInlineStyle then
		if childNumber == 1 then
	        borderStyle = 'border-left:none;border-right:none;border-bottom:' .. bottomBorder .. ';' 
	        --borderStyle = 'border-bottom:' .. bottomBorder .. ';' 
	 	else -- for 2-17
	 		if reverseClade then
	    		borderStyle  = 'border-left:none;border-right:' .. leftBorder .. ';border-bottom:' .. bottomBorder .. ';' 
	 	    else
	    		borderStyle  = 'border-left:' .. leftBorder .. ';border-bottom:' .. bottomBorder .. ';' 
	    	end
		end
	end
	
   	if useInlineStyle or branchStyle ~= '' or branchLength ~= "" or labelStyle ~= "" then
   		local branchLengthStyle = ""
   		if branchLength ~= "" then 
   			if childNumber == 1 then
   				branchLengthStyle = 'width:' .. branchLength .. ';'  -- add width to first element
   			end
   			--if childNumber > 1 then prefix = 'max-' end
   			branchLengthStyle = branchLengthStyle --= prefix  .. 'width:' .. branchLength .. ';' 
   										.. 'max-width:' .. branchLength ..';'
   			                            .. 'padding:0em;'         -- remove padding to make calculation easier
   						-- following moved to styles.css
   						--				.. 'white-space:nowrap'
   						--				.. 'overflow:hidden;'    -- clip labels longer than the max-width
   						--				.. 'text-overflow:clip;' -- ellipsis;'
   		    widthClass = " clade-fixed-width"
   		end
   		styleString = 'style="' .. borderStyle .. branchLengthStyle .. branchStyle .. labelStyle .. '"'
    end	

	if childNumber == 1 then
		classString= 'class="clade-label first'.. widthClass .. '" '                  -- add class "first" for top row
    else
    	classString = 'class="clade-label' .. reverseClass .. widthClass .. '" ' -- add "reverse" class if ltr cladogram
    end

    --  wikitext for cell with label
    local labelCellString = '\n|' .. classString .. styleString  .. '|' .. p.addLabel(childNumber,nodeLabel) -- p.addLabel(nodeLabel)
    
    --cladeString = cladeString .. labelCellString

	---------------------------------------------------------------------------------
	-- (3) add cell with leaf (which may be a table with transluded clade content)
    
    if barRight  ~= "0"  then 
    	if reverseClade then -- we want the bar on the left
    		styleString = ' style="border-left:' .. barRight .. ';border-bottom:' .. barBottom .. ';border-top:' .. barTop .. ';' .. branchStyle .. '"'
    	else
    		styleString = ' style="border-right:' .. barRight .. ';border-bottom:' .. barBottom .. ';border-top:' .. barTop .. ';' .. branchStyle .. '"'
    	end
    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 <p> with style="margin:0.4em 0 0.5em 0;" 
    	       this adds spacing to rows, but is set by defaults rather than the clade template
    	       it also means there are two newlines when it is a clade structure (which might explain some past issues)
    ]]
    local content = '\n' .. nodeLeaf  -- the newline is not necessary, but keep for backward compatibility

    -- test using image parameter
    local image = pargs['image'..tostring(childNumber)] or ""  

    if image ~= "" then                                   
    	--content = content .. image -- basic version
      	content = '\n{| class="clade" style="width:auto;"'  --defaults to width:100% because of class "clade"
    	       .. '\n| class="clade-leaf" ' .. '|\n' .. nodeLeaf 
    	       .. '\n| class="clade-leaf" ' .. '|\n' .. image 
    	       .. '\n|}'
      	-- note: the classes interfere with the node counter, so try simpler version with style
        content = '\n{| style="width:100%;border-spacing:0;" '  --width:auto is "tight"; 100% needed for image alignment
    	       .. '\n| style="border:0;padding:0;" ' .. '|\n' .. nodeLeaf 
    	       .. '\n| style="border:0;padding:0;" ' .. '|\n' .. image 
    	       .. '\n|}'
    end


    -- wikitext for leaf cell (note: nodeLeaf needs to be on newline if new wikitable)
    --                         but that is no longer the case (newline is now forced)
    --                         the newline wraps plain leaf terminals in <P> with vertical padding (see above)

    --local leafCellString = '\n|rowspan=2 ' .. classString  .. styleString .. ' |\n' .. content -- the new line causes <p> wrapping for plain leaf terminals
    local leafCellString = '\n|rowspan=2 ' .. classString  .. styleString .. ' |' .. content
   
    --cladeString = cladeString .. leafCellString

    
    -------------------------------------------
    -- (4) stuff for right-hand bracket labels

  	classString='class="clade-bar' .. reverseClass .. '"'
    	
    local barLabelCellString = ''
	if barRight  ~= "0"  and barLabel ~= "" then 
	   barLabelCellString = '\n|rowspan=2 ' .. classString .. ' |' .. barLabel
	else -- uncomment following line to see the cell structure
	   --barLabelCellString = '\n|rowspan=2 ' .. classString .. ' |' .. 'BL'
	end 
	
	if groupLabel ~= "" then 		
		barLabelCellString = barLabelCellString .. '\n|rowspan=2 ' .. classString .. ' style="'.. groupLabelStyle .. '" |' .. groupLabel 
	else -- uncomment following line to see the cell structure
	    --barLabelCellString = barLabelCellString .. '\n|rowspan=2 ' .. classString .. '" |' .. 'GL' 
   end 	

   --cladeString = cladeString .. barLabelCellString
    
	-------------------------------------------------------------------------------------
	-- (5) add second row (only one cell needed for sublabel because of rowspan=2); 
	--     note: earlier versions applied branch style to row rather than cell
	--           for consistency, it is applied to the sublabel cell as with the label cell
	
	--cladeString = cladeString .. '\n|-' 
	
	-----------------------------------
	-- (6) add cell containing sublabel
	
	local subLabel = pargs['sublabel'..tostring(childNumber)] or ""  -- request in addLabel
	
	-- FOR TESTING: use subLabel for annotating the clade structues to use structure information (DEBUGGIING ONLY)
	--if childNumber==lastNode then subLabel= infoOutput end
	-- END TESTING
	
	borderStyle = ''
	styleString = ''
	if useInlineStyle then
		if childNumber==lastNode then 		-- if childNumber==lastNode we don't want left border, otherwise we do
			borderStyle = 'border-right:none;border-left:none;'
	    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 '<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
		-- 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 
		--TODO test following alternative
		--if nodeLabel:find( "%b<>") then stylingElementDetected = true end 
		
		if stylingElementDetected == true then 
			return '<div style="display:inline;" class="nowrap">' .. nodeLabel .. '</div>'
    	else	
     		local nowrapString = string.gsub(nodeLabel," ", "&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 .. '{| style="border-collapse:collapse;border-spacing:0;border:0;margin:0;'
	cladeString = cladeString .. '{| class="clade" '
	
	local j,k
	j,k = string.find(newickString, '%(.*%)')                 -- find location of outer parenthesised term
	local innerTerm = string.sub(newickString, j+1, k-1)      -- select content in parenthesis
	local outerTerm = string.gsub(newickString, "%b()", "")   -- delete parenthetic term
	if outerTerm == 'panthera' then outerTerm = "x" end     -- how is this set in local variable for inner nodes?
	
	outerTerm = tostring(count)
	
	-- need to remove commas in bracket terms before split, so temporarily replace commas between brackets
    local innerTerm2 =  string.gsub(innerTerm, "%b()",  function (n)
                                         	return string.gsub(n, ",%s*", "XXX")  -- also strip spaces after commas here
                                            end)
	--cladeString = cladeString .. '\n' .. p.addTaxon(1, innerTerm2, "")

    -- this needs a lastNode variable
	local s = p.strsplit(innerTerm2, ",")
	--oldLastNode=lastNode
	local lastNode=table.getn(s) -- number of child branches
	local i=1	
	while s[i] do	
		local restoredString = string.gsub(s[i],"XXX", ",")   -- convert back to commas
		--restoredString = s[i]
		local outerTerm = string.gsub(restoredString, "%b()", "")
		if string.find(restoredString, '%(.*%)') then
			--cladeString = cladeString .. '\n' .. p.addTaxon(i, restoredString, "x")
			cladeString = cladeString .. '\n' .. p.addTaxon(i, p.newick(count,restoredString), outerTerm, lastNode)
			-- p.addTaxon(2, p.newick(count,newickString2), "root")
		else
			cladeString = cladeString .. '\n' .. p.addTaxon(i, restoredString, "", lastNode) --count)
		end
		i=i+1
	end
   -- lastNode=oldLastNode
    
	-- close table
	--cladeString = cladeString ..  '\n' .. '| style="border: 0; padding: 0; vertical-align: top;" | <br/> \n|}'
	--cladeString = cladeString ..  '\n| <br/> \n|}' -- is this legacy for extra sublabel?
	cladeString = cladeString ..  '\n|}'
	return cladeString
end
-- emulate a standard split string function
-- why not use mw.text.split(s, sep)?
function p.strsplit(inputstr, sep) 
        if sep == nil then
                sep = "%s"
        end
        local t={} 
        local i=1
        for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
                t[i] = str
                i = i + 1
        end
        return t
end


-- =================== experimental Newick to clade parser function =============================

--[[Function of convert Newick strings to clade format

Usage: {{#invoke:Module:Sandbox/Jts1882/CladeN|newickConverter|newickstring={{{NEWICK_STRING}}} }}
]]
function p.newickConverter(frame)
	
	local newickString = frame.args['newickstring'] or pargs['newick']
	
	--if newickString == '{{{newickstring}}}' then return newickString  end

    newickString = p.processNewickString(newickString,"") -- "childNumber")
    
    
	-- show the Newick string
	local cladeString = ''
	local levelNumber = 1           --  for depth of iteration
	local childNumber = 1           --  number of sister elements on node  (always one for root)
	
	--  converted the newick string to the clade structure
	cladeString = cladeString .. '{{clade'
	cladeString = cladeString .. p.newickParseLevel(newickString, levelNumber, childNumber) 
	cladeString = cladeString .. '\r}}'  

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

    return resultString
end

--[[ Parse one level of Newick string
     This function receives a Newick string, which has two components
      1. the right hand term is a clade label: |labelN=labelname
      2. the left hand term in parenthesis has common delimited child nodes, each of which can be
           i.  a taxon name which just needs:  |N=leafname 
           ii. a Newick string which needs further processing through reiteration
]]
function p.newickParseLevel(newickString,levelNumber,childNumber)

    
	local cladeString = ""
	local indent = p.getIndent(levelNumber) 
	--levelNumber=levelNumber+1
	
	local j=0
	local k=0
	j,k = string.find(newickString, '%(.*%)')                 -- find location of outer parenthesised term
	local innerTerm = string.sub(newickString, j+1, k-1)      -- select content in parenthesis
	local outerTerm = string.gsub(newickString, "%b()", "")   -- delete parenthetic term

	cladeString = cladeString .. indent .. '|label'..childNumber..'='  .. outerTerm
	cladeString = cladeString .. indent .. '|' .. childNumber..'='  .. '{{clade'

	levelNumber=levelNumber+1
	indent = p.getIndent(levelNumber)
	
		-- protect commas in inner parentheses from split; temporarily replace commas between parentheses
	    local innerTerm2 =  string.gsub(innerTerm, "%b()",  function (n)
	                                         	return string.gsub(n, ",%s*", "XXX")  -- also strip spaces after commas here
	                                            end)
	
		local s = p.strsplit(innerTerm2, ",")
		local i=1	
		while s[i] do	
			local restoredString = string.gsub(s[i],"XXX", ",")   -- convert back to commas
	
			local outerTerm = string.gsub(restoredString, "%b()", "")
			if string.find(restoredString, '%(.*%)') then
				--cladeString = cladeString .. indent .. '|y' .. i .. '=' .. p.newickParseLevel(restoredString,levelNumber+1,i) 
				cladeString = cladeString  .. p.newickParseLevel(restoredString,levelNumber,i) 
			else
				cladeString = cladeString .. indent .. '|' .. i .. '=' .. restoredString --.. '(level=' .. levelNumber .. ')'
			end
			i=i+1
		end
--    end -- end splitting of strings

	cladeString = cladeString .. indent .. '}}'  
    return cladeString
end

function p.getIndent(levelNumber)
	local indent = "\r"
	local extraIndent = pargs['indent'] or mw.getCurrentFrame().args['indent'] or 0
	
	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 < maxPatterns do
		i=i+1
		pattern = pattern or pargs['newick'..tostring(childNumber)..'-pattern'..tostring(i)]
		replace = replace or pargs['newick'..tostring(childNumber)..'-replace'..tostring(i)] or ""
	
		if pattern then
			newickString = string.gsub (newickString, pattern, replace)
		end
        pattern = nil; replace = nil
	end
	newickString = string.gsub (newickString, "_", " ") -- replace underscore with space
	return newickString
end
------------------------------------------------------------------------------------------


function p.test2(target)
	local target ="User:Jts1882/sandbox/templates/Template:Passeroidea"
	local result = mw.getCurrentFrame():expandTemplate{ title = target, args = {['style'] = '' } }
	return result
end
-------------------------------------------------------------------------------------------

-----------------------------------------------------------------------------------------------

--[[function getCladeTreeInfo()
	    this preprocessing loop gets information about the whole structure (number of nodes, leaves etc)
		it makes a redundant calls to the templates through transclusion, but doen't affect the template depths; 
		it provides the global lastNode that is used to limit the main while loop
--]]
function p.getCladeTreeInfo()

    -- enable proprocessing loop
    local childNumber = 0
    local childCount =0
    local maxChildren =20
    
    --info veriables (these are global for now)
    nodeCount=0
    cladeCount=0
    leafCount=0
    
	while 	childNumber < maxChildren do -- preprocessing loop
		childNumber = childNumber + 1 -- so we start with 1
		local nodeLeaf,data = pargs[tostring(childNumber)] or ""  -- get data from |N=
        local newickString = pargs['newick'..tostring(childNumber)] or ""  -- get data from |labelN=
        local listString = pargs['list'..tostring(childNumber)] or ""  -- get data from |labelN=
		if newickString ~= "" or nodeLeaf ~= "" or listString ~= "" then
		--if nodeLeaf ~= "" then 
			childCount = childCount + 1  -- this counts child elements in this clade 
		    --[[]
		    for i in string.gmatch(nodeLeaf, "||rowspan") do -- count number of rows started (transclusion)
   				nodeCount = nodeCount + 1
     		end
		    for i in string.gmatch(nodeLeaf, '{|class="clade"') do -- count number of tables started (transclusion)
   				cladeCount = cladeCount + 1
     		end
     		]]
     		-- count occurences of clade structure using number of classes used and add to counters
            local _, nClades = string.gsub(nodeLeaf, 'class="clade"', "") 
            local _, nNodes = string.gsub(nodeLeaf, 'class="clade%-leaf"', "")
            cladeCount = cladeCount + nClades
            nodeCount = nodeCount  + nNodes
            
			lastNode = childNumber -- this gets the last node with a valid entry, even when missing numbers
		end
	end
--]]	
    -- nodes can be either terminal leaves or a clade structure (table)
    --    note: should change class clade-leaf to clade-node to reflect this
    nodeCount = nodeCount            -- number of nodes (class clade-leaf) passed down by transduction 
                    + childCount + 1 --  plus one for current clade and one for each of its child element
	cladeCount = cladeCount + 1       -- number of clade structure tables passed down by transduction (plus one for current clade)
	leafCount = nodeCount-cladeCount   -- number of terminal leaves (equals height of cladogram)
	
	-- output for testing: number of clades / total nodes / terminal nodes (=leaves)
	--                     (internal nodes)                   (cladogram height)
	infoOutput = '<small>[' .. cladeCount .. '/' .. nodeCount .. '/' .. leafCount .. ']</small>'
	
	return infoOutput 
	
end

--[[ code for placing TemplateStyles from the module
     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.firstToUpper(str)
    return (str:gsub("^%l", string.upper))
end
--[[ function to generate cladogram from a wikitext-like list
         - uses @ instead of * because we don't want wikitext processed and nowiki elements are passed as stripmarkers (?)
]]
function p.list(count,listString)
	
	local cladeString = ""
	--count = count+1
    local list = mw.text.split(listString, "\n")
    local i=1
    local child=1
	local lastNode=0--table.getn(list) -- number of child branches (potential)

	cladeString = cladeString .. '{| class="clade" '
	
	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 .. '\n' .. p.addTaxon(child, recombined, label, lastNode) 
	    	cladeString = cladeString .. '\n' .. p.addTaxon(child, p.list(count,recombined), label, lastNode) 
	    else
	    	cladeString = cladeString .. '\n' .. p.addTaxon(child, list[i], "", lastNode) 	
	    end
		i=i+1
		child=child+1
	end


	cladeString = cladeString .. '\n|}'
	
	mw.addWarning("WARNING. This is a test feature only.")
	return cladeString  
end
-- =================== experimental 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.cladeConverter(frame)
	
end
function p.listConverter(frame)
	
	local listString = frame.args['list'] or pargs['list']

	-- show the list string
	local cladeString = ''
	local levelNumber = 1           --  for depth of iteration
	local childNumber = 1           --  number of sister elements on node  (always one for root)
	local indent = p.getIndent(levelNumber)
	--  converted the newick string to the clade structure
	cladeString = cladeString .. indent .. '{{clade'
	cladeString = cladeString .. p.listParseLevel(listString, levelNumber, childNumber) 
	--cladeString = cladeString .. '\r}}'  

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

    return resultString
end

function p.listParseLevel(listString,levelNumber,childNumber)

	local cladeString = ""
	local indent = p.getIndent(levelNumber)
    levelNumber=levelNumber+1

    local 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 ..'=' ..  '{{clade'
	    	                          .. p.listParseLevel(recombined,levelNumber,i)  
	    else
	    	cladeString = cladeString .. indent .. '|' .. child ..'=' ..  list[i]	
	    end
		i=i+1
		child=child+1
	end


	cladeString = cladeString .. indent .. '}}'  
	return cladeString
end



-- this must be at end
return p