Moduł:Navbox
Moduł z funkcją generowania szablonu nawigacyjnego.
Użycie
[edytuj kod]{{#invoke:Navbox|Navbox|...}}
Opis parametrów
[edytuj kod]Typowe ogólne wywołanie
[edytuj kod]{{#invoke:Navbox|Navbox |tytuł = {{{tytuł}}} |góra = {{{góra}}} |przed = {{{przed}}} |opis1 = {{{opis1}}} |spis1 = {{{spis1}}} |opis2 = {{{opis2}}} |opis2.1 = {{{opis2.1}}} |spis2.1 = {{{spis2.1}}} |opis2.2 = {{{opis2.2}}} |spis2.2 = {{{spis2.2}}} |opis3 = {{{opis3}}} |spis3 = {{{spis3}}} |po = {{{po}}} |dół = {{{dół}}} }}
Układ z kolumnami
[edytuj kod]Włączane przez | spis = * kolumny *
.
{{#invoke:Navbox|Navbox |tytuł = {{{tytuł}}} |spis = * kolumny * |opis1 = {{{opis1}}} |spis1 = {{{spis1}}} |opis2 = {{{opis2}}} |spis2 = {{{spis2}}} |opis3 = {{{opis3}}} |spis3 = {{{spis3}}} }}
Układ z grupami
[edytuj kod]Włączane przez | spis = * grupa *
. Zwijaniem można sterować przez | zwijanie.N = zwinięte
, | zwijanie.N = rozwinięte
lub | zwijanie.N = auto
. Ostatni tryb pozostawia grupę rozwiniętą jeśli znajduje się link do bieżącej strony. Jednak na stronie szablonu treść jest zawsze w postaci rozwiniętej.
{{#invoke:Navbox|Navbox |tytuł = {{{tytuł}}} |spis = * grupa * |opis1 = {{{opis1}}} |spis1 = {{{spis1}}} |zwijanie.2 = rozwinięte |opis2 = {{{opis2}}} |spis2 = {{{spis2}}} |zwijanie.3 = zwinięte |opis3 = {{{opis3}}} |spis3 = {{{spis3}}} }}
Wywołanie z nietypową zawartością
[edytuj kod]Treść przekazywana przez | spis =
.
{{#invoke:Navbox|Navbox |tytuł = {{{tytuł}}} |spis = {{{spis}}} }}
Inny
[edytuj kod]Włączane przez | spis = * inny *
. Generuje drzewo zagnieżdżonych elementów <div>
. Przeznaczony do ręcznego dostosowywania przez dedykowany CSS.
{{#invoke:Navbox|Navbox |tytuł = {{{tytuł}}} |spis = * inny * |opis1 = {{{opis1}}} |spis1 = {{{spis1}}} |opis2 = {{{opis2}}} |spis2 = {{{spis2}}} |opis3 = {{{opis3}}} |spis3 = {{{spis3}}} }}
Stylizacja
[edytuj kod]Za dodatkowe sterowanie wyglądem służy | klasa =
, do którego można przekazać zbiór opcjonalnych wartości:
pionowy
- włącza generowanie pionowego szablonu nawigacyjnego
medaliści
- włącza kolorowanie tła pierwszych trzech nagłówków pierwszego poziomu na złoto, srebro i brąz.
kz-linia
- zezwala na wyświetlanie dekoracyjnych ikon linii transportu publicznego miast lub aglomeracji
hwrap
- zezwala na łamanie linii wewnątrz elementów list
Błędy
[edytuj kod]Błędy należy zgłaszać na stronie Wikipedia:Kawiarenka/Kwestie techniczne.
Zobacz podstrony tego modułu.
require ("strict")
local res = mw.loadData('Moduł:Navbox/res')
local function encodeClass(name)
local t = mw.getContentLanguage():lcfirst(name)
local s = mw.ustring.gsub(t, ".", { ['ł'] = 'l', ['Ł'] = 'L', ['_'] = ' '}) -- wstępne zmiany ł->l i _-> spacja
local p = mw.ustring.gsub(s, "%p+", "-") -- znaczki nieliterowe na minusy
local q = mw.ustring.gsub(p, "%s+", "_") -- odstępy na podkreślenia
local r = mw.ustring.toNFD(q) -- wyodrębnij diakrytyki
local e = string.gsub(r,"[\127-\255]+",'') -- usuń to czego nie ma w ASCII
return e
end
local function getPrinter(name)
local printer = res.printers[name]
return printer and require(printer) or nil
end
local function printSubTree(container, tree, currentPrinter, printlog)
local function printList(container, node, collapsibleContent)
local mwCCC = collapsibleContent and 'mw-collapsible-content' or nil
if #node <= 0 then
if printlog then
printlog.printedLeafs = (printlog.printedLeafs or 0) + 1
end
local leaf
if (currentPrinter.leaf == 'table') and mwCCC then
-- zwijanie tabelki i skórka timeless się gryzą
leaf = container:tag('div')
:addClass(mwCCC)
:tag(currentPrinter.leaf)
elseif currentPrinter.leaf then
leaf = container:tag(currentPrinter.leaf)
:addClass(mwCCC)
else
leaf = container
:addClass(mwCCC)
end
if currentPrinter.update then currentPrinter.update(leaf, true, node, printlog) end
leaf
:addClass(node.odd())
:addClass(node.even())
:newline()
:wikitext(node.get(res.arg.list.name))
:newline()
else
local subprinter = getPrinter(node.peek(res.arg.list.name))
if subprinter then
node.use(res.arg.list.name)
else
subprinter = getPrinter(true)
end
local subtag = subprinter.root or subprinter.leaf or 'div'
local subtree = ((subtag == 'table') and mwCCC)
and container:tag('div'):addClass(mwCCC):tag(subtag) -- zwijanie tabelki i skórka timeless się gryzą
or container:tag(subtag):addClass(mwCCC)
if currentPrinter.update then currentPrinter.update(subtree, false, node, printlog) end
printSubTree(subtree, node, subprinter, printlog)
end
end
currentPrinter.print(container, tree, printList, printlog)
end
local function argsService(templateContext)
local args = {}
local function add(k, v, prefix)
if v and #v > 0 then
args[k] = v
end
end
local peekName = function(name)
if not args[name] and templateContext and templateContext.aliases then
local alias = templateContext.aliases[name]
if alias and args[alias] then
return alias
end
end
return name
end
local function peek(name)
return args[peekName(name)]
end
local function get(name)
return peek(peekName(name)) or (res.aux.missingArgNamePrefix..name..res.aux.missingArgNameSuffix)
end
local function dump()
return mw.text.jsonEncode(args)
end
return {
add = add,
peek = peek,
use = peek,
get = get,
dump = dump,
}
end
local function loadArgsTree(args, printlog, templateContext)
local dynamicArgs = {}
for k, v in pairs(res.arg) do
if templateContext and templateContext.aliases then
local alias = templateContext.aliases[v.name]
if v.dynamic and alias then dynamicArgs[alias] = v.dynamic end
end
if v.dynamic then dynamicArgs[v.name] = v.dynamic end
end
local splitArgName = function(name)
if type(name) ~= "string" then
--mw.logObject(name, "to nie jest tekst")
return false, nil
end
local prefix, suffix = mw.ustring.match(name, "^(.-)([1-9][%.0-9]*)$")
if not prefix or not dynamicArgs[prefix] then
--mw.logObject(name, "wzór nie pasuje lub nierozpoznany")
return false, nil
end
local keys = mw.text.split(suffix,'.',true)
for i = 1, #keys do
keys[i] = tonumber(keys[i])
if not keys[i] or (keys[i] == 0) then
--mw.logObject({name, keys, i, keys[i]}, "wzór ma błędy")
return false, nil
end
end
return prefix, keys
end
local updateIndex = function(node, k)
node.index = node.index or {}
for i, v in ipairs(node.index) do
if v == k then
return
end
end
table.insert(node.index, k)
table.sort(node.index)
end
local argsTree = not printlog
and argsService(templateContext)
or require('Moduł:Navbox/diag').argsService(templateContext)
local argsTree_add = argsTree.add
argsTree.add = nil
local argsTree_tree = nil
local tree = function()
if not argsTree_tree or (#argsTree_tree <= 0) then
return nil
end
local function createNode(v)
local peekName = function(name)
if not v.address then
return nil
end
if argsTree.peek(name..v.address) then
return name..v.address
end
if templateContext and templateContext.aliases then
local alias = templateContext.aliases[name]
if alias and peek(alias..v.address) then
return alias..v.address
end
end
return nil
end
local node = {
address = function() return v.address and "a"..string.gsub(v.address, '%.', '_') or nil end,
peek = function(name)
return peekName(name)
and argsTree.peek(peekName(name))
or (templateContext and templateContext.defaults and templateContext.defaults[name])
end,
use = function(name) return peekName(name) and argsTree.use(peekName(name)) or nil end,
get = function(name) return peekName(name) and argsTree.get(peekName(name)) or nil end,
odd = function() return v.odd and res.class.odd or nil end,
even = function() return v.even and res.class.even or nil end,
}
local function calculateTest()
local pattern = mw.loadData("Moduł:Navbox/title").wikilinkPattern
local s1 = argsTree.peek(res.arg.group.name..v.address)
if s1 and mw.ustring.match(s1, pattern) then return true end
local s2 = argsTree.peek(res.arg.list.name..v.address)
if s2 and mw.ustring.match(s2, pattern) then return true end
for i, n in ipairs(node) do
if n.test(pattern) then return true end
end
return false
end
local testCache
node.test = function()
if testCache == nil then
testCache = calculateTest()
end
return testCache
end
return node
end
local function buildTree(buffer, tree)
for i, v in ipairs(tree.index) do
local data = tree[v]
local node = createNode(data)
table.insert(buffer, node)
if data.index then
buildTree(node, data)
end
end
end
local rootNode = createNode({})
buildTree(rootNode, argsTree_tree)
return rootNode
end
local function analyzeArg(k, v, prefix, keys)
argsTree_add(k, v, prefix)
if prefix and v and (#v > 0) then
argsTree_tree = argsTree_tree or {}
local node = argsTree_tree
for i = 1, #keys do
local k = keys[i]
local child = node[k]
updateIndex(node, k)
if not child then
child = {
address = node.address and (node.address..'.'..tostring(k)) or tostring(k),
}
node[k] = child
end
node = child
end
end
end
for k, v in pairs(args) do
local prefix, keys = splitArgName(k)
local json, subargs
if (prefix == res.arg.list.name) and v and (#v > 0) then
json, subargs = pcall(mw.text.jsonDecode, v)
end
if json then
--analyzeArg(k, '* szablon *', prefix, keys)
for vk, vv in pairs(subargs) do
local vprefix, vkeys = splitArgName(vk)
if not vprefix and (vk == res.arg.list.name) then
vprefix = vk
vkeys = {}
end
if vprefix and prefix then
local newKeys = {}
for j, u in ipairs(keys) do table.insert(newKeys, u) end
for j, u in ipairs(vkeys) do table.insert(newKeys, u) end
local n = {}
for j, u in ipairs(newKeys) do table.insert(n, tostring(u)) end
local name = vprefix..table.concat(n, '.')
analyzeArg(name, vv, vprefix, newKeys)
end
end
else
analyzeArg(k, v, prefix, keys)
end
end
if templateContext and getPrinter(templateContext.list) then
analyzeArg(res.arg.list.name, templateContext.list, nil, nil)
end
if argsTree_tree then
local tree = argsTree_tree
local function loadLeafNodes(tree, buffer, level)
if not tree.index then
table.insert(buffer, tree)
else
local n = level
for i, v in ipairs(tree.index) do
local k = loadLeafNodes(tree[v], buffer, level + 1)
if k > n then
n = k
end
end
level = n
end
return level
end
local buffer = {}
local levels = loadLeafNodes(tree, buffer, 0)
for i, v in ipairs(buffer) do
v.odd = (i % 2) == 1
v.even = (i % 2) == 0
end
if printlog then
printlog.levels = levels
printlog.leafs = #buffer
end
end
argsTree.tree = tree
--mw.logObject(printlog, 'printlog')
--mw.logObject(argsTree, 'argsTree')
return argsTree
end
local function drawMiniNavBar(container, title, tags)
local pde = container:tag('ul')
:addClass('tnavbar')
:addClass('noprint')
:addClass('plainlinks')
:addClass('hlist')
-- p
local p = pde:tag('li'):wikitext("[[", title.nsText, ':', title.text, '|')
p:tag('span'):attr('title', res.navbar.p):wikitext(tags.p)
p:wikitext(']]')
-- d
if not title.isTalkPage then
local tt = title.talkPageTitle
local d = pde:tag('li'):wikitext("[[", tt.nsText, ':', tt.text, '|')
d:tag('span'):attr('title', res.navbar.d):wikitext(tags.d)
d:wikitext(']]')
end
-- e
pde:tag('li')
:attr('title', res.navbar.e)
:wikitext('[', title:fullUrl("action=edit"), ' ', tags.e, ']')
-- U
if mw.title.equals(title, mw.title.getCurrentTitle()) and (title.namespace == 10) then
local fullpagenamee = mw.uri.encode(title.fullText, "WIKI")
pde:tag('li')
:attr('title', res.navbar.U)
:wikitext('[https://tools.wmflabs.org/templatetransclusioncheck/index.php?lang=pl&name=', fullpagenamee, ' ', tags.U, ']')
end
end
local function splitCustomClasses(customClasses, navboxName)
if not customClasses then
return {}
end
local result = {}
local cc = mw.text.split(customClasses,"%s+")
for i, v in ipairs(cc) do
result[v] = res.validExtraClasses[v] ~= nil
end
-- pionowy szablon tylko na początku artykułu
if result.pionowy and navboxName then
local title = mw.title.getCurrentTitle()
if title and title.namespace == 0 then
local content = title:getContent()
if content then
local navboxStart = mw.ustring.find(content, "{{%s*"..mw.ustring.gsub( navboxName, "([%(%)%.%%%+%-%*%?%[%^%$%]])", "%%%1" ).."%s*}}")
if navboxStart then
local firstSectionStart = mw.ustring.find(content, "\n==.-==[ \t]*\n")
if firstSectionStart and (firstSectionStart < navboxStart) then
mw.logObject(result, "splitCustomClasses KASUJĘ pionowy bo nie jest na początku artykułu")
result.pionowy = false
end
end
end
end
end
return result
end
local function makeContainer(args, title, contentTag, printlog)
local templateClassName = (title and (title.namespace > 0) and not title.isTalkPage)
and encodeClass(title.namespace == 10 and title.text or title.fullText)
or nil
local builder = mw.html.create('div')
:addClass(res.class.navbox)
:addClass('do-not-make-smaller')
local classes = splitCustomClasses(args.use(res.arg.class.name), args.peek(res.arg.name.name))
for c, valid in pairs(classes) do
builder:addClass((valid and res.validExtraClasses[c]) and c or nil)
end
if title and mw.title.equals(title, mw.title.getCurrentTitle()) or (classes[templateClassName] ~= nil) then
builder:addClass(templateClassName and res.class.name..templateClassName or nil)
end
builder:wikitext(mw.getCurrentFrame():extensionTag( 'templatestyles', '', {src = "Szablon:Navbox/styles.css"}))
local privateCSS = false
if classes[templateClassName] ~= nil then
privateCSS = tostring(title.fullText.."/styles.css")
builder:wikitext(mw.getCurrentFrame():extensionTag( 'templatestyles', '', {src = privateCSS}))
end
if not classes.pionowy then
builder:addClass('mw-collapsible'):attr('data-expandtext', res.aux.expandText):attr('data-collapsetext', res.aux.collapseText)
local collapse = function()
if mw.title.getCurrentTitle().namespace == 0 then
return 'mw-collapsed'
end
if args.peek(res.arg.collapsible.name) == res.arg.collapsible.collapsed then
return 'mw-collapsed'
end
if mw.title.getCurrentTitle().namespace ~= 10 then
return 'autocollapse'
end
return nil
end
if title then
builder:addClass(not mw.title.equals(title, mw.title.getCurrentTitle()) and collapse() or nil)
drawMiniNavBar(builder, title, res.navbar.mini)
else
builder
:addClass(mw.title.getCurrentTitle().namespace ~= 10 and collapse() or nil)
:tag('span'):addClass(res.navbar.fake)
end
end
builder:tag('div')
:addClass(res.class.caption)
:wikitext(args.get(res.arg.title.name))
local content
if not args.peek(res.arg.above.name) and not args.peek(res.arg.below.name) and not args.peek(res.arg.before.name) and not args.peek(res.arg.after.name) and not classes.pionowy then
if contentTag == 'table' then -- zwijanie tabeli gryzie się ze skórką timeless
content = builder:tag('div')
:addClass('mw-collapsible-content')
:tag(contentTag)
else
content = builder:tag(contentTag)
:addClass('mw-collapsible-content')
end
elseif not args.peek(res.arg.above.name) and not args.peek(res.arg.below.name) and not classes.pionowy then
local flex = builder:tag('div')
:addClass('mw-collapsible-content')
:addClass(res.class.flex)
if args.peek(res.arg.before.name) then flex:tag('div'):addClass(res.class.before):newline():wikitext(args.get(res.arg.before.name)):newline() end
content = flex:tag(contentTag)
if args.peek(res.arg.after.name) then flex:tag('div'):addClass(res.class.after):newline():wikitext(args.get(res.arg.after.name)):newline() end
else
local content1 = builder:tag('div')
:addClass('mw-collapsible-content')
if args.peek(res.arg.above.name) then content1:tag('div'):addClass(res.class.hlist):addClass(res.class.above):newline():wikitext(args.get(res.arg.above.name)):newline() end
local flex = (not classes.pionowy and ((args.peek(res.arg.before.name) or args.peek(res.arg.after.name)))) and content1:tag('div'):addClass(res.class.flex) or content1
if args.peek(res.arg.before.name) then flex:tag('div'):addClass(res.class.before):newline():wikitext(args.get(res.arg.before.name)):newline() end
content = flex:tag(contentTag)
if args.peek(res.arg.after.name) then flex:tag('div'):addClass(res.class.after):newline():wikitext(args.get(res.arg.after.name)):newline() end
if (title and classes.pionowy) or args.peek(res.arg.below.name) then
local below = content1:tag('div'):addClass(res.class.hlist):addClass(res.class.below)
if args.peek(res.arg.below.name) then
below:newline():wikitext(args.get(res.arg.below.name)):newline()
end
if title and classes.pionowy then
if args.peek(res.arg.below.name) then
below:wikitext("----"):newline()
end
drawMiniNavBar(below, title, res.navbar.short)
end
end
end
content:addClass(res.class.main)
for c, valid in pairs(classes) do
content:addClass((valid and not res.validExtraClasses[c]) and c or nil)
end
if printlog then
printlog.pionowy = classes.pionowy
printlog.templateClassName = templateClassName
printlog.privateCSS = privateCSS
for c, valid in pairs(classes) do
if not valid and (c ~= templateClassName) then
local u = printlog.unknownClasses or {}
table.insert(u, c)
printlog.unknownClasses = u
end
end
end
return builder, content
end
local function createNavbox(args, title, printlog)
local rootPrinter = getPrinter(args.peek(res.arg.list.name))
if rootPrinter then
args.use(res.arg.list.name)
end
local rootTree = args.tree()
local rootTag = 'div'
if rootTree then
rootPrinter = rootPrinter or getPrinter(true)
rootTag = rootPrinter and (rootPrinter.root or rootPrinter.leaf) or 'div'
end
local builder, content = makeContainer(args, title, rootTag, printlog)
if rootTree then
printSubTree(content, rootTree, rootPrinter or getPrinter(true), printlog)
elseif not rootPrinter and args.peek(res.arg.list.name) then
if printlog then printlog.notree = true end
content
:newline()
:wikitext(args.get(res.arg.list.name))
:newline()
else
if printlog then printlog.example = true end
local exampleTree = {{
address = function() return res.class.address.."1" end,
peek = function(name) return nil end,
use = function(name) return nil end,
get = function(name) return args.get(name.."1") end,
odd = function(name) return nil end,
even = function(name) return nil end,
}}
printSubTree(content, exampleTree, rootPrinter or getPrinter(true), printlog)
end
return not printlog and tostring(builder) or require('Moduł:Navbox/diag').diagnosticView(builder, args, printlog)
end
return {
["Navbox"] = function(frame)
local title = mw.title.new(frame:getParent():getTitle())
local printlog = mw.title.equals(title,mw.title.getCurrentTitle()) and {} or nil
local args = loadArgsTree(frame.args, printlog)
return createNavbox(args, title, printlog)
end,
["Template"] = function(frame)
mw.log('\n== '..frame:getParent():getTitle()..' ==\n')
-- specjalne traktowanie nazwy
local name = frame:getParent().args[res.arg.name.name]
local expectedTitle = (name and (#name > 0)) and mw.title.new(name, "Template") or nil
local printlog = false
if expectedTitle then
local currentTitle = mw.title.getCurrentTitle()
if (currentTitle.namespace == expectedTitle.namespace)
and (currentTitle.namespace ~= 0) -- bez artykułów
and (currentTitle.namespace ~= 2) -- bez brudnopisów
and not currentTitle.isTalkPage
and (currentTitle.subpageText ~= res.aux.docSubpageText) -- bez opisów
and (not expectedTitle.exists or not require('Moduł:Navbox/diag').verifyTemplateName(currentTitle, expectedTitle, frame:getParent():getTitle())) then
printlog = {}
printlog.badName = true
elseif mw.title.equals(currentTitle, expectedTitle) then
printlog = {}
end
elseif name and (#name > 0) then
-- tutaj nie ma żadnych możliwości sprawdzenia czy to jest szablon
-- czy jego transkluzja
local currentTitle = mw.title.getCurrentTitle()
if (currentTitle.namespace ~= 0) and not currentTitle.isTalkSpace then
printlog = {}
printlog.badName = true
end
end
local status, templateContext = pcall(mw.text.jsonDecode, frame.args.context or '[]')
if not status then
-- mw.logObject(frame.args.context, templateContext)
templateContext = {}
end
-- mw.logObject(templateContext, 'templateContext')
local args = loadArgsTree(frame:getParent().args, printlog, templateContext)
if args.peek(res.arg.name.name) then args.use(res.arg.name.name) end
--mw.logObject(mw.text.jsonDecode(args.dump()), 'args.dump()')
if printlog and templateContext.list then
printlog.useTemplateCategories = true
end
local navbox = args.peek(res.arg.name.name)
or args.peek(res.arg.title.name)
or args.peek(res.arg.class.name)
or args.peek(res.arg.above.name)
or args.peek(res.arg.below.name)
or args.peek(res.arg.before.name)
or args.peek(res.arg.after.name)
if navbox then
-- mw.log("----> createNavbox")
return createNavbox(args, expectedTitle, printlog)
end
-- mw.log("----> dump")
return args.dump()
end,
}