模块:TemplateVariadicArgumentSingle

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