Moduł:Navbox/diag

Z VatoWiki
Przejdź do nawigacji Przejdź do wyszukiwania

Dokumentacja dla tego modułu może zostać utworzona pod nazwą Moduł:Navbox/diag/opis

Błąd skryptu: Błąd Lua: Nie można utworzyć procesu: proc_open(/dev/null): failed to open stream: Operation not permitted

local res = mw.loadData('Moduł:Navbox/res')

local function fullMatch ( str )
	return '^'..mw.ustring.gsub( str, "([%(%)%.%%%+%-%*%?%[%^%$%]])", "%%%1" )..'$'
end

local function templateMatch ( name )
	local title = mw.title.new(name)
	local text = title.namespace ~= 10 and name or title.text
	return "{{%s*"..mw.ustring.gsub( text, "([%(%)%.%%%+%-%*%?%[%^%$%]])", "%%%1" ).."[%s|]"
end

local function loadKeys ( tab )
	local result = {}
	for k, v in pairs(tab) do
		table.insert(result, k)
	end
	
	return result
end

local function copySequenceExcept(tab, unwanted)
	local skip = {}
	if unwanted then
		for i, v in ipairs(unwanted) do
			skip[v] = true
		end
	end
	
	local result = {}
	for i, v in ipairs(tab) do
		if not skip[v] then
			table.insert(result, v)
		end
	end
	return result
end

local FindPositionsInReverseOrder = function(text, plainPattern)
	local result = {}
	if #plainPattern > 0 then
		local init = 1
		while init do
			init = string.find(text, plainPattern, init, true)
			if init then
				table.insert(result, 1, init)
				init = init + #plainPattern
			end
		end
	end
	
	return result
end

local RemoveInPlace = function(text, start, len)
	local before = start > 1 and string.sub(text, 1, start-1) or ""
	local stop = start + len
	local after = stop <= #text and string.sub(text, stop) or ""
	return before..string.rep("_", len)..after
end

local diag = {
	classStats = "navbox-statistics",
	categoryCSS = "[[Kategoria:Szablony nawigacyjne ze stylami]]",
	categoryProblems = "[[Kategoria:Szablony nawigacyjne – spisy do sprawdzenia]]",
	categorySuspected = "[[Kategoria:Szablony nawigacyjne – spisy podejrzane]]",
	categoryWikilinks0 = "[[Kategoria:Szablony nawigacyjne – spisy bez linków]]",
	categoryWikilinks1 = "[[Kategoria:Szablony nawigacyjne – tylko 1 link]]",
	categoryWikilinks2 = "[[Kategoria:Szablony nawigacyjne – tylko 2 linki]]",
	categoryWikilinks3 = "[[Kategoria:Szablony nawigacyjne – tylko 3 linki]]",
	categoryWikilinks4 = "[[Kategoria:Szablony nawigacyjne – tylko 4 linki]]",
	categoryLargeList = "[[Kategoria:Szablony nawigacyjne – ponad 500 pozycji]]",
	categoryUnknownArgs = "[[Kategoria:Szablony nawigacyjne – nieznane parametry]]",
	categoryManyColumns = "[[Kategoria:Szablony nawigacyjne – dużo kolumn]]",
	categoryVertical = "[[Kategoria:Szablony nawigacyjne – pionowe]]",
	categoryBadName = "[[Kategoria:Szablony nawigacyjne ze złym parametrem nazwa]]",
	categoryNavboxInside = "[[Kategoria:Szablony nawigacyjne – zagnieżdżone]]",
	categoryAnonymousLists = "[[Kategoria:Szablony nawigacyjne – nieopisane spisy]]",
	categoryCheckWikicode = "[[Kategoria:Szablony nawigacyjne – wikikod do sprawdzenia]]",
	categoryUnusedArgs = "[[Kategoria:Szablony nawigacyjne – nieużywane parametry]]",
	categoryUnverifiedArgs = "[[Kategoria:Szablony nawigacyjne – nieprawidłowe parametry]]",
	categoryMissingArgs = "[[Kategoria:Szablony nawigacyjne – brakujące parametry]]",
	categoryEmptyArgs = "[[Kategoria:Szablony nawigacyjne – puste parametry]]",
	categoryMissingWikilink = "[[Kategoria:Szablony nawigacyjne – spis bez linków]]",
	listItem = "^[%*#:]",
	wikilink = "%[%[ *%S[^\r\n]- *%]%]",
	problemIndicator = '<sup class="problemy-w-navbox problemy">?</sup>',
	suspectedIndicator = '<sup class="suspected problemy">!</sup>',
	navboxInside = '^<([a-z]+) class="navbox ',

	-- wzory wyrażeń regularnych
	verificators = {
		["nazwa"] = {
			inverted = true,
			patterns = {
				"[%c#%[%]%{%}|<>_\127]", -- niedozwolone znaki
				"^:", -- artykuły?
				"^%.$",
				"^%.%.$",
				"^%./",
				"^%.%./",
				"/%./",
				"/%.%./",
				"/%.$",
				"/%.%.$",
				"~~+",
			},
		},
	
		["tytuł"] = {
			inverted = true,
			patterns = {
				-- pliki
				"%[%[ *[Pp][Ll][Ii][Kk] *:.-%]%]",
				"%[%[ *[Ff][Ii][Ll][Ee] *:.-%]%]",
				-- listy
				"^[%*#:]",
				"\n[%*#:]",
			},
		},
	
		["klasa"] = {
			inverted = false,
			patterns = {
				"^[%w%-_ ]+$",
			},
		},
	
		["lista"] = {
			inverted = false,
			patterns = {
				"^[%*#:]",
				"\n[%*#:]",
			},
		},
	
		["tekst lub lista"] = {
			inverted = false,
			patterns = {
				"%S", -- cokolwiek byle nie pusto
			},
		},
	
		["grafika"] = {
			inverted = false,
			patterns = {
				"%[%[ *[Pp][Ll][Ii][Kk] *:.-%]%]",
				"%[%[ *[Ff][Ii][Ll][Ee] *:.-%]%]",
				"%[%[ *[Gg][Rr][Aa][Ff][Ii][Kk][Aa] *:.-%]%]",
			},
		},
	
		["zwijanie"] = {
			inverted = false,
			patterns = {
				fullMatch(res.arg.collapsible.collapsed),
				fullMatch(res.arg.collapsible.expanded),
				fullMatch(res.arg.collapsible.auto),
				fullMatch(res.arg.collapsible.disabled),
			},
		},
	},
	
	statsMessage = "Pozycje linkujące:&nbsp;$1, specjalne:&nbsp;$2, problemy$3:&nbsp;$4, '''RAZEM:&nbsp;$5'''",
	suspectedMessage = " (podejrzane$1:&nbsp;$2)",
	
	templateCats = {
		args = { "kategoria", "kategoria2", "kategoria3", "kategoria4", "kategoria5", "kategoria6", },
		ns = "Kategoria",
		prefix = "Szablony nawigacyjne - ",
		default = "Kategoria:Szablony nawigacyjne",
		disabled = "nie",
	},

	sourceCatsPatterns = {
		"%[%[ *[Kk][Aa][Tt][Ee][Gg][Oo][Rr][Ii][Aa] *: *([^|%[%]]-) *|[^|%[%]]-%]%]",
		"%[%[ *[Kk][Aa][Tt][Ee][Gg][Oo][Rr][Ii][Aa] *: *([^|%[%]]-) *%]%]",
		"%[%[ *[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy] *: *([^|%[%]]-) *|[^|%[%]]-%]%]",
		"%[%[ *[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy] *: *([^|%[%]]-) *%]%]",
	},
	
	brPattern = "<[bB][Rr] ?/?>",
	suspectedText = {
		'•', '&bull;', '&#x2022;', '&#8226;',
		'·', '&middot;', '&#xB7;', '&#183;',
		'∙', '&#x2219;', '&#8729;',
	},

	brackets = {
		pattern = "[%(%)%[%]]",
		['('] = ')',
		['['] = ']',
		[')'] = '(',
		[']'] = '[',
	}
}

local function printStatistics(builder, diagItems)
	local wikilinks = 0
	local nowikis = 0
	local problems = 0
	local suspected = 0
	for k, v in pairs(diagItems) do
		wikilinks = wikilinks + v.wikilinks
		nowikis = nowikis + v.nowikis
		problems = problems + v.problems
		suspected = suspected + v.suspected
	end
	--local lang = mw.getContentLanguage()
	local message = mw.message.newRawMessage(diag.statsMessage)
		:numParams(wikilinks, nowikis)
		:rawParams(diag.problemIndicator)
		:numParams(problems, wikilinks + nowikis + problems)
	local report = builder:tag('div')
		:addClass(diag.classStats)
		:wikitext(message:plain())
	if suspected > 0 then
		local message = mw.message.newRawMessage(diag.suspectedMessage)
			:rawParams(diag.suspectedIndicator)
			:numParams(suspected)
		report:wikitext(message:plain())
	end

	report:wikitext(problems > 0 and diag.categoryProblems or nil)
	report:wikitext(suspected > 0 and diag.categorySuspected or nil)

	if wikilinks == 0 then
		report:wikitext(diag.categoryWikilinks0)
	elseif wikilinks == 1 then
		report:wikitext(diag.categoryWikilinks1)
	elseif wikilinks == 2 then
		report:wikitext(diag.categoryWikilinks2)
	elseif wikilinks == 3 then
		report:wikitext(diag.categoryWikilinks3)
	elseif wikilinks == 4 then
		report:wikitext(diag.categoryWikilinks4)
	elseif (wikilinks + nowikis + problems) > 500 then
		report:wikitext(diag.categoryLargeList)
	end
end

local function printListWithoutWikilinks(report, diagItems)
	local nowikilinks = {}
	for k, v in pairs(diagItems) do
		if (v.wikilinks <= 0) and mw.ustring.match(k, '^'..res.arg.list.name..'%d') then
			table.insert(nowikilinks, k)
		end
	end
	if #nowikilinks > 0 then
		report:newline():wikitext("; brak wikilinków: ", #nowikilinks, diag.categoryMissingWikilink)
		for i, v in ipairs(nowikilinks) do
			report:newline():wikitext(":* ", v)
		end
	end
end

local function printNested(report, diagItems)
	--mw.logObject(diagItems, "nested")
	local nested = {}
	for k, v in pairs(diagItems) do
		if v.inside then
			table.insert(nested, k)
		end
	end
	if #nested > 0 then
		report:newline():wikitext("; zagnieżdżone szablony nawigacyjne: ", #nested, diag.categoryNavboxInside)
		for i, v in ipairs(nested) do
			report:newline():wikitext(":* ", v)
		end
	end
end

local function printWikicodeAnalyze(report)
	local contents = mw.title.getCurrentTitle():getContent()
	--mw.logObject(contents, 'contents')
	local text = contents or "" -- pusty tekst jeśli nie ma strony
	-- gumkowanie
	text = string.gsub(text, "<!%-%-.-%-%->", '')
	text = string.gsub(text, "<nowiki>.-</nowiki>", '')
	text = string.gsub(text, "<math>.-</math>", '')
	text = string.gsub(text, "<pre>.-</pre>", '')
	text = string.gsub(text, "<noinclude>.-</noinclude>", '')
	text = string.gsub(text, "<includeonly>(.-)</includeonly>", '%1')
	--mw.logObject(text, 'text')
	
	local cleanContents = text
	-- wikilinki iteracyjnie od końca
	for _, startPos in ipairs(FindPositionsInReverseOrder(text, "[[")) do
		local endPos = string.find(text, "]]", startPos, true)
		if endPos then
			local eol = string.find(text, "\n", startPos, true)
			if eol and (eol < endPos) then
				endPos = nil
			end
		end
		
		local len = endPos and (endPos - startPos + 2) or 2
		text = RemoveInPlace(text, startPos, len)
	end

	local paramPos = {}
	local templPos = {}
	local ob = string.byte('{')
	local cb = string.byte('}')
	for _, startPos in ipairs(FindPositionsInReverseOrder(text, "{{")) do
		local endPos = string.find(text, "}}", startPos, true)
		local len = endPos and (endPos - startPos + 2) or 2
		local sPos = startPos
		if (startPos > 1) and ((startPos + len) <= #text) and (string.byte(text, startPos + 2) == ob) and (string.byte(text, startPos + len) == cb) then
			-- parametr szablonu
			len = len + 1
			paramPos[startPos] = len
		elseif len ~= 2 then
			templPos[startPos] = len
		end
		
		-- usuń szablony zawarte wewnątrz znaleziska
		local inside = {}
		local ePos = sPos + len
		for s, l in pairs(templPos) do
			local e = s + l
			if (s > sPos) and (s < ePos) and (e > sPos) and (e < ePos) then
				table.insert(inside, s)
			end
		end
		for i, v in ipairs(inside) do
			templPos[v] = nil
		end
		
		-- zarejestruj pozycję znaleziska
		text = RemoveInPlace(text, sPos, len)
	end
	
	local notFound = 10000000000
	
	local params = {}
	for k, v in pairs(paramPos) do
		local e1 = string.find(cleanContents, "|", k, true) or notFound
		local e2 = string.find(cleanContents, "\n", k, true) or notFound
		local e3 = string.find(cleanContents, "}}}", k, true) or notFound
		local e = math.min(e1, e2, e3)
		assert(e < notFound)
		assert(e > (k + 2))
		local paramname = mw.text.trim(string.sub(cleanContents, k + 3, e - 1))
		params[paramname] = true
	end
	local paramnames = loadKeys(params)

	local templates = {}
	for k, v in pairs(templPos) do
		local e1 = string.find(cleanContents, "|", k, true) or notFound
		local e2 = string.find(cleanContents, "\n", k, true) or notFound
		local e3 = string.find(cleanContents, "}}", k, true) or notFound
		local e = math.min(e1, e2, e3)
		local p1 = false
		if (e3 < notFound) and (e1 < e3) then
			local e11 = string.find(cleanContents, "|", e1 + 1, true) or e3
			p1 = mw.text.trim(string.sub(cleanContents, e1+1, e11-1))
		end
		assert(e < notFound)
		assert(e > (k + 2))
		local templateName = mw.text.trim(string.sub(cleanContents, k + 2, e - 1))
		local invokeName = mw.ustring.match(templateName, "^#invoke:%s*(.+)$")
		if invokeName and p1 then
			templateName = "#invoke:"..mw.title.new(invokeName, "Module").text..'|'..p1
		end
		table.insert(templates, {
			start = k,
			len = v,
			name = templateName,
		})
	end
	
	report:newline():wikitext("; definicja szablonu")
	if #templates == 1 then
		local template = templates[1]
		report:newline():wikitext(":"):tag('code'):wikitext(template.name)
		if template.start > 1 then
			local before = mw.text.nowiki(mw.text.trim(string.sub(cleanContents, 1, template.start - 1)))
			if #before > 0 then
				report:newline():wikitext(": znaleziono treść przed szablonem")
					:newline():wikitext(":*"):tag('code'):wikitext(before)
			else
				report:newline():wikitext(": znaleziono odstęp przed szablonem")
			end
			report:wikitext(diag.categoryCheckWikicode)
		end
		local endPos = template.start + template.len
		--mw.logObject(endPos, 'endPos')
		--mw.logObject(#cleanContents, '#cleanContents')
		if (endPos == #cleanContents) and (string.sub(cleanContents, endPos, endPos) == '\n') then
			report:newline():wikitext(": znaleziono znak nowej linii za szablonem")
		elseif endPos <= #cleanContents then
			local after = mw.text.nowiki(mw.text.trim(string.sub(cleanContents, endPos)))
			if #after > 0 then
				report:newline():wikitext(": znaleziono treść za szablonem")
					:newline():wikitext(":*"):tag('code'):wikitext(after)
			else
				report:newline():wikitext(": znaleziono odstęp za szablonem")
			end
			report:wikitext(diag.categoryCheckWikicode)
		end
	elseif #templates < 1 then
		local text = mw.text.trim(cleanContents)
		if #text > 0 then
			report:newline():wikitext(": znaleziono jakąś treść zamiast szablonu")
		else
			report:newline():wikitext(": nie znaleziono żadnego szablonu")
		end
		report:wikitext(diag.categoryCheckWikicode)
	else
		report:newline():wikitext(": znaleziono wywołanie wielu szablonów", diag.categoryCheckWikicode)
		for i, t in ipairs(templates) do
			report:newline():wikitext(":*"):tag('code'):wikitext(t.name)
		end
	end
	if #paramnames > 0 then
		report:newline():wikitext("; wykryto parametry")
		for i, v in ipairs(paramnames) do
			report:newline():wikitext(':'):tag('code'):wikitext(v)
		end
	end
end

local function printTemplateCategories(report, categories)
	local pagename = mw.title.getCurrentTitle().text
	report:newline():wikitext("; zadeklarowane kategorie: ")
	if categories == false then
		report:wikitext("''wyłączone''")
	else
		local count = 0
		for k, v in pairs(categories) do
			count = count + 1
		end
		
		if count <= 0 then
			report:wikitext("''brak''", '[[', diag.templateCats.default, '|', pagename, ']]')
		else
			report:wikitext(count)
			local nameIndex = mw.ustring.len(diag.templateCats.ns)
				+ mw.ustring.len(diag.templateCats.prefix)
				+ 2
			for v, s in pairs(categories) do
				report:newline():wikitext(':*', mw.ustring.sub(v, nameIndex), '[[', v, '|', s, ' ]]')
				if #s == 0 then
					report:wikitext(' '):tag('small'):wikitext('(pusty klucz)')
				elseif s ~= pagename then
					report:wikitext(' '):tag('small'):wikitext('(klucz: ', s, ')')
				end
			end
		end
	end
end

local function printSourceCategories(report)
	
	local function print(description, content)
		if not content then
			return
		end
		
		local categories = {}
		for i, pattern in ipairs(diag.sourceCatsPatterns) do
			for c in mw.ustring.gmatch(content, pattern) do
				local title = mw.title.makeTitle(diag.templateCats.ns, c)
				if title then
					table.insert(categories, title.text)
				end
			end
		end
	
		report:newline():wikitext("; ", description, " : ", #categories)
		for i, v in ipairs(categories) do report:newline():wikitext(':*', v) end
	end
	
	local content = mw.title.getCurrentTitle():getContent() or ""
	print('kategorie w kodzie', content)
	local docTitle = mw.title.getCurrentTitle():subPageTitle(res.aux.docSubpageText)
	if docTitle and docTitle.exists then
		print('kategorie w dokumentacji', docTitle:getContent())
	end
	local otherDoc = mw.ustring.match(content, "{{ *[Dd]okumentacja *| *([^|%[%]]-/opis) *}}")
	if otherDoc then
		local otherDocTitle = mw.title.new(otherDoc)
		if otherDocTitle then
			print('kategorie w dołączonej dokumentacji', otherDocTitle:getContent())
		end
	end
end

local function printLogSummary(report, printlog)
	if printlog.levels then
		report:newline():wikitext("; głębokość drzewa :", printlog.levels)
	end
	if printlog.leafs then
		report:newline():wikitext("; liczba liści :", printlog.leafs)
	end
	if printlog.notree then
		report:newline():wikitext("; drzewo : ''nie''")
	end
	if printlog.example then
		report:newline():wikitext("; przykład : ''tak''")
	end
	if printlog.pionowy then
		report:wikitext(diag.categoryVertical)
	end
	if printlog.cols then
		report:newline():wikitext("; liczba kolumn :", printlog.cols)
		if printlog.cols > 3 then
			report:wikitext(diag.categoryManyColumns)
		end
	end
	if printlog.allCols then
		report:newline():wikitext("; liczba wszystkich komórek kolumnowych :", printlog.allCols)
	end
	if printlog.templateClassName then
		report:newline():wikitext("; prywatny CSS")
		report:newline():wikitext(": ", printlog.templateClassName)
		if printlog.privateCSS then
			report:newline():wikitext(": [[", printlog.privateCSS, "|styles.css]]", diag.categoryCSS)
		end
	end
	if printlog.unknownClasses then
		report:newline():wikitext("; nieużywane klasy", diag.categoryUnknownArgs)
		for i, v in ipairs(printlog.unknownClasses) do
			report:newline():wikitext(": ", #v <= 0 and "''pusta''" or v)
		end
	end
end

local function printEmptyArguments(report, argsTree)
	local result = {}
	for k, v in pairs(argsTree.args) do
		if not v.value or (#v.value <= 0) then
			table.insert(result, k)
		end
	end
	
	if #result <= 0 then
		return
	end
	
	report:newline():wikitext("; puste pola", diag.categoryEmptyArgs)
	for i, v in ipairs(result) do
		report:newline():wikitext(": ", v)
	end
end

local function printUnverifiedArguments(report, argsTree)
	local result = {}
	for k, v in pairs(argsTree.args) do
		if argsTree.used[k] and not v.verified then
			table.insert(result, k)
		end
	end
	
	if #result <= 0 then
		return
	end
	
	report:newline():wikitext("; nieprawidłowe pola", diag.categoryUnverifiedArgs)
	for i, v in ipairs(result) do
		report:newline():wikitext(": ", v)
	end
end

local function printUnknownArguments(report, argsTree)
	local result = {}
	for k, v in pairs(argsTree.args) do
		if not v.recognized and not v.obsolete and not argsTree.used[k] then
			table.insert(result, k)
		end
	end
	
	if #result <= 0 then
		return
	end
	
	report:newline():wikitext("; nieznane pola", diag.categoryUnknownArgs)
	for i, v in ipairs(result) do
		report:newline():wikitext(": ", v)
	end
end

local function printUnusedArguments(report, argsTree)
	local result = {}
	local cat = false
	for k, v in pairs(argsTree.args) do
		if (#v.value > 0) and not argsTree.used[k] and (v.recognized or v.obsolete) then
			cat = k ~= res.arg.collapsible.name -- omiń ponad 10k wywołań
			table.insert(result, k)
		end
	end
	
	if #result <= 0 then
		return
	end
	
	report:newline():wikitext("; nieużywane pola", cat and diag.categoryUnusedArgs or '')
	for i, v in ipairs(result) do
		report:newline():wikitext(": ", v)
	end
end

local function printMissingArguments(report, missing)
	local result = {}
	for k, v in pairs(missing) do
		table.insert(result, k)
	end
	
	if #result <= 0 then
		return
	end
	
	report:newline():wikitext("; brakujące pola", diag.categoryMissingArgs)
	for i, v in ipairs(result) do
		report:newline():wikitext(": ", v)
	end
end

local function printTestListDescriptions(report, tree)
	
	local direct = {}
	local parent = {}
	
	local function argName(node)
		local address = string.sub(node.address(), 2)
		return res.arg.list.name..string.gsub(address, '_', '.')
	end
	
	local function parseNode(node)
		if #node <= 0 then
			-- liści nie obrabiamy
			return
		end
		
		local suspected = {}
		for i, n in ipairs(node) do
			if not n.peek(res.arg.group.name) and n.peek(res.arg.list.name) then
				table.insert(suspected, i)
			end
		end
		
		if #suspected > 1 then
			for i, v in ipairs(suspected) do
				table.insert(direct, argName(node[v]))
			end
		elseif (#suspected > 0) and node.address() and not node.peek(res.arg.group.name) then
			table.insert(parent, argName(node[suspected[1]]))
		end
		
		for i, n in ipairs(node) do
			parseNode(n)
		end
	end

	parseNode(tree)
	if (#direct <= 0) and (#parent <= 0) then
		return
	end
	
	if #direct > 0 then
		report:newline():wikitext("; nieopisany spis w grupie", diag.categoryAnonymousLists)
		for i, v in ipairs(direct) do
			report:newline():wikitext(": ", v)
		end
	end
	
	if #parent > 0 then
		report:newline():wikitext("; zagnieżdżony nieopisany spis", diag.categoryAnonymousLists)
		for i, v in ipairs(parent) do
			report:newline():wikitext(": ", v)
		end
	end
end

local function formatArgs(args)
	local padindex = '................................................'
	local orders = {
		["nazwa"]    = { p = 'A', s = '1', n = ' ', },
		["tytuł"]    = { p = 'A', s = '2', n = ' ', },
		["klasa"]    = { p = 'A', s = '3', n = ' ', },
		["góra"]     = { p = 'B', s = '1', n = '\n', },
		["przed"]    = { p = 'C', s = '1', n = ' ', },
		["po"]       = { p = 'C', s = '2', n = ' ', },
		["grafika"]  = { p = 'C', s = '3', n = ' ', },
		["opis"]     = { p = 'D', s = '1', n = ' ', },
		["zwijanie"] = { p = 'D', s = '2', n = ' ', },
		["spis"]     = { p = 'D', s = '3', n = '\n', },
		["dół"]      = { p = 'E', s = '1', n = '\n', },
		[false]      = { p = 'Z', s = '1', n = ' ', },
	}
	local list = {}
	local i = 0
	for k, v in pairs(args) do
		i = i + 1
		local prefix, suffix = mw.ustring.match(k, "^(.-)([1-9][%.0-9]*)$")
		if not prefix then
			prefix = k
			suffix = padindex
		else
			suffix = string.sub(suffix..padindex, 1, #padindex)
		end
		
		local ord = orders[prefix] or orders[false]
		local item = {
			o = string.format('%s-%s-%s-%08d', ord.p, suffix, ord.s, i),
			k = k,
			v = v,
			n = ord.n,
		}
		table.insert(list, item)
	end
	
	table.sort(list, function(a,b) return a.o < b.o end)
	local result = {}
	for i, v in ipairs(list) do
		table.insert(result, '| '..v.k..' ='..v.n..v.v..(v.n == '\n' and v.n or ''))
	end
	
	return table.concat(result, '\n')
end

local function verify(argInfo)
	local verificator = diag.verificators[argInfo.recognized]
	if verificator then
		argInfo.verified = verificator.inverted
		for i, v in ipairs(verificator.patterns) do
			if mw.ustring.match(argInfo.value, v) then
				argInfo.verified = not verificator.inverted
				break
			end
		end
	end
end

local function testBrackets(text)
	local stack = {}
	for b in mw.ustring.gmatch(text, diag.brackets.pattern) do
		if (b == '(') or (b == '[') then
			table.insert(stack, b)
		elseif (b == ')') or (b == ']') then
			if (#stack > 0) and stack[#stack] == diag.brackets[b] then
				table.remove(stack, #stack)
			else
				return false -- brak otwartego nawiasu
			end
		end
	end
		
	return #stack == 0 -- oczekiwane zamknięte wszystkie nawiasy
end

local function check(text, br)
	local lines = mw.text.split(text, '\n', true)
	local wikilinks = 0
	local nowikis = 0
	local problems = 0
	local suspected = 0
	local result = {}
	for i, v in ipairs(lines) do
		local t = v
		if not mw.ustring.match(v, diag.listItem) then
			-- to nie jest wikilista
		elseif mw.ustring.match(v, diag.wikilink) then
			wikilinks = wikilinks + 1
		elseif string.match(v,'\127') then
			-- nowiki, ref, etc
			nowikis = nowikis + 1
		else
			problems = problems + 1
			t = v..diag.problemIndicator
		end
		
		if br and mw.ustring.match(v, diag.brPattern) then
			suspected = suspected + 1
			t = t..diag.suspectedIndicator
		elseif not testBrackets(v) then
			suspected = suspected + 1
			t = t..diag.suspectedIndicator
		else
			-- zamieniam wikilinki na tekst do testowania
			local s, c = string.gsub(v, "%[%[.-|(.-)%]%]", "%1") -- nie patrz w linki
			local s, c = string.gsub(s, "%[%[[^|\n%[%]]+%]%]", "ok") -- lub zamień je na dobry tekst
			for i, p in ipairs(diag.suspectedText) do
				if mw.ustring.match(s, p, 1, true) then
					suspected = suspected + 1
					t = t..diag.suspectedIndicator
					break
				end
			end
		end

		table.insert(result, t)
	end
	
	return {
		value = table.concat(result,'\n'),
		wikilinks = wikilinks,
		nowikis = nowikis,
		problems = problems,
		suspected = suspected,
		inside = string.match(lines[1], diag.navboxInside) and true or false,
	}
end

local function argsService(templateContext)
	local argsTree = {
		args = {},
		missing = {},
		used = {},
		diag = {},
	}
	
	local staticArgs = {}
	local dynamicArgs = {}
	for k, v in pairs(res.arg) do
		if templateContext and templateContext.aliases then
			local alias = templateContext.aliases[v.name]
			if v.static and alias then staticArgs[alias] = v.static end
			if v.dynamic and alias then dynamicArgs[alias] = v.dynamic end
		end
		if v.static then staticArgs[v.name] = v.static end
		if v.dynamic then dynamicArgs[v.name] = v.dynamic end
		if v.template and templateContext then staticArgs[v.name] = v.template end
	end
	
	local function add(k, v, prefix)
		--mw.logObject({k, v, prefix}, "analyzeArg")
		local argInfo = {
			value = v,
			recognized = staticArgs[k] or (prefix and dynamicArgs[prefix])
		}
		if argInfo.recognized then
			verify(argInfo)
		elseif templateContext and templateContext.obsolete then
			argInfo.obsolete = prefix and templateContext.obsolete[prefix]
				or (templateContext.obsolete[k] == false)
		end
		argsTree.args[k] = argInfo
		--mw.logObject(argInfo, k)
	end

	local peekName = function(name)
		if (not argsTree.args[name] or (#argsTree.args[name].value <= 0)) and templateContext and templateContext.aliases then
			local alias = templateContext.aliases[name]
			if alias and argsTree.args[alias] and (#argsTree.args[alias].value > 0) then
				return alias
			end
		end
		
		return name
	end

	local function peek(name)
		--mw.logObject(name, "peek")
		local name = peekName(name)
		local arg = argsTree.args[name]
		return (arg and (#arg.value > 0)) and arg.value or nil
	end
	
	local function use(name)
		local name = peekName(name)
		local arg = argsTree.args[name]
		local result = arg and arg.value or nil
		--mw.logObject({name, result}, "use")
		argsTree.used[name] = (argsTree.used[name] or 0) + 1
		return result
	end
	
	local function get(name)
		local name = peekName(name)
		local result = use(name)
		if result == nil then
			argsTree.missing[name] = (argsTree.missing[name] or 0) + 1
			result = res.aux.missingArgNamePrefix..name..res.aux.missingArgNameSuffix
		elseif not argsTree.diag[name] then
			local br = mw.ustring.match(name, '^'..res.arg.list.name..'%d')
			argsTree.diag[name] = check(result, br)
			result = argsTree.diag[name].value
		end
		--mw.logObject({name, result}, "get")
		return result
	end
	
	local function dump()
		local buffer = {}
		for k, v in pairs(argsTree.args) do
			buffer[k] = v.value
		end
		return mw.text.jsonEncode(buffer)
	end
	
	local function loadTemplateCategories()
		local pagename = mw.title.getCurrentTitle().text
		local map = {}
		for i, argName in ipairs(diag.templateCats.args) do
			if i > 1 then -- piersza inaczej
				local catName = peek(argName)
				if catName and (#catName > 0) then
					local name, sort = mw.ustring.match(catName, "^(.-)%s*|%s*(.*)$")
					if not name then
						name = catName
						sort = pagename
					end
					local categoryTitle = mw.title.makeTitle(diag.templateCats.ns, diag.templateCats.prefix..name)
					if categoryTitle and (#categoryTitle.fragment <= 0) and not map[categoryTitle.fullText] then
						use(argName)
						argsTree.args[argName].verified = true
						map[categoryTitle.fullText] = sort
					end
				end
			end
		end
	
		local cat1arg = diag.templateCats.args[1]
		local cat1 = peek(cat1arg)
		if cat1 == diag.templateCats.disabled then
			use(cat1arg)
			argsTree.args[cat1arg].verified = true
			for k, v in pairs(map) do
				return map
			end
			
			return false
		end
		
		if cat1 and (#cat1 > 0) then
			local name, sort = mw.ustring.match(cat1, "^(.-)%s*|%s*(.*)$")
			if not name then
				name = cat1
				sort = pagename
			end
			local categoryTitle = mw.title.makeTitle(diag.templateCats.ns, diag.templateCats.prefix..name)
			if categoryTitle and (#categoryTitle.fragment <= 0) and not map[categoryTitle.fullText] then
				use(cat1arg)
				argsTree.args[cat1arg].verified = true
				map[categoryTitle.fullText] = sort
			end
		end
		
		return map
	end

	local function diagnosticView (builder, printlog, tree)
		printStatistics(builder, argsTree.diag)
		local summaryReport = mw.html.create('div')
			:addClass('navbox-summary')
			:addClass("mw-collapsible mw-collapsed")
		summaryReport:tag('div')
			:addClass('title')
			:wikitext('Informacje diagnostyczne')
		local report = summaryReport:tag('div')
			--:addClass('hlist')
			:addClass('mw-collapsible-content')
		if printlog.badName then
			report:wikitext(diag.categoryBadName)
		end
		printWikicodeAnalyze(report)
		if printlog.useTemplateCategories then
			printTemplateCategories(report, loadTemplateCategories())
		end
		printSourceCategories(report)
		report:newline():wikitext('----')
		printLogSummary(report, printlog)
		report:newline():wikitext('----')
		printListWithoutWikilinks(report, argsTree.diag)
		printEmptyArguments(report, argsTree)
		printUnverifiedArguments(report, argsTree)
		printUnusedArguments(report, argsTree)
		printUnknownArguments(report, argsTree)
		printMissingArguments(report, argsTree.missing)
		printNested(report, argsTree.diag)
		if tree then
			printTestListDescriptions(report, tree)
		end
		
		local argsReport = ''
		local formattedArgs = formatArgs(mw.text.jsonDecode(dump()))
		if formattedArgs and (#formattedArgs > 0) then
			local report = mw.html.create('div')
				:addClass('navbox-summary')
				:addClass("mw-collapsible mw-collapsed")
			report:tag('div')
				:addClass('title')
				:wikitext('Kanoniczne zestawienie parametrów')
			local content = mw.getCurrentFrame():extensionTag('pre', formattedArgs, {class='mw-collapsible-content'})
			report:wikitext(content)
			argsReport = tostring(report)
		end

		local navbox = tostring(builder)
		local details = tostring(summaryReport)
		local catReport = ''
		
		local function hideCategories()
			local currentTitle = mw.title.getCurrentTitle()
			if currentTitle.isTalkPage then
				return true
			elseif currentTitle.namespace ~= 2 then
				return false
			else -- Wikipedysta - może programista?
				local navboxDeveloper = mw.title.makeTitle(2, currentTitle.rootText..'/navbox-developer')
				return not navboxDeveloper or not navboxDeveloper.exists
			end
		end
		
		if hideCategories() then
			local categories = {}
			local function extractCategories(text)
				local function foundCat(category)
					table.insert(categories, category)
					return ''
				end
				local t1, c1 = mw.ustring.gsub(text, diag.sourceCatsPatterns[1], foundCat)
				local t2, c2 = mw.ustring.gsub(t1, diag.sourceCatsPatterns[2], foundCat)
				return t2
			end
			navbox = extractCategories(navbox)
			details = extractCategories(details)
			if #categories > 0 then
				local report = mw.html.create('div')
					:addClass('navbox-summary')
					:addClass("mw-collapsible mw-collapsed")
				report:tag('div')
					:addClass('title')
					:wikitext('Kategorie')
				local content = report:tag('div')
					:addClass('mw-collapsible-content')
				for i, v in ipairs(categories) do
					content:newline():wikitext('*', '[[:Kategoria:', v, ']]')
				end
				content:newline()
				catReport = tostring(report)
			end
		end

		local result = navbox..details..argsReport..catReport
		return result
	end

	return {
		add = add,
		peek = peek,
		use = use,
		get = get,
		dump = dump,
		diagnosticView = diagnosticView,
	}
end

return {
	
argsService = argsService,

diagnosticView = function(builder, args, printlog)
	return args.diagnosticView(builder, printlog, args.tree and args.tree() or nil) 
end,

verifyTemplateName = function(currentTitle, expectedTitle, templateName)
	local content = currentTitle:getContent()
	if not content then
		mw.log("Navbox:verifyTemplateName: brak zawartości strony")
		return false
	end

	mw.logObject(templateName, "Navbox:verifyTemplateName: templateName")
	local navboxTemplate = templateMatch(templateName)
	mw.logObject(navboxTemplate, "Navbox:verifyTemplateName: navboxTemplate")
	local s, e = mw.ustring.find(content, navboxTemplate, 1)
	if not s then
		mw.log("Navbox:verifyTemplateName: myślę, że jest ok, bo tu nie widzę kodu szablonu nawigacyjnego")
		return true
	end

	local exists1 = function(pattern)
		local s, e = mw.ustring.find(content, pattern, s)
		if not s then
			return 0
		end
		
		local s, e = mw.ustring.find(content, pattern, e)
		if not s then
			return 1
		end
		
		return 2
	end
	
	if 1 == exists1("|%s*nazwa%s*=%s*{{%s*#invoke:Navbox%s*|%s*Name%s*}}%s*[|}]") then
		mw.log("Navbox:verifyTemplateName: myślę, że jest ok, automatyczna nazwa jest tylko raz -> true")
		return true
	end
	
	mw.logObject(expectedTitle, "Navbox:verifyTemplateName: zadeklarowana nazwa w szablonie")
	mw.logObject(currentTitle, "Navbox:verifyTemplateName: obrabiana strona")
	-- w naszym szablonie oczekuję jednej nazwy
	-- w przeciwnym razie nie oczekuję żadnej nazwy
	local expectedCount = mw.title.equals(currentTitle, expectedTitle) and 1 or 0
	mw.logObject(expectedCount, "Navbox:verifyTemplateName: oczekiwana liczba wystąpień podanej nazwy w szablonie")
	local name = mw.ustring.gsub(expectedTitle.text, "([%(%)%.%%%+%-%*%?%[%^%$%]])", "%%%1" );
	local pattern = ((expectedTitle.namespace == 10) or (0 < exists1("|%s*przestrzeń%s*=%s*"..expectedTitle.nsText.."%s*[|}]")))
		and ("|%s*nazwa%s*=%s*"..name.."%s*[|}]") -- zwykła nazwa (w szablonie lub jawnie zadeklarowanej przestrzeni)
		or ("|%s*nazwa%s*=%s*"..expectedTitle.nsText..":"..name.."%s*[|}]") -- pełna nazwa (w szablonie w innej przestrzeni)
	mw.logObject(pattern, "Navbox:verifyTemplateName: szukam nazwy w szablonie według wzoru")
	local count = exists1(pattern)
	mw.logObject(count, "Navbox:verifyTemplateName: liczba wyników wyszukiwania")
	return expectedCount == count
end,

}