Mô đun:Sidebar

require('strict')
local cfg = mw.loadData('Module:Sidebar/configuration')

local p = {}

local getArgs = require('Module:Arguments').getArgs

--
Categorizes calling templates and modules with a 'style' parameter of any sort
for tracking to convert to TemplateStyles.

TODO after a long cleanup: Catch sidebars in other namespaces than Template and Module.
TODO would probably want to remove /log and /archive as CS1 does

local function categorizeTemplatesWithInlineStyles(args)
local title = mw.title.getCurrentTitle()
if title.namespace ~= 10 and title.namespace ~= 828 then return end
for _, pattern in ipairs (cfg.i18n.pattern.uncategorized_conversion_titles) do
if title.text:match(pattern) then return end
end

for key, _ in pairs(args) do
if mw.ustring.find(key, cfg.i18n.pattern.style_conversion) or key == 'width' then
return cfg.i18n.category.conversion
end
end
end

--
For compatibility with the original
implementation, which passed some parameters through to trim their
whitespace. This also triggered the automatic newline behavior.

-- See (meta:Help:Newlines and spacesAutomatic newline)
local function trimAndAddAutomaticNewline(s)
s = mw.ustring.gsub(s, "^%s(.-)%s$", "%1")
if mw.ustring.find(s, '^[:;]') or mw.ustring.find(s, '^{|') then
return '\n' .. s
else
return s
end
end

--
Finds whether a sidebar has a subgroup sidebar.

local function hasSubgroup(s)
if mw.ustring.find(s, cfg.i18n.pattern.subgroup) then
return true
else
return false
end
end

local function has_navbar(navbar_mode, sidebar_name)
return navbar_mode ~= cfg.i18n.navbar_none and
navbar_mode ~= cfg.i18n.navbar_off and
(
sidebar_name or
mw.getCurrentFrame():getParent():getTitle():gsub(cfg.i18n.pattern.sandbox, ) ~=
cfg.i18n.title_not_to_add_navbar
)
end

local function has_list_class(args, htmlclass)
local patterns = {
'^' .. htmlclass .. '$',
'%s' .. htmlclass .. '$',
'^' .. htmlclass .. '%s',
'%s' .. htmlclass .. '%s'
}

for arg, value in pairs(args) do
if type(arg) == 'string' and mw.ustring.find(arg, 'class') then
for _, pattern in ipairs(patterns) do
if mw.ustring.find(args[arg] or , pattern) then
return true
end
end
end
end
return false
end

-- there are a lot of list classes in the wild, so we add their TemplateStyles
local function add_list_styles(args)
local frame = mw.getCurrentFrame()
local function add_list_templatestyles(htmlclass, templatestyles)
if has_list_class(args, htmlclass) then
return frame:extensionTag{
name = 'templatestyles', args = { src = templatestyles }
}
else
return
end
end

local plainlist_styles = add_list_templatestyles('plainlist', cfg.i18n.plainlist_templatestyles)
local hlist_styles = add_list_templatestyles('hlist', cfg.i18n.hlist_templatestyles)

-- a second workaround for phab:T303378
-- when that issue is fixed, we can actually use has_navbar not to emit the
-- tag here if we want
if has_navbar(args.navbar, args.name) and hlist_styles == then
hlist_styles = frame:extensionTag{
name = 'templatestyles', args = { src = cfg.i18n.hlist_templatestyles}
}
end

-- hlist -> plainlist is best-effort to preserve old Common.css ordering. [hlist_note]
return hlist_styles .. plainlist_styles
end

-- work around phab:T303378
-- for each arg: find all the templatestyles strip markers, insert them into a
-- table. then remove all templatestyles markers from the arg
local function move_hiding_templatestyles(args)
local gfind = string.gfind
local gsub = string.gsub
local templatestyles_markers = {}
local strip_marker_pattern = '(\127[^\127]UNIQ%-%-templatestyles%-%x+%-QINU[^\127]\127)'
for k, arg in pairs(args) do
for marker in gfind(arg, strip_marker_pattern) do
table.insert(templatestyles_markers, marker)
end
args[k] = gsub(arg, strip_marker_pattern, )
end
return templatestyles_markers
end

--
Main sidebar function. Takes the frame, args, and an optional collapsibleClass.
The collapsibleClass is and should be used only for sidebars with collapsible
lists, as in p.collapsible.

function p.sidebar(frame, args, collapsibleClass)
if not args then
args = getArgs(frame)
end
local hiding_templatestyles = table.concat(move_hiding_templatestyles(args))
local root = mw.html.create()
local child = args.child and mw.text.trim(args.child) == cfg.i18n.child_yes

root = root:tag('table')
if not child then
root
:addClass(cfg.i18n.class.sidebar)
-- force collapsibleclass to be sidebar-collapse otherwise output nothing
:addClass(collapsibleClass cfg.i18n.class.collapse and cfg.i18n.class.collapse or nil)
:addClass('nomobile')
:addClass(args.float cfg.i18n.float_none and cfg.i18n.class.float_none or nil)
:addClass(args.float == cfg.i18n.float_left and cfg.i18n.class.float_left or nil)
:addClass(args.wraplinks ~= cfg.i18n.wrap_true and cfg.i18n.class.wraplinks or nil)
:addClass(args.bodyclass or args.class)
:css('width', args.width or nil)
:cssText(args.bodystyle or args.style)

if args.outertitle then
root
:tag('caption')
:addClass(cfg.i18n.class.outer_title)
:addClass(args.outertitleclass)
:cssText(args.outertitlestyle)
:wikitext(args.outertitle)
end

if args.topimage then
local imageCell = root:tag('tr'):tag('td')

imageCell
:addClass(cfg.i18n.class.top_image)
:addClass(args.topimageclass)
:cssText(args.topimagestyle)
:wikitext(args.topimage)

if args.topcaption then
imageCell
:tag('div')
:addClass(cfg.i18n.class.top_caption)
:cssText(args.topcaptionstyle)
:wikitext(args.topcaption)
end
end

if args.pretitle then
root
:tag('tr')
:tag('td')
:addClass(args.topimage and cfg.i18n.class.pretitle_with_top_image
or cfg.i18n.class.pretitle)
:addClass(args.pretitleclass)
:cssText(args.basestyle)
:cssText(args.pretitlestyle)
:wikitext(args.pretitle)
end
else
root
:addClass(cfg.i18n.class.subgroup)
:addClass(args.bodyclass or args.class)
:cssText(args.bodystyle or args.style)
end

if args.title then
if child then
root
:wikitext(args.title)
else
root
:tag('tr')
:tag('th')
:addClass(args.pretitle and cfg.i18n.class.title_with_pretitle
or cfg.i18n.class.title)
:addClass(args.titleclass)
:cssText(args.basestyle)
:cssText(args.titlestyle)
:wikitext(args.title)
end
end

if args.image then
local imageCell = root:tag('tr'):tag('td')

imageCell
:addClass(cfg.i18n.class.image)
:addClass(args.imageclass)
:cssText(args.imagestyle)
:wikitext(args.image)

if args.caption then
imageCell
:tag('div')
:addClass(cfg.i18n.class.caption)
:cssText(args.captionstyle)
:wikitext(args.caption)
end
end

if args.above then
root
:tag('tr')
:tag('td')
:addClass(cfg.i18n.class.above)
:addClass(args.aboveclass)
:cssText(args.abovestyle)
:newline() -- newline required for bullet-points to work
:wikitext(args.above)
end

local rowNums = {}
for k, v in pairs(args) do
k = .. k
local num = k:match('^heading(%d+)$') or k:match('^content(%d+)$')
if num then table.insert(rowNums, tonumber(num)) end
end
table.sort(rowNums)
-- remove duplicates from the list (e.g. 3 will be duplicated if both heading3
-- and content3 are specified)
for i = rowNums, 1, -1 do
if rowNums[i] == rowNums[i - 1] then
table.remove(rowNums, i)
end
end

for i, num in ipairs(rowNums) do
local heading = args['heading' .. num]
if heading then
root
:tag('tr')
:tag('th')
:addClass(cfg.i18n.class.heading)
:addClass(args.headingclass)
:addClass(args['heading' .. num .. 'class'])
:cssText(args.basestyle)
:cssText(args.headingstyle)
:cssText(args['heading' .. num .. 'style'])
:newline()
:wikitext(heading)
end

local content = args['content' .. num]
if content then
root
:tag('tr')
:tag('td')
:addClass(hasSubgroup(content) and cfg.i18n.class.content_with_subgroup
or cfg.i18n.class.content)
:addClass(args.contentclass)
:addClass(args['content' .. num .. 'class'])
:cssText(args.contentstyle)
:cssText(args['content' .. num .. 'style'])
:newline()
:wikitext(content)
:done()
-- Without a linebreak after the , a nested list like
-- " " doesn't parse correctly.
:newline()
end
end

if args.below then
root
:tag('tr')
:tag('td')
:addClass(cfg.i18n.class.below)
:addClass(args.belowclass)
:cssText(args.belowstyle)
:newline()
:wikitext(args.below)
end

if not child and has_navbar(args.navbar, args.name) then
root
:tag('tr')
:tag('td')
:addClass(cfg.i18n.class.navbar)
:cssText(args.navbarstyle)
:wikitext(require('Module:Navbar')._navbar{
args.name,
mini = 1,
fontstyle = args.navbarfontstyle
})
end

local base_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = cfg.i18n.templatestyles }
}

local templatestyles =
if args['templatestyles'] and args['templatestyles'] ~= then
templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['templatestyles'] }
}
end

local child_templatestyles =
if args['child templatestyles'] and args['child templatestyles'] ~= then
child_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['child templatestyles'] }
}
end

local grandchild_templatestyles =
if args['grandchild templatestyles'] and args['grandchild templatestyles'] ~= then
grandchild_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['grandchild templatestyles'] }
}
end

return table.concat({
add_list_styles(args), -- see [hlist_note] above about ordering
base_templatestyles,
templatestyles,
child_templatestyles,
grandchild_templatestyles,
hiding_templatestyles,
tostring(root),
(child and cfg.i18n.category.child or ),
categorizeTemplatesWithInlineStyles(args)
})
end

local function list_title(args, is_centered_list_titles, num)

local title_text = trimAndAddAutomaticNewline(args['list' .. num .. 'title']
or cfg.i18n.default_list_title)

local title
if is_centered_list_titles then
-- collapsible can be finicky, so provide some CSS/HTML to support
title = mw.html.create('div')
:addClass(cfg.i18n.class.list_title_centered)
:wikitext(title_text)
else
title = mw.html.create()
:wikitext(title_text)
end

local title_container = mw.html.create('div')
:addClass(cfg.i18n.class.list_title)
-- don't /need/ a listnumtitleclass because you can do
-- .templateclass .listnumclass .sidebar-list-title
:addClass(args.listtitleclass)
:cssText(args.basestyle)
:cssText(args.listtitlestyle)
:cssText('color: var(--color-base)')
:cssText(args['list' .. num .. 'titlestyle'])
:node(title)
:done()

return title_container
end

--
Main entry point for sidebar with collapsible lists.
Does the work of creating the collapsible lists themselves and including them
into the args.

function p.collapsible(frame)
local args = getArgs(frame)
if not args.name and
frame:getParent():getTitle():gsub(cfg.i18n.pattern.collapse_sandbox, ) ==
cfg.i18n.collapse_title_not_to_add_navbar then
args.navbar = cfg.i18n.navbar_none
end

local contentArgs = {}

local is_centered_list_titles = false
if args['centered list titles'] and args['centered list titles'] ~= then
is_centered_list_titles = true
end

for k, v in pairs(args) do
local num = string.match(k, '^list(%d+)$')
if num then
local expand = args.expanded and
(args.expanded 'all' or args.expanded args['list' .. num .. 'name'])
local row = mw.html.create('div')
row
:addClass(cfg.i18n.class.list)
:addClass('mw-collapsible')
:addClass((not expand) and 'mw-collapsed' or nil)
:addClass(args['list' .. num .. 'class'])
:cssText(args.listframestyle)
:cssText(args['list' .. num .. 'framestyle'])
:node(list_title(args, is_centered_list_titles, num))
:tag('div')
:addClass(cfg.i18n.class.list_content)
:addClass('mw-collapsible-content')
-- don't /need/ a listnumstyleclass because you can do
-- .templatename .listnumclass .sidebar-list
:addClass(args.listclass)
:cssText(args.liststyle)
:cssText(args['list' .. num .. 'style'])
:wikitext(trimAndAddAutomaticNewline(args['list' .. num]))

contentArgs['content' .. num] = tostring(row)
end
end

for k, v in pairs(contentArgs) do
args[k] = v
end

return p.sidebar(frame, args, cfg.i18n.class.collapse)
end

return p