模組:TemplateParameters
此模块被引用於約910,000個頁面。 為了避免造成大規模的影響,所有對此模块的編輯應先於沙盒或測試樣例上測試。 測試後無誤的版本可以一次性地加入此模块中,但是修改前請務必於討論頁發起討論。 模板引用數量會自動更新。 |
本模組可以將參數填入以普通模板參數定義的格式化字符串
設計緣由
自定義的模板參數讀取之構想早在2018年11月就已提出。當時數學類條目面臨大量WP:114.27的似是而非的破壞,不易查證真偽,因此建立了Module:Number(及複變部分的Module:Complex Number,如高斯整數分解)來自動計算並輸出數學性質,避免遭WP:114.27竄改為錯誤資訊而不易查證(例如多1少1的破壞難以察覺和驗證),但有用戶認為輸出固定字串無法被修改或添加註解、參考文獻不妥,因此出現了格式化字符串的需求,而部分用戶認為,應儘量是要設計一般維基人都能編輯的語法,因此最後決議選擇wikitext模板中的模板參數語法,但當時此功能獨自分散在各個需要格式化字符串功能的模組裡,例如Module:Number、Module:PeriodicTable和Module:Delcat等地方。
最初的版本僅包含簡單文字替換,只能處理已知數量的參數,而在2019年6月,有用戶問到能否不寫新的模組、也不使用大量{{#if:}}
實現,因此認為可以把自動讀取參數填入格式化字符串的功能獨立出來(即建立本模組),順便提供些特殊參數解析功能,如參數批次trim和數字解析功能(根據WP:TG1請求用於{{Infobox 日本的町村}})。
使用說明
本模組主要是提供wikitext模板中的模板參數一個API,而非設計給其他Lua函數使用,因此專門給lua呼叫的函數較少。
本模組包括的函數如下:
- getFormatingStringByArgument :固定參數的格式化字符串方法
- FormatingArguments :不定參數的格式化字符串方法
- containsNumber :判斷參數是否包含數字
- getNumberValue :析出參數包含的數字
- arg_to_spstr與pass_spstr :提供不定參數迭代(跨層傳遞)的一種方式
- argTrim :參數批次Trim 或 已展開的參數避免
|
後方內容遺失的方法 - listArguments :列出所有接收到的參數,提供模板可見的除錯方式。
- call :將模板參數轉為Lua參數的方法,提供呼叫一些不支援以模板參數呼叫之Module的功能。
- templateArgWarp :將模組調用轉換為另一個模板且輸入參數為從外部模板(父模板)讀取之參數的未解析字串,用於替換引用。
- makeTemplateParameter :產生模板參數的未解析字串,如
{{{arg}}}
用法
getFormatingStringByArgument
功能:直接將參數透過簡單文字替換填入形如{{{X}}}
的位置內
{{#invoke:TemplateParameters | getFormatingStringByArgument | 在{{{學科}}}中,{{{名稱}}}是一種{{{種類}}} | 學科=數學 | 名稱=三角形 | 種類=多邊形}}
- 顯示為「在數學中,三角形是一種多邊形」
- 用途
- Module:Number中,客製化的性質輸出格式。
- Module:PeriodicTable中,選擇要在元素週期表輸出的元素性質。
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}}}
。
- 指定了數量為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:}}}!}}
、{{[[Template:|]]}}
、{{ {{{|safesubst:}}}=}}
等) - 若覺得編輯困難可以使用 子頁面搭配
{{msgnw:模板名}}
以及| delmsgnw =
參數 來完成- 詳細使用方式可參考User:A2569875/SubstTest搭配User:A2569875/SubstTest/Cell的範例。
- 使用
{{msgnw:模板名}}
需要(※)注意的是:由於使用msgnw,因此<noinclude></noinclude>等標記無效,因此不應該在該子模板中直接加入註解或{{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
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化的物件導致失敗。
選項:
注意事項:若指定的模組中函數本身已支援模板調用則可能出錯。因為支援模板調用的模組通常是用第一參數接收所有模板參數,而此函數會先將模板參數全部展開才傳入,可能會有參數無效或者第一參數無法使用(如支援模板調用的第一參數預期是一個包含所有模板參數資訊的lua table,但本函數將參數展開可能導致第一參數變為字符串或數字)等問題。
templateArgWarp
功能:替換引用時,將自身替換為另一個模板與參數
用途:如防止替換引用(替換引用又換回自身)或替換引用時轉成其他等價模板等。
參數:第一參數為要替換成的模板名稱。只有第一參數有效,其餘參數皆為從父模板讀取。
makeTemplateParameter
功能:產生模板參數的未解析字串,如{{{arg}}}
用途:搭配{{虛擬模板}}作為替換引用的替代方案,參見{{參數}}。
- 輸入
{{#invoke:TemplateParameters|makeTemplateParameter|arg|default}}
- 顯示為:「{{{arg|default}}}」
用途
- Module:Number中,客製化的性質輸出格式。
- Module:PeriodicTable中,選擇要在元素週期表輸出的元素性質。
相關模板
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