local p = {}
local lib_arg = {}
--檢查指定物件是否存在此參數
local function checkParaExist(obj, para_list)
local result = false --先假定參數不存在
--讀取待檢查之參數列表中欲檢查的所有參數名稱
for _, target_par_name in ipairs(para_list) do
--物件存在該參數,紀錄該物件中有存在待搜尋的參數
result = result or (type(obj[target_par_name]) ~= type(nil))
end
--返回最終判斷有沒有參數的那個結果
return result
end
--建構不定參數模板的語法
function p.build_template(frame)
local args, working_frame
local inner_args
--參數和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})
inner_args = frame.args
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
inner_args = args
end
local empty_string = "" --空字串
local parent_arg_sym = "@" --"@"是父參數計數
local current_arg_sym = "$" --"$"是不定參數計數
local can_add_variadic_arg = true
local module_warn = "Module:TemplateVariadicArgumentSingle警告:"
--目前本模組是通用版,不限定於Template:US_county_navigation_box的使用
--但預設值暫且設定為 Template:US_county_navigation_box
local core_template_name = inner_args._core_template or "Template:US_county_navigation_box/core"
local core_insert_point = inner_args._core_insert_point or "__LUA_INSERT_CORE__"
local parent_start = tonumber(inner_args._parent_start or "1") or 1
local core_insert_code = inner_args._core_insert_code or "\n\n" ..
"|group"..parent_arg_sym.." = {{{title"..current_arg_sym.."|}}}\n".. --Template:US_county_navigation_box的不定參數語法
"|list"..parent_arg_sym.." = {{#if:{{{body"..current_arg_sym.."|}}}|<div>\n"..
"{{{body"..current_arg_sym.."}}}\n"..
"</div>}}"
core_insert_code = mw.text.decode(mw.text.unstripNoWiki(core_insert_code))
--讀取所要搜索的不定參數
local core_args_input, core_args = (mw.text.split(inner_args._core_args or "body,title",",") or {inner_args._core_args}), {}
for key, value in ipairs(core_args_input) do --掃描所有待檢查的不定參數
local trimed_value = mw.text.trim(value) --參數名稱不該有頭尾空格
--忽略參數名稱空白的參數
if trimed_value ~= empty_string then
local arg_value = mw.ustring.lower(trimed_value) --統一以小寫檢查不定參數
core_args[#core_args + 1] = arg_value --記載進待檢查的不定參數表
--處理含底線的參數
underline, underline_count = mw.ustring.gsub(arg_value, '_', ' ')
if underline_count > 0 and underline ~= arg_value then --如果有含底線的參數
core_args[#core_args + 1] = underline --同樣記載進待檢查的不定參數表
end
end
end
if #core_args < 1 then --找無可用參數
mw.addWarning(module_warn..'在模板 "'..core_template_name..'" 中未提供可供使用的不定參數')
can_add_variadic_arg = false
end
local template_obj = mw.title.new( core_template_name ) --載入模板樣板
if not template_obj then --如模板樣板載入失敗,返回空
mw.addWarning(module_warn..'模板 "'..core_template_name..'" 載入失敗')
return empty_string
end
local template_body = template_obj:getContent() --讀取模板樣板
if mw.text.trim(template_body or empty_string) == empty_string then --如果讀取不到模板樣板,返回空
mw.addWarning(module_warn..'模板 "'..core_template_name..'" 沒有內容')
return empty_string
end
if mw.text.trim(core_insert_point) == empty_string then
mw.addWarning(module_warn..'不定參數插入點不能為空')
can_add_variadic_arg = false
end
--關於noinclude、includeonly和onlyinclude的regexp
local re_noinclude_head = "<%s*[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]%s*>"
local re_noinclude_tail = "<%s*/%s*[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]%s*>"
local re_noinclude = "<%s*/?%s*[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]%s*/?%s*>"
local re_includeonly = "<%s*/?%s*[Ii][Nn][Cc][Ll][Uu][Dd][Ee][Oo][Nn][Ll][Yy]%s*/?%s*>"
local re_onlyinclude = "<%s*/?%s*[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]%s*/?%s*>"
local re_removes = {{"noinclude",re_noinclude}, {"includeonly",re_includeonly}, {"onlyinclude",re_onlyinclude}}
--摘除模板樣板中的noinclude與當中的內容
local noinclude = mw.ustring.find(template_body, re_noinclude_head)
local _i = 1
while noinclude do --如果找到<noinclude>
--尋找</noinclude>
local _, noinclude_end = mw.ustring.find(template_body, re_noinclude_tail)
--如無</noinclude>,視為noinclude到頁面結尾
noinclude_end = noinclude_end or -1
--去除掉<noinclude>...</noinclude>與其之間的內容
template_body = mw.ustring.sub(template_body, 1, noinclude-1) .. ((noinclude_end < 0) and empty_string or mw.ustring.sub(template_body, noinclude_end + 1, -1))
--尋找下一個<noinclude>
noinclude = mw.ustring.find(template_body, re_noinclude_head)
_i = _i + 1 --避免無窮迴圈
if _i > 100 then mw.addWarning(module_warn..'<noinclude>過多') break end
end --<noinclude></noinclude>內容移除完畢
--移除其他會影響解析功能的標籤
for _, to_remove in pairs(re_removes) do _i = 1
--處理巢狀引用
local remove_count = 1 --紀錄移除數量
while (tonumber(remove_count) or 0) > 0 do --如果還有殘留的相關標籤
--移除殘留的相關標籤,並記錄移除數量
template_body, remove_count = mw.ustring.gsub(template_body, to_remove[2], empty_string)
_i = _i + 1 --避免無窮迴圈
if _i > 100 then mw.addWarning(module_warn..'<'..to_remove[1]..'>過多') break end
end --如果沒有殘留的相關標籤則結束迴圈
end
local body = empty_string --用於儲存生成的不定參數語法
if not mw.ustring.find(template_body, core_insert_point) --如果找無不定參數插入點
or not can_add_variadic_arg then --或者無法找到待搜索的不定參數
body = template_body --不做處理
if not mw.ustring.find(template_body, core_insert_point) then
mw.addWarning(module_warn..'在模板 "'..core_template_name..'" 中找不到不定參數的插入點')
end
else --如果有找到不定參數插入點
local row_list = {} --用於儲存有多少組不定參數
for key, value in pairs(args) do --查閱所有已輸入參數
local par_name_id, _ = mw.ustring.find(key,"[%+%-]?%d+%.?%d-$") --參數字尾是否存在數字
local par_name = par_name_id and mw.ustring.sub(key, 1, par_name_id - 1) or nil --取得參數名稱
local par_id = par_name_id and mw.ustring.sub(key, par_name_id, -1) or nil --取得參數數字字串
local par_num = tonumber(par_id) --將參數數字轉換成數字
if par_name and par_id then --如果參數名稱和參數數字都存在,才嘗試匹配參數
local par_name_check = mw.ustring.lower(par_name) --統一以小寫匹配
for __, target_par_name in ipairs(core_args) do
if par_name_check == target_par_name then --匹配到參數
if type(row_list[par_num]) == type(nil) then row_list[par_num] = {}end --新的一組不定參數
row_list[par_num][target_par_name] = value --存入參數
end
end
end
end
--將讀到的不定參數照數字由小到大順序輸出
local row_keys = {} --用於儲存有多少組不定參數的「數字id」
for key, _ in pairs(row_list) do row_keys[#row_keys + 1] = key end --記錄所有不定參數的「數字id」
table.sort(row_keys) --排序所有不定參數的「數字id」
local body = empty_string --準備生成不定參數語法
local j = parent_start --父參數獨立計數
for _i = 1, #row_keys do --將所有不定參數組跑一遍
local i = row_keys[_i] --從排序好的「數字id表」取出不定參數的id
local para_obj = row_list[i] --取得當前參數組物件
if para_obj then --如果是有效的不定參數組
if checkParaExist(para_obj, core_args) then --如果該不定參數組有輸入其一參數
--生成這組參數的對應語法
body = body .. mw.ustring.sub(mw.ustring.gsub(' '..core_insert_code..' ',
"([%"..current_arg_sym.."%"..parent_arg_sym.."])(.)", function(sym,txt)
if sym == parent_arg_sym then --父參數計數
if txt ~= parent_arg_sym then return j .. txt --父參數計數的數量
elseif txt == parent_arg_sym then return parent_arg_sym --跳脫的符號
end
elseif sym == current_arg_sym then --主參數計數
if txt ~= current_arg_sym then return i .. txt --主參數計數的數量
elseif txt == current_arg_sym then return current_arg_sym --跳脫的符號
end
end
return sym..txt --其餘情況原封不動輸出
end),2,-2)
end
j = j + 1 --父參數獨立計數
end
end --不定參數語法生成完畢。
--尋找模板樣板中的不定參數組的插入點,並插入不定參數語法
template_body = mw.ustring.gsub(template_body, core_insert_point, body)
end
--解析要使用外層解析器 (才讀得到那些不定參數,內層是{{#invoke:}})
working_frame = working_frame:getParent() or working_frame
--將包含了不定參數的模板展開
template_body = working_frame:preprocess(template_body)
return template_body --回傳展開了不定參數的模板結果
end
return p