模块:TemplateParameters

求闻百科,共笔求闻

本模组可以将参数填入以普通模板参数定义的格式化字符串

设计缘由

自定义的模板参数读取之构想早在2018年11月就已提出。当时数学类条目面临大量QW:114.27的似是而非的破坏,不易查证真伪,因此建立了Module:Number来自动输出数学性质,避免遭QW:114.27窜改为错误资讯而不易查证(例如多1少1的破坏难以察觉和验证),但有用户认为输出固定字串无法被修改或添加注解、参考文献不妥,因此出现了格式化字符串的需求,而部分用户认为,应尽量是要设计一般维基人都能编辑的语法,因此最后决议选择wikitext模板中的模板参数语法,但当时此功能独自分散在各个需要格式化字符串功能的模组里,例如Module:NumberModule:PeriodicTableModule:Delcat等地方。

最初的版本仅包含简单文字替换,只能处理已知数量的参数,而在2019年6月,有用户问到能否不写新的模组、也不使用大量{{{#if:}}}实现,因此认为可以把自动读取参数填入格式化字符串的功能独立出来(即建立本模组),顺便提供些特殊参数解析功能,如参数批次trim和数字解析功能(根据QW:TG1请求用于模板链接:{{Infobox 日本的町村}})。

使用说明

本模组主要是提供wikitext模板中的模板参数一个API,而非设计给其他Lua函数使用,因此专门给lua呼叫的函数较少。

本模组包括的函数如下:

用法

getFormatingStringByArgument

功能:直接将参数透过简单文字替换填入形如{{{X}}}的位置内

  • {{#invoke:TemplateParameters | getFormatingStringByArgument | 在{{{學科}}}中,{{{名稱}}}是一種{{{種類}}} | 學科=數學 | 名稱=三角形 | 種類=多邊形}}
    显示为“在数学中,三角形是一种多边形”
用途

FormatingArguments

功能:将未知数量的参数根据{{{X}}}规则依序填入给定的字符串中。

  • 模板中输入
{{#invoke:TemplateParameters|FormatingArguments|格式=這是第{\{\{1}\}\}個參數;|count=1|usingConditionalExpressions=yes}}
  • 叫用模板{{<模板名稱>|一|二|三|四}}
  • 显示为:“这是第一个参数;这是第二个参数;这是第三个参数;这是第四个参数;”
  • 解释:
    • 模板中“格式”参数原本为這是第{{{1}}}個參數;,透过加入跳脱字元防止先被解析后,透过FormatingArguments函数将传入模组读所有参数依序填入,不必透过大量的{{#if:{{{N}}}就能达成不定数量的参数传递。

  • 模板中输入
{{#invoke:TemplateParameters|FormatingArguments|格式=*:[[畢氏三元數]]:{\{#tag:math{{!}}\\left({\{\{1}\}\},{\{\{2}\}\} ,{\{\{3}\}\}\\right)}\}\n|count=3|usingConditionalExpressions=yes}}
  • 叫用模板{{<模板名稱>|3|4|5|5|12|13|7|24|25}}
  • 显示为:
    毕氏三元数
    毕氏三元数
    毕氏三元数
  • 解释:
    • 模板中“格式”参数原本为*:[[畢氏三元數]]:{{#tag:math|\left({{{1}}},{{{2}}} ,{{{3}}}\right)}}\n
      指定了数量为3( | count=3)后,会将参数每三个一组代入{{{1}}}{{{2}}}{{{3}}}

格式字串中支援的参数

下面为FormatingArguments支援的参数,若有同名参数可能会影响运作

  • 读取不定参数:
    • {{{1}}}{{{2}}}{{{3}}}...即以组为单位读取不定参数。
      例如{{|1|2|3|4|5|6|7|8|9}}如果设定为每组3个参数,
      则第一组的{{{1}}}{{{2}}}{{{3}}}分别会代入1、2和3;
      第二组的{{{1}}}{{{2}}}{{{3}}}分别会代入4、5和6
      以此类推。
    • {{{count+1}}}{{{count+2}}}{{{count+3}}}...读取下一组的不定参数。
      例如{{|1|2|3|4|5|6|7|8|9}}如果设定为每组3个参数,此时{{{count+1}}}即为{{{4}}}
      则第一组的{{{4}}}{{{5}}}{{{6}}}分别会代入4、5和6;
      则第二组的{{{4}}}{{{5}}}{{{6}}}分别会代入7、8和9;
      则第三组的{{{4}}}{{{5}}}{{{6}}}因为没有第四组,而会变成空值、预设参数或保持原样(即{{{4}}});
    • {{{0}}}{{{-1}}}{{{-2}}}...读取前一组的不定参数。
      例如{{|1|2|3|4|5|6|7|8|9}}如果设定为每组3个参数
      则第一组的{{{0}}}{{{-1}}}{{{-2}}}因为第一组没有钱一组,而会变成空值、预设参数或保持原样(即{{{0}}});
      则第二组的{{{0}}}{{{-1}}}{{{-2}}}分别会代入3、2和1;
      则第三组的{{{0}}}{{{-1}}}{{{-2}}}分别会代入6、5和4;
      以此类推。
    • {{{random}}}:位于本组的随机参数。
    • {{{allRandom}}}:所有参数的随机参数。
    • {{{last}}}:本组的最后一个参数。
  • 判断用参数
    • {{{isFirst}}}:如果这是第一组参数则为1,否则为空值
    • {{{isLast}}}:如果这是最后一组参数则为1,否则为空值
    • {{{count}}}:参数组的数量。
    • {{{argGroupID}}}:返回目前作用中的参数是第几组。

Subst方法

此模板可以使用Subst,不过若用<nowiki></nowiki>搭配 | delnowiki = 参数的话会导致替换失败。

  • 替代方案可以使用跳脱字元(如\{\}等)以及H:魔术字(如模板链接:{{!}}{{ {{{|safesubst:}}}!}}、、{{ {{{|safesubst:}}}=}}等)
  • 若觉得编辑困难可以使用 子页面搭配{{msgnw:模板名}}以及 | delmsgnw = 参数 来完成
    • 详细使用方式可参考User:A2569875/SubstTest搭配User:A2569875/SubstTest/Cell的范例。
    • 使用{{msgnw:模板名}}需要注意的是:由于使用msgnw,因此等标记无效,因此不应该在该子模板中直接加入注解或{{Doc}}的内容,需要使用模板链接:{{ifsubst}}才能运作,例如{{ {{{|safesubst:}}}ifsubst||<noinclude>{{documentation}}</noinclude>}}
      或者:
{{ {{{|safesubst:}}}ifsubst||<noinclude>
不被包含引用的部分
</noinclude>}}

containsNumber

功能:用于确定一个参数内是否有包含阿拉伯数字

  • {{#invoke:TemplateParameters|containsNumber|hijk42lm}}→“1”
  • {{#invoke:TemplateParameters|containsNumber|hijklm}}→“”

用法

  • {{#if:{{#invoke:TemplateParameters|containsNumber|hijk42lm}}|有數字|沒數字}}→“有数字”
  • {{#if:{{#invoke:TemplateParameters|containsNumber|hijklm}}|有數字|沒數字}}→“没数字”

getNumberValue

功能:尝试从一个参数内读出一个阿拉伯数字,若读不出来,则传回0

  • {{#invoke:TemplateParameters|getNumberValue|hijk42lm}}→“42”
  • {{#invoke:TemplateParameters|getNumberValue|hijklm}}→“0”

用法

  • {{#expr: hijk42lm }}→“表达式错误:无法识别词语“hijk”。
  • {{#expr:{{#invoke:TemplateParameters|getNumberValue|hijk42lm}} }}→“42”
  • {{#expr:hijklm }}→“表达式错误:无法识别词语“hijklm”。
  • {{#expr:{{#invoke:TemplateParameters|getNumberValue|hijklm}} }}→“0”

arg_to_spstr与pass_spstr

功能:arg_to_spstr会将参数转变成一个可供pass_spstr使用的字串;pass_spstr可以将arg_to_spstr给出的参数套入模板语法

设计动机:批次大量传送参数的方案
{{#invoke:TemplateParameters|pass_spstr
 | code = {{{1}}}{{2}}{{{3}}}
 | args = {{#invoke:TemplateParameters|arg_to_spstr|A|B|C}}
}}
显示为“A-B-C”

argTrim

功能:适用于本系列模板的trim方案(delnowiki可正常使用模板链接:{{trim}}),模板链接:{{trim}}会导致使用“模板链接:{{!}}”的后方内容被舍弃

  • 一般Trim
    • 模板内容{{#invoke:TemplateParameters|FormatingArguments|格式=[\[:{\{Trim{{!}} {\{\{1}\}\} }\}]\]|count=1|usingConditionalExpressions=yes}}
    • 叫用模板{{<模板名稱>|一{{!}}遺失的內容}}
    • 显示为:“
  • 一般Trim + delnowiki
    • 模板内容{{#invoke:TemplateParameters|FormatingArguments|格式=<nowiki>[[:{{Trim| {{{1}}} }}]]</nowiki>|delnowiki=1|count=1|usingConditionalExpressions=yes}}
    • 叫用模板{{<模板名稱>|一{{!}}遺失的內容}}
    • 显示为:“遗失的内容
  • 使用argTrim
    • 模板内容{{#invoke:TemplateParameters|FormatingArguments|格式=[\[:{{((}}#invoke:TemplateParameters{{!}}argTrim{{!}}{{{1}}} {{))}}]\]|count=1|usingConditionalExpressions=yes}}
    • 叫用模板{{<模板名稱>|一{{!}}遺失的內容}}
    • 显示为:“遗失的内容
  • 使用argTrim + delnowiki
    • 模板内容{{#invoke:TemplateParameters|FormatingArguments|格式=<nowiki>[[:{{#invoke:TemplateParameters|argTrim|{{{1}}} }}]]</nowiki>[\[:{\{#invoke:TemplateParameters{{!}}argTrim{{!}}{{{1}}} }\}]\]|count=1|usingConditionalExpressions=yes}}
    • 叫用模板{{<模板名稱>|一{{!}}遺失的內容}}
    • 显示为:“遗失的内容

listArguments

功能:列出模板接收到的参数

用途:侦错用

  • 输入{{#invoke:TemplateParameters|listArguments|A|B|C|99=D|E=F}}
  • 显示为:
  • {{#invoke:}}呼叫参数:
    1 : A
    2 : B
    3 : C
    99 : D
    E : F
  • {{Module:TemplateParameters/doc}}呼叫参数:
    isdoc : 1

call

功能:将模板参数转为Lua参数的方法,提供呼叫一些不支援以模板参数呼叫之Module。

用途:能让一些未提供模板参数呼叫之Module在不建立新Module的情况下由模板调用。

例如Module:yesno目前暂未支援由模板直接调用。
  • 输入{{#invoke:TemplateParameters|call|Module:yesno|T}}
  • 显示为:“1”(参见下方#回传值子章节)

参数

  • 第一参数为模板与模组名称,以点“ . ”运算子来从模组中获得函数名称,如“Module:Foo.bar”指的是Module:Foo中的function bar( ),需要注意的是,为了提升效能,因此判断是用简单的字串分割达成,因此若模组名称含有点“ . ”符号,则无法使用此功能。
  • 其他参数则会依序传入指定的模组函数中

回传值

  • 若模组回传值为数字字串,则会直接显示结果;
  • 若模组回传值为布林值则:true以“1”表示,否则输出空字串。这是为了让结果能给{{#if:}}使用
  • 若模组没有回传值则输出空字串;
  • 若模组回传值为其他物件,则会尝试将物件转为JSON后输出。此步骤有可能因为物件中包含了无法JSON化的物件导致失败。

选项

  • | isJSON = :将所有输入参数视为JSON
    部分模组中函数可能有需要传入特殊格式物件的需求,此时可以透过设定此选项来以JSON来输入支援的物件。

注意事项:若指定的模组中函数本身已支援模板调用则可能出错。因为支援模板调用的模组通常是用第一参数接收所有模板参数,而此函数会先将模板参数全部展开才传入,可能会有参数无效或者第一参数无法使用(如支援模板调用的第一参数预期是一个包含所有模板参数资讯的lua table,但本函数将参数展开可能导致第一参数变为字符串或数字)等问题。

templateArgWarp

功能:替换引用时,将自身替换为另一个模板与参数

用途:如防止替换引用(替换引用又换回自身)或替换引用时转成其他等价模板等。

参数:第一参数为要替换成的模板名称。只有第一参数有效,其余参数皆为从父模板读取。

makeTemplateParameter

功能:产生模板参数的未解析字串,如{{{arg}}}

用途:搭配模板链接:{{虚拟模板}}作为替换引用的替代方案,参见模板链接:{{参数}}。

  • 输入{{#invoke:TemplateParameters|makeTemplateParameter|arg|default}}
  • 显示为:“{{{arg|default}}}”

用途

相关模板

上述文档内容嵌入自Module:TemplateParameters/doc编辑 | 历史
编者可以在本模块的沙盒创建 | 镜像和测试样例创建页面进行实验。
请将模块自身所属的分类添加在文档中。本模块的子页面
local p = {}
local lib_arg = {}
local yesno = {}
local lib_editstate={};
function p.containsNumber(frame)
	-- For calling from #invoke.
    local args
    local can_call_parse = false
    if frame == mw.getCurrentFrame() then
        -- We're being called via #invoke. The args are passed through to the module
        -- from the template page, so use the args that were passed into the template.
        args = frame.args
        can_call_parse = true
    else
        -- We're being called from another module or from the debug console, so assume
        -- the args are passed in directly.
        args = frame
        if type(args) ~= type({}) then args = {frame} end
    end
    local input_str=mw.ustring.gsub(args[1] or args['1'],',','')
    local g0=mw.ustring.gmatch(input_str,"[%+%-]?[0-9%.]+e[%+%-]?[0-9%.]+")
    local out_str = nil
    if g0 then out_str = g0() end
    if (out_str or '') == '' then 
    	g0=mw.ustring.gmatch(input_str,"[%+%-]?[0-9%.]+") 
    else
    	return '1'
    end
    if g0() then return '1' end
    return ''
end

function p.getNumberValue(frame)
	-- For calling from #invoke.
    local args
    local can_call_parse = false
    if frame == mw.getCurrentFrame() then
        -- We're being called via #invoke. The args are passed through to the module
        -- from the template page, so use the args that were passed into the template.
        args = frame.args
        can_call_parse = true
    else
        -- We're being called from another module or from the debug console, so assume
        -- the args are passed in directly.
        args = frame
        if type(args) ~= type({}) then args = {frame} end
    end
    local input_str=mw.ustring.gsub(args[1] or args['1'],',','')
    local g0=mw.ustring.gmatch(input_str,"[%+%-]?[0-9%.]+e[%+%-]?[0-9%.]+")
    local out_str = nil
    if g0 then out_str = g0() end
    if (out_str or '') == '' then 
    	g0=mw.ustring.gmatch(input_str,"[%+%-]?[0-9%.]+") 
    else
    	return out_str
    end
    return g0() or '0'
end

function p.argTrim(frame)
	-- For calling from #invoke.
    local args
    local can_call_parse = false
    if frame == mw.getCurrentFrame() then
        -- We're being called via #invoke. The args are passed through to the module
        -- from the template page, so use the args that were passed into the template.
        args = frame.args
        can_call_parse = true
    else
        -- We're being called from another module or from the debug console, so assume
        -- the args are passed in directly.
        args = frame
        if type(args) ~= type({}) then args = {frame} end
    end
    local arg_counts = 1
    local input_str = ''
    local first_str = ''
    local one_str = mw.text.trim(args['1'] or args[1] or '')
    local count = 0
    local pipe_to_magic_word = false
    if (args["pipe"] or '') ~= '' then
		if type(yesno) ~= type(tonumber) then yesno = require('Module:Yesno') end
		pipe_to_magic_word = yesno(args["pipe"] or 'no')
	end
	local num_arg_count = 0
	local first_arg = true
	for id,_ in pairs(args) do if (tonumber(id) or 0) > num_arg_count then num_arg_count = tonumber(id) or 0 end end
	for id=1,num_arg_count do 
    	local it_str = mw.text.trim(args[tostring(id)] or args[id] or '')
    	if pipe_to_magic_word then it_str = mw.ustring.gsub(it_str,"%|", "{{!}}") end
    	if not first_arg then input_str = input_str .. '|' else first_arg = false end
    	input_str = input_str .. it_str
    	if first_str == '' then first_str = it_str end
    	if it_str~='' then count = count+1 end
	end
    for key,val in pairs(args) do
    	local it_str = mw.text.trim(val)
    	if pipe_to_magic_word then it_str = mw.ustring.gsub(it_str,"%|", "{{!}}") end
    	if (tonumber(key) or 0) < 1 and it_str~='' and mw.text.trim(tostring(key)) ~= 'pipe' then 
    		if input_str ~= '' then input_str = input_str .. '|' end
    		input_str = input_str .. mw.text.trim(tostring(key)) .. '=' .. it_str
    		if first_str == '' then first_str = it_str end
    		count = count+1
    	end
    end
    if count > 1 then return mw.text.trim(input_str)
    else 
    	if one_str~='' then return first_str
    	else return mw.text.trim(input_str) end
    end
end

function p.listArguments(frame)
	local body = ''
	function print_args(farg, str)
		local body = ''
		local flag = false
		if farg.args then
			body = body .. '*' .. "{{" .. (str or farg:getTitle())  .. "}}呼叫參數:" .. '\n'
			for k,v in pairs(farg.args) do
				if(type(v) ~= type(tonumber) and type(v) ~= type({}))then
					flag = true
					body = body .. '*:' .. "'''" .. k .. "''' : " .. tostring(v) .. '\n'
				end
			end
		end
		if flag then return body else return '' end
	end
	if frame ~= mw.getCurrentFrame() and type(frame) == type({}) then
		body = body .. '*' .. "函數呼叫參數:" .. '\n'
		for k,v in pairs(frame) do
			body = body .. '*:' .. "'''" .. k .. "''' : " .. v .. '\n'
		end
	end
	local this_arg = frame.args
	if type(this_arg) == type({}) then body = body .. print_args(frame, "#invoke:") end
	local iter = mw.getCurrentFrame()
	if iter == frame then iter = mw.getCurrentFrame():getParent() end
	while iter do
		body = body .. print_args(iter)
		iter = iter:getParent()
	end
	return body
end
local argument_check = false
function p.checkArgument(frame)
	argument_check = true
	return p.getParentArgument(frame)
end

function p.makeTemplateParameter(frame)  --產生模板參數字串
	local args, working_frame
    if frame == mw.getCurrentFrame() then
        -- We're being called via #invoke. The args are passed through to the module
        -- from the template page, so use the args that were passed into the template.
        args = {}
        for key,value in frame:argumentPairs() do
        	args[key] = value or ''
        end
        local Parent = frame:getParent();
        if Parent then for key,value in Parent:argumentPairs() do
        	args[key] = args[key] or value or ''end 
    	end
        working_frame = frame
    else
        -- We're being called from another module or from the debug console, so assume
        -- the args are passed in directly.
        args = frame
        working_frame = mw.getCurrentFrame()
        if type(args) ~= type({}) then args = {frame} end
    end
    local argname = args[1] or args['1'] or args['name']
    local argDefault = args[2] or args['2'] or args['default']
    local default_text = ''
    if argname then
    	argname = mw.text.trim(argname or '')
    	if argDefault then
    		default_text = '|' .. mw.text.trim(argDefault or '')
    	end
    else
    	argname, default_text = '', '|' .. mw.text.trim(argDefault or '')
    end
    if mw.text.trim(argname .. default_text) == '' then return '' end
    local result = '{{{' .. argname .. default_text .. '}}}'
    if args[4] or args['4'] then return result end
    if type(lib_editstate.isPreview) ~= type(tonumber) then lib_editstate = require('Module:EditState') end
    if ((mw.isSubsting() or lib_editstate.isPreview()) and (args[3] or args['3'])) then
    	if working_frame:getParent() then return working_frame:getParent():preprocess(result) end
    end
    return result
end

function p.call(frame) --轉換模板參數為lua參數
	local args, working_frame
    if frame == mw.getCurrentFrame() then
        -- We're being called via #invoke. The args are passed through to the module
        -- from the template page, so use the args that were passed into the template.
        if lib_arg.getArgs == nil then lib_arg = require('Module:Arguments') end
        args = lib_arg.getArgs(frame, {parentFirst=true})
        working_frame = frame
    else
        -- We're being called from another module or from the debug console, so assume
        -- the args are passed in directly.
        args = frame
        working_frame = mw.getCurrentFrame()
        if type(args) ~= type({}) then args = {frame} end
    end
    local max_arg = 0 
    local num_args = {}
    local isJSON = false
    if args.isJSON then
    	if type(yesno) ~= type(tonumber) then yesno = require('Module:Yesno') end
    	if yesno(args.isJSON or 'no') then --local delnowiki=require('Module:Delcat').delnowiki --備用
			isJSON = true
		end
	end
	for key,value in pairs( args ) do
		local arg_id = tonumber(key or '')
		if arg_id then
			if arg_id > max_arg then max_arg = arg_id end
			num_args[arg_id] = value
		end
	end
	local func_name = num_args[1]
	if mw.text.trim(func_name or '') == '' then return '' end
	local call_args = {}
	local get_obj = function(obj)return obj end
	if isJSON then get_obj = function(obj)return mw.text.jsonDecode(obj)end end
	if max_arg > 1 then
		for i = 2,max_arg do
			if num_args[i] then 
				local try_to_get_obj = mw.clone(num_args[i])
				if not xpcall( function() --try
					try_to_get_obj = get_obj(num_args[i])
				end, function(msg)return msg end ) then --catch
					call_args[i-1] = num_args[i]
				else --finally
					call_args[i-1] = try_to_get_obj
				end
			else call_args[i-1] = '' end
		end
	end

	local func_path = mw.text.split(func_name,'%.') or { [1]=func_name }
	local func_body = mw[func_path[1]] or p[func_path[1]] or _G[func_path[1]]
	if mw.ustring.find(func_path[1],':') then
		func_body = require(func_path[1]) or func_body
	end
	if func_body then
		local old_obj = func_body
		for i=2,#func_path do
			func_body = func_body[func_path[i]]
			if func_body then
				old_obj = func_body
			else
				return ''
			end
		end
		local func_type = type( func_body )
		local times = 10
		for ti = 1,times do
			if func_type == type(nil) then
				return ''
			elseif func_type == type(0) then
				return '' .. func_body
			elseif func_type == type(true) then
				if func_body then return '1' end
				return ''
			elseif func_type == type(type) then
				if max_arg > 1 then 
					func_body = func_body(unpack(call_args))
					if func_body == nil then func_body = call_args[1] end
				else func_body = func_body() end
			elseif func_type == type("string") then
				return func_body
			elseif func_type == type({}) then
				return mw.text.jsonEncode(func_body)
			end
			func_type = type( func_body )
		end
	end
	return ''
end

function p.getParentArgument(frame)
	local this_arg = frame.args
	local check_if = this_arg['if'] or this_arg['if value'] or this_arg.if_value or ''
	if mw.ustring.find(check_if,'%[%[') then check_if=require('Module:Delcat').delete_category(check_if, {}) end
	if mw.text.trim(check_if) ~= '' then return check_if end
	local out_arg = mw.getCurrentFrame():getParent().args
	if tonumber(this_arg.out or 1) > 1 then
		local iter = mw.getCurrentFrame()
		for i=1,tonumber(this_arg.out) do
			if iter:getParent() then
				iter = iter:getParent()
			end
		end
		if iter.args then out_arg=iter.args end
	end
	local result = ''
	local is_used = false
	if this_arg.name and out_arg then
		if out_arg[this_arg.name] then 
			if result ~= '' and result ~= out_arg[this_arg.name] then is_used =true end
			result = out_arg[this_arg.name] 
		end
	end
	if out_arg then
		for k,v in ipairs(this_arg) do
			if out_arg[v] then 
				if result ~= '' and result ~= out_arg[v] then is_used =true end
				result = out_arg[v] 
			end
		end
	end
	if mw.getCurrentFrame():getParent() then
		if argument_check then argument_check = false else
			if is_used then mw.addWarning( mw.getCurrentFrame():getParent():getTitle() .. "中,參數 \""
				.. (this_arg.name or this_arg[1]) ..  "\" 被用不同的內容定義了多次。"  ) end
		end
	end
	if result == '' then return this_arg.value or this_arg['if value'] or this_arg.if_value or '' end
	return result
end

function getMaxArgCount(out_args, max_count, prefixs)
	for index, arg_val in pairs(out_args) do 
		local start_id, end_id = mw.ustring.find(index,'%d+$')
		if start_id then
			local index_name = mw.text.trim(mw.ustring.sub(index, 1, start_id-1))
			local index_num = mw.ustring.sub(index, start_id, end_id)
			for ____,prefix in pairs(prefixs) do 
				if mw.text.trim(prefix) == index_name then
					if (tonumber(index_num) or 0) > max_count then 
						max_count = (tonumber(index_num) or 0) 
					end
					break
				end
			end
		end
	end
	return max_count
end

--外部讀取不定參數
function p.FormatingArguments(frame)
	-- For calling from #invoke.
    local args
    --是否存在有效的frame可供呼叫
    local can_call_parse = false
    if frame == mw.getCurrentFrame() then
        -- We're being called via #invoke. The args are passed through to the module
        -- from the template page, so use the args that were passed into the template.
        args = frame.args
        can_call_parse = true
    else
        -- We're being called from another module or from the debug console, so assume
        -- the args are passed in directly.
        args = frame
    end
    local input_str = '' --輸入的格式化字串
    local count = 1 --預設參數數量為1個一組
    if args["format"] or args["Format"] or args["格式"] then input_str = args["format"] or args["Format"] or args["格式"] or '' end
    if args["count"] then count = tonumber(args["count"] or 1) or 1 end
    local display_all = false --是否顯示跳過的參數 (如輸入1,2,5參數,決定是否輸出3,4參數)
    local delnowiki = false --是否從nowiki中解除包裝
    local hasPrefixArg = false 
    local arrPrefixArg = {}
    local allexpr = false --是否每組參數全部都呼叫一次帶參數的新解析器 (高開銷,最多100次)
    local allexpr_count = 1 --紀錄呼叫帶參數的新解析器次數,以免超過100次lua停止運作
    if args["displayAll"] then
    	if type(yesno) ~= type(tonumber) then yesno = require('Module:Yesno') end
		display_all = yesno(args["displayAll"] or 'no')
	end
    if args["PrefixArgs"] then
	    if args["hasPrefixArg"] then
	    	if type(yesno) ~= type(tonumber) then yesno = require('Module:Yesno') end
			hasPrefixArg = yesno(args["hasPrefixArg"] or 'no')
			arrPrefixArg = mw.text.split(args["PrefixArgs"] or '',',');
		end 
	end

    if args["delnowiki"] then
    	if type(yesno) ~= type(tonumber) then yesno = require('Module:Yesno') end
    	if yesno(args["delnowiki"] or 'no') then --local delnowiki=require('Module:Delcat').delnowiki --備用
			input_str = mw.text.unstripNoWiki( input_str )
		end
	end
	if args["delmsgnw"] then
    	if type(yesno) ~= type(tonumber) then yesno = require('Module:Yesno') end
    	if yesno(args["delmsgnw"] or 'no') then 
			input_str = mw.text.decode( input_str )
		end
	end
    if args["allexpr"] then --較耗費資源
    	if type(yesno) ~= type(tonumber) then yesno = require('Module:Yesno') end
		allexpr = yesno(args["allexpr"] or 'no')
	end
    count = math.floor(count) --不處理小數
    if count < 1 then count = 1 end --非正整數當作1
    
    local other_num_args = {} --紀錄範圍外參數
    if args["checkMoreArg"] then --處理範圍外參數 (如已定義每組3參數,但想用{{{4}}}存取下一組的第一參數)
    	if type(yesno) ~= type(tonumber) then yesno = require('Module:Yesno') end
    	if yesno(args["checkMoreArg"] or 'no') then --較耗費資源
			local allnumarg_checker = mw.ustring.gmatch(input_str,"%{%{%{[^%{%}%|]+[%}%|]")
			local pre_arg_str = allnumarg_checker() --匹配模板參數
			local find_num_args = {}
			while(pre_arg_str) do
				local arg_name_str = mw.ustring.gsub(pre_arg_str,"[^%d%l%u%+%-%.]",'')
				local arg_name_num = tonumber(arg_name_str)
				if arg_name_num then
					find_num_args[arg_name_num] = true
				end
				pre_arg_str = allnumarg_checker()
			end
			for check_is_other_num_args, _ in pairs(find_num_args) do --紀錄範圍外參數
				if check_is_other_num_args > count or check_is_other_num_args < 1 then
					other_num_args[#other_num_args + 1] = check_is_other_num_args
				end
			end
		end
	end

    local usingConditionalExpressions = false --是否額外再呼叫解析器函數
    if args.usingConditionalExpressions then
    	if type(yesno) ~= type(tonumber) then yesno = require('Module:Yesno') end
    	if yesno(args.usingConditionalExpressions) == true and can_call_parse then
    		usingConditionalExpressions = true
    		input_str = p._get_escape(input_str)
    	end
    end
    local out_frame = mw.getCurrentFrame():getParent() --模板外層
    local out_args = {} --紀錄來自模板外層提供的參數
    local body = ''
    if out_frame then out_args = out_frame.args or out_args end
    
	local valueTable = {} --紀錄可用值 (參數) 提供填入參數之用
    local max_count, i, j = 1, 0, 0 for index, arg_val in pairs(out_args) do if (tonumber(index) or 0) > max_count then max_count = (tonumber(index) or 0) end valueTable[#valueTable + 1] = arg_val end
    max_count = math.ceil(max_count / count); --獲輸入參數之最大數量 (以組為單位)
    
    if hasPrefixArg then max_count = getMaxArgCount(out_args, max_count, arrPrefixArg) end

    local last_one = false
    for i = 1, max_count do
    	local local_arg = {} --紀錄本組的參數
    	local_arg.isLast, local_arg["isFirst"]= '', '' --提供判斷的魔術參數
    	local_arg.count=tostring(max_count)
    	local_arg.argGroupID = tostring(i)
    	local_arg.last = out_args[tostring(i * count)] or out_args[i * count] or args[tostring(count)] or args[count]
    	if local_arg.last == nil and not allexpr then local_arg.last=('{{{' .. tostring(count) .. '}}}') end
    	if i == max_count then local_arg["isLast"] = '1' end
    	if i == 1 then local_arg["isFirst"] = '1' end
		if allexpr and allexpr_count > 99 then
		 	allexpr = false  --紀錄呼叫帶參數的新解析器次數,超過100次停止運作,改用純字串替換完成輸出
		    mw.addWarning( "指定 \"allexpr\" 參數全部展開已超過展開上限,後面的參數將使用低消耗函式填入數值。" )
		end
    	if(#valueTable > 1) then --提供部分須輪播展示的參數組
			local_arg.allRandom = valueTable[math.random( 1, #valueTable )]
			local rand_local = math.random(1, count)
			local rand_it = (i-1) * count + rand_local --查無參數則從外層抓
	   		local_arg.random = out_args[tostring(rand_it)] or out_args[rand_it] or args[tostring(rand_local)] or args[rand_local]
	    		if local_arg.random == nil and not allexpr then local_arg.random=('{{{' .. tostring(rand_local) .. '}}}') end
    	end
    	local is_ready = false --本組參數是否有值
    	for j = 1, count do
    		local it = (i-1) * count + j
    		local_arg.ArgID = tostring(it)
    		local_arg[j] = out_args[tostring(it)] or out_args[it] or args[tostring(j)] or args[j]
    		if local_arg[j] == nil and not allexpr then local_arg[j]=('{{{' .. tostring(j) .. '}}}') end
    		--找到本組參數的其中一個值
    		if out_args[tostring(it)] or out_args[it] then is_ready = true end
    	end
    	
    	if hasPrefixArg then local_arg, is_ready = addPrefixArgToArgList(out_args, args, local_arg, tostring(i), arrPrefixArg, allexpr, is_ready) end
    	
    	for _, other_arg_it in ipairs(other_num_args) do --處理範圍外參數
    		local it = (i-1) * count + other_arg_it
    		local_arg[other_arg_it] = out_args[tostring(it)] or out_args[it] or args[tostring(other_arg_it)] or args[other_arg_it]
    		if local_arg[other_arg_it] == nil and not allexpr then local_arg[other_arg_it]=('{{{' .. tostring(other_arg_it) .. '}}}') end
    	end
	    if is_ready or display_all then --本組參數有值存在才輸出; 如果設定全部顯示也顯示
		    local output_str = tostring(input_str)
		    if allexpr then 
				for org_arg, org_arg_val in pairs(args) do
					if (local_arg[org_arg] or '') == '' then local_arg[org_arg] = org_arg_val end
				end
		    	local working_frame = frame:newChild{ title = frame:getTitle(), args = local_arg }
		    	body=body..working_frame:preprocess( output_str ) --呼叫帶參數的新解析器
		    	allexpr_count=allexpr_count+1
			else
				body=body..p._getFormatingStringByArgument(output_str, local_arg) --使用一般字串取代填入參數
		    end
		    
	   	end
	end

	if usingConditionalExpressions and can_call_parse then
    	body = frame:preprocess( body )
    end
    return body
end

--內部讀取確定參數
function p.getFormatingStringByArgument(frame)
	-- For calling from #invoke.
    local args
    local can_call_parse = false
    if frame == mw.getCurrentFrame() then
        -- We're being called via #invoke. The args are passed through to the module
        -- from the template page, so use the args that were passed into the template.
        if lib_arg.getArgs == nil then lib_arg = require('Module:Arguments') end
        args = lib_arg.getArgs(frame) --frame.args
        can_call_parse = true
    else
        -- We're being called from another module or from the debug console, so assume
        -- the args are passed in directly.
        args = frame
    end
    local targs = {}
    for arg, val in pairs(args) do
    	if tonumber(arg) ~= 1 then
    		targs[arg] = val
    	end
    end
    local input_str = args[1] or args['1']
    local usingConditionalExpressions = false
    if args.usingConditionalExpressions then
    	if type(yesno) ~= type(tonumber) then yesno = require('Module:Yesno') end
    	if yesno(args.usingConditionalExpressions) == true and can_call_parse then
    		usingConditionalExpressions = true
    		input_str = p._get_escape(input_str)
    	end
    end
    local output_str = p._getFormatingStringByArgument(input_str, targs)
    if usingConditionalExpressions and can_call_parse then
    	output_str = frame:preprocess( output_str )
    end
    return output_str
end

function p._findNullArgument(str, args)
	local result = str or "*{{{1}}}\n"
	local emptylist = {}
	for k,v in pairs(args) do
		local ke = p._getEscapeString(k)
		local pattern = "%{%{%{%s*".. ke .. "%s*%}%}%}"
		if mw.ustring.find(result, pattern) then
			if string.gsub(string.gsub(v or '' , ' ', "") , "%s$", "") == '' then emptylist[k] = true end
		else emptylist[k] = true end
		result = mw.ustring.gsub(result, pattern, tostring(args[k]) )
	end
	return emptylist
end

--格式化字串的處理,會將特殊的 {{{}}} 轉換
function p._getEscapeString(str) str = mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(str, "%%", "%%%%"), "%]", "%]"), "%{", "%{"), "%}", "%}"), "%[", "%["), "%)", "%)"), "%-", "%-"), "%^", "%^"), "%$", "%$"), "%(", "%("), "%.", "%."), "%*", "%*"), "%+", "%+"), "%|", "%|"); return str; end function p._getFormatingStringByArgument(str, args)
	local result = str or "*{{{1}}}\n"
	for k,v in pairs(args) do
		local ke = p._getEscapeString(k)
		result = mw.ustring.gsub(result, "%{%{%{%s*".. ke .. "%s*%}%}%}", tostring(args[k]) )
	end
	return result
end

function p._get_escape(str)
 	local escapestrs = mw.text.split( str, '\\\\')
 	local escapechars = {{"\\n", "\n"},{"\\r", "\r"},{"\\t", "\t"},{"\\b", "\b"},{"\\f", "\f"},{"\\v", "\v"},{"\\", ""},}
 	for i,text in pairs(escapestrs) do 
 		for _, escape_result in ipairs(escapechars) do
 			escapestrs[i] = mw.ustring.gsub(escapestrs[i], escape_result[1], escape_result[2])
 		end
 	end
 	return table.concat(escapestrs, '\\')
end

function p.arg_to_spstr(frame)
	-- For calling from #invoke.
    local args
    if frame == mw.getCurrentFrame() then
        -- We're being called via #invoke. The args are passed through to the module
        -- from the template page, so use the args that were passed into the template.
        if lib_arg.getArgs == nil then lib_arg = require('Module:Arguments') end
        args = lib_arg.getArgs(frame)
    else
        -- We're being called from another module or from the debug console, so assume
        -- the args are passed in directly.
        args = frame
        if type(frame) ~= type({}) then args = {frame} end
    end
    body=''
    for k,v in pairs(args) do
		body = body .. "<參數分隔/>" .. k .. "<參數值/>" .. v
	end
	return body
end

function p.pass_spstr(frame)
	-- For calling from #invoke.
    local args
    if frame == mw.getCurrentFrame() then
        -- We're being called via #invoke. The args are passed through to the module
        -- from the template page, so use the args that were passed into the template.
        if lib_arg.getArgs == nil then lib_arg = require('Module:Arguments') end
        args = lib_arg.getArgs(frame) --frame.args
    else
        -- We're being called from another module or from the debug console, so assume
        -- the args are passed in directly.
        if type(frame) ~= type({}) then args = {frame}
        else args = frame end
    end
    local input_text = args['code'] or ''
    if args["delnowiki"] then
    	if type(yesno) ~= type(tonumber) then yesno = require('Module:Yesno') end
    	if yesno(args["delnowiki"] or 'no') then --local delnowiki=require('Module:Delcat').delnowiki --備用
			input_text = mw.text.unstripNoWiki( input_text )
		end
	end
	if args["delmsgnw"] then
    	if type(yesno) ~= type(tonumber) then yesno = require('Module:Yesno') end
    	if yesno(args["delmsgnw"] or 'no') then 
			input_text = mw.text.decode( input_text )
		end
	end
    local input_text = p._get_escape(input_text)
    local input_args = { text = input_text }
    local input_title = args['TemplateTitle']
    local spilt_args = mw.text.split( args['args'] or '', '<參數分隔/>')
    local working_frame = frame
	if type(yesno) ~= type(tonumber) then yesno = require('Module:Yesno') end
	local child_args = {}
	for aname, aval in pairs(args) do child_args[aname] = aval end
    if yesno(args.useParent or 'no') == true then 
    	working_frame = frame:getParent() or frame 
    	for aname, aval in pairs(working_frame.args) do child_args[aname] = aval end
    end
    for _,v in ipairs(spilt_args) do
    	local text = mw.text.trim(v)
    	if text ~= '' then
    		local spilt_kv = mw.text.split( text, '<參數值/>')
    		local key = mw.text.trim(spilt_kv[1])
    		local nkey = tonumber(key)
    		if key ~= '' then input_args[key],child_args[key] = spilt_kv[2],spilt_kv[2] end
    		--if nkey ~= nil then input_args[nkey],child_args[nkey] = spilt_kv[2],spilt_kv[2] end
    	end
    end
    working_frame = working_frame:newChild{ title = input_title or working_frame:getTitle(), args = child_args }
	return working_frame:preprocess(input_args)
end

function addPrefixArgToArgList(in_arg1, in_arg2, out_args, item_id, prefixs, allexpr, is_ready)
	for ____,prefix in pairs(prefixs) do 
		local argName = prefix .. item_id
		out_args[prefix] = in_arg1[argName] or in_arg2[argName]
		if out_args[prefix] then is_ready = true end
		if out_args[prefix] == nil and not allexpr then out_args[prefix]=('{{{' .. prefix .. '}}}') end
	end
	return out_args, is_ready
end

function p.templateArgWarp(frame, warpargs)
	local args, new_name
    if frame == mw.getCurrentFrame() then
        -- We're being called via #invoke. The args are passed through to the module
        -- from the template page, so use the args that were passed into the template.
        new_name = frame.args['1'] or frame.args[1] or frame.args.name or frame.args.Name
        local parent = frame:getParent() or {['args']={}}
        args = parent.args
    else
        -- We're being called from another module or from the debug console, so assume
        -- the args are passed in directly.
        if type(frame) == type('string') then new_name = frame
        elseif type(frame) == type({}) then new_name = frame['1'] or frame[1] or frame.name or frame.Name
        end
        args = warpargs or mw.getCurrentFrame().args
    end
    local result = require('Module:Template invocation').invocation(new_name, args)
    local args_other = mw.getCurrentFrame().args.args
    if args_other then result = mw.ustring.gsub(mw.text.trim(result),"}}$",'') .. '|' .. args_other .. "}}" end
    if mw.getCurrentFrame().args["preprocess"] then
    	if type(yesno) ~= type(tonumber) then yesno = require('Module:Yesno') end
    	if yesno(mw.getCurrentFrame().args["preprocess"] or 'no') then
			return mw.getCurrentFrame():preprocess(result)
		end
	end
	return result
end

return p