Module:Transcluder more

文档图示 模块文档[创建]
-- Provide more transclusion funtions
-- Modified from Module:Transcluder: https://en.wikipedia.org/wiki/Module:Transcluder
-- Authors of Module:Transcluder: User:Sophivorus, User:Certes & others
-- License of Module:Transcluder: CC-BY-SA-3.0

local p = {}

-- Helper function to test for truthy and falsy values
-- @todo Somehow internationalize it
local function truthy(value)
	if not value or value == '' or value == 0 or value == '0' or value == 'false' or value == 'no' or value == 'non' then
		return false
	end
	return true
end

-- Helper function to convert a comma-separated list of numbers or min-max ranges into a list of booleans
-- @param flags Comma-separated list of numbers or min-max ranges, for example '1,3-5'
-- @return Map from integers to booleans, for example {1=true,2=false,3=true,4=true,5=true}
-- @return Boolean indicating whether the flags should be treated as a blacklist or not
local function parseFlags(value)
	local flags = {}
	local blacklist = false

	if not value then return nil, false end

	if type(value) == 'number' then
		if value < 0 then
			value = -value
			blacklist = true
		end
		flags = { [value] = true }

	elseif type(value) == 'string' then
		if string.sub(value, 1, 1) == '-' then
			blacklist = true
			value = string.sub(value, 2)
		end
		local ranges = mw.text.split(value, ',') -- split ranges: '1,3-5' to {'1','3-5'}
		for _, range in pairs(ranges) do
			range = mw.text.trim(range)
			local min, max = mw.ustring.match(range, '^(%d+)%s*[-–—]%s*(%d+)$') -- '3-5' to min=3 max=5
			if not max then min, max = string.match(range, '^((%d+))$') end -- '1' to min=1 max=1
			if max then
				for i = min, max do flags[i] = true end
			else
				flags[range] = true -- if we reach this point, the string had the form 'a,b,c' rather than '1,2,3'
			end
		end

	-- List has the form { [1] = false, [2] = true, ['c'] = false }
	-- Convert it to { [1] = true, [2] = true, ['c'] = true }
	-- But if ANY value is set to false, treat the list as a blacklist
	elseif type(value) == 'table' then
		for i, v in pairs(value) do
			if v == false then blacklist = true end
			flags[i] = true
		end
	end

	return flags, blacklist
end

-- Helper function to see if a value matches any of the given flags
local function matchFlag(value, flags)
	if not value then return false end
	value = tostring(value)
	local lang = mw.language.getContentLanguage()
	local lcvalue = lang:lcfirst(value)
	local ucvalue = lang:ucfirst(value)
	for flag in pairs(flags) do
		if value == tostring(flag)
		or lcvalue == flag
		or ucvalue == flag
		or ( not tonumber(flag) and mw.ustring.match(value, flag) ) then
			return true
		end
	end
end

-- Helper function to remove a string from a text
local function removeString(text, str)
	local pattern = escapeString(str)
	if #pattern > 9999 then -- strings longer than 10000 bytes can't be put into regexes
		pattern = escapeString(mw.ustring.sub(str, 1, 999)) .. '.-' .. escapeString(mw.ustring.sub(str, -999))
	end
	return string.gsub(text, pattern, '')
end

-- Get the templates from the given wikitext with name=template_name
-- @param text Required. Wikitext to parse.
-- @param flags Range of templates to return, for example 2 or '1,3-5'. Omit to return all templates.
-- @return Sequence of strings containing the wikitext of the requested templates with the specified name
-- @return Original wikitext minus requested templates.
local function getTemplatesByName(text, template_name, flags)
	local templates = {}
	local flags, blacklist = parseFlags(flags)
	local name
	local count = 0
	for template in string.gmatch(text, '{%b{}}') do
		name = mw.text.trim( string.match(template, '{{([^}|\n]+)') or "" ) -- get the template name
		if name == template_name then
			count = count + 1
			if not blacklist and ( not flags or flags[count] or matchFlag(name, flags) )
			or blacklist and flags and not flags[count] and not matchFlag(name, flags) then
				table.insert(templates, template)
			else
				text = removeString(text, template)
			end
		end
	end
	return templates, text
end

-- Get the position parameter(s) of templates from the given wikitext with name=template_name
local function getTemplateParameterByName(text, template_name, flags)
	local templates = {}
	local flags, blacklist = parseFlags(flags)
	local name
	local count = 0
	local params, parts, key, value
	for template in string.gmatch(text, '{%b{}}') do
		name = mw.text.trim( string.match(template, '{{([^}|\n]+)') or "" ) -- get the template name
		if name == template_name then
			count = count + 1
			if not blacklist and ( not flags or flags[count] or matchFlag(name, flags) )
			or blacklist and flags and not flags[count] and not matchFlag(name, flags) then
				params = string.match(template, '{{[^|}]-|(.*)}}')
				local parameters = {}
				table.insert(templates, parameters)
				local parameter_count = 0
				for parameter in mw.text.gsplit(params, '|') do
					parts = mw.text.split(parameter, '=')
					key = mw.text.trim(parts[1])
					if #parts == 1 then
						value = key
						parameter_count = parameter_count + 1
						key = parameter_count
						value = string.gsub(string.gsub(value, '@@:@@', '|'), '@@_@@', '=')
						parameters[key] = value
					end
				end
			end
		end
	end
	return templates
end

-- Entry points for modules
function p.getTemplatesByName(text, template_name, flags) return getTemplatesByName(text, template_name, flags) end
function p.getTemplateParameterByName(text, template_name, flags) return getTemplateParameterByName(text, template_name, flags) end

return p