Jump to content

Module:Clade/transclude: Difference between revisions

From Wikipedia, the free encyclopedia
Content deleted Content added
Undid revision 1021242106 by Jts1882 (talk) just warning for now, and let's just add the styles instead of removing the class
require one space/punctuation character so it matches whole word labels
Line 150: Line 150:
or targetX = [[ name ]] |subcladeX = {...}
or targetX = [[ name ]] |subcladeX = {...}
]=]
]=]
local pattern = targetType.."=[%s%p]*"..section .. "[%s%p]*.-"..cladePrefix.."=.-(%b{})"
local pattern = targetType.."=[%s%p]*"..section .. "[%s%p]+.-"..cladePrefix.."=.-(%b{})"
-- this .- skips section tags before {{clade ...}}
-- this .- skips section tags before {{clade ...}}
-- this .- skips |sublabel and styling following the label (but can return wrong clade when a subtree)
-- this .- skips |sublabel and styling following the label (but can return wrong clade when a subtree)

Revision as of 10:06, 16 June 2021

require('Module:No globals')

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 pargs[1] 
	if section then
		selectedTree = p._section(frame, content, section)
	end
	
	local label =  pargs['label'] or pargs['label1'] or pargs[1] 
	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 pargs[1] 
	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("<small>.-</small>", "")
	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)
		local styleString = "" 
		if  pargs['style'] then  styleString = '|style=' .. pargs['style']  end
		
		if wrap ~= "" then label1 = wrap end
		output = "{{clade " .. styleString .. " |label1=" .. p.firstToUpper(label1) .. "|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.args[1] --"User:Jts1882/sandbox/test/Passeriformes"
	local label = frame.args[1] or frame.args['label'] or frame.args['label1']
	local wrap = frame.args['wrap'] 
	
	local output = p._label (frame, page, frame.args[2], frame.args[3], frame.args[4], frame.args[5] )
	if wrap then
		local label1 = string.lower(frame.args[2])
		if wrap ~= "" then label1 = wrap end
		output = "{{clade |label1=" .. p.firstToUpper(label1) .. "|1=" .. output .. "}}"
    end
	return frame:preprocess(output)
end

p._label = function (frame, content, ... )
--	local page = "User:Jts1882/sandbox/test/Passeriformes"
--	local label = frame.args[1] or frame.args['label']
	local args = { ... }
	local output = ""
	
	if not args[1] 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 {{clade ...}}
                                                           -- 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 
            --[[ the tree can contain markers for subtrees like {FABIDS} 
                    when the form is |N={FABIDS} we want to substitute the subtree
                    but not when the form is |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)

		            	 	--[[method 2: add the subtree code before the final double brace
		            	 	    substitute "|targetX={FABIDS} |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 '<span class="error">Section for label not found</span>' 
    end
end	

--================================== exclude LABEL ================================================

p.xlabel = function (frame, page, ...)
	local page = frame.args[1] --"User:Jts1882/sandbox/test/Passeriformes"
	local label = frame.args[1] 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.args[2], frame.args[3], frame.args[4], frame.args[5])



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 .. '<span class="error">Nothing pruned</span>' 
		--return frame:preprocess(fullOutput)
	else 
		return '<span class="error">Section for label not found</span>' -- shouldn't get here 
    end
end	

--======================================== SECTION ==================================
p.section = function (frame)
	-------------------------target page  ---- sections
	return frame:preprocess(p._section(frame, mw.text.trim(frame.args[1]),frame.args[2],frame.args[3],frame.args[4],frame.args[5]))
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 = "<section begin="..section.."[ ]*/>(.-)<section end="..section.."[ ]*/>"

		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 =  "{{clade |label1=" .. label1 .. "|1=" .. value .. "}}"
			    end
				output = output .. value
			end

		end
	end
    if pargs['norefs'] or pargs['noref'] then                                                -- strip out references
	   --output =   mw.text.killMarkers( output ) 
	   if output:find("<ref")  then                       
			output = output:gsub('<ref[%w%p%s]-%/>', "") 
			output = output:gsub("<ref.-<%/ref>", "")                                      -- %C works, %w%p%s%c doesn't
	   end
	end
    
    if output ~= "" then 
		--return  frame:preprocess(output)
		return output -- leave preprocessing for entry function
	else 
		return '<span class="error">Section not found</span>' 
    end

end 

p.xsection = function (frame)
	local page = frame.args[1] --"User:Jts1882/sandbox/test/Passeriformes"
	local label = frame.args[1] or frame.args['label'] or frame.args['label1']
	                                       -- page , target tree,  sections to exclude ...	
	return frame:preprocess(p._xsection(frame, page ,frame.args[2],frame.args[3],frame.args[4],frame.args[5]))
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 = "(<section begin="..section.."[ ]*/>)(.-)(<section end="..section.."[ ]*/>)"

            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 .. "<pre>" .. trimmedTree .. "</pre>"
                fullOutput = trimmedTree
            else
            	output = output .. p.errorMsg ("Failed to capture subclade with label "..section)
            end

		end
		

    else
    	return  '<span class="error">No page title found</span>'
	end
    
    if output ~= "" then 
		--return  frame:preprocess(output)
		return output -- leave preprocessing for entry function
	else 
		return '<span class="error">Section not found</span>' 
    end

end 

function p.firstToUpper(str)
    return (str:gsub("^%l", string.upper))
end
p.errorMsg = function (message)
	return '<span class="error">' .. message .. '</span>' 
end	
p.warningMsg = function (message)
	return '<span class="warning" style="color:#ac6600;font-size:larger;">' .. message .. '</span>'
end	
return p