diff --git a/Lua banner/main.lua b/Lua banner/main.lua
new file mode 100644
index 0000000..c654761
--- /dev/null
+++ b/Lua banner/main.lua
@@ -0,0 +1,128 @@
+-- This module implements the {{lua}} template.
+local yesno = require('Module:Yesno')
+local mList = require('Module:List')
+local mTableTools = require('Module:TableTools')
+local mMessageBox = require('Module:Message box')
+
+local p = {}
+
+function p.main(frame)
+ local origArgs = frame:getParent().args
+ local args = {}
+ for k, v in pairs(origArgs) do
+ v = v:match('^%s*(.-)%s*$')
+ if v ~= '' then
+ args[k] = v
+ end
+ end
+ return p._main(args)
+end
+
+function p._main(args)
+ local modules = mTableTools.compressSparseArray(args)
+ local box = p.renderBox(modules)
+ local trackingCategories = p.renderTrackingCategories(args, modules)
+ return box .. trackingCategories
+end
+
+function p.renderBox(modules)
+ local boxArgs = {}
+ if #modules < 1 then
+ boxArgs.text = 'Error: no modules specified'
+ else
+ local moduleLinks = {}
+ for i, module in ipairs(modules) do
+ moduleLinks[i] = string.format('[[:%s]]', module)
+ local maybeSandbox = mw.title.new(module .. '/sandbox')
+ if maybeSandbox.exists then
+ moduleLinks[i] = moduleLinks[i] .. string.format(' ([[:%s|sandbox]])', maybeSandbox.fullText)
+ end
+ end
+ local moduleList = mList.makeList('bulleted', moduleLinks)
+ local title = mw.title.getCurrentTitle()
+ if title.subpageText == "doc" then
+ title = title.basePageTitle
+ end
+ if title.contentModel == "Scribunto" then
+ boxArgs.text = 'This module depends on the following other modules:' .. moduleList
+ else
+ boxArgs.text = 'This template uses [[BKDatabase:Lua|Lua]]:\n' .. moduleList
+ end
+ end
+ boxArgs.type = 'notice'
+ boxArgs.small = true
+ boxArgs.image = '[[File:Lua-Logo.svg|30px|alt=|link=]]'
+ return mMessageBox.main('mbox', boxArgs)
+end
+
+function p.renderTrackingCategories(args, modules, titleObj)
+ if yesno(args.nocat) then
+ return ''
+ end
+
+ local cats = {}
+
+ -- Error category
+ if #modules < 1 then
+ cats[#cats + 1] = 'Lua templates with errors'
+ end
+
+ -- Lua templates category
+ titleObj = titleObj or mw.title.getCurrentTitle()
+ local subpageBlacklist = {
+ doc = true,
+ sandbox = true,
+ sandbox2 = true,
+ testcases = true
+ }
+ if not subpageBlacklist[titleObj.subpageText] then
+ local protCatName
+ if titleObj.namespace == 10 then
+ local category = args.category
+ if not category then
+ local categories = {
+ ['Module:String'] = 'Templates based on the String Lua module',
+ ['Module:Math'] = 'Templates based on the Math Lua module',
+ ['Module:BaseConvert'] = 'Templates based on the BaseConvert Lua module',
+ ['Module:Citation/CS1'] = 'Templates based on the Citation/CS1 Lua module'
+ }
+ category = modules[1] and categories[modules[1]]
+ category = category or 'Lua-based templates'
+ end
+ cats[#cats + 1] = category
+ protCatName = "Templates using under-protected Lua modules"
+ elseif titleObj.namespace == 828 then
+ protCatName = "Modules depending on under-protected modules"
+ end
+ if not args.noprotcat and protCatName then
+ local protLevels = {
+ autoconfirmed = 1,
+ extendedconfirmed = 2,
+ templateeditor = 3,
+ sysop = 4
+ }
+ local currentProt
+ if titleObj.id ~= 0 then
+ -- id is 0 (page does not exist) if am previewing before creating a template.
+ currentProt = titleObj.protectionLevels["edit"][1]
+ end
+ if currentProt == nil then currentProt = 0 else currentProt = protLevels[currentProt] end
+ for i, module in ipairs(modules) do
+ if module ~= "WP:libraryUtil" then
+ local moduleProt = mw.title.new(module).protectionLevels["edit"][1]
+ if moduleProt == nil then moduleProt = 0 else moduleProt = protLevels[moduleProt] end
+ if moduleProt < currentProt then
+ cats[#cats + 1] = protCatName
+ break
+ end
+ end
+ end
+ end
+ end
+ for i, cat in ipairs(cats) do
+ cats[i] = string.format('[[Category:%s]]', cat)
+ end
+ return table.concat(cats)
+end
+
+return p
diff --git a/Testcases/Includes.lua b/Testcases/Includes.lua
new file mode 100644
index 0000000..faa4c57
--- /dev/null
+++ b/Testcases/Includes.lua
@@ -0,0 +1,30 @@
+-- Unit tests for [[Module:{{ROOTPAGENAME}}]]. Click talk page to run tests.
+local p = require('Module:UnitTests')
+
+-- Example unit test.
+function p:test_hello()
+ local includes = require('Module:Includes')
+
+ -- These will return true
+ self:equals('includes({"a", "b", "c", "d"}, "b")', includes({"a", "b", "c", "d"}, "b"), true)
+ self:equals('includes({"a", "b", "c", "d"}, "b", 0)', includes({"a", "b", "c", "d"}, "b", 0), true)
+ self:equals('includes({"a", "b", "c", "d"}, "b", 1)', includes({"a", "b", "c", "d"}, "b", 1), true)
+ self:equals('includes({"a", "b", "c", "d"}, "b", 2)', includes({"a", "b", "c", "d"}, "b", 2), true)
+ self:equals('includes({"a", "b", "c", "d"}, "b", -3)', includes({"a", "b", "c", "d"}, "b", -3), true)
+ self:equals('includes({"a", "b", "c", "d"}, "b", -5)', includes({"a", "b", "c", "d"}, "b", -5), true)
+ self:equals('includes({[1] = "a",[100] = "b",[101] = "c"}, "b")', includes({[1] = "a",[100] = "b",[101] = "c"}, "b"), true)
+ self:equals('includes({[1] = "a",[2] = "b",[3] = "c"}, "b", 0)', includes({[1] = "a",[2] = "b",[3] = "c"}, "b", 0), true)
+ self:equals('includes({first = "a", second = "b", third = "c"}, "b")', includes({first = "a", second = "b", third = "c"}, "b"), true)
+
+ --these will return false
+ self:equals('includes("b","b")', includes("b","b"), false) -- array is not a table
+ self:equals('includes({"a", "b", "c", "d"})', includes({"a", "b", "c", "d"}), false) -- value missing
+ self:equals('includes({"a", "b", "c", "d"}, "e")', includes({"a", "b", "c", "d"}, "e"), false) -- "e" is not in array
+ self:equals('includes({"a", "b", "c", "d"}, "b", 3)', includes({"a", "b", "c", "d"}, "b", 3), false) -- "b" is before position 3
+ self:equals('includes({"a", "b", "c", "d"}, "b", 5)', includes({"a", "b", "c", "d"}, "b", 5), false) -- 5 is larger than #array
+ self:equals('includes({"a", "b", "c", "d"}, "b", -2)', includes({"a", "b", "c", "d"}, "b", -2), false) -- "b" is not in the last two positions
+ self:equals('includes({[1] = "a", [100] = "b", [101] = "c"}, "b", 0)', includes({[1] = "a", [100] = "b", [101] = "c"}, "b", 0), false) -- key 100 is non-consecutive
+ self:equals('includes({first = "a", second = "b", third = "c"}, "b", 0)', includes({first = "a", second = "b", third = "c"}, "b", 0), false) -- key "second" is not an integer
+end
+
+return p
diff --git a/Testcases/Infobox.lua b/Testcases/Infobox.lua
new file mode 100644
index 0000000..9b660ff
--- /dev/null
+++ b/Testcases/Infobox.lua
@@ -0,0 +1,43 @@
+-- Ví dụ các trường hợp kiểm thử đơn vị cho [[Mô đun:Infobox]]. Nhấn vào trang thảo luận để
+-- chạy các trường hợp kiểm thử.
+local p = require('Mô đun:UnitTests')
+
+function p:test_hello()
+ self:preprocess_equals_preprocess_many('{{infobox/sandbox', '}}', '{{Infobox', '}}', {
+ {[=[
+ |label1 = Nhãn 1
+ |data1 = Dữ liệu 1
+ ]=]},
+ }, {nowiki = 'yes'})
+end
+
+function p:test_ids()
+ self:preprocess_equals_preprocess_many('{{infobox/sandbox', '}}', '{{Infobox', '}}', {
+ {[=[
+ |label2 = Nhãn 2
+ |labelid2 = lable
+ |data2 = Dữ liệu 2
+ |dataid2 = data
+ |rowid1 = row
+ |header1 = Đầu đề 1
+ |headerid1 = header
+ ]=]},
+ }, {nowiki = 'yes'})
+end
+
+function p:test_ids_name()
+ self:preprocess_equals_preprocess_many('{{infobox/sandbox', '}}', '{{Infobox', '}}', {
+ {[=[
+ |name = qw er tz
+ |label2 = Nhãn 2
+ |labelid2 = lable
+ |data2 = Dữ liệu 2
+ |dataid2 = data
+ |rowid1 = row
+ |header1 = Đầu đề 1
+ |headerid1 = header
+ ]=]},
+ }, {nowiki = 'yes'})
+end
+
+return p
diff --git a/Testcases/InfoboxImage.lua b/Testcases/InfoboxImage.lua
new file mode 100644
index 0000000..bb7d304
--- /dev/null
+++ b/Testcases/InfoboxImage.lua
@@ -0,0 +1,109 @@
+-- Unit tests for [[Module:InfoboxImage]]. Click talk page to run tests.
+
+local p = require('Module:UnitTests')
+
+function p:test1_parameters_output()
+ self:preprocess_equals_preprocess_many('{{#invoke:InfoboxImage/sandbox |InfoboxImage |image=', '}}', '{{#invoke:InfoboxImage |InfoboxImage |image=', '}}', {
+ {'Small Sailboat Image.jpg |alt=Alt'},
+ {'Small Sailboat Image.jpg |border=yes'},
+ {'Small Sailboat Image.jpg |center=yes'},
+ {'Small Sailboat Image.jpg |link=Link'},
+ {'Small Sailboat Image.jpg |thumbtime=Thumbtime'},
+ {'Small Sailboat Image.jpg |title=Title'},
+ {'Small Sailboat Image.jpg |size=75'},
+ {'Small Sailboat Image.jpg |size=200'},
+ {'Small Sailboat Image.jpg |size=200|sizedefault=260|maxsize=275'},
+ {'Small Sailboat Image.jpg |size=400|sizedefault=260|maxsize=275'},
+ {'Small Sailboat Image.jpg |sizedefault=260|maxsize=275'},
+ {'Small Sailboat Image.jpg |maxsize=275'},
+ {'Small Sailboat Image.jpg |maxsize=275|sizedefault=frameless'},
+ {'[[File:Small Sailboat Image.jpg]]'},
+ {'Mustela erminea upright.jpg'},
+ {'Mustela erminea upright.jpg |upright=1'},
+ {'Mustela erminea upright.jpg |upright=yes'},
+ {'Mustela erminea upright.jpg |upright=0.75'},
+ {'Mustela erminea upright.jpg |upright=1.2'},
+ {'Mustela erminea upright.jpg |upright=1|maxsize=275'},
+ {'Mustela erminea upright.jpg |upright=1|maxsize=275|sizedefault=frameless'},
+ {'Mustela erminea upright.jpg |upright=1|sizedefault=260|maxsize=275'},
+ {'Mustela erminea upright.jpg |upright=1.5|sizedefault=260|maxsize=275'},
+ {'Mustela erminea upright.jpg |upright=1|size=200|sizedefault=260|maxsize=275'},
+ {'Mustela erminea upright.jpg |upright=1|size=|sizedefault=|maxsize='},
+ {'Replace this image.svg |suppressplaceholder=no'},
+ {'Replace this image.svg'},
+ {'The Universal Magazine, Vol. XCV (July 1794).djvu |page=8'},
+ {'[[File:Abbey Rd Studios.jpg|200px]]'},
+ {'[[File:Abbey Rd Studios.jpg|thumb]]'},
+ {'[[File:Abbey Rd Studios.jpg|thumb|caption]]'},
+ {'Http-fakezarathustra.blogspot.com-2010-10-12 31.html http-fakezarathustra.blogspot.com-2010-10-22 31.html - panoramio.jpg'},
+ {'Abbey Rd Studios.jpg|alt=http:test.com'},
+ {'Abbey Rd Studios.jpg|alt=[http:test.com]'},
+ {'Abbey Rd Studios.jpg|class=notpageimage'},
+ })
+end
+
+function p:test2_parameters_nowiki()
+ self:preprocess_equals_preprocess_many('{{#invoke:InfoboxImage/sandbox |InfoboxImage |image=', '}}', '{{#invoke:InfoboxImage|InfoboxImage |image=', '}}', {
+ {'Small Sailboat Image.jpg |alt=Alt'},
+ {'Small Sailboat Image.jpg |border=yes'},
+ {'Small Sailboat Image.jpg |center=yes'},
+ {'Small Sailboat Image.jpg |link=Link'},
+ {'Small Sailboat Image.jpg |thumbtime=Thumbtime'},
+ {'Small Sailboat Image.jpg |title=Title'},
+ {'Small Sailboat Image.jpg |size=75'},
+ {'Small Sailboat Image.jpg |size=200'},
+ {'Small Sailboat Image.jpg |size=200|sizedefault=260|maxsize=275'},
+ {'Small Sailboat Image.jpg |size=400|sizedefault=260|maxsize=275'},
+ {'Small Sailboat Image.jpg |sizedefault=260|maxsize=275'},
+ {'Small Sailboat Image.jpg |maxsize=275'},
+ {'Small Sailboat Image.jpg |maxsize=275|sizedefault=frameless'},
+ {'Small Sailboat Image.jpg |size=20%|sizedefault=10em'},
+ {'[[File:Small Sailboat Image.jpg]]'},
+ {'Mustela erminea upright.jpg'},
+ {'Mustela erminea upright.jpg |upright=1'},
+ {'Mustela erminea upright.jpg |upright=yes'},
+ {'Mustela erminea upright.jpg |upright=0.75'},
+ {'Mustela erminea upright.jpg |upright=1.2'},
+ {'Mustela erminea upright.jpg |upright=1|maxsize=275'},
+ {'Mustela erminea upright.jpg |upright=1|maxsize=275|sizedefault=frameless'},
+ {'Mustela erminea upright.jpg |upright=1|sizedefault=260|maxsize=275'},
+ {'Mustela erminea upright.jpg |upright=1.5|sizedefault=260|maxsize=275'},
+ {'Mustela erminea upright.jpg |upright=1|size=200|sizedefault=260|maxsize=275'},
+ {'Mustela erminea upright.jpg |upright=1|size=|sizedefault=|maxsize='},
+ {'Replace this image.svg |suppressplaceholder=no'},
+ {'Replace this image.svg'},
+ {'The Universal Magazine, Vol. XCV (July 1794).djvu |page=8'},
+ {'Http-fakezarathustra.blogspot.com-2010-10-12 31.html http-fakezarathustra.blogspot.com-2010-10-22 31.html - panoramio.jpg'},
+ {'Abbey Rd Studios.jpg|alt=http:test.com'},
+ {'Abbey Rd Studios.jpg|alt=[http:test.com]'},
+ {'Abbey Rd Studios.jpg|class=notpageimage'},
+ {'[[File:Abbey Rd Studios.jpg|200px]]'},
+ {'[[File:Abbey Rd Studios.jpg|thumb]]'},
+ {'[[File:Abbey Rd Studios.jpg|thumb|caption]]'},
+ },{nowiki=1})
+end
+
+function p:test3_errors()
+ self:preprocess_equals_preprocess_many('{{#invoke:InfoboxImage/sandbox |InfoboxImage |image=', '}}', '{{#invoke:InfoboxImage|InfoboxImage |image=', '}}', {
+ {'Mustela erminea upright.jpg |size=1.2'},
+ {'Mustela erminea upright.jpg |size=300x'},
+ {'Mustela erminea upright.jpg |sizedefault=300x | maxsize=200'},
+ {'Mustela erminea upright.jpg |size=300 | maxsize=200x'},
+ {'Mustela erminea upright.jpg |sizedefault=145 | maxsize=300x'},
+ {'http:test.com'},
+ {'[http:test.com]'},
+ {'[[http:test.com]]'},
+ {'https:test.com'},
+ {'[https:test.com]'},
+ {'[[https:test.com]]'},
+ },{nowiki=1})
+end
+
+function p:test4_stripmarkers()
+ self:preprocess_equals_preprocess(
+ '{{#invoke:InfoboxImage/sandbox |InfoboxImage |image={{multiple image | width = 60 | image1 = Yellow card.svg | image2 = Red card.svg}}}}',
+ '{{#invoke:InfoboxImage |InfoboxImage |image={{multiple image | width = 60 | image1 = Yellow card.svg | image2 = Red card.svg}}}}',
+ {stripmarker=1})
+end
+
+return p
diff --git a/Uses TemplateStyles/config.lua b/Uses TemplateStyles/config.lua
new file mode 100644
index 0000000..3a34837
--- /dev/null
+++ b/Uses TemplateStyles/config.lua
@@ -0,0 +1,58 @@
+local cfg = {} -- Don’t touch this line.
+
+-- Subpage blacklist: these subpages will not be categorized (except for the
+-- error category, which is always added if there is an error).
+-- For example “Template:Foo/doc” matches the `doc = true` rule, so it will have
+-- no categories. “Template:Foo” and “Template:Foo/documentation” match no rules,
+-- so they *will* have categories. All rules should be in the
+-- [''] = true,
+-- format.
+cfg['subpage_blacklist'] = {
+ ['doc'] = true,
+ ['sandbox'] = true,
+ ['sandbox2'] = true,
+ ['testcases'] = true,
+}
+
+-- Sandbox title: if the stylesheet’s title is /.css, the
+-- stylesheet’s sandbox is expected to be at //.css
+-- Set to nil to disable sandbox links.
+cfg['sandbox_title'] = 'sandbox'
+
+-- Error category: this category is added if the module call contains errors
+-- (e.g. no stylesheet listed). A category name without namespace, or nil
+-- to disable categorization (not recommended).
+cfg['error_category'] = 'Bản mẫu Uses TemplateStyles có lỗi'
+
+-- Default category: this category is added if no custom category is specified
+-- in module/template call. A category name without namespace, or nil
+-- to disable categorization.
+cfg['default_category'] = 'Bản mẫu sử dụng TemplateStyles'
+
+-- Protection conflict category: this category is added if the protection level
+-- of any stylesheet is lower than the protection level of the template. A category name
+-- without namespace, or nil to disable categorization (not recommended).
+cfg['protection_conflict_category'] = 'Bản mẫu sử dụng TemplateStyles có mức khóa khác nhau'
+
+-- Hierarchy of protection levels, used to determine whether one protection level is lower
+-- than another and thus should populate protection_conflict_category. No protection is treated as zero
+cfg['protection_hierarchy'] = {
+ autoconfirmed = 1,
+ extendedconfirmed = 2,
+ templateeditor = 3,
+ sysop = 4
+}
+
+-- Padlock pattern: Lua pattern to search on protected stylesheets for, or nil
+-- to disable padlock check.
+cfg['padlock_pattern'] = '{{pp-'
+
+-- Missing padlock category: this category is added if a protected stylesheet
+-- doesn’t contain any padlock template (specified by the above Lua pattern).
+-- A category name without namespace (no nil allowed) if the pattern is not nil,
+-- unused (and thus may be nil) otherwise.
+cfg['missing_padlock_category'] = 'Bản mẫu sử dụng TemplateStyles không khoá'
+
+-- Default subpage for the stylesheet if none is given
+cfg['default_subpage_name'] = 'styles.css'
+return cfg -- Don’t touch this line.
diff --git a/Uses TemplateStyles/main.lua b/Uses TemplateStyles/main.lua
new file mode 100644
index 0000000..0189f02
--- /dev/null
+++ b/Uses TemplateStyles/main.lua
@@ -0,0 +1,157 @@
+local yesno = require('Module:Yesno')
+local mList = require('Module:List')
+local mTableTools = require('Module:TableTools')
+local mMessageBox = require('Module:Message box')
+local TNT = require('Module:TNT')
+
+local p = {}
+
+local function format(msg, ...)
+ return TNT.format('I18n/Uses TemplateStyles', msg, ...)
+end
+
+local function getConfig()
+ return mw.loadData('Module:Uses TemplateStyles/config')
+end
+
+local function makeEditSup(pageName)
+ return string.format('[[[Special:Edit/%s|e]]]', pageName)
+end
+
+-- Build just the "(sandbox)" suffix using the i18n message, without duplicating the main link.
+local function makeSandboxSuffix(tsSandboxPrefixed)
+ -- i18n message is: "$1 ([[$2|sandbox]])"
+ -- Passing empty $1 yields " ([[:...|sandbox]])" -> trim leading whitespace to get "(sandbox)" portion.
+ local s = format('sandboxlink', '', ':' .. tsSandboxPrefixed)
+ return (s:gsub('^%s+', ''))
+end
+
+local function renderBox(tStyles)
+ local boxArgs = {
+ type = 'notice',
+ small = true,
+ image = string.format('[[Tập tin:Farm-Fresh css add.svg|32px|alt=%s]]', format('logo-alt'))
+ }
+
+ if #tStyles < 1 then
+ boxArgs.text = string.format('%s', format('error-emptylist'))
+ else
+ local cfg = getConfig()
+ local tStylesLinks = {}
+
+ for i, ts in ipairs(tStyles) do
+ local viewLink = string.format('[[:%s]]', ts)
+ local editSup = makeEditSup(ts)
+
+ local out = viewLink .. editSup
+
+ -- Optional sandbox link + sandbox edit link
+ local tsTitle = mw.title.new(ts)
+ if tsTitle and cfg['sandbox_title'] then
+ local tsSandboxTitle = mw.title.new(string.format(
+ '%s:%s/%s/%s',
+ tsTitle.nsText,
+ tsTitle.baseText,
+ cfg['sandbox_title'],
+ tsTitle.subpageText
+ ))
+
+ if tsSandboxTitle and tsSandboxTitle.exists then
+ local sandboxSuffix = makeSandboxSuffix(tsSandboxTitle.prefixedText)
+ local sandboxEditSup = makeEditSup(tsSandboxTitle.prefixedText)
+ out = out .. ' ' .. sandboxSuffix .. ' ' .. sandboxEditSup
+ end
+ end
+
+ tStylesLinks[i] = out
+ end
+
+ local tStylesList = mList.makeList('bulleted', tStylesLinks)
+ boxArgs.text = format(
+ mw.title.getCurrentTitle():inNamespaces(828, 829) and 'header-module' or 'header-template'
+ ) .. '\n' .. tStylesList
+ end
+
+ return mMessageBox.main('mbox', boxArgs)
+end
+
+local function renderTrackingCategories(args, tStyles, titleObj)
+ if yesno(args.nocat) then
+ return ''
+ end
+
+ local cfg = getConfig()
+ local cats = {}
+
+ -- Error category
+ if #tStyles < 1 and cfg['error_category'] then
+ cats[#cats + 1] = cfg['error_category']
+ end
+
+ -- TemplateStyles category
+ titleObj = titleObj or mw.title.getCurrentTitle()
+ if (titleObj.namespace == 10 or titleObj.namespace == 828)
+ and not cfg['subpage_blacklist'][titleObj.subpageText]
+ then
+ local category = args.category or cfg['default_category']
+ if category then
+ cats[#cats + 1] = category
+ end
+ if not yesno(args.noprotcat) and (cfg['protection_conflict_category'] or cfg['padlock_pattern']) then
+ local currentProt = titleObj.protectionLevels["edit"] and titleObj.protectionLevels["edit"][1] or nil
+ local addedLevelCat = false
+ local addedPadlockCat = false
+ for _, ts in ipairs(tStyles) do
+ local tsTitleObj = mw.title.new(ts)
+ local tsProt = tsTitleObj.protectionLevels["edit"] and tsTitleObj.protectionLevels["edit"][1] or nil
+ if cfg['padlock_pattern'] and tsProt and not addedPadlockCat then
+ local content = tsTitleObj:getContent()
+ if not content:find(cfg['padlock_pattern']) then
+ cats[#cats + 1] = cfg['missing_padlock_category']
+ addedPadlockCat = true
+ end
+ end
+ if cfg['protection_conflict_category'] and currentProt and tsProt ~= currentProt and not addedLevelCat then
+ currentProt = cfg['protection_hierarchy'][currentProt] or 0
+ tsProt = cfg['protection_hierarchy'][tsProt] or 0
+ if tsProt < currentProt then
+ addedLevelCat = true
+ cats[#cats + 1] = cfg['protection_conflict_category']
+ end
+ end
+ end
+ end
+ end
+
+ for i, cat in ipairs(cats) do
+ cats[i] = string.format('[[Thể loại:%s]]', cat)
+ end
+ return table.concat(cats)
+end
+
+function p._main(args)
+ local cfg = getConfig()
+ if #args == 0 then
+ local prefixed = mw.title.getCurrentTitle().prefixedText
+ prefixed = prefixed:gsub("/doc", "")
+ args[1] = prefixed .. "/" .. cfg["default_subpage_name"]
+ end
+ local tStyles = mTableTools.compressSparseArray(args)
+ local box = renderBox(tStyles)
+ local trackingCategories = renderTrackingCategories(args, tStyles)
+ return box .. trackingCategories
+end
+
+function p.main(frame)
+ local origArgs = frame:getParent().args
+ local args = {}
+ for k, v in pairs(origArgs) do
+ v = v:match('^%s*(.-)%s*$')
+ if v ~= '' then
+ args[k] = v
+ end
+ end
+ return p._main(args)
+end
+
+return p