Module:WikiProject banner

require('strict') local p = {} local sandbox-- = '/sandbox' local cfg = mw.loadData('Module:WikiProject banner/config' .. (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 ..display.. or ..link.. else return display or '' end end

local image = function(image_name, size, alt, position) return image_name and 		.. image_name		.. (size and  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 isarticle = function(class) local article = true for _,v in ipairs(cfg.quality.non_article_classes) do		if class==v then -- class matches one of the non-article classes article = false break end end return article end

--- -- Importance mask --- local importance_mask = function(raw_importance, class, scale, banner_name) local importance if scale=='inline' then -- pass importance without change importance = raw_importance 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 = mw.text.trim(frame:expandTemplate{				title = custom_mask.prefixedText,				args = {importance=raw_importance or '¬', class=class}			}) end else importance = frame:expandTemplate{ title = 'Template:Importance mask', args = {raw_importance or '¬', class=class}} end if importance=='¬' then importance = nil end return importance 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').getValue local success, result = get_parameter_value(page, cfg.WPBS_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) 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' or class=='dab' or class=='disambig' or class=='disambiguation' or class=='disamb' then out = resolveFQSgrade('disambig') elseif pagetype=='article' then if 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' and FQS then out = 'FM' else out = resolveFQSgrade('file') end elseif ns==15 then -- Category talk out = resolveFQSgrade('category') elseif ns==101 then -- Portal talk out = resolveFQSgrade('portal') elseif ns==11 or ns==829 then -- Template talk out = resolveFQSgrade('template') elseif ns==5 then -- Wikipedia talk out = resolveFQSgrade('project') elseif ns==119 then -- Draft talk out = resolveFQSgrade('draft') else out = 'NA' 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('') end

local bubble = function(text, colour, conflict) local out = mw.html.create('span') :addClass('wpb-header-bubbles') :css('color', '#202122') :css('background', colour) :css('border', conflict and cfg.quality.conflict.border or (cfg.quality.border..' '..colour)) :wikitext(text) return tostring(out) end

p._main = function(args, raw_args, demo, 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 and not args.demo_page and 'article' or require('Module:Pagetype' .. (sandbox or ''))._main({	page = args.demo_page,	dab = 'disambiguation page' }) 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_text = function(text) return text and text:gsub('_PAGETYPE_', pagetype) 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 warning = '' --- -- Location warning --- --- local show_namespace_warning = not (current_title.isTalkPage or demo) if show_namespace_warning then local text = cfg.namespace_warning.text:format(		pagetype,		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 = '',		type = cfg.namespace_warning.type_,		text = 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,		' '	) warning = warning .. mbox('ombox', {		image = '',		type = cfg.subst_warning.type_,		text = text,	}) .. cfg.subst_warning.categories end --- -- Primary image/text - --- local assessment_cat = args.ASSESSMENT_CAT or project .. ' articles' local primary_image = function(image_name, size) 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)) 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_text(args.MAIN_TEXT) or cfg.main_text:format( pagetype, 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)) :node(text_cell) :node(primary_image(args.IMAGE_RIGHT, args.IMAGE_RIGHT_SIZE or cfg.image.default_size)) table.insert(rows, primary_row) --- -- 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_exists = function(class, assessment_cat) -- check if category exists and is not blank if not isarticle(class) then local cat = mw.title.new('Category:' .. class .. '-Class' .. ' ' .. assessment_cat) return (cat.exists and #cat:getContent>0) and class or 'NA' -- automatically use NA for non-article pages if category does not exist else return class end end local class = raw_args.class if class then -- banner gives quality ratings local title = args.demo_page and mw.title.new(args.demo_page) or current_title local article_class = p.readarticleclass({ignore_subtemplates=true}, title.prefixedText) if not article_class then if pagetype=='article' then add_category(cfg.category.no_banner_shell_articles) else add_category(cfg.category.no_banner_shell) end end article_class = article_class and p.class_mask(article_class, title, false, pagetype) local show_quality, conflict = true, false if args.QUALITY_CRITERIA=='custom' then -- project has opted out of standard assessment scale and uses a custom mask local custom_mask = banner_name:subPageTitle('class') if custom_mask.exists and #custom_mask:getContent>1 then raw_args.demo_page = args.demo_page -- send demo_page to custom mask class = mw.text.trim(frame:expandTemplate{				title = custom_mask.prefixedText,				args = raw_args			}) if class== and article_class and article_class~= then -- if unassessed and article 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 = article_class -- replace class with inherited class local article_class_normalised = mw.text.trim(frame:expandTemplate{					title = custom_mask.prefixedText,					args = new_arg_table				}) if article_class_normalised and article_class_normalised~='' then class = article_class_normalised -- inherit class from article_class normalised by custom mask else article_class = nil -- effectively no article_class for this banner end end end else class = p.class_mask(class, title, true, pagetype) end if article_class then -- banner shell exists if article_class=='' then -- no article class defined if class=='' then -- local class also does not exist, check whether any other class parameters are defined inside the shell local classparam = p.readarticleclass({ignore_blank=true, only_subtemplates=true}, title.prefixedText) if classparam=='' then -- no class parameters defined, display as globally unassessed show_quality = false -- hide quality class in project banner end else add_category(cfg.category.no_quality_rating) end elseif class=='' or class==article_class then -- local class matches article class or is blank show_quality = false -- hide quality class in project banner class = article_class if raw_args.class~='' and args.QUALITY_CRITERIA~='custom' then add_category(cfg.category.redundant_class) end elseif (article_class=='NA') and not isarticle(class) then -- article class and local class are both non-article classes show_quality = false else -- article class exists and differs from local class if args.QUALITY_CRITERIA~='custom' then conflict = true add_category(cfg.quality.conflict.category) end end end if not isarticle(class) then local cat = mw.title.new(cfg.quality.assessment_category:format(class, assessment_cat)) if not (cat.exists and #cat:getContent>0) then --check if category exists and is not blank class = 'NA' -- automatically use NA for non-article pages if category does not exist end end local category = (class=='' and 'Unassessed' or class..'-Class') .. ' ' .. assessment_cat if show_quality then -- quality rating shown in banner local rating if pagetype=='article' then rating = class=='' and cfg.quality.not_yet or cfg.quality.rated:format(class) else rating = cfg.quality.not_required end local scale = args.QUALITY_CRITERIA=='custom' and assessment_link and cfg.quality.project_scale:format(wikilink(assessment_link..'#'..lang:ucfirst(cfg.quality.name), cfg.quality.name)) or cfg.quality.default_scale local quality_rating = conflict and cfg.quality.conflict.text or cfg.quality.rating:format(pagetype, rating, scale) local colour = cfg.quality.colour[class] or cfg.quality.colour.default local class_row = mw.html.create('tr') :tag('td') :addClass('assess'):addClass('assess-' .. class) :css('background', colour):css('color', 'inherit'):wikitext(wikilink(':Category:' .. category, class=='' and '???' or class)) :css('border', conflict and cfg.quality.conflict.border or (cfg.quality.border..' '..colour)) :done :tag('td') :addClass('mbox-text'):attr('colspan', '2') :wikitext(quality_rating) :done table.insert(rows, class_row) table.insert(			nested_ratings,			1,			bubble(class=='' and 'Unassessed' or (class..'‑class'), colour, conflict)		) end add_category(category) 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(cfg.auxiliary_module .. (sandbox or '')).b_checklist(args, raw_args, class, demo, assessment_link) table.insert(rows, b_checklist) end --- -- Importance assessment -- --- local importance = importance_mask(raw_args.importance or raw_args.priority, class, args.IMPORTANCE_SCALE, banner_name) local importance_name = args.IMPN or (raw_args.priority and 'priority' or cfg.importance.default_name) if importance then -- banner gives importance ratings local category = importance .. '-' .. importance_name .. ' ' .. assessment_cat 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_name = cfg.importance.scale:format(importance_name) local scale = assessment_link and cfg.importance.project_scale:format(assessment_link..'#'..lang:ucfirst(scale_name), scale_name) or cfg.importance.default_scale local importance_rating = cfg.importance.rating:format(pagetype, rating, scale) local colour = cfg.importance.colour[importance] or cfg.importance.colour.default local importance_row = mw.html.create('tr') :tag('td') :addClass('assess'):addClass('import'):addClass('import-' .. importance) :css('color', '#202122') :css('background', colour):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, colour)			) end end add_category(category) end page_assessment(project, class, importance) if args.HOOK_IMPORTANCE then table.insert(rows, args.HOOK_IMPORTANCE) end if args.QII_FORMAT then add_category(require(cfg.auxiliary_module .. (sandbox or '')).quality_importance_insection(args, class, importance, importance_name)) 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('/ '):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 = args[tf_prefix..'ASSESSMENT_CAT'] or (args[tf_prefix..'NAME'] or '')..' articles' local tf_importance if raw_args['tf '..k..' importance'] then tf_importance = importance_mask(raw_args['tf '..k..' importance'], class, args.IMPORTANCE_SCALE, banner_name) if tf_importance=='Unknown' and yesno(args.INHERIT_IMPORTANCE) then tf_importance = importance end 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 '') :gsub('_PAGETYPE_', pagetype) 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 .. '-' .. importance_name .. ' ' .. tf_assessment_cat, tf_importance .. '-' .. importance_name)				) or '' text = portal .. cfg.task_force.text:format(				pagetype,				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') end local taskforce = mw.html.create('tr') :tag('td'):wikitext(tf_image):done :tag('td'):addClass('mbox-text'):attr('colspan','2'):wikitext(text):done table.insert(taskforce_output, taskforce) end if args[tf_prefix..'HOOK'] then table.insert(taskforce_output, args[tf_prefix..'HOOK']) end if yesno(args[tf_prefix..'QUALITY']) and class then local tf_class = check_exists(class, tf_assessment_cat) add_category((tf_class=='' and 'Unassessed' or tf_class..'-Class') .. ' ' .. tf_assessment_cat) end if tf_importance then add_category(tf_importance .. '-' .. importance_name .. ' ' .. tf_assessment_cat) end if args[tf_prefix..'QII_FORMAT'] then add_category(require(cfg.auxiliary_module .. (sandbox or '')).quality_importance_insection(args, class, tf_importance, importance_name, 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 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') local new_note = mw.html.create('tr') :tag('td'):css('background', note_args.background):wikitext(note_image):done :tag('td'):addClass('mbox-text'):attr('colspan', '2'):wikitext(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('') 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(		pagetype,		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:format(pagetype), 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:format(pagetype), image_name = cfg.infobox.icon, category = infobox_cat } end for _, k in ipairs(notes) do local note_prefix = 'NOTE_' .. k .. '_'	render_note{ text = parse_text(args[note_prefix..'TEXT']), image_name = args[note_prefix..'IMAGE'], size = args[note_prefix..'SIZE'], category = args[note_prefix..'CAT'] } end if yesno(args['image-needed'], true) then local image_needed_args = require(cfg.auxiliary_module .. (sandbox or '')).image_needed(args, pagetype) 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(cfg.auxiliary_module .. (sandbox or '')).collaboration(args, pagetype, 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(cfg.auxiliary_module .. (sandbox or '')).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(cfg.auxiliary_module .. (sandbox or '')).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(cfg.auxiliary_module .. (sandbox or '')).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_text(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')		:css('font-weight', 'normal') --TODO: move to TemplateStyles.		: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') :attr('width', '50px') --TODO: move to TemplateStyles. :css('text-align', 'center') --TODO: move to TemplateStyles. :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 args.demo_page 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') :css('background-color', '#F5F5F5'):css('border-width', '1px'):css('color', '#202122') --TODO: move to TemplateStyles. :css('width', '500px') --TODO: move to TemplateStyles. :wikitext('Categories:') :node(category_list) categories_formatted = tostring(category_box) elseif not demo 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') :addClass(inactive and cfg.status.inactive_class or nil) :css('table-layout', 'fixed') --TODO: move to TemplateStyles. :node(header_row) :tag('tr'):tag('td') :addClass('mbox-text wpb-main') :attr('colspan','2') :node(banner_rows) :allDone if args.listas then frame:preprocess('') end 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 parameter_check = function(frame, banner_name, project_name) --- -- Unknown parameters - --- local parent_args = args_module.getArgs(frame, {parentOnly = true}) local parameters = {} for parameter in banner_name:getContent:gmatch('{{{([^|}]+)') do	table.insert(parameters, parameter) end 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 '_VALUE_' or '' return require('Module:Check for unknown parameters')._check(parameters, parent_args) end

local initialise = function(args, raw_args, inactive_status) args.demo_page = args_module.getArgs(frame, {parentOnly = true}).demo_page local project_name = args.PROJECT_NAME or 'WikiProject ' .. (args.PROJECT or 'PROJECT') local banner_name = mw.title.new(args.BANNER_NAME or 'Template:WikiProject ' .. (args.PROJECT or 'PROJECT')) local demo = not yesno(args.category or true, true) or args.demo_page local on_template_page = not demo and current_title.rootPageTitle==banner_name.rootPageTitle local unknown_parameters = banner_name.exists and not demo and parameter_check(frame, banner_name, project_name) or '' if on_template_page then local templatepage = require('Module:WikiProject banner/templatepage' .. (sandbox or '')).templatepage return templatepage(args, raw_args, inactive_status) else return unknown_parameters .. p._main(args, raw_args, demo or inactive_status, 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.status[args.PROJECT_STATUS] or cfg.status.default_inactive local main_text = cfg.inactive.text:format(		'_PAGETYPE_',		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		},		{			class = frame.args.class,			substcheck = '' -- to prevent warning on templatepage		},		_status	) end

return p