模組:PJBSClass/main

本页使用了标题或全文手工转换
被永久保护的模块
维基百科,自由的百科全书
文档图示 模块文档[查看] [编辑] [历史] [清除缓存]

本模組的功能:

  • 透過調用Module:PJBSClass/page間接調用Module:PJBSClass
    • 讀取{{PJBS}}中的評級值
    • 自動透過命名空間盼判斷評級值
  • 進階自動評級
    • 透過解析頁面內容判斷頁面是否為軟重定向
    • 透過解析頁面內容判斷頁面是否為消歧義
  • 判斷專題橫幅是繼承{{PJBS}}中的評級值還是自定評級值並加入維護分類
  • 輔助{{PJBS}}中的一些與專題評級值相關的功能

用法

本模組主要用於讀取頁面中定義於{{PJBS}}或專題橫幅中的評級值,或自動根據命名空間或維基代碼判斷評級。

維基代碼可判斷的評級值
判斷種類 判斷依據 對應的評級
典范条目 典范条目模板(如{{Featured article}})  典范
优良条目 优良条目模板(如{{Good article}})  优良
特色列表 特色列表模板(如{{Featured list}})  特色列表
特色图片 特色图片模板(如{{Featured picture}})  特色图片
重定向 頁面物件isRedirect欄位。  重定向
軟重定向 軟重定向模板(如{{軟重定向}})  重定向
沙盒 沙盒模板(如{{Sandbox}})  沙盒
消歧義 消歧義模板(如{{消歧義}})
以及相關魔術字(如__DISAMBIG__
 消歧义
列表 名稱以「列表」結尾的頁面  列表
同類索引 同類索引模板(如{{同類索引}})  同类索引
小作品 條目的位元組數是否小於2000
(2000~3000間的過渡數值不自動評)
 小作品
命名空間可判斷的評級值
命名空間 前綴 對應的評級
模板 Template: 模板
模組 Module: 模块
分類 Category: 分类
文件 File: 文件
草稿 Draft: 草稿
主題 Portal: 主题
專題 PJ: 专题
用戶 User: 用户
說明 Help: 使用说明
MediaWiki MediaWiki: 界面
字幕 TimedText: 非条目
媒體 Media: 非条目
特殊 Special: 非条目

模組中的主要函數

getClass
模組調用:p.getClass(class_name)
模板調用:{{#invoke:PJBSClass/main|getClass| class_name | auto=yes/no | inheritance=yes/no | demo=yes/no }}
間接調用Module:PJBSClass#getClassByPage
取得所在頁面的評級,可以輸入一個評級值(class_name)作為覆蓋與否的判斷依據。auto為是否要以自動判斷的評級為優先;inheritance為是否要以繼承值為優先;demo為是否為模板演示。
getClassAuto
模組調用:p.getClassAuto(page_name, class_default, demo)
模板調用:{{#invoke:PJBSClass/main|getClassAuto| class_name | class=class_default | demo=yes/no }}
間接調用Module:PJBSClass#getAutoClass,並額外加入了從維基代碼判斷評級的功能,見維基代碼可判斷的評級值
自動判斷名為「page_name」頁面的評級,若「page_name」未輸入則判斷所在頁面的評級。若無法判斷則回傳「class_default」。
class_json
模組調用:p.class_json()
模板調用:{{#invoke:PJBSClass/main|class_json}}
提供Module:PJBSClass#{{Module:PJBSClass/page}}轉換Lua到JSON的函數。此函數沒有參數,僅回傳所在條目mw.loadData("Module:PJBSClass/page")JSON結果。模板調用應使用{{Module:PJBSClass/page|json=yes}}
getPageType
判斷該級別的分類頁面種類。

模組中的元函數

_getClass
模組調用:p._getClass(class_name, auto, inheritance, demo)
模板調用:{{#invoke:PJBSClass/main|_getClass| class_name | auto=yes/no | inheritance=yes/no | demo=yes/no }}
#getClass
_getProjects
模組調用:p._getProjects(html)
模板調用:(無法使用)
解析「html」中的評級模板,列出當中的所有專題名稱。

專題橫幅內部函數

subjectPageTitle
模組調用:p.subjectPageTitle(page_name)
模板調用:{{#invoke:PJBSClass/main|subjectPageTitle| page_name }}
無論名為「page_name」頁面是否是討論頁都會回傳其所對應之主要頁面的名稱。如果「page_name」不是討論頁,則回傳原頁面名稱。如果「page_name」未輸入則輸出所在頁面(無論是否是討論頁)所對應之主要頁面的名稱。如果所在頁面不是討論頁,則回傳所在頁面名稱。
listProjects
模組調用:p.listProjects(html, code, comma, max_num)
模板調用:{{#invoke:PJBSClass/main|listProjects| html | code=code | comma=comma | tail=tail | max_num=max_num }}
列舉「html」中所有專題橫幅的專題,主要由{{PJBS}}調用。「code」為輸出的格式,$1代表專題名稱、$2代表分隔符號。「comma」為分隔符號。「tail」為自動加在尾端的文字,預設為專題。「max_num」為最大列舉數量。
checkClassInput
檢查是否評級繼承與否
wrongClass
檢查是否誤將橫幅與評級輸入在一起
hasCustomClassName
檢查專題橫幅有無自訂評級級別
customClassName
處理專題橫幅的自訂評級級別
processNotice
將輸入到{{PJBS}}的專題橫幅處理成符合{{PJBS}}的樣式,並移除專題橫幅內自帶的提示模板,{{PJBS}}會使用#getNotice將之統一加入。同時解析內容添加適當的維護分類。
getNotice
解析輸入到{{PJBS}}的專題橫幅有無橫幅內自帶的提示模板,移除重複的提示模板後,加以顯示。

判斷頁面屬性函數

is_softredirect
判斷頁面是否為軟重定向。
is_disambiguation
判斷頁面是否為消歧義頁。
is_sandbox
判斷頁面是否為沙盒。

可供其他模組調用的私有函數

_re_softredirect_template
匹配軟重定向模板的正則函數。
_re_disambiguation_title
匹配消歧義標題的正則函數。
_re_disambiguation_template
匹配消歧義模板的正則函數。
_re_sandbox_template
匹配沙盒模板的正則函數。
_remove_wikiproject_flag
用於避免消歧義判斷模板循環的跳脫函數,會破壞輸入的內容、移除魔術字,僅保留模板,以供展看測試有無消歧義魔術字。

除錯用函數

_checker
檢查函數是否會出錯的函數。如無出錯,將頁面加入分類:可以正常讀取評級的條目;如出錯,將頁面加入分類:無法正常讀取評級的條目
check_disambig_process
列出#is_disambiguation的中間過程,以便除錯。

local p = {}
local yesno = require('Module:Yesno')
local cat_auto_class = "自動評級的頁面"
local cat_use_user_defined_class = "使用自訂專題評級的條目"
local cat_auto_disambig_class = '自動評級的消歧義級頁面'
local cat_auto_softredirect_class = '自動評級的軟重定向頁面'
local class_normalize = require("Module:Class/convert")._main
local TrackingCategory = require('Module:TrackingCategory')

local function isEmptyString(str) return mw.text.trim(tostring(str or '')) == '' end

--取得欲評級頁面的標題
function p.subjectPageTitle(input_data)
	local page_name = input_data
	local is_lua = true
	if type((((type(input_data)==type(0)) and {} or input_data) or {}).args) ~= type(nil) then --input_data is a frame
		page_name = input_data.args['1'] or input_data.args[1]
		is_lua = false
	end
	local page = (type(page_name) ~= type(nil)) and ({pcall(mw.title.new, page_name)})[2] or mw.title.getCurrentTitle()
	local subject_page = (page or {}).subjectPageTitle
	if is_lua then return subject_page end
	return subject_page.fullText
end

--讀取所在頁面的評級 函數本體
function p._getClass(input_data, _auto_first, _inheritance_first, _demo, _napagens)
	local class_name = input_data
	local auto_first = _auto_first
	local inheritance_first = _inheritance_first
	local demo = _demo
	local no_cat = demo
	local napagens = _napagens
	if type((input_data or {}).args) ~= type(nil) then --input_data is a frame
		class_name = input_data.args['1'] or input_data.args[1]
		auto_first = yesno(input_data.args.auto)
		inheritance_first = yesno(input_data.args.inheritance)
		demo = yesno(input_data.args.demo)
		no_cat = yesno(input_data.args.no_cat) or demo
		napagens = input_data.args.napagens
	end
	--使用一頁只讀取一次的函數,讀取目前頁面中WPBS的評級
	local WPBS_data = mw.loadData("Module:PJBSClass/page")
	local WPBS_class = WPBS_data.class or '' --讀取到的目前頁面中WPBS的評級
	if auto_first or --如果 "自動評級" 優先,或
		(inheritance_first and (isEmptyString(WPBS_class) or WPBS_data.is_auto))  then --"繼承優先" 且{{WPBS}}無值且自動
		--嘗試進行自動判斷評級作業
		local success, auto_class = pcall(p.getClassAuto, nil, (class_name == 'no_input' or class_name == '¬') and '' or class_name, demo, napagens)
		if success then --自動判斷評級作業成功
			class_name = (not isEmptyString(auto_class)) and auto_class or class_name
		end
	end
	if class_name ~= nil and --如果並非 "未輸入" 級
		(mw.ustring.lower(class_name or '') ~= 'no_input') and
		(mw.ustring.lower(class_name or '') ~= '¬') 
	then
		--WPBS的級空白就返回輸入的評級
		if mw.text.trim(WPBS_class) == '' then return class_name end
		--找不到WPBS模板也返回輸入的評級
		if not WPBS_data.has_WPBS then return class_name end
		--評級繼承與否判斷作業
		return p.checkClassInput(WPBS_class, class_name, no_cat)
	end
	return WPBS_class
end

--讀取所在頁面的評級 供呼叫的函數
function p.getClass(input_data)
	local success, result = pcall(p._getClass, input_data)
	if not success then return "" end
	return result
end

--匹配軟重定向模板的正則函數
local function re_softredirect_template(input_text)
	local text = input_text or ""
	local match_list = {--各種軟重定向模板及其重定向匹配表
		--注意:這裡必須全部使用小寫字母
		--{"軟重定向模板正則", 是否允許前方有其他字, 是否後方不能有其他字}
		{"[軟软]重新?[導导定]向",true},
		{"soft[%s_%-]*redirect",true},
		--{"sr",false, true}, --會誤判{{SR}}
		{"wikispecies[%s_%-]*redirect",false},
		{"wiki[vs]o[yu][ar][gc]e[%s_%-]*redirect",false},
		{"wiki[bq][ou]o[kt][se]?[%s_%-]*redirect",false},
		{"commons[%s_%-]*redirect",false},
		--如有新種軟重定向模板及其重定向被建立請加入於此
	}--致管理員:如有新種軟重定向模板及其重定向被建立,請優先受理軟重定向模板及其重定向匹配項目的編輯請求
	local black_list = { --不是軟重定向模板的模板名稱匹配表
		"soft[%s-_]*redirect[%s-_]*protection",
	}--致管理員:如有新各種軟重定向模板及其重定向被建立,請優先受理軟重定向模板及其重定向匹配項目的編輯請求
	local lotext = mw.ustring.lower(text..'\n') --以小寫進行匹配
	for i = 1,#match_list do --對每一個軟重定向模板及其重定向進行匹配
		local j, k = mw.ustring.find(lotext, "%{%{"..
			(match_list[i][2] and "[^%}]*" or "%s*")..match_list[i][1]..
			(match_list[i][3] and "%s*[%}%|]+" or "[^%}%|]*")
		)
		if j ~= nil then --匹配到了軟重定向模板或其重定向
			local result = mw.ustring.sub(text, j, k)
			result = mw.ustring.match(result, "%{%{%s*([^%}%|]+)[%}%|]*$") --讀取匹配到的名稱
			if result then
				local is_redirect = true
				for i1 = 1,#black_list do --檢查是否在黑名單中
					if mw.ustring.match(mw.ustring.lower(result), black_list[i1]) then
						is_redirect = false --如果不是
						break
					end
				end
				if is_redirect then --是軟重定向模板,回傳模板名稱
					return mw.text.trim(result)
				end
			end
		end
	end
	return nil
end
p._re_softredirect_template = re_softredirect_template

--偵測軟重定向
function p.is_softredirect(input_data)
	local page_name = input_data
	if type((input_data or {}).args) ~= type(nil) then --input_data is a frame
		page_name = input_data.args['1'] or input_data.args[1]
	end
	local page = (type(page_name) ~= type(nil)) and ({pcall(mw.title.new, page_name)})[2] or mw.title.getCurrentTitle()
	local talk_page = (page or {}).talkPageTitle
	local subject_page = (page or {}).subjectPageTitle
	if not subject_page:inNamespaces( --排除沒有軟重定向的命名空間
		mw.title.new("File:Ex").namespace,
		mw.title.new("MediaWiki:Ex").namespace,
		mw.title.new("TimedText:Ex").namespace,
		mw.title.new("Media:Ex").namespace,
		mw.title.new("Special:Ex").namespace,2600)
	then
		if subject_page.isRedirect then return false end --"硬" 重定向不是 "軟" 重定向
		local body = ""
		if subject_page.getContent then body = subject_page:getContent() or "" end
		local body_len = mw.ustring.len(body)
		if body_len > 10000 or body_len <= 0 then return false end --依據常識,沒有那麼大的軟重定向頁
		if re_softredirect_template(body) then --尋找軟重定向的模板
			return true
		end
	end
	return false
end

--匹配消歧義標題的正則函數
local function re_disambiguation_title(input_text)
	local text = input_text or ""
	local match_list = {
		--注意:這裡必須全部使用小寫字母
		"%([消分]歧[義义頁页]?%)",
		"%([消分]歧[義义][頁页]?%)",
		"%(disambiguous%)",
		"%(disambiguation%)",
		"%(disambi?g?%)",
	}
	local lotext = mw.ustring.lower(text)
	for i = 1,#match_list do
		local j, k = mw.ustring.find(lotext, "%s+"..match_list[i])
		if j ~= nil then 
			local result = mw.ustring.sub(text, j, k)
			result = mw.ustring.match(result, "%(([^%)]+)%)$")
			return result
		end
	end
	return nil
end
p._re_disambiguation_title = re_disambiguation_title

--匹配消歧義模板的正則函數
local function re_disambiguation_template(input_text)
	local text = input_text or ""
	local match_list = {--各種消歧義模板及其重定向匹配表
		--注意:這裡必須全部使用小寫字母
		--{"消歧義模板正則", 是否允許前方有其他字, 是否後方不能有其他字}
		{"[^要][消分]歧[義义頁页]?",true},
		{"[^要][消分]歧[義义][頁页]?",true},
		{"[消分]歧[義义頁页]?",false},
		{"[消分]歧[義义][頁页]?",false},
		{"dab",false,true},
		{"disambiguous",true},
		{"disambiguation",true},
		{"disambi?g?",true},
		{"aimai",false,true},
		{"disambiguation%s*page",true},
		--如有新消歧義模板及其重定向被建立請加入於此
	}--致管理員:如有新消歧義模板及其重定向被建立,請優先受理新消歧義模板及其重定向匹配項目的編輯請求
	local black_list = { --不是消歧義模板的模板名稱匹配表
		"disambiguation[%s-_]*need", "disambiguous[%s-_]*need",
		"r to disambig", "disamb[%s-_]*link",
		"disambig[%s-_]*list", "disambig[%s-_]*nav",
		"title[%s-_]*without[%s-_]*disambig","[條条]目", "[专專][题題]",
		"disambig[%s-_]*class", "改為", "星章", "需要", "[链連][結接]",
		--如有新的非消歧義模板但會被匹配到的請加入於此
	}--致管理員:如有新非消歧義模板被建立,請優先受理其編輯請求
	local lotext = mw.ustring.lower(text..'\n') --以小寫進行匹配
	for i = 1,#match_list do --對每一個消歧義模板及其重定向進行匹配
		local j, k = mw.ustring.find(lotext, "%{%{"..
			(match_list[i][2] and "[^%}]*" or "%s*")..match_list[i][1]..
			(match_list[i][3] and "%s*[%}%|]+" or "[^%}%|]*"))
		if j ~= nil then --匹配到了消歧義模板或其重定向
			local result = mw.ustring.sub(text, j, k)
			result = mw.ustring.match(result, "%{%{%s*([^%}%|]+)[%}%|]*$") --讀取匹配到的名稱
			if result then
				local is_correct = true
				for i1 = 1,#black_list do --檢查是否在黑名單中
					if mw.ustring.match(mw.ustring.lower(result), black_list[i1]) then
						is_correct = false --如果不是
						break
					end
				end
				if is_correct then --是消歧義模板,回傳模板名稱
					return mw.text.trim(result)
				end
			end
		end
	end
	return nil
end
p._re_disambiguation_template = re_disambiguation_template

local function remove_wikiproject_flag(input_text)
	local text = input_text or ""
	local match_list = {
		"[Ww][Ii][Kk][Ii]%s*[Pp][Rr][Oo][Jj][Ee][Cc][Tt]", 
		"[Ww][Pp][Jj]", "[Ww][Pp][Bb][Ss]","[Pp][Jj][Bb][Ss]",
		"[維维]基[专專][题題]", "[专專][题題]", --取消專題橫幅
		"[<>%[%]]","#%s*[Tt][Aa][Gg]%s*:",--取消參考文獻
		"%{%{%s*[Tt][Aa]%s*[%}%|]",":[Tt][Aa][^0-9a-zA-Z]",
		"#%s*[Ii][Nn][Vv][Oo][Kk][Ee]%s*:","/",
		"[Nn][Oo][Tt][Ee]%s*[TtAa][TtAa]", --取消{{NoteTA}}
		"DISPLAYTITLE", "[Tt]itle", "[Ff]romis","僻字", --取消DISPLAYTITLE
		"[Bb]ox", "[Ss]ubpagelist", "[Dd]ownsize",
		"DEFAULTSORT","DISPLAYTITLE", "NOEXTERNALLANGLINKS", "TOC__", 
		"SECTION__", "TALK__", "LINK__", "GALLERY__", "CAT__", 
		"INDEX__", "REDIRECT__" --取消影響頁面內容的魔術字
	}
	for _, match_pattern in ipairs(match_list) do
		text = mw.ustring.gsub(text,match_pattern, "-")
	end
	return text
end
p._remove_wikiproject_flag = remove_wikiproject_flag

--偵測消歧義頁
function p.is_disambiguation(input_data)
	local page_name = input_data
	if type((input_data or {}).args) ~= type(nil) then --input_data is a frame
		page_name = input_data.args['1'] or input_data.args[1]
	end
	local page = (type(page_name) ~= type(nil)) and ({pcall(mw.title.new, page_name)})[2] or mw.title.getCurrentTitle()
	local talk_page = (page or {}).talkPageTitle
	local subject_page = (page or {}).subjectPageTitle
	if not subject_page:inNamespaces( --排除沒有消歧義的命名空間
		mw.title.new("File:Ex").namespace,
		mw.title.new("Module:Ex").namespace,
		mw.title.new("Template:Ex").namespace,
		mw.title.new("MediaWiki:Ex").namespace,
		mw.title.new("TimedText:Ex").namespace,
		mw.title.new("Media:Ex").namespace,
		mw.title.new("Special:Ex").namespace,2600)
	then
		if subject_page.isRedirect then return false end --重定向不是消歧義
		if re_disambiguation_title(subject_page.fullText or "") then --檢查是否有消歧義標題
			return true
		end
		local body = ""
		if subject_page.getContent then body = subject_page:getContent() or "" end
		local body_len = mw.ustring.len(body)
		if body_len > 10000 or body_len <= 0 then return false end --依據常識,沒有那麼大的消歧義頁
		if re_disambiguation_template(body) then --尋找消歧義的模板
			return true
		end
	
	-- 这块代码过于消耗性能
	--	if not mw.title.equals(subject_page or "", mw.title.getCurrentTitle()) then
			 --頭尾限400字,以節省效能
	--		local check_body = (body_len > 800) and (mw.ustring.sub(body, 1, 400).."{{tl|tl}} }}{{ void | "..mw.ustring.sub(body, -400, -1)) or body
	--		local check_body = remove_wikiproject_flag(check_body) --避免模板循環
	--		if mw.ustring.match((mw.getCurrentFrame():newChild{ title = tostring(mw.title.getCurrentTitle())}):preprocess(check_body.."{{reflist}}")
	--			, "__DISAMBIG__") then --匹配展開的模板有無消歧義的魔術字
	--			TrackingCategory.append(mw.getCurrentFrame(), "使用魔術字判斷消歧義的頁面")
	--			return true
	--		end
	--	end
	end
	return false
end

function p.check_disambig_process(input_data)
	local page_name = input_data
	if type((input_data or {}).args) ~= type(nil) then --input_data is a frame
		page_name = input_data.args['1'] or input_data.args[1]
	end
	local page = (type(page_name) ~= type(nil)) and ({pcall(mw.title.new, page_name)})[2] or mw.title.getCurrentTitle()
	local talk_page = (page or {}).talkPageTitle
	local subject_page = (page or {}).subjectPageTitle
	local body = ""
	if subject_page.getContent then body = subject_page:getContent() or "" end
	local body_len = mw.ustring.len(body)
	if not mw.title.equals(subject_page or "", mw.title.getCurrentTitle()) then
		 --頭尾限400字,以節省效能
		local check_body = (body_len > 800) and (mw.ustring.sub(body, 1, 400).."{{tl|tl}} }}{{ void |"..mw.ustring.sub(body, -400, -1)) or body
		local check_body = remove_wikiproject_flag(check_body) --避免模板循環
		return (mw.getCurrentFrame():newChild{ title = tostring(mw.title.getCurrentTitle())}):preprocess(check_body.."{{reflist}}")
	end
	return body
end

--匹配沙盒模板的正則函數
local function re_sandbox_template(input_text)
	local text = input_text or ""
	local match_list = {--各種沙盒模板及其重定向匹配表
		--注意:這裡必須全部使用小寫字母
		--{"沙盒模板正則", 是否允許前方有其他字}
		{"[請请]注意",false},
		{"沙盒",true},
		{"請勿編輯此行",false},
		{"sandbox",true},
		--如有新沙盒模板及其重定向被建立請加入於此
	}--致管理員:如有新沙盒模板及其重定向被建立,請優先受理新沙盒模板及其重定向匹配項目的編輯請求
	local black_list = { --不是沙盒模板的模板名稱匹配表
		"/沙盒","#invoke:沙盒","/sandbox",
		--如有新的非沙盒模板但會被匹配到的請加入於此
	}--致管理員:如有新非沙盒模板被建立,請優先受理其編輯請求
	local lotext = mw.ustring.lower(text..'\n') --以小寫進行匹配
	for i = 1,#match_list do --對每一個沙盒模板及其重定向進行匹配
		local j, k = mw.ustring.find(lotext, "%{%{"..
			(match_list[i][2] and "[^%}]*" or "%s*")..match_list[i][1].."[^%}%|]*")
		if j ~= nil then --匹配到了沙盒模板或其重定向
			local result = mw.ustring.sub(text, j, k)
			result = mw.ustring.match(result, "%{%{%s*([^%}%|]+)[%}%|]*$") --讀取匹配到的名稱
			if result then
				local is_correct = true
				for i1 = 1,#black_list do --檢查是否在黑名單中
					if mw.ustring.match(mw.ustring.lower(result), black_list[i1]) then
						is_correct = false --如果不是
						break
					end
				end
				if is_correct then --是沙盒模板,回傳模板名稱
					return mw.text.trim(result)
				end
			end
		end
	end
	return nil
end
p._re_sandbox_template = re_sandbox_template

function p.is_sandbox(input_data)
	local page_name = input_data
	if type((input_data or {}).args) ~= type(nil) then --input_data is a frame
		page_name = input_data.args['1'] or input_data.args[1]
	end
	local current_title = mw.title.getCurrentTitle()
	--取得頁面標題物件
	local page = (type(page_name) ~= type(nil)) and ({pcall(mw.title.new, page_name)})[2] or current_title
	local current_page = page.subjectPageTitle or current_title.subjectPageTitle
	if current_page:inNamespace(mw.title.new(":EX").namespace) then return false end
	local check_title = mw.ustring.lower(tostring(current_page.fullText))
	if current_page.isRedirect then return false end
	if mw.ustring.match(check_title, "sandbox") or mw.ustring.match(check_title, "沙盒") then
		return true
	end
	local body = ""
	if current_page.getContent then body = current_page:getContent() or "" end
	body = mw.ustring.sub(body, 1, 400) --檢查頭部
	if re_sandbox_template(body) then --尋找沙盒的模板
		return true
	end
	return false
end

--匹配同類索引模板的正則函數
local function re_sia_template(input_text)
	local text = input_text or ""
	local match_list = {--同類索引模板及其重定向匹配表
		--注意:這裡必須全部使用小寫字母
		--{"同類索引模板正則", 是否允許前方有其他字, 是否後方不能有其他字}
		{"sia",false, true},
		{"set index",false, true},
		{"set index article",false, true},
		{"同[類类]索引",false, true},
		--如有新同類索引重定向被建立請加入於此
	}--致管理員:如有新同類索引模板及其重定向被建立,請優先受理同類索引模板及其重定向匹配項目的編輯請求
	local black_list = { --不是同類索引模板的模板名稱匹配表
		--暫無
	}--致管理員:如有新同類索引模板及其重定向被建立,請優先受理同類索引模板及其重定向匹配項目的編輯請求
	local lotext = mw.ustring.lower(text..'\n') --以小寫進行匹配
	for i = 1,#match_list do --對每一個同類索引模板及其重定向進行匹配
		local j, k = mw.ustring.find(lotext, "%{%{"..
			(match_list[i][2] and "[^%}]*" or "%s*")..match_list[i][1]..
			(match_list[i][3] and "%s*[%}%|]+" or "[^%}%|]*")
		)
		if j ~= nil then --匹配到了同類索引模板或其重定向
			local result = mw.ustring.sub(text, j, k)
			result = mw.ustring.match(result, "%{%{%s*([^%}%|]+)[%}%|]*$") --讀取匹配到的名稱
			if result then
				local is_sia = true
				for i1 = 1,#black_list do --檢查是否在黑名單中
					if mw.ustring.match(mw.ustring.lower(result), black_list[i1]) then
						is_sia = false --如果不是
						break
					end
				end
				if is_sia then --是同類索引模板,回傳模板名稱
					return mw.text.trim(result)
				end
			end
		end
	end
	return nil
end
p._re_sia_template = re_sia_template

--偵測同類索引
function p.is_sia(input_data)
	local page_name = input_data
	if type((input_data or {}).args) ~= type(nil) then --input_data is a frame
		page_name = input_data.args['1'] or input_data.args[1]
	end
	local page = (type(page_name) ~= type(nil)) and ({pcall(mw.title.new, page_name)})[2] or mw.title.getCurrentTitle()
	local talk_page = (page or {}).talkPageTitle
	local subject_page = (page or {}).subjectPageTitle
	if not subject_page:inNamespaces( --排除沒有同類索引的命名空間
		mw.title.new("File:Ex").namespace,
		mw.title.new("Module:Ex").namespace,
		mw.title.new("Template:Ex").namespace,
		mw.title.new("MediaWiki:Ex").namespace,
		mw.title.new("TimedText:Ex").namespace,
		mw.title.new("Media:Ex").namespace,
		mw.title.new("Special:Ex").namespace,2600)
	then
		if subject_page.isRedirect then return false end --重定向不是同類索引
		local body = ""
		if subject_page.getContent then body = subject_page:getContent() or "" end
		if re_sia_template(mw.ustring.sub(body, -400, -1)) then --尋找同類索引的模板
			return true
		end
	end
	return false
end

--匹配特色內容模板的正則函數
local function re_featured_content_template(input_text, _fc_type)
	local text = input_text or ""
	local fc_type = mw.ustring.lower(_fc_type or "fa")
	local match_list = {
		fa={--典範條目模板及其重定向匹配表
			--注意:這裡必須全部使用小寫字母
			--{"典範條目模板正則", 是否允許前方有其他字, 是否後方不能有其他字}
			{"featured?%s*articles?",false, true},
			{"fa",false, true},
			{"特色",false, true},
			{"典[範范]",false, true},
			{"典[範范][条條]目",false, true},
			{"特色[条條]目",false, true}
			--如有新典範條目模板重定向被建立請加入於此
		},
		ga={--優良條目模板及其重定向匹配表
			--注意:這裡必須全部使用小寫字母
			--{"優良條目模板正則", 是否允許前方有其他字, 是否後方不能有其他字}
			{"good%s*articles?",false, true},
			{"ga",false, true},
			{"[优優]良",false, true},
			{"[优優]?[良好][条條]目",false, true},
			--如有新優良條目模板重定向被建立請加入於此
		},
		fl={--特色列表模板及其重定向匹配表
			--注意:這裡必須全部使用小寫字母
			--{"特色列表模板正則", 是否允許前方有其他字, 是否後方不能有其他字}
			{"featured?%s*lists?",false, true},
			{"fl",false, true},
			{"特色列表",false, true},
			--如有新特色列表模板重定向被建立請加入於此
		},
		fm={--特色圖片模板及其重定向匹配表
			--注意:這裡必須全部使用小寫字母
			--{"特色圖片模板正則", 是否允許前方有其他字, 是否後方不能有其他字}
			{"featured?%s*pictures?",false, true},
			{"fp",false, true},
			{"特色[圖图]片",false, true},
			--如有新特色圖片模板重定向被建立請加入於此
		},
	}--致管理員:如有新特色內容模板及其重定向被建立,請優先受理特色內容模板及其重定向匹配項目的編輯請求
	local black_list = { --不是特色內容模板的模板名稱匹配表
		fa={},ga={},fl={},fm={}
		--暫無
	}--致管理員:如有新特色內容模板及其重定向被建立,請優先受理特色內容模板及其重定向匹配項目的編輯請求
	if match_list[fc_type] then
		local lotext = mw.ustring.lower(text..'\n') --以小寫進行匹配
		for i = 1,#(match_list[fc_type]) do --對每一個特色內容模板及其重定向進行匹配
			local j, k = mw.ustring.find(lotext, "%{%{"..
				(match_list[fc_type][i][2] and "[^%}]*" or "%s*")..match_list[fc_type][i][1]..
				(match_list[fc_type][i][3] and "%s*[%}%|]+" or "[^%}%|]*")
			)
			if j ~= nil then --匹配到了特色內容模板或其重定向
				local result = mw.ustring.sub(text, j, k)
				result = mw.ustring.match(result, "%{%{%s*([^%}%|]+)[%}%|]*$") --讀取匹配到的名稱
				if result then
					local is_featured_content = true
					for i1 = 1,#(black_list[fc_type]) do --檢查是否在黑名單中
						if mw.ustring.match(mw.ustring.lower(result), black_list[fc_type][i1]) then
							is_featured_content = false --如果不是
							break
						end
					end
					if is_featured_content then --是特色內容模板,回傳模板名稱
						return mw.text.trim(result)
					end
				end
			end
		end
	end
	return nil
end
p._re_featured_content_template = re_featured_content_template

--偵測特色內容
function p.is_featured_content(input_data, _fc_type)
	local page_name = input_data
	local fc_type = mw.ustring.lower(_fc_type or "fa")
	if type((input_data or {}).args) ~= type(nil) then --input_data is a frame
		page_name = input_data.args['1'] or input_data.args[1]
		fc_type = mw.ustring.lower(input_data.args['2'] or input_data.args[2] or "fa")
	end
	local page = (type(page_name) ~= type(nil)) and ({pcall(mw.title.new, page_name)})[2] or mw.title.getCurrentTitle()
	local talk_page = (page or {}).talkPageTitle
	local subject_page = (page or {}).subjectPageTitle
	if not subject_page:inNamespaces( --排除沒有特色內容的命名空間
		mw.title.new("MediaWiki:Ex").namespace,
		mw.title.new("Module:Ex").namespace,
		mw.title.new("Template:Ex").namespace,
		mw.title.new("TimedText:Ex").namespace,
		mw.title.new("Media:Ex").namespace,
		mw.title.new("Special:Ex").namespace,2600)
	then
		if subject_page.isRedirect then return false end --重定向不是特色內容
		local body = ""
		if subject_page.getContent then body = subject_page:getContent() or "" end
		local body_len = mw.ustring.len(body)
		local check_body = (body_len > 600) and (mw.ustring.sub(body, 1, 300).."{{tl|tl}} }}{{ {{tl|tl}}"..mw.ustring.sub(body, -300, -1)) or body
		if re_featured_content_template(check_body, fc_type) then --尋找特色內容的模板
			return true
		end
	end
	return false
end

--自動偵測重定向、消歧義和命名空間 (模板、模組、分類、檔案、草稿、主題、專題) 產生評級
function p.getClassAuto(input_data, class_input, demo, _napagens)
	local page_name = input_data
	local page_name_inputed = false
	local class_default = class_input or ''
	local demo_flag = yesno(demo or "no")
	local napagens = _napagens
	local no_auto = false
	local no_cat = false
	if type((input_data or {}).args) ~= type(nil) then --input_data is a frame
		page_name = input_data.args['1'] or input_data.args[1]
		class_default = input_data.args.class or input_data.args.CLASS or ''
		demo_flag = yesno(input_data.args.demo or "no")
		no_auto = yesno(input_data.args.no_auto or "no")
		no_cat = yesno(input_data.args.no_cat or "no")
		napagens = input_data.args.napagens
	end
	local napagens_list_ = mw.text.split(napagens or '', ",")
	local napagens_list = {}
	for _, _napagens_item in pairs(napagens_list_) do
		local ns_id = tonumber(_napagens_item)
		if type(ns_id) == type(0) then
			napagens_list[#napagens_list + 1] = ns_id
		else
			local napagens_item = mw.text.trim(mw.ustring.lower(_napagens_item or ''))
			if napagens_item == '' then napagens_item = "special:" end
			if napagens_item == 'article' or napagens_item == '条目' or napagens_item == '條目' or napagens_item == '主要' or napagens_item == 'article:' or napagens_item == '条目:' or napagens_item == '條目:' or napagens_item == '主要:' then
				napagens_item = ":"
			end
			xpcall(function()
				napagens_list[#napagens_list + 1] = mw.title.new(((mw.ustring.sub(napagens_item,-1,-1)==':') and mw.ustring.sub(napagens_item,1,-2) or napagens_item)..':EX').namespace
			end,function(ex)return ex end)
		end
	end
	if not isEmptyString(page_name) then page_name_inputed = true end
	if demo_flag then return class_default end
	if no_auto and mw.text.trim(class_default or '') ~= '' then return class_default end
	local current_title = mw.title.getCurrentTitle()
	--取得頁面標題物件
	local page = (type(page_name) ~= type(nil)) and ({pcall(mw.title.new, page_name)})[2] or current_title
	local current_page = page.subjectPageTitle or current_title.subjectPageTitle
	page_name = current_page.fullText
	--success=執行成功與失敗, result=執行結果
	local success, result = false, ''
	if isEmptyString(class_default)then --沒有預輸入評級
		local function _returned(_returned_value)
			if not no_cat then
				TrackingCategory.append(mw.getCurrentFrame(),cat_auto_class,_returned_value)
			end
			return _returned_value
		end
		if current_page:inNamespaces(unpack(napagens_list)) then
			return _returned('非页面')
		end
		if not current_page.isRedirect then
			local page_is_disambiguation = false
			local page_is_softredirect = false
			local page_is_sandbox = false
			local page_is_sia = false
			local page_is_ga = false
			local page_is_fa = false
			local page_is_fl = false
			local page_is_fm = false
			if page_name_inputed then --目前正在讀取其他頁面
				page_is_disambiguation = p.is_disambiguation(page_name)
				page_is_softredirect = p.is_softredirect(page_name)
				page_is_sandbox = p.is_sandbox(page_name)
				page_is_sia = p.is_sia(page_name)
				page_is_ga = p.is_featured_content(page_name, "ga")
				page_is_fa = p.is_featured_content(page_name, "fa")
				page_is_fl = p.is_featured_content(page_name, "fl")
				page_is_fm = p.is_featured_content(page_name, "fm")
			else --如果是讀取本頁,用讀取一次的方案 : mw.loadData("Module:PJBSClass/page")
				local PJBSClassData = mw.loadData("Module:PJBSClass/page")
				page_is_disambiguation = PJBSClassData.is_disambiguation
				page_is_softredirect = PJBSClassData.is_softredirect
				page_is_sandbox = PJBSClassData.is_sandbox
				page_is_sia = PJBSClassData.is_sia
				page_is_ga = PJBSClassData.is_ga
				page_is_fa = PJBSClassData.is_fa
				page_is_fl = PJBSClassData.is_fl
				page_is_fm = PJBSClassData.is_fm
			end
			if page_is_softredirect then --如果偵測到是軟重定向
				if not no_cat then
					TrackingCategory.append(mw.getCurrentFrame(), cat_auto_softredirect_class)
				end
				return _returned('重定向')
			end
			if page_is_sia then --如果偵測到是同類索引
				return _returned('同类索引')
			end
			
			 --如果偵測到是特色內容
			if page_is_ga then return _returned('优良') end
			if page_is_fa then return _returned('典范') end
			if page_is_fl then return _returned('特色列表') end
			if page_is_fm then return _returned('特色图片') end
			
			if page_is_disambiguation then --如果偵測到是消歧義
				if not no_cat then
					TrackingCategory.append(mw.getCurrentFrame(), cat_auto_disambig_class)
				end
				return _returned('消歧义')
			end
			
			if page_is_sandbox then --如果偵測到是沙盒
				return _returned('沙盒')
			end
			
			if current_page.namespace == 0 then
				--如果偵測到是列表
				if mw.ustring.match(current_page.fullText or '', ".列表$") then
					return _returned('列表')
				end
				
				--如果條目不足2000位元組 (不對2000~3000的過渡數值自動評)
				if current_page.getContent then
					local contentLen = (current_page:getContent() or ''):len()
					if contentLen > 0 and contentLen < 2000 and not mw.ustring.match(current_page.fullText or '', "列表") then
						return _returned('小作品')
					end
				end
			end
		end
	end
	local class_default_lower = mw.ustring.lower(class_default)
	if current_page.isRedirect or current_page:inNamespaces(
		mw.title.new("Template:Ex").namespace, 
		mw.title.new("Module:Ex").namespace, 
		mw.title.new("Category:Ex").namespace, 
		mw.title.new("File:Ex").namespace, 
		mw.title.new("Draft:Ex").namespace, 
		mw.title.new("Portal:Ex").namespace, 
		mw.title.new("PJ:Ex").namespace,
		mw.title.new("User:Ex").namespace,
		mw.title.new("Help:Ex").namespace,
		mw.title.new("MediaWiki:Ex").namespace,
		mw.title.new("TimedText:Ex").namespace,
		mw.title.new("Media:Ex").namespace,
		mw.title.new("Special:Ex").namespace,2600) 
	then
		if current_page:inNamespace(mw.title.new("File:Ex").namespace) and 
			(class_default_lower == "fm" or class_default_lower == "fi" or
			 mw.ustring.match(class_default_lower, "特[圖图]") or
			 mw.ustring.match(class_default_lower, "特色[圖图媒][片體体]") ) 
		then
			return class_default
		end
		success, result = pcall(require("Module:PJBSClass").getAutoClass, page_name, class_default)
		if success and not isEmptyString(result) then --如果成功讀取,並有讀到評級
			if not no_cat then
				local orig_class_norm = class_normalize({class_default})
				local result_class_norm = class_normalize({result})
				if orig_class_norm ~= result_class_norm and result_class_norm ~= 'unassessed' then
					TrackingCategory.append(mw.getCurrentFrame(), cat_auto_class, result)
				end
			end
		end
	end
	--讀取不成功,回傳預輸入評級
	if not success then return class_default end
	return result
end

--偵錯用 : 只要[[Cat:無法正常讀取評級的條目]]是空的,代表本模組沒問題。
function p._checker(input_data)
	local success, result = pcall(p.getClass, input_data)
	if not mw.loadData("Module:PJBSClass/page").has_WPBS then return '' end
	if not success then return "[[Cat:無法正常讀取評級的條目]]" end
	local result_show = (not isEmptyString(result)) and result or ' '
	if not isEmptyString(result)then 
		return "[[Cat:可以正常讀取評級的條目|".. result_show .."]]" 
	end
	return ''
end
--取得分類的頁面種類
function p.getPageType(input_data)
	local class_name = input_data
	local non_article = {
		"file","fm",--[[:Category:特色图片级页面]]
		"image",--[[:Category:图像级页面]]
		"portal","sandbox","template","module","book",
		"category","draft","project","user","help","interface","na"
	}
	if type((input_data or {}).args) ~= type(nil) then --input_data is a frame
		class_name = input_data.args['1'] or input_data.args[1]
	end
	class_name = mw.text.trim(class_name)
	local norm_class = class_normalize({class_name})
	if norm_class == "complete" or norm_class == "substantial" or norm_class == "basic" then
		return "主题"
	end
	for _, na_class in pairs(non_article) do
		if norm_class == na_class then return "页面" end
	end
	return "条目"
end
function p.checkIfRating(input_data)
	local class_input = input_data
	if type((input_data or {}).args) ~= type(nil) then --input_data is a frame
		class_input = input_data.args[1] or input_data.args['1'] or ''
	elseif type(input_data) == type({}) then
		class_input = input_data[1] or input_data['1'] or ''
	end
	local norm_class = mw.ustring.lower(class_normalize({class_input}))
	local non_rating = {
		portal=true, category=true, draft=true, file=true, audio=true, 
		sandbox=true, project=true, template=true, module=true, 
		user=true, help=true, interface=true, council=true,
	}
	return not non_rating[norm_class]
end
function p.processCatPageType(input_data, _page_name)
	local class_input = input_data
	local page_name = _page_name
	if type((input_data or {}).args) ~= type(nil) then --input_data is a frame
		class_input = input_data.args[1] or input_data.args['1'] or ''
		page_name = input_data.args.page
	elseif type(input_data) == type({}) then
		class_input = input_data[1] or input_data['1'] or ''
		page_name = input_data.page or ''
	end
	local current_title = mw.title.getCurrentTitle()
	--取得頁面標題物件
	local page = (type(page_name) ~= type(nil)) and ({pcall(mw.title.new, page_name)})[2] or current_title
	local current_page = page.subjectPageTitle or current_title.subjectPageTitle
	page_name = current_page.fullText
	if not current_page:inNamespace(mw.title.new(":條目").namespace) then
		class_input = mw.ustring.gsub(class_input, "条目", "页面")
		class_input = mw.ustring.gsub(class_input, "條目", "頁面")
	end
	return class_input
end
--列舉WPBS中的專題
function p.listProjects(frame, _code, _comma, _max_num)
	local project_list = p._getProjects(frame)
	local code = _code or '$1$2'
	local comma = _comma or '、'
	local max_num = tonumber("inf")
	local tail = "專題"
	local default_text = ''
	if type(frame.args) == type({"table"}) then
		code = frame.args.code or code
		comma = frame.args.comma or comma
		tail = frame.args.tail or tail
		max_num = tonumber(frame.args.max_num or frame.args["max num"]) or max_num
		default_text = frame.args.default or default_text
	elseif type(frame) == type({"table"}) then
		code = frame.code or frame[2] or code
		comma = frame.comma or frame[3] or comma
		max_num = tonumber(frame.max_num or frame["max num"] or frame[4]) or max_num
		tail = frame.tail or tail
		default_text = frame.default or default_text
	end
	if #project_list <= 0 then return default_text end
	local result = ''
	for i=1,#project_list do
		if i > max_num then break end
		result = result .. mw.ustring.gsub(code, "%$([%+%-]?%d+)", function(_id)
			local id = tonumber(_id)
			if id == 1 then
				return project_list[i].name
			elseif id == 2 then
				return project_list[i].link
			elseif id == 3 then
				if #project_list == 1 or max_num == 1 then return '' end
				return ((i==#project_list or i == max_num) and '' or ((i + 1 == max_num or i + 1 == #project_list) and "和" or comma))
			end
			return "$".._id
		end)
	end
	if max_num > 1 then
		result = result .. "等"
	end
	result = result .. tail
	return result
end
--取得WPBS中的所有已輸入的專題
function p._getProjects(frame)
	local text = frame
	if type(frame.args) == type({"table"}) then
		text = frame.args[1] or ''
	elseif type(frame) == type({"table"}) then
		text = frame[1] or ''
	end
	local project_list = {}
	local _,j,i,length = 1,1,1,mw.ustring.len(text)
	while i < length do
		--類似於$.(".wpb-project")以抓取專題元數據 (metadata)
		_,j = mw.ustring.find(text, "class%s*=%s*\"%s*wpb%-project%s*\"", i)
		if j then
			local k = mw.ustring.find(text, "<%s*/%s*span%s*>", j)
			local pj_text = mw.ustring.sub(text, j+1, k or -1)
			--獲取專題名稱
			local project_name = mw.text.trim(mw.ustring.match(pj_text, "^>%s*(.*)%s*<$") or '')
			_,j = mw.ustring.find(text, "class%s*=%s*\"%s*wpb%-project_link%s*\"", i)
			k = mw.ustring.find(text, "<%s*/%s*span%s*>", j)
			pj_text = mw.ustring.sub(text, j+1, k or -1)
			--獲取專題連結
			local project_link = mw.text.trim(mw.ustring.match(pj_text, "^>%s*(.*)%s*<$") or '')
			--如果成功獲取
			if project_name ~= "" or project_link ~= "" then
				if project_link == "" then --沒連結則產生連結
					project_link = "PJ:"..project_name
				end
				if project_name == "" then --沒名稱則用連結產生名稱
					local _c = mw.ustring.find(project_link, ":")
					project_name = mw.ustring.sub(_c or 1, -1)
				end
				--紀錄找到的專題
				project_list[#project_list + 1] = {
					name=project_name,
					link=project_link
				}
			end
			--獲取下一個
			i = k or length
		else break end --沒有下一個了
	end
	return project_list
end
--評級繼承與否判斷作業
function p.checkClassInput(_class_input, _class_given, _no_cat)
	local class_input = _class_input
	local class_given = _class_given or _class_input
	local no_cat = yesno(_no_cat or false)
	local frame = mw.getCurrentFrame()
	if type(_class_input.args) == type({"table"}) then
		class_input = _class_input.args[1] or ''
		class_given = _class_input.args[2] or _class_input.args[1] or ''
		frame = _class_input
	elseif type(_class_input) == type({"table"}) then
		class_input = _class_input[1] or ''
		class_given = _class_input[2] or _class_input[1] or ''
	end
	class_input = mw.text.trim(class_input)
	class_given = mw.text.trim(class_given)
	if class_input == '' then return class_given end --無輸入為繼承
	--轉換為標準評級字串
	local norm_class_input = class_normalize({class_input})
	local norm_class_given = class_normalize({class_given})
	if class_input~='' and class_given~='' then
		--如果不繼承評級
		if norm_class_input ~= norm_class_given then
			--添加維護分類
			if not no_cat then
				TrackingCategory.append(frame, cat_use_user_defined_class)
			end
		end
	end
	return class_given
end
--用於Module:PJBSClass/page模板調用
function p.class_json()
	return mw.text.jsonEncode(mw.loadData("Module:PJBSClass/page"))
end
function p.PJBSClassItem(frame)
	if type(frame.getParent) == type(tostring) then
		local p_frame = frame:getParent()
		local value_type = mw.ustring.lower(mw.text.trim(p_frame.args.value_type or ''))
		return frame.args[value_type] or ''
	end
	return ''
end
--處理class參數輸入成橫幅的情況
function p.wrongClass(input_data, _check_class)
	local wikitext = input_data or ""
	local check_class = _check_class or ""
	if type((input_data or {}).args) ~= type(nil) then --input_data is a frame
		wikitext = input_data.args['1'] or input_data.args[1] or ""
		check_class = input_data.args.class or ""
	end
	local re_pj = '<table[^>]-class="[^"]-wpb[^"]-"'
	if mw.ustring.match(check_class, re_pj) then
		return mw.ustring.gsub(check_class, "^[^<]*<", "<")..wikitext
	end
	return wikitext
end
--是否有自定義級別
function p.hasCustomClassName(input_data)
	if input_data.getParent then
		for key, value in pairs(input_data:getParent().args or {}) do
			local key_name = mw.ustring.match(mw.text.trim(tostring(key or '')), "^(.*)自订级")
			if type(key_name) == type("string") then
				return true
			end
		end
	end
	return false
end
--處理自定義級別
function p.customClassName(input_data)
	local wikitext = input_data or ""
	if type((input_data or {}).args) ~= type(nil) then --input_data is a frame
		wikitext = input_data.args['1'] or input_data.args[1] or ""
	end
	if input_data.getParent then
		for key, value in pairs(input_data:getParent().args or {}) do
			local key_name = mw.ustring.match(mw.text.trim(tostring(key or '')), "^(.*)自订级")
			if type(key_name) == type("string") then
				wikitext = mw.ustring.gsub(wikitext, key_name, value)
				wikitext = mw.ustring.gsub(wikitext, "assess%-"..value, "assess-"..key_name)
			end
		end
	end
	return wikitext
end
--檢查有無不存在的專題橫幅,加入警示
local function _processUndefinedWikiproject(wikitext)
	if not mw.ustring.match(wikitext, "%[%[:Template:") then return wikitext, 0 end
	local count = 0
	local function _repe(text, count)local _list = {} for i=1,count do _list[#_list+1]=text end return _list end
	local function _repl(non_exist_pj)
		local pj_name = mw.text.trim(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(non_exist_pj, "[专專][题題]", ""), "[Ww][Ii][Kk][Ii][Pp][Rr][Oo][Jj][Ee][Cc][Tt]", ""), "%%", "%%%%"))
		if pj_name == "" then pj_name = "專題" end
		return ('<table class="tmbox tmbox-notice collapsible innercollapse wpb wpb-nonexist" height="0"><tr class="wpb-header">'..
				'<td style="text-align:right; padding:0.3em 1em 0.3em 0.3em; width:50%%; font-weight:bold;"> [[Template:%s|%s]]</td>'..
				'<th style="text-align:left; width:50%%; padding:0.3em 0.3em 0.3em 0;"><span class="assess assess-error">專題模板不存在!</span></th>'..
				'<td class="mbox-empty-cell"></td></tr><tr><td class="mbox-text" style="padding:3px 0 3px 5px;" colspan="2">'..
				'<table style="background:transparent; border:none; padding:0; width:100%%;" cellspacing="0">'..
				'<tr><td style="padding:2px 0;"></td><td class="mbox-text">'..
				'<div class="navbar plainlinks hlist navbar-mini" style="float:right; clear:right; margin:-1em 0.1em; display:none;"><ul><li class="nv--{zh-hans:查看;zh-hant:檢視;}-">[[Template:%s|<abbr title="-{zh-hans:查看;zh-hant:檢視;}-该模板">-{zh-hans:查;zh-hant:閱;}-</abbr>]]</li><li class="nv-讨论">[[Template talk:%s|<abbr title="讨论该模板">论</abbr>]]</li><li class="nv-编辑">[//zh.wikipedia.org/w/index.php?title=Template:%s&action=edit<abbr title="编辑该模板">编</abbr>]</li></ul></div>'..
				"本頁面属于'''[[PJ:"..pj_name.."|%s]]'''范畴,该专题旨在改善[[中文维基百科]]的[["..pj_name.."]]类内容。但該專題模板[[Template:%s]]尚未創建,請檢查是否輸入有誤,或專題不存在。如專題不存在請移除本模板。</td>"..
				'<td class=mbox-empty-cell></td></tr></table></td><td class="mbox-empty-cell"></td></tr></table>'
		):format(unpack(_repe(non_exist_pj, 20)))
	end
	local match_list = {
		"%[%[:Template:([^%[%]]*[专專][题題][^%[%]%|]*)%]%]",
		"%[%[:Template:([^%[%]]*[Ww][Ii][Kk][Ii][Pp][Rr][Oo][Jj][Ee][Cc][Tt][^%[%]%|]*)%]%]"
	}
	local i = 0
	for _, match_item in ipairs(match_list) do
		wikitext, i = mw.ustring.gsub(wikitext, match_item, _repl)
		count = count + (i or 0)
	end
	return wikitext, count
end

--處理專題橫幅中包含的警告標示,以及圖示加到標題,同時標記維護分類
function p.processNotice(input_data)
	local wikitext = input_data or ""
	local no_cat = false
	if type((input_data or {}).args) ~= type(nil) then --input_data is a frame
		wikitext = input_data.args['1'] or input_data.args[1] or ""
		no_cat = yesno(input_data.args.no_cat or "no")
	end
	wikitext = mw.ustring.gsub(wikitext,'(class="[^"]-messagebox[^"]-)"', "%1 wpb-outside\"")
	local nonexist_count = 0
	--檢查有無不存在的專題橫幅,添加維護分類
	wikitext, nonexist_count = _processUndefinedWikiproject(wikitext)
	if nonexist_count > 0 and not no_cat then
		TrackingCategory.append(mw.getCurrentFrame(), "WPBS中含有不存在專題橫幅的頁面")
	end
	--移除專題橫幅內自帶的提示模板 (稍後將會用p.getNotice統一補上避免重複列初)
	local re_mbox = '<table[^>]-class="[^"]-[aico]mbox[^"]-"'
	local re_wpb_outside = '<table[^>]-class="[^"]-wpb%-outside[^"]-"'
	local wikitext_len = mw.ustring.len(wikitext)
	--尋找專題橫幅內自帶的提示模板
	local mbox_head, mbox_head_end = mw.ustring.find(wikitext,re_mbox)
	if type(mbox_head) ~= type(0) then mbox_head, mbox_head_end = mw.ustring.find(wikitext,re_wpb_outside) end
	local mbox_end = nil
	while type(mbox_head) == type(0) do
		local _, mbox_end = mw.ustring.find(wikitext,'<%s*/%s*table%s*>', mbox_head_end)
		--找到專題橫幅內自帶的提示模板
		if type(mbox_end) == type(0) then
			--移除之
			wikitext = mw.ustring.sub(wikitext, 1, mbox_head-1) .. ((mbox_end + 1 >= wikitext_len) and "" or mw.ustring.sub(wikitext, mbox_end + 1, -1))
			wikitext_len = mw.ustring.len(wikitext)
		end
		--尋找下一個專題橫幅內自帶的提示模板
		mbox_head, mbox_head_end = mw.ustring.find(wikitext,re_mbox)
		if type(mbox_head) ~= type(0) then mbox_head, mbox_head_end = mw.ustring.find(wikitext,re_wpb_outside) end
	end
	--加入圖示與維護分類
	local pj_count = 0
	local re_pj = '<table[^>]-class="[^"]-wpb[^"]-"'
	local re_image_td = '<td[^>]-class="[^"]-%-image[^"]-"'
	wikitext_len = mw.ustring.len(wikitext)
	--尋找專題橫幅
	mbox_head, mbox_head_end = mw.ustring.find(wikitext,re_pj)
	local pj_list = {}
	while type(mbox_head) == type(0) and (mbox_head or wikitext_len) < wikitext_len - 1 do
	 	pj_count = pj_count + 1
	 	local _, mbox_end = mw.ustring.find(wikitext,'<%s*/%s*table%s*>', mbox_head_end)
		--找到一個完整的專題橫幅模板 (適應電子遊戲專題)
		if type(mbox_end) == type(0) then
			--取得專題橫幅模板內容
			local mbox_content = mw.ustring.sub(wikitext, mbox_head, mbox_end)
			--處理自訂專題值 (如主題專題)
			local TARGETPAGETYPE = mw.ustring.match(mbox_content, '<span[^>]-class="[^"]-wpb%-target%-page%-type[^"]-"[^>]->%s*(.-)%s*<%s*/%s*span%s*>')
			if mw.text.trim(TARGETPAGETYPE or '') ~= '' then --自訂專題非頁面
				TARGETPAGETYPE = mw.text.unstripNoWiki( TARGETPAGETYPE )
				if mw.text.trim(TARGETPAGETYPE or '') ~= '' then
					TARGETPAGETYPE = mw.text.unstripNoWiki( TARGETPAGETYPE )
					mbox_content = mw.ustring.gsub(mbox_content, '非页面', '非'..TARGETPAGETYPE)
					mbox_content = mw.ustring.gsub(mbox_content, 'Category:非'..TARGETPAGETYPE, 'Category:非页面')
				end
			end
			--取得專題名稱
			local pj_name = mw.text.trim(mw.ustring.match(mbox_content, '<span[^>]-class="[^"]-wpb%-project[^"]-"[^>]->%s*(.-)%s*<%s*/%s*span%s*>') or '')
			if pj_name ~= '' then
				--如果有重複的專題名稱,加上維護分類
				if pj_list[pj_name] then
					mbox_content = mw.ustring.gsub(mbox_content, '(</th>%s*<td[^>]-class="[^"]-mbox%-empty%-cell[^"]-">%s*<span[^>]-class="[^"]-metadata[^"]-")', '<span class="duplicate-notice">重複</span>%1')
					if not no_cat then
						TrackingCategory.append(mw.getCurrentFrame(), "WPBS中含有重複專題橫幅的頁面")
					end
				end
				--紀錄已找到的專題
				pj_list[pj_name] = true
			end
			local header_fix = 0 --專題頭部改以WPBS的模板樣式
			mbox_content = mw.ustring.gsub(mbox_content, "width:50%%", function(str)
				local fix_list = {50}
				header_fix = header_fix + 1
				if fix_list[header_fix] then return "" end
				return str
			end)
			--尋找專題縮圖
			local image_head, image_head_end =  mw.ustring.find(mbox_content, re_image_td)
			local icon_added = false
			if type(image_head) == type(0) then
				local _, image_end = mw.ustring.find(mbox_content,'<%s*/%s*td%s*>', image_head_end)
				if type(image_end) == type(0) then --找到專題縮圖
					local icon_td_html = mw.ustring.sub(mbox_content, image_head, image_end)
					--取得專題縮圖
					local icon_wikitext = mw.ustring.match(icon_td_html, ">(%[%[.*%]%])%s*<%s*/%s*td%s*>")
					--改成圖示大小
					icon_wikitext = mw.ustring.gsub(icon_wikitext, "%|%s*%d+px", "|40px|alt=|link=")
					--將圖示放入表格頭部
					mbox_content = mw.ustring.gsub(mbox_content,'(<tr[^>]-class="[^"]-wpb%-header[^"]-"[^>]->)', function(tr_head)
						return tr_head..'<td class="wpb-header-icon"><div>'..icon_wikitext..'</div></td>'
					end)
					icon_added = true
				end
			end
			if not icon_added then
				mbox_content = mw.ustring.gsub(mbox_content,'(<tr[^>]-class="[^"]-wpb%-header[^"]-"[^>]->)', function(tr_head)
					return tr_head..'<td class="wpb-header-icon"><div></div></td>'
				end)
			end
			--更新專題模板
			wikitext = mw.ustring.sub(wikitext, 1, mbox_head-1) .. mbox_content .. ((mbox_end + 1 >= wikitext_len) and "" or mw.ustring.sub(wikitext, mbox_end + 1, -1))
			wikitext_len = mw.ustring.len(wikitext)
		end
		--尋找下一個專題模板
		mbox_head, mbox_head_end = mw.ustring.find(wikitext, re_pj, (mbox_head_end or (wikitext_len - 6)) + 5)
	end
	--追蹤套娃式的基礎條目橫幅
	if mw.ustring.match(wikitext, "基础条目") then
		if not no_cat then
			wikitext = wikitext .. "[[Cat:錯誤放置基礎條目橫幅的頁面]]"
		end
	end
	--檢查正裡完後是否為空
	local tail_check = wikitext
	--移除HTML的TAG
	tail_check = mw.ustring.gsub(tail_check, "<[^<>]->", "")
	local old_tail_check = tail_check .."-"
	while old_tail_check ~= tail_check do
		old_tail_check = tail_check
		tail_check = mw.text.trim(mw.ustring.gsub(tail_check, "<[^<>]->", ""))
	end
	--移除擴展標籤TAG
	tail_check = mw.ustring.gsub(tail_check, "\127'\"`UNIQ%-%-[^%-]*%-[^%-]*%-QINU`\"'\127", "")
	tail_check = mw.text.trim(tail_check)
	--如果整串是空的,或沒有找到專題,代表專題橫幅放置不正確,加入維護分類
	if tail_check == '' or pj_count <= 0 then 
		wikitext = wikitext .. '<div class="wpbs-empty-notice">(未輸入專題)</div>'
		if not no_cat then
			TrackingCategory.append(mw.getCurrentFrame(), "專題橫幅放置不正確的頁面")
		end
	end
	return mw.text.trim(wikitext)
end
--顯示專題橫幅中包含的警告標示
function p.getNotice(input_data)
	--主要目的是讓專題橫幅內自帶的提示模板重複的話只顯示一次
	local wikitext = input_data or ""
	if type((input_data or {}).args) ~= type(nil) then --input_data is a frame
		wikitext = input_data.args['1'] or input_data.args[1] or ""
	end
	local body = ''
	--加上識別項
	wikitext = mw.ustring.gsub(wikitext,'(class="[^"]-messagebox[^"]-)"', "%1 wpb-outside\"")
	local wikitext_len = mw.ustring.len(wikitext)
	local re_mbox = '<table[^>]-class="[^"]-[aico]mbox[^"]-"'
	local re_wpb_outside = '<table[^>]-class="[^"]-wpb%-outside[^"]-"'
	local mbox_list = {}
	--尋找專題橫幅內自帶的提示模板
	local mbox_head, mbox_head_end = mw.ustring.find(wikitext,re_mbox)
	local mbox_head_o, mbox_head_end_o = mw.ustring.find(wikitext,re_wpb_outside)
	if (mbox_head_o or wikitext_len) < (mbox_head or wikitext_len) then
		mbox_head, mbox_head_end = mbox_head_o, mbox_head_end_o
	end
	if type(mbox_head) ~= type(0) then mbox_head, mbox_head_end = mw.ustring.find(wikitext,re_wpb_outside) end
	local mbox_end = nil
	while type(mbox_head) == type(0) and (mbox_head or wikitext_len) < wikitext_len - 1 do
		local _, mbox_end = mw.ustring.find(wikitext,'<%s*/%s*table%s*>', mbox_head_end)
		--找到專題橫幅內自帶的提示模板
		if type(mbox_end) == type(0) then
			--取得專題橫幅內自帶的提示模板的內容
			local mbox_content = mw.ustring.sub(wikitext, mbox_head, mbox_end)
			--建立 "檢查是否重複" 用之字串
			local checker = mw.ustring.lower(mw.ustring.gsub(mbox_content, "[%s_-]", ""))
			--檢查是否重複
			local has_same = false
			--逐一查閱已有的專題橫幅內自帶的提示模板
			for i=1,#mbox_list do
				--如果重複
				if mbox_list[i][2] == checker then
					--標記這是重複的
					has_same = true
					break
				end
			end
			--不重複的才列入
			if not has_same then
				mbox_list[#mbox_list + 1] = {mbox_content, checker}
			end
		end
		--尋找下一個專題橫幅內自帶的提示模板
		mbox_head, mbox_head_end = mw.ustring.find(wikitext,re_mbox, mbox_end or (wikitext_len - 1))
		mbox_head_o, mbox_head_end_o = mw.ustring.find(wikitext,re_wpb_outside, mbox_end or (wikitext_len - 1))
		if (mbox_head_o or wikitext_len) < (mbox_head or wikitext_len) then
			mbox_head, mbox_head_end = mbox_head_o, mbox_head_end_o
		end
	end
	--將所有不重複的專題橫幅內自帶的提示模板列出
	for i=1,#mbox_list do
		body = body..mbox_list[i][1]
	end
	return mw.text.trim(body)
end
return p