Mô đun:Clade/transclude

require('strict')
local DEBUG=false
--DEBUG=true -- comment out or not runtime or debug
local p ={}
local pargs ={}
p.main = function(frame) -- called from template
	pargs = frame:getParent().args
	local output 
	local selectedTree -- subtree extracted from page content
	local modifiedTree -- subtree after pruning and grafting
	
	-- (1) get page
	local page = pargs'page' or frame.args'page'
	if not page then 
		return p.errorMsg("Target page not provided") 
end 
	
	-- (2) get content of page (move from _section(), _label, etc)
	local content
	local title = mw.title.new( mw.text.trim(page)) -- , ns)  -- creates object if page doesn't exist (and valid page name)
	                                            --TODO: could use mw.title.makeTitle(), but that needs ns
	if title then  
		if  title.exists then
	  		content = title:getContent()
			if not content then return p.errorMsg("Content of " .. page .. " not loaded.") end
		else
			return p.errorMsg('Page with title "' .. page .. '" not found.') 
	end
	end
	-- (3) select from content
	
	local section =  pargs'section' or pargs'section1' or pargs1 
	if section then
		selectedTree = p._section(frame, content, section)
	end
	
	local label =  pargs'label' or pargs'label1' or pargs1 
	if label then
		selectedTree = p._label(frame, content, label)
	end   
	--TODO does this need to be separate from label?
	local subtree =  pargs'subtree' or pargs'subtree1' or pargs1 
	if subtree then
		selectedTree = p._label(frame, content, subtree)
	end  
	
if not selectedTree then -- if none of options retrieve anything
	p.errorMsg("Nothing retrieved for selection option " .. (label or subtree or section or "none"))
end
if DEBUG then return selectedTree end      --- returns the code captured without processing

	--(4) modify content (excise and replace; prune and graft)
	local exclude = pargs'exclude' or pargs'exclude1'
	if exclude then
	    if pargs'exclude' then pargs'exclude1' = pargs'exclude' end
		if pargs'replace' then pargs'replace1' = pargs'replace' end
		
		modifiedTree = selectedTree
		
	    local i = 1
	    while pargs'exclude'..i do
	       local exclude = pargs'exclude'..i
		   local replace = pargs'replace'..i or " "  -- must be something
		   modifiedTree = p._xlabel(frame, modifiedTree, exclude, replace)
		   i=i+1
		end
	else
		modifiedTree = selectedTree
	end
	--(5) other options
	----- suppress hidden elements
	if pargs'nohidden' then
		modifiedTree = modifiedTree:gsub("lade hidden", "lade")
	end
	----- suppress authorities (or anything in small tags)
	if pargs'noauthority' then
		modifiedTree = modifiedTree:gsub(".-", "")
	end	
	----- suppress images
	if pargs'noimages' then
		modifiedTree = modifiedTree:gsub("%%File:.-%%", "")
	end
	----- wrap in outer clade 
	local wrap = pargs'wrap' 
	if wrap and (label or subtree) then
		local label1 = label or string.lower(subtree)
		if wrap ~= "" then label1 = wrap end
		output = "1=" .. modifiedTree .. " " -- last space before double brace important
else
	output	= modifiedTree
end
	
	--(6) return final tree
	if output then
		if pargs'raw' then
			return output
		else
			return frame:preprocess(output)
		end
	end
	
return p.errorMsg("No valid option for transclusion")
end
--=============================== extract LABELS or SUBTREES===================================**
p.label = function (frame, page, ...)
	local page = frame.args1 --"User:Jts1882/sandbox/test/Passeriformes"
	local label = frame.args1 or frame.args'label' or frame.args'label1'
	local wrap = frame.args'wrap' 
	
	local output = p._label (frame, page, frame.args2, frame.args3, frame.args4, frame.args5 )
	if wrap then
		local label1 = string.lower(frame.args2)
		if wrap ~= "" then label1 = wrap end
		output = "1=" .. output .. ""
end
	return frame:preprocess(output)
end
p._label = function (frame, content, ... )
--	local page = "User:Jts1882/sandbox/test/Passeriformes"
--	local label = frame.args1 or frame.args'label'
	local args = { ... }
	local output = ""
	
	if not args1 then return p.errorMsg ("Label name not provided") end
	
local mode = "label"
	local targetType = "label(%d)"                   -- standard label of form |labelN= (captures N)
	local cladePrefix = "(%d)"                       -- standard node of form |N= (captures N)
	for k,v in pairs(args) do
		local section = mw.text.trim(v)
		if string.upper( section) == section then
			 mode        = "subtree"
			 targetType  = "target(%u)"               -- targets of form targetX (X=uppercase letter)
			 cladePrefix = "subclade(%u)"             -- subclades of form subcladeX (captures X)
	    end

--= the pattern to capture is one of two forms: labelN=Name |N={...} 
targetX=NAME |subcladeX={...} 
		          labelN      =          name                
N           =    {...}
		   or    targetX      =          name                
subcladeX   =    {...}
**
local pattern = targetType.."=%s%p*"..section .. "%s%p*.-"..cladePrefix.."=.-(%b{})"
-- this .- skips section tags before 
-- this .- skips |sublabel and styling following the label (but can return wrong clade when a subtree)
local index1, index2, selectedTree = string.match( content , pattern )
-- note index1 and index2 should match (X=X or N=N)
if selectedTree then 
--targetX={FABIDS}


local pattern2 = "({%u-})"   -- this captures both |N={FABIDS} and |targetX={FABIDS}
-- we only want to substitute a subtree in the first kind 
-- will exclude second with pattern3 test below

if string.find(selectedTree, pattern2 ) then          -- if a subtree that hasn't been substituted.
	--local i,j,target = string.find(value, pattern2) -- only one subtree
	local i=0
	for bracedMarker in string.gmatch( selectedTree , pattern2 ) do
i=i+1
-- bracedMarker is either a marker in the tree or part of following
--     targetX={bracedMarker} ... |subcladeX=s  then
local pattern3 = "target(%u)=%s*"..bracedMarker
--?? if selectedTree == bracedMarker
			        if not string.find(selectedTree, pattern3 ) then

	            	  	local subtree = p._label (frame, content, bracedMarker) 
		            	if subtree then
		            	 	-- method 1: the subtree code is substituted into main tree
		            	 	        this substitutes the subtree within the clade structure before processing
		            	 	        thus there will be a problem with large trees exceeding the expansion depth
		            	 	        however, they can be pruned before processing
		            	 	  
--disable method 1		            	 	selectedTree = string.gsub(selectedTree, bracedMarker, subtree, 1)
		            	 	--subcladeX=subtree" before last double brace of selectedTree
		            	 	    use capture in pattern3 to find X
		            	 	  
		            	 	local i,j,X = string.find(content, pattern3)
		            	 	  
		            	 	if selectedTree == bracedMarker then
		            	 	    selectedTree = subtree
		            	 	else 
		            	 	   	  selectedTree = selectedTree:sub(1,-3)  -- trim final double brace
		            	 	          .. "\n|target" .. X .. "=" .. bracedMarker 
		            	 	          .. "\n|subclade" .. X .. "=" .. subtree .. ""
		            	 	          .. "\n }}"
		            	 	end 
		            	end
	            	end --substitution of subtree
	end
end
	output = output .. selectedTree
else
	output = output .. p.errorMsg ("Failed to capture subclade with " .. mode .. " " ..section)
end
	end
	
if output ~= "" then 
		return output -- preprocess moved to entry function
	else 
		return 'Section for label not found' 
end
end	
--================================== exclude LABEL ============================================**
p.xlabel = function (frame, page, ...)
	local page = frame.args1 --"User:Jts1882/sandbox/test/Passeriformes"
	local label = frame.args1 or frame.args'label' or frame.args'label1'
	                      -- page , target tree,  subtrees to exclude ...
	
	
	
	--                       page,   include clade, multple clades to exclude |
	return p._xlabel (frame, page, frame.args2, frame.args3, frame.args4, frame.args5)
end
p._xlabel = function (frame, targetTree, exclude, replace)
	
	local fullOutput =  targetTree
	--local fullOutput =  p._section(frame, page, target) 
	local output=targetTree -- return unmodified tree if nothing happens
	local section = exclude
	
	local targetType = "label%d"
	local cladePrefix = "%d"
	if string.upper( section) == section then
		 targetType = "target%u"               -- by convention subtrees must be uppercase
		 cladePrefix = "subclade%u"
end
	--                label      =         name                        
n=   {...}
local pattern = "("..targetType.."=%s%p*"..section .. "%s%p*.-"..cladePrefix.."=.-)(%b{})"
-- ^^ this .- skips section tags before clade
-- ^^this .- skips |sublabel and styling following the label (but can return wrong clade when a subtree)

local value = string.match( fullOutput , pattern ) 
if value then
	local trimmedTree, matches = string.gsub(fullOutput, pattern, "%1"..replace)--replaces pattern with capture %1
return trimmedTree
else
	local message = ""
	if string.upper(section) == section then 
		message = "; subtree may have been substituted, try label"
end
	output = output .. p.warningMsg ("Failed to capture subclade for exclusion with label "..section..message)
end
if output ~= "" then 
		return  output .. 'Nothing pruned' 
		--return frame:preprocess(fullOutput)
	else 
		return 'Section for label not found' -- shouldn't get here 
end
end	
--======================================== SECTION ==============================**
p.section = function (frame)
	-------------------------target page  ---- sections
	return frame:preprocess(p._section(frame, mw.text.trim(frame.args1),frame.args2,frame.args3,frame.args4,frame.args5))
end
p._section = function (frame,content,...)
	local args = { ... }
	local output = ""
	for k,v in pairs(args) do
		local section = mw.text.trim(v)
		-- note: using the non-greedy - in (.-) to allow capture of several sections 
		    this allows internal clade structures to be closed without capturing sisters clades
		    e.g. see section Tyranni in User:Jts1882/sandbox/test/Passeriformes
		
		local pattern = "(.-)"
		for value in string.gmatch( content , pattern ) do
		    if value then 
				if frame.args.wrap or frame:getParent().args.wrap then
					local label1 = frame.args.wrap or frame:getParent().args.wrap 
					if label1 == "" then label1 = section end
					value =  "1=" .. value .. ""
			    end
				output = output .. value
			end
		end
	end
	

if output ~= "" then 
		--return  frame:preprocess(output)
		return output -- leave preprocessing for entry function
	else 
		return 'Section not found' 
end
end 
p.xsection = function (frame)
	local page = frame.args1 --"User:Jts1882/sandbox/test/Passeriformes"
	local label = frame.args1 or frame.args'label' or frame.args'label1'
	                                       -- page , target tree,  sections to exclude ...	
	return frame:preprocess(p._xsection(frame, page ,frame.args2,frame.args3,frame.args4,frame.args5))
end
p._xsection = function (frame,page, target, ...)
	local args = { ... }
	local output = ""
	local title = mw.title.new( page) -- , ns)  -- creates object if page doesn't exist (and valid page name)
	                                            --TODO: could use mw.title.makeTitle(), but that needs ns
	                                            
	
	
	                                            
	if title and title.exists then 
		local content = title:getContent()
		local fullOutput =  p._section(frame, page, target) 
	    output=fullOutput
	    
		
		for k,v in pairs(args) do
			local section = mw.text.trim(v)
			-- note: using the non-greedy - in (.-) to allow capture of several sections 
			    this allows internal clade structures to be closed without capturing sisters clades
			    e.g. see section Tyranni in User:Jts1882/sandbox/test/Passeriformes
			
			local pattern = "()(.-)()"
local value = string.match( fullOutput , pattern ) 

if value then
	local trimmedTree, matches = string.gsub(fullOutput, pattern, "replacement string")--replaces pattern with capture %1

	output = output .. trimmedTree
	output = output .. "" .. trimmedTree .. ""
fullOutput = trimmedTree
else
	output = output .. p.errorMsg ("Failed to capture subclade with label "..section)
end
		end
		
else
	return  'No page title found'
	end

if output ~= "" then 
		--return  frame:preprocess(output)
		return output -- leave preprocessing for entry function
	else 
		return 'Section not found' 
end
end 
function p.firstToUpper(str)
return (str:gsub("^%l", string.upper))
end
p.errorMsg = function (message)
	return  .. message ..  
end	
p.warningMsg = function (message)
	return  .. message ..  
end	
return p