Module:WikiProject banner

require('strict')
local p = {}
local sandbox --= '/sandbox' -- BE SURE TO COMMENT OUT this definition when deploying to live
local cfg = mw.loadData('Module:WikiProject banner/config' .. (sandbox or ''))
local auxiliary = cfg.auxiliary_module .. (sandbox or '')
local args_module = require('Module:Arguments')
local mbox = require('Module:Message box').main
local yesno = require('Module:Yesno')
local frame = mw.getCurrentFrame()
local lang = mw.getLanguage(cfg.language)
local current_title = mw.title.getCurrentTitle()
local parameter_format = function(parameter, value)
	return frame:expandTemplate{title='para', args={parameter, value or ''}}
end

local wikilink = function(link, display)
	if link then
		return display and '[['..link..'|'..display..']]' or '[['..link..']]'
	else
		return display or ''
	end
end

local display_error = function(text)
	local span = mw.html.create('div')
		:addClass('error')
		:wikitext(text)
	return tostring(span)	
end

local image = function(image_name, size, alt, position, class)
	return image_name and '[[File:'
		.. image_name
		.. (size and '|' .. size or '')
		.. (position and '|' .. position or '')
		.. (alt and '|alt=' .. alt or '')
	    .. '|class=noviewer'..(class and ' '..class or '')
		.. ']]'
end

local if_exists = function(target, fallback) -- function to add wikilink if target exists
	local title = mw.title.new(target)
	if title and title.exists then
		return wikilink(target)
	else
		return fallback or target
	end
end

local importance_mask = function(importance, scale, banner_name, pagetype, class)
---------------------------
-- Importance mask --------
---------------------------
local importance_normalised
if scale=='inline' then -- pass importance without change
	importance_normalised = importance or ''
elseif scale=='subpage' then
	local custom_mask = banner_name:subPageTitle('importance')
	if custom_mask.exists and #custom_mask:getContent()>1 then -- pass to custom importance mask
		importance_normalised = mw.text.trim(frame:expandTemplate{
			title = custom_mask.prefixedText,
			args = {
				importance = importance,
				class = class,
				pagetype = pagetype
			}
		})
	end
else -- standard importance scale
	importance_normalised = cfg.importance.na
	if pagetype=='article' or pagetype=='set index article' or pagetype=='redirect' or pagetype=='draft' then
		local mask = cfg.importance.mask
		if importance and mask[importance:lower()] then -- valid importance specified
			importance_normalised = mask[importance:lower()]
		elseif pagetype=='article' or pagetype=='set index article' then -- unspecified or invalid importance, use "Unknown" for articles
			importance_normalised = cfg.importance.unknown
		end
	end
end
return importance_normalised
end

---------------------------
-- Quality class mask -----
---------------------------
p.readarticleclass = function(options, page) -- used by _main and also Module:Banner shell
	page = page or current_title.prefixedText
	local get_parameter_value = require('Module:Template parameter value').getParameter
	local success, result = get_parameter_value(page, cfg.banner_shell.redirects, 'class', options)
	return success and result
	-- returns FALSE if banner shell template does not exist on page
	-- returns BLANK if class parameter is not defined or is defined blank
	-- otherwise returns class parameter
end
p.class_mask = function(class, title, FQS, pagetype, article)
	local resolveFQSgrade = function(class)
		return FQS and lang:ucfirst(class) or 'NA'
	end
	local out
	title = title or mw.title.getCurrentTitle()
	local ns = title.namespace
	class = class:match('^%s*(.-)%s*$'):lower()
	if pagetype=='redirect' or pagetype=='soft redirect' then
		out = resolveFQSgrade('redirect')
	elseif pagetype=='disambiguation page' then
		out = resolveFQSgrade('disambig')
	elseif article or pagetype=='article' or pagetype=='set index article' then
		if pagetype=='set index article' then
			out = 'List'
		elseif class=='start' or class=='stub' then -- Ucfirst
			out = lang:ucfirst(class)
		elseif class=='b' or class=='c' or class=='fa' or class=='fl' or class=='a' or class=='ga' then -- Upper-case
			out = class:upper()
		elseif class=='list' or class=='sia' or class=='si' or class=='sl' then-- List
			out = 'List'
		else
			out = '' -- unassessed
		end
	elseif ns==7 or ns==711 then -- File talk
		if class=='fm' then
			out = 'FM'
		else
			out = resolveFQSgrade('file')
		end
	else
	    local grade = cfg.quality.ns_to_class[ns] or 'NA'
		out = resolveFQSgrade(grade)
	end
	return out
end

local page_assessment = function(project, class, importance) -- add PageAssessments parser function
	local assessment = table.concat({project, class or '', importance or ''},'|')
	frame:preprocess('{{#assessment:' .. assessment .. '}}')
end

local bubble = function(text, style)
	local out = mw.html.create('span')
		:addClass('wpb-header-bubbles')
		:addClass(style)
		:wikitext(text)
	return tostring(out)
end

p._main = function(args, raw_args, demo_page, banner_name, inactive)
---------------------------
-- Initialise parameters --
---------------------------
local project = args.PROJECT or 'PROJECT'
local project_name = args.PROJECT_NAME or 'WikiProject ' .. project
local project_link = mw.title.new(args.PROJECT_LINK or 'Wikipedia:' .. project_name)
local pagetype = demo_page==true and 'article' or require('Module:Pagetype')._main({
	page = demo_page,
	dab = 'disambiguation page',
	sia = 'set index article',
	draft = 'draft'
})
local article = pagetype=='article' or pagetype=='set index article'
local rows, nested_ratings, task_forces, notes, categories, taskforce_categories = {}, {}, {}, {}, {}, {}
local add_category = function(category, key)
	if category and category~='none' then
		table.insert(categories, {category = category, key = key})
	end
end
local parse_pt = function(text) -- function to replace _PAGETYPE_ with the actual page type
	local ptype = article and 'article' or pagetype -- display "article" for articles otherwise page type
	return text and text:gsub('_PAGETYPE_', ptype)
end
for arg_name, arg_value in pairs(args) do
	local tf_match = mw.ustring.match(arg_name,'^tf (%d+)$')
	local note_match = mw.ustring.match(arg_name,'^note (%d+)$')
	if tf_match and yesno(arg_value, true) then
		table.insert(task_forces, tf_match)
	elseif note_match and yesno(arg_value, true) then
		table.insert(notes, note_match)
	else
		local tf, cat = mw.ustring.match(arg_name,'^tf (%d+) cat (%d+)$')
		if tf and yesno(arg_value, true) then
			if not taskforce_categories[tf] then -- initialise table
				taskforce_categories[tf] = {}
			end
			table.insert(taskforce_categories[tf], cat)
		end
	end
end
table.sort(task_forces, function (x, y) return tonumber(x) < tonumber(y) end)
table.sort(notes, function (x, y) return tonumber(x) < tonumber(y) end)
local assessment_category = function(cat, name)
	if cat then
		return cat:gsub(' articles', '') -- remove "articles" from category
	else
		return name or ''
	end
end
local assessment_cat = assessment_category(args.ASSESSMENT_CAT, project)
---------------------------
-- Location warning -------
---------------------------
local warning = ''
if not current_title.isTalkPage and not demo_page then
	local text = cfg.namespace_warning.text:format(
		current_title.talkPageTitle.fullText,
		parameter_format('category', 'no')
	)
	local sortkey = current_title.namespace==10 and cfg.namespace_warning.sortkey_on_template_page or cfg.namespace_warning.sortkey
	if current_title.namespace==10 then -- on the Template namespace
		text = text .. '  ' .. cfg.namespace_warning.on_template_page:format(
			parameter_format('BANNER_NAME'),
			current_title.prefixedText
		)
	end
	warning = mbox('ombox', {
		image = '[[File:' .. cfg.namespace_warning.image .. '|40px]]',
		type = cfg.namespace_warning.type_,
		text = parse_pt(text)
	})
	if not current_title.subjectPageTitle:inNamespace(2) then
		add_category(cfg.namespace_warning.categories, sortkey)
	end
end
---------------------------
-- Substitution warning ---
---------------------------
if args.substcheck=='SUBST' then
	local text = cfg.subst_warning.text:format(
		project_name,
		'<code>&#123;&#123;'..banner_name.prefixedText..'&#125;&#125;</code>'
	)
	warning = warning .. mbox('ombox', {
		image = '[[File:' .. cfg.subst_warning.image .. '|40px]]',
		type = cfg.subst_warning.type_,
		text = text,
	}) .. cfg.subst_warning.categories
end
---------------------------
-- Primary image/text -----
---------------------------
local primary_image = function(image_name, size, image_class)
	local cell = mw.html.create('td')
	if image_name and image_name~='' then
		cell:addClass('mbox-image wpb-image')
			:wikitext(image(image_name, size, cfg.image.alt, nil, image_class))
	else
		cell:addClass('mbox-empty-cell')
	end
	return cell
end
local portal = args.PORTAL
local portal_box = portal and frame:expandTemplate{title='Portal', args={portal}} or ''
local main_text = portal_box .. parse_pt(args.MAIN_TEXT or cfg.main_text:format(
	project_link.prefixedText,
	project_name,
	args.MAIN_ARTICLE and if_exists(args.MAIN_ARTICLE) or if_exists(project, project .. ' articles'),
	project_link.talkPageTitle.prefixedText
))
local image_left_size = args.IMAGE_LEFT_SIZE or cfg.image.default_size
local metadata = function(class, data)
	return mw.html.create('span')
		:addClass(class)
		:wikitext(data)
end
local text_cell = mw.html.create('td')
	:addClass('mbox-text')
	:wikitext(main_text)
	:tag('span')
		:addClass('metadata wpb-metadata')
		:node(metadata('wpb-project', project))
		:node(metadata('wpb-project_link', project_link.prefixedText))
		:node(metadata('wpb-banner_name', banner_name.prefixedText))
		:node(metadata('wpb-assessment_cat', assessment_cat))
	:done()
local primary_row = mw.html.create('tr')
	:node(primary_image(args.IMAGE_LEFT, image_left_size,
			args.IMAGE_LEFT_CLASS or args.IMAGE_CLASS))
	:node(text_cell)
	:node(primary_image(args.IMAGE_RIGHT, args.IMAGE_RIGHT_SIZE or cfg.image.default_size,
			args.IMAGE_RIGHT_CLASS or args.IMAGE_CLASS))
table.insert(rows, primary_row)
---------------------------
-- Banner shell checks ----
---------------------------
local title = demo_page and demo_page~=true and mw.title.new(demo_page) or current_title
local class = p.readarticleclass({ignore_subtemplates=true}, title.prefixedText)
if class then -- banner shell exists
	local special_chars = '([%%%(%)%.%+%-%*%?%[%]%^%$])'
	local banner_name_escaped = banner_name.text
	local page_content = require('Module:Wikitext Parsing' .. (sandbox or '')).PrepareText(title:getContent()) -- get content of current page
	local content_without_shell
	for capture in mw.ustring.gmatch(page_content, '%b{}') do -- look for possible templates on page
		for _, redirect in ipairs(cfg.banner_shell.redirects) do
			if mw.ustring.find(capture, '^{{%s*' .. redirect .. '%s*[|}].*}}$') then -- found a banner shell
				banner_name_escaped = banner_name_escaped:gsub(special_chars, '%%%1') -- escape each special character
				capture = capture:gsub(special_chars, '%%%1')
				content_without_shell = mw.ustring.gsub(page_content, capture, '') -- remove banner shell content from page content
			end
			if content_without_shell then break end
		end
		if content_without_shell then break end
	end
	local template_outside_shell
	if content_without_shell and mw.ustring.find(content_without_shell, '{{%s*' .. banner_name_escaped .. '%s*[|}]') then -- found banner template outside of the shell
		add_category(cfg.banner_shell.category.outside_shell)
	end
else -- no banner shell on page
	if title.namespace==3 then --User talk namespace
		for _, user in ipairs(cfg.banner_shell.valid_users) do
			if string.find(title.rootText, user) then
				add_category(cfg.banner_shell.category.no_banner_shell)
			end
		end
	elseif title.namespace~=2 then -- not in user namespace
		add_category(cfg.banner_shell.category.no_banner_shell)
	end
	class = ''
end
---------------------------
-- Quality assessment -----
---------------------------
local assessment_link = args.ASSESSMENT_LINK
if not assessment_link then
	local fallback = mw.title.new(project_link.prefixedText .. '/Assessment')
	assessment_link = fallback.exists and fallback.prefixedText
elseif assessment_link=='no' then
	assessment_link = nil
end
local check_fallbacks = function(class, category) -- function to check non-article classes and automatically fall back to NA (or other) class if category does not exist
	if article or args.QUALITY_CRITERIA=='custom' then -- no fallbacks for articles or projects with custom quality scales
		return class
	else -- check fallbacks for non-article classes
		local new_class = class
		local category_exists = function(class)
			local cat =  mw.title.new(cfg.quality.assessment_category:format(class, category .. ' ' .. (article and 'articles' or 'pages')))
			return cat.exists and #cat:getContent()>0 -- check if category exists and is not blank
		end
		if class=='FM' and not category_exists('FM') then
			new_class = 'File' -- fall back to File-class if FM category does not exist
		end
		if not category_exists(new_class) then
			new_class = 'NA' -- use NA for non-article pages if category does not exist
		end
		return new_class
	end
end
if raw_args.class then -- banner gives quality ratings
	class = class and p.class_mask(class, title, true, pagetype, article) -- put PIQA class through standard class mask
	if args.QUALITY_CRITERIA=='custom' then -- project has opted out of standard assessment scale and uses a custom mask
		local project_class
		local custom_mask = banner_name:subPageTitle('class')
		if custom_mask.exists and #custom_mask:getContent()>1 then
			raw_args.demo_page = demo_page -- send demo_page to custom mask
			project_class = mw.text.trim(frame:expandTemplate{
				title = custom_mask.prefixedText,
				args = raw_args
			})
			if project_class=='' and class and class~='' then -- if unassessed and PIQA class exists, check if it can be inherited
				local new_arg_table = {}
				for arg, val in pairs(raw_args) do -- construct new argument table to send to custom mask
					new_arg_table[arg] = val
				end
				new_arg_table.class = class -- replace class with inherited class
				project_class = mw.text.trim(frame:expandTemplate{
					title = custom_mask.prefixedText,
					args = new_arg_table
				}) -- inherit project class from PIQA class normalised by custom mask
			end
			local category = (project_class=='' and 'Unassessed' or project_class..'-Class') .. ' ' .. assessment_cat .. ' ' .. (article and 'articles' or 'pages')
			add_category(category)
			if class~=project_class then -- display project class in banner
				class = project_class
				local rating = project_class=='' and cfg.quality.not_yet or cfg.quality.rated:format(class)
				local scale = cfg.quality.project_scale:format(wikilink((assessment_link or '')..'#'..lang:ucfirst(cfg.quality.name), cfg.quality.name))
				local cssClass = 'class-' .. (class=='' and 'unassessed' or class:lower())
				local class_row =  mw.html.create('tr')
					:tag('td')
						:addClass('assess'):addClass(cssClass)
						:wikitext(wikilink(':Category:' .. category, class=='' and '???' or class))
					:done()
					:tag('td')
						:addClass('mbox-text')
						:attr('colspan', '2')
						:wikitext(parse_pt(cfg.quality.rating:format(rating, scale)))
					:done()
				table.insert(rows, class_row)
				table.insert(
					nested_ratings,
					1,
					bubble(class=='' and 'Unassessed' or (class..'‑class'), cssClass)
				)
			end
		end
	else -- project uses standard PIQA assessment
		class = check_fallbacks(class, assessment_cat) -- check fallbacks for non-article classes
		local category = (class=='' and 'Unassessed' or (class..'-Class')) .. ' ' .. assessment_cat .. ' ' .. (article and 'articles' or 'pages')
		add_category(category)
		if raw_args.class~='' then -- page has a non-blank class value which is ignored
			add_category(cfg.banner_shell.category.ignored_class)
		end
	end
end
if args.HOOK_ASSESS then
	table.insert(rows, args.HOOK_ASSESS)
end
if raw_args.b1 or raw_args.b2 or raw_args.b3 or raw_args.b4 or raw_args.b5 or raw_args.b6 then
	local b_checklist = require(auxiliary).b_checklist(args, raw_args, class, demo_page, assessment_link)
	table.insert(rows, b_checklist)
end
---------------------------
-- Priority assessment ----
---------------------------
local importance
local importance_name = args.IMPN or (raw_args.priority and 'priority' or cfg.importance.default_name)
if raw_args.importance or raw_args.priority then -- banner gives priority ratings
	importance = importance_mask(args.importance or args.priority, args.IMPORTANCE_SCALE, banner_name, pagetype, class)
	local category = importance .. '-' .. importance_name .. ' ' .. assessment_cat .. ' ' .. (importance=='NA' and 'pages' or 'articles')
	if importance~='NA' then -- display importance rating
		local rating = importance=='Unknown' and cfg.importance.not_yet or cfg.importance.rated:format(importance, importance_name)
		local scale = assessment_link
			and cfg.importance.project_scale:format(assessment_link..'#'..lang:ucfirst(cfg.importance.scale:format(importance_name)), cfg.importance.scale:format(importance_name))
			or cfg.importance.default_scale
		local importance_rating = parse_pt(cfg.importance.rating:format(rating, scale))
		local cssClass = 'import-' .. importance:lower()
		local importance_row =  mw.html.create('tr')
			:tag('td')
				:addClass('assess')
				:addClass(cssClass)
				:wikitext(wikilink(':Category:' .. category, importance=='Unknown' and '???' or importance))
			:done()
			:tag('td')
				:addClass('mbox-text')
				:attr('colspan', '2')
				:wikitext(importance_rating)
			:done()
		table.insert(rows, importance_row)
		if importance~='Unknown' then -- importance is not NA or Unknown
			table.insert(
				nested_ratings,
				bubble(importance .. '‑' .. importance_name, cssClass)
			)
		end
	end
	add_category(category)
	if args.QII_FORMAT then
		add_category(require(auxiliary).quality_importance_insection(args, class, importance, importance_name, assessment_cat, article))
	end
end
if class or importance then
	page_assessment(project, class, importance)
end
if args.HOOK_IMPORTANCE then
	table.insert(rows, args.HOOK_IMPORTANCE)
end
---------------------------
-- Collapsing sections ----
---------------------------
local collapse_section = function(collapse, new_rows, header)
	if collapse then
		local header_row = mw.html.create('tr')
			:tag('th')
				:attr('colspan','3')
				:addClass('wpb-collapsed-head')
				:wikitext(header)
			:done()
		local blank_row = mw.html.create('tr')
			:tag('td')
				:addClass('mbox-image wpb-gutter')
				:css('min-width', image_left_size)
				:tag('span')
					:addClass('wpb-iefix')
					:wikitext('/&nbsp;')
					:done() --TO FIX IE
			:done()
			:tag('td'):done()
			:tag('td'):done()
		local collapsed_rows = mw.html.create('table')
			:addClass('mw-collapsible mw-collapsed')
			:node(header_row)
			:node(blank_row)
			for _, row in ipairs(new_rows) do
				collapsed_rows:node(row)
			end
		local collapsed_section = mw.html.create('tr')
			:tag('td')
				:attr('colspan','3')
				:addClass('wpb-collapsed-notes')
				:node(collapsed_rows)
			:done()
		table.insert(rows, collapsed_section)
	else
		for _, row in ipairs(new_rows) do
			table.insert(rows, row)
		end
	end
end
---------------------------
-- Task forces ------------
---------------------------
local nested_tf, taskforce_output = {}, {}
local tf_default_size = args.TF_SIZE or cfg.task_force.default_size
for _, k in ipairs(task_forces) do
	local tf_prefix = 'TF_' .. k .. '_'
	local tf_assessment_cat = assessment_category(
		args[tf_prefix..'ASSESSMENT_CAT'],
		args[tf_prefix..'NAME']
	)
	if yesno(args[tf_prefix..'QUALITY']) and class then
		local tf_class = check_fallbacks(class, tf_assessment_cat)
		add_category((tf_class=='' and 'Unassessed' or tf_class..'-Class') .. ' ' .. tf_assessment_cat .. ' ' .. (article and 'articles' or 'pages'))
	end
	local tf_importance, tf_importance_category
	if raw_args['tf '..k..' importance'] then
		tf_importance = importance_mask(args['tf '..k..' importance'], args.IMPORTANCE_SCALE, banner_name, pagetype, class)
		if tf_importance=='Unknown' and yesno(args.INHERIT_IMPORTANCE) then
			tf_importance = importance
		end
		tf_importance_category = tf_importance .. '-' .. importance_name .. ' ' .. tf_assessment_cat .. ' ' .. (tf_importance=='NA' and 'pages' or 'articles')
		add_category(tf_importance_category)
	end
	if args[tf_prefix .. 'TEXT']~='none' then
		local portal = args[tf_prefix..'PORTAL'] and frame:expandTemplate{
			title='Portal',
			args={args[tf_prefix .. 'PORTAL'], height='15', margin='0'}
		} or ''
		local text = ''
		local tf_text = args[tf_prefix..'TEXT'] or args.TF_TEXT
		if tf_text then
			text = portal .. tf_text
				:gsub('_NAME_', args[tf_prefix .. 'NAME'] or '')
				:gsub('_LINK_', args[tf_prefix .. 'LINK'] or '')
				:gsub('_IMPORTANCE_', tf_importance or '')
		else
			local tf_importance_text = tf_importance
				and tf_importance~='NA'
				and tf_importance~='Unknown'
				and ' ' .. cfg.task_force.importance:format(
					wikilink(':Category:' .. tf_importance_category, tf_importance .. '-' .. importance_name)
				) or ''
			text = portal .. cfg.task_force.text:format(
				wikilink(args[tf_prefix .. 'LINK'], args[tf_prefix .. 'NAME']),
				tf_importance_text
			)
		end
		local tf_size = args[tf_prefix .. 'SIZE'] or tf_default_size
		local tf_image = ''
		if args[tf_prefix .. 'IMAGE'] then
			tf_image = image(args[tf_prefix .. 'IMAGE'], tf_size, cfg.task_force.icon_alt, 'center', args[tf_prefix .. 'IMAGE_CLASS'])
		end
		local taskforce = mw.html.create('tr')
			:tag('td')
				:wikitext(tf_image)
			:done()
			:tag('td')
				:addClass('mbox-text')
				:attr('colspan','2')
				:wikitext(parse_pt(text))
			:done()
		table.insert(taskforce_output, taskforce)
	end
	if args[tf_prefix..'HOOK'] then
		table.insert(taskforce_output, args[tf_prefix..'HOOK'])
	end
	if args[tf_prefix..'QII_FORMAT'] then
		add_category(require(auxiliary).quality_importance_insection(args, class, tf_importance, importance_name, tf_assessment_cat, article, tf_prefix))
	end
	if args[tf_prefix..'NAME'] then
		page_assessment(project..'/'..args[tf_prefix..'NAME'], class, tf_importance)
	end
	if args[tf_prefix..'MAIN_CAT'] then
		add_category(args[tf_prefix..'MAIN_CAT'])
	end
	if args[tf_prefix..'NESTED'] then
		table.insert(nested_tf, wikilink(args[tf_prefix..'LINK'], args[tf_prefix..'NESTED']))
	end
	for _, c in ipairs(taskforce_categories[k] or {}) do-- add additional taskforce categories
		add_category(args[tf_prefix..'CAT_'..c])
	end
end
if args.HOOK_TF then
	table.insert(taskforce_output, args.HOOK_TF)
end
local threshold = tonumber(args.TF_COLLAPSE) or (args.TF_HEADER and cfg.task_force.lower_threshold) or cfg.task_force.upper_threshold
collapse_section(
	#taskforce_output > threshold,
	taskforce_output,
	args.TF_HEADER or cfg.task_force.header
)
---------------------------
-- Notes ------------------
---------------------------
local note_output = {}
local note_default_size = args.NOTE_SIZE or args.NOTE_1_SIZE or cfg.note.default_size
local render_note = function(note_args)--text, image_name, size, category, sort_prefix, class
	local sort = note_args.sort_prefix and note_args.sort_prefix .. current_title.text
	add_category(note_args.category, sort)
	add_category(note_args.category2, sort)
	if note_args.text then
		local note_image = image(
			note_args.image_name,
			note_args.size or note_default_size,
			cfg.note.icon_alt,
			'center',
			note_args.class
		)
		local new_note = mw.html.create('tr')
			:tag('td')
				:css('background', note_args.background)
				:css('color', note_args.color)
				:wikitext(note_image)
			:done()
			:tag('td')
				:addClass('mbox-text')
				:attr('colspan', '2')
				:wikitext(parse_pt(note_args.text))
			:done()
		table.insert(note_output, new_note)
		if note_image then
			local icon = mw.html.create('span')
				:addClass('wpb-header-bubbles')
				:wikitext('[[File:' .. note_args.image_name .. '|' .. cfg.note.header_icon .. '|' .. parse_pt(note_args.text) .. '|alt=icon]]')
			table.insert(nested_ratings, tostring(icon))
		end
	end
end
local auto = false
local auto_arg = args.auto and args.auto:lower()
if (auto_arg=='yes' or auto_arg=='stub') and class=='Stub' then
	auto = 'stub'
elseif (auto_arg=='inherit' or auto_arg=='length') and class and class~='' then
	auto = auto_arg
end
if auto then
	local auto_cat = args.AUTO_ASSESS_CAT or cfg.auto.default_cat:format(project)
	local auto_text = cfg.auto.assessed:format(
		cfg.auto[auto], -- method of automatic assessment
		parameter_format('auto')
	)
	local sort_prefix
	if auto=='stub' then
		sort_prefix = 'S'
	elseif auto=='length' then
		sort_prefix = 'L'
	elseif auto=='inherit' then
		local sort_codes = cfg.auto.sort_codes
		sort_prefix = sort_codes[class] or cfg.auto.default_sort_code
	end
	render_note{
		text = auto_text,
		image_name =  cfg.auto.icon,
		category = auto_cat,
		sort_prefix = sort_prefix
	}
end
if yesno(args.attention, true) then
	local attention_cat = args.ATTENTION_CAT or cfg.attention.default_cat:format(project)
	render_note{
		text = cfg.attention.text,
		image_name = cfg.attention.icon,
		category = attention_cat
	}
end
if yesno(args.infobox, true) then
	local infobox_cat = args.INFOBOX_CAT or cfg.infobox.default_cat:format(project)
	render_note{
		text = cfg.infobox.text,
		image_name = cfg.infobox.icon,
		category = infobox_cat
	}
end
for _, k in ipairs(notes) do
	local note_prefix = 'NOTE_' .. k .. '_'
	render_note{
		text = parse_pt(args[note_prefix..'TEXT']),
		image_name = args[note_prefix..'IMAGE'],
		size = args[note_prefix..'SIZE'],
		category = args[note_prefix..'CAT'],
		class = args[note_prefix..'IMAGE_CLASS']
	}
end
if yesno(args['image-needed'], true) then
	local image_needed_args = require(auxiliary).image_needed(args)
	render_note(image_needed_args)
end
if yesno(args['collaboration-candidate'], true) or yesno(args['collaboration-current'], true) or yesno(args['collaboration-past'], true) then
	local collaboration_args = require(auxiliary).collaboration(args, current_title)
	render_note(collaboration_args.candidate)
	render_note(collaboration_args.current)
	render_note(collaboration_args.past)
end
if yesno(args['a class'], true) then
	local a_class_args = require(auxiliary).a_class(args, lang)
	render_note(a_class_args)
end
if yesno(args['peer review'], true) or yesno(args['old peer review'], true) then
	local peer_review_args = require(auxiliary).peer_review(args, current_title)
	render_note(peer_review_args.current)
	render_note(peer_review_args.past)
end

local note_count = #note_output
if args.HOOK_NOTE then
	table.insert(note_output, args.HOOK_NOTE)
	local hook_collapsed = 0
	if args.HOOK_COLLAPSED then
		local success, result = pcall(mw.ext.ParserFunctions.expr, args.HOOK_COLLAPSED)
		hook_collapsed = success and tonumber(result) or 0
		if args.HOOK_COLLAPSED=='auto' then
			hook_collapsed = 1
		end
	end
	note_count = note_count + hook_collapsed
end
collapse_section(
	note_count > (tonumber(args.COLLAPSED) or cfg.note.threshold),
	note_output,
	args.COLLAPSED_HEAD or cfg.note.header
)
---------------------------
-- Bottom text ------------
---------------------------
if args.HOOK_BOTTOM then
	table.insert(rows, args.HOOK_BOTTOM)
end
if args.TODO_LINK or args.TODO_TEXT then
	local todolist = require(auxiliary).todo_list(args, frame)
	table.insert(rows, todolist)
end
if args.BOTTOM_TEXT then
	local bottom_text = mw.html.create('tr')
		:tag('td')
			:attr('colspan','3')
			:wikitext(parse_pt(args.BOTTOM_TEXT))
		:done()
	table.insert(rows, bottom_text)
end
if args.MAIN_CAT then
	add_category(args.MAIN_CAT)
end
---------------------------
-- Nested display ---------
---------------------------
if args.HOOK_NESTED then
	local hook_nested = args.HOOK_NESTED:gsub('^&#32;/ ', '') -- remove initial slash, will be added later
	table.insert(nested_tf, hook_nested)
end
local nested_tf_str = ''
if #nested_tf>0 then
	nested_tf_str = tostring(mw.html.create('span')
		:addClass('wpb-nested-task-force')
		:wikitext(': ' .. table.concat(nested_tf, ' / '))
	)
end
local nested_ratings_str = #nested_ratings>0 and table.concat(nested_ratings, ' ') or ''
if args.HOOK_NESTED_ASSESS then
	nested_ratings_str = nested_ratings_str .. tostring(mw.html.create('span')
		:addClass('wpb-header-bubbles')
		:wikitext(args.HOOK_NESTED_ASSESS)
	)
end
local header_row = mw.html.create('tr')
	:addClass('wpb-header')
	:tag('td')
		:addClass('wpb-header-icon')
		:wikitext(image(args.IMAGE_LEFT, cfg.image.header_size, cfg.image.alt))
		:done()
	:tag('td')
		:addClass('wpb-header-combined')
		:wikitext(wikilink(project_link.prefixedText, project) .. nested_tf_str .. '  ' .. nested_ratings_str)
		:done()
---------------------------
-- Prepare categories -----
---------------------------
local categories_formatted = ''
if demo_page and demo_page~=true then -- for testing purposes
	local category_list = mw.html.create('ul')
	for _, cat in ipairs(categories) do
		local item = mw.html.create('li')
			:wikitext(wikilink(':Category:' .. cat.category, cat.category))
		category_list:node(item)
	end
	local category_box = mw.html.create('div')
		:addClass('wpb-category-box')
		:wikitext('Categories:')
		:node(category_list)
	categories_formatted = tostring(category_box)
elseif not demo_page then
	local categories_linked = {}
	for _, cat in ipairs(categories) do
		local cat_link = wikilink('Category:' .. cat.category, cat.key)
		table.insert(categories_linked, cat_link)
	end
	categories_formatted = table.concat(categories_linked)
end
---------------------------
-- Make banner ------------
---------------------------
local banner_rows = mw.html.create('table')
for _, row in ipairs(rows) do
	banner_rows:node(row)
end
local banner = mw.html.create('table')
	:addClass('tmbox tmbox-notice mw-collapsible innercollapse wpb wpb-table')
	:addClass(inactive and cfg.inactive.class or nil)
	:node(header_row)
	:tag('tr')
		:tag('td')
			:addClass('mbox-text wpb-main')
			:attr('colspan','2')
			:node(banner_rows)
	:allDone()
local tstyle = frame:extensionTag('templatestyles', '', {src='Module:Message box/tmbox.css'}) ..
	frame:extensionTag ('templatestyles', '', {src = 'Module:WikiProject banner' .. (sandbox or '') .. '/styles.css'})
return warning .. tstyle .. tostring(banner) .. categories_formatted, note_count, #taskforce_output, assessment_link
end

local initialise = function(args, raw_args, inactive_status)
---------------------------
-- Initialise arguments ---
---------------------------
local parent_args = args_module.getArgs(frame, {parentOnly = true})
local category = parent_args.category or args.category or true
local demo_page = parent_args.demo_page
local config, project
if args.project then -- check for config page
	project = args.project
	local config_file = mw.title.new('Template:WikiProject ' .. args.project .. '/config')
	if config_file.exists then
		config = mw.loadJsonData(config_file.fullText)
	else
		return nil
	end
end
local parameters, unknown_parameters = {}, ''
if config then -- use config file
	args, raw_args, parameters = require(auxiliary).map_config(config, parent_args) -- map parameters from config page
	args.PROJECT = project
end
local on_template_page = false
local banner_name = mw.title.new(args.BANNER_NAME or 'Template:WikiProject ' .. (args.PROJECT or 'PROJECT'))
if not demo_page then
	if yesno(category, true) then
		on_template_page = current_title.rootPageTitle==banner_name.rootPageTitle
	else
		demo_page = true
	end
end
local project_name = args.PROJECT_NAME or 'WikiProject ' .. (args.PROJECT or 'PROJECT')
if banner_name.exists and not demo_page then -- check for unknown parameters
	if not config then -- read template code to find valid parameters
		local parameter_list = {}
		for parameter in banner_name:getContent():gmatch('{{{([^|}]+)') do
			parameter_list[parameter] = true
		end
		if parameter_list.importance==true then -- add priority as a valid alias of importance
			parameter_list.priority = true
		end
		if args.QUALITY_CRITERIA~='custom' then -- class is unrecognised, so remove from list
			parameter_list.class = false
		end
		parameter_list.listas = false -- remove listas even if parameter used (should be in banner shell)
		for k, v in pairs(parameter_list) do
			if v then
				table.insert(parameters, k)
			end
		end
	end
	parameters.showblankpositional = "1"
	local check_for_unknown = require('Module:Check for unknown parameters')._check
	local unknowns = check_for_unknown(parameters, parent_args)
	if unknowns and unknowns~='' then -- there are some unknown parameters
		parameters.preview = cfg.unknown_parameters.preview:format(wikilink(banner_name.fullText))
		local unknown_category = cfg.unknown_parameters.tracking:format(project_name)
		if not mw.title.new(unknown_category).exists then
			unknown_category = cfg.unknown_parameters.default
		end
		parameters.unknown = unknown_category and '[[' .. unknown_category .. '|_VALUE_]]' or ''
		unknown_parameters = check_for_unknown(parameters, parent_args)
	end
end
if not args.importance then -- allow priority to be used even by banners which don't support it
	args.importance = parent_args.priority
end
if on_template_page then
	local templatepage = require('Module:WikiProject banner/templatepage' .. (sandbox or '')).templatepage
	return templatepage(args, raw_args, inactive_status, config)
else
	return unknown_parameters
		.. p._main(args, raw_args, demo_page, banner_name, inactive_status and true or false), nil -- nil to disregard subsequent returned values
end
end

p.main = function(frame)
	local args = args_module.getArgs(frame, {frameOnly = true})
	local raw_args = args_module.getArgs(frame, {frameOnly = true, removeBlanks = false})
	return initialise(args, raw_args)
end
---------------------------
-- Inactive projects ------
---------------------------
p.inactive = function(frame)
	local args = args_module.getArgs(frame, {frameOnly = true})
	local project_name = args.PROJECT_NAME or 'WikiProject ' .. (args.PROJECT or 'PROJECT')
	local project_link = mw.title.new(args.PROJECT_LINK or 'Wikipedia:' .. project_name)
	local _status = cfg.inactive.status[args.PROJECT_STATUS] or cfg.inactive.default
	local main_text = cfg.inactive.text:format(
		project_link.prefixedText,
		project_name,
		_status
	)
	return initialise(
		{
			PROJECT = args.PROJECT,
			BANNER_NAME = args.BANNER_NAME,
			IMAGE_LEFT = cfg.inactive.image,
			IMAGE_LEFT_SIZE = cfg.inactive.image_size,
			MAIN_TEXT = main_text,
			HOOK_NESTED_ASSESS = ' ' .. cfg.inactive.nested:format(_status),
			substcheck = args.substcheck,
			category = args.category
		}, {
			substcheck = '' -- to prevent warning on templatepage
		}, _status
	)
end

return p

Content Disclaimer

Informasi ini disarikan dari Wikipedia dan disajikan kembali untuk tujuan edukasi. Konten tersedia di bawah lisensi CC BY-SA 3.0. Kami tidak bertanggung jawab atas ketidakakuratan data yang bersumber dari kontribusi publik tersebut.

  1. The information displayed on this website is sourced in part or in whole from Wikipedia and has been adapted for the purpose of restating it. We strive to provide accurate and relevant information, however:
  2. There is no guarantee of absolute accuracy. Wikipedia is an open, collaborative project that can be edited by anyone, so information is subject to change.
  3. It is not intended to constitute professional advice. The content displayed is for informational and educational purposes only. For important decisions (e.g., medical, legal, or financial), please consult a professional.
  4. Content copyright. Wikipedia is licensed under the Creative Commons Attribution-ShareAlike License (CC BY-SA). This means that content may be reused with appropriate attribution and shared under a similar license.
  5. Responsible use. Any risk arising from the use of information from this website is entirely the responsibility of the user.