Module:BSicon

From Wikimedia Commons, the free media repository
Jump to navigation Jump to search

Documentation for this module may be created at Module:BSicon/doc

local p = {}

-- local test = {}

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

local function makeInvokeFunction(funcName)
	-- makes a function that can be returned from #invoke, using
	-- [[Module:Arguments]].
	return function(frame)
		local args = getArgs(frame, {parentOnly = true})
		return p[funcName](args)
	end
end

p.category = makeInvokeFunction('_category')

function p._category(args)
	local title, categories, color, result, tmp = mw.title.getCurrentTitle(), '', {''}, ''
	if title.namespace ~= 14 then return error("This template should only be used on category pages") end
	title = title.text
	title = mw.ustring.gsub(title, '%+', '#')
	title = mw.ustring.gsub(title, '%-', '_')
	local title2 = mw.clone(title)
	local rgb, contrast = require('Module:Routemap')._RGBbyCode, require('Module:Color contrast')._ratio
	local function colortext(x)
		x = tostring(x) or 'BE2D2C'
		return '<kbd class="rgb-'..((contrast{x, 'FFF'} < 4.5) and 'light' or 'dark')..'" style="background-color: #'..x..'">#'..x..'</kbd>'
	end
	local function hex(x, ex)
		x = rgb{mw.ustring.gsub((ex and 'ex_' or '')..(tostring(x) or ''), '^ex_([ufg]?)$', '%1ex', 1)}
		return colortext(x)
	end
	local roots = {
		BHF = 'stations and stops/',
		HST = 'stations and stops/',
		DST = 'stations and stops/',
		BST = 'stations and stops/',
		INT = 'stations and stops/',
		ACC = 'stations and stops/',
		INTACC = 'stations and stops/',
		HSTACC = 'stations and stops/',
		suburban = 'stations and stops/',
		SBHF = 'stations and stops/',
		SHST = 'stations and stops/',
		['S#BHF'] = 'stations and stops/',
		['S#HST'] = 'stations and stops/',
		HSTACC = 'stations and stops/',
		RD = 'generic road/',
		RP1 = 'generic road/',
		RP2 = 'generic road/',
		RP4 = 'generic road/',
		wye = 'junction/',
		split = 'junction/',
		formations = 'legende/',
		['straight#curve'] = 'curve/',
		['straight#junction'] = 'junction/',
		['straight#corner'] = 'corner/',
	}
	local compounds = {
		['road–rail'] = 'railway/road',
		INTACC = 'INT/ACC',
		HSTACC = 'HST/ACC',
		['S#BHF'] = 'suburban/BHF',
		['S#HST'] = 'suburban/HST',
		SBHF = 'suburban/BHF',
		SHST = 'suburban/HST',
		['curve#corner'] = 'curve/corner',
		['crossing#corner'] = 'crossing/corner',
		['crossing#junction'] = 'crossing/junction',
		['junction#corner'] = 'junction/corner',
	}
	local used_roots = {}
	local matches = {}
	local used_compounds = {}
	title = title..'/'
	for k, v in pairs(roots) do
		if mw.ustring.match(title, '/'..k..'/') then
			title = mw.ustring.gsub(title, k, v..k)
			table.insert(used_roots, k)
			used_roots[k] = true
		end
	end
	title = mw.ustring.gsub(title, '/$', '')
	for k, v in pairs(compounds) do
		if mw.ustring.match(title, k) then
			title = mw.ustring.gsub(title, k, v)
			table.insert(matches, k)
			table.insert(used_compounds, v)
		end
	end
	local legende_color
	if mw.ustring.match(title, 'road–rail') then title = mw.ustring.gsub(title, 'road–rail', 'railway/road') end
	if mw.ustring.match(title, 'water') then
		result = result..'\n<tr><td>water color<td>'..colortext('007CC3')
	end
	if mw.ustring.match(title, 'tunnel to') or mw.ustring.match(title, 'portal') or mw.ustring.match(title, 'elevated') or mw.ustring.match(title, 'bridge') or mw.ustring.match(title, 'crossing') or mw.ustring.match(title, '/tower') or mw.ustring.match(title, 'cutting') or mw.ustring.match(title, 'embankment') then
		result = result..'\n<tr><td>structure color<td>'..colortext('80A080')
		legende_color = true
	end
	if mw.ustring.match(title, 'line endings') then
		result = result..'\n<tr><td>line ending colors<br/>(open, closed)<td>'..colortext('000')..'<br/>'..colortext('AAA')
		legende_color = true
	end
	if mw.ustring.match(title, 'platform') then
		result = result..'\n<tr><td>platform colors<br/>(open, closed)<td>'..colortext('888')..'<br/>'..colortext('CCC')
		legende_color = true
	end
	if mw.ustring.match(title, 'mask') then
		result = result..'\n<tr><td>mask color<td>'..colortext('F9F9F9')
	end
	if mw.ustring.match(title, 'INT') then
		result = result..'\n<tr><td>INT colors<br/>(open, closed)<td>'..colortext('000')..'<br/>'..colortext('AAA')
		legende_color = true
	end
	if mw.ustring.match(title, 'ACC') then
		result = result..'\n<tr><td>ACC colors<br/>(open, closed)<td>'..colortext('034EA2')..'<br/>'..colortext('6592C5')
		legende_color = true
	end
	if mw.ustring.match(title, 'CPIC') then
		result = result..'\n<tr><td>cross-platform<br/>interchange colors<td>'..colortext('000')..'<br/>'..colortext('B3B3B3')
	end
	if mw.ustring.match(title, 'S#?BHF') or mw.ustring.match(title, 'S#?HST') or mw.ustring.match(title, 'suburban') then
		result = result..'\n<tr><td>S-Bahn colors<br/>(open, closed)<td>'..colortext('006E34')..'<br/>'..colortext('5ABF89')
		legende_color = true
	end
	if mw.ustring.match(title, 'DST') or mw.ustring.match(title, 'BST') or mw.ustring.match(title, 'ACC') or mw.ustring.match(title, 'INT') or mw.ustring.match(title, 'S#?BHF') or mw.ustring.match(title, 'S#?HST') then
		result = result..'\n<tr><td>fill color<td>'..colortext('FFF')
	end
	legende_color = legende_color and mw.ustring.match(title, 'legende')
	title_string = mw.clone(title)..'/'
	title = mw.text.split(title, '/')
	if title[1] ~= 'BSicon' then return '' end
	if title[2] == 'railway' and (((title[3] or 'road') ~= 'road') or title[4]) and (not used_roots['formations']) and (not legende_color) then
		if mw.ustring.match(((title[3] ~= 'road') and title[3] or title[4]), '^set ') then color[1] = mw.ustring.match(((title[3] ~= 'road') and title[3] or title[4]), '^set (.+)$') or '' end
		if color[1] == 'mixed' then
			color = {'', 'u'}
		elseif mw.ustring.match(color[1], '–') then
			color = mw.text.split(color[1], '–')
		end
		result = "\n"..(color[2] and ('<tr><td>main colors<td>'..hex(color[1])..'<br/>'..hex(color[2])..'\n<tr><td>ex colors<td>'..hex(color[1], true)..'<br/>'..hex(color[2], true)) or ('<tr><td>main color<td>'..hex(color[1])..'\n<tr><td>ex color<td>'..hex(color[1], true)))..result
	end
	if mw.ustring.match(result, '<td>') then
		result = "<table class=\"wikitable\"><tr><th>Color<th>RGB hex triplet"..result.."</tr></table>"
	end
	result = "These [[BSicon]]s are to be used with '''route diagram templates'''. For an overview, see [[:Category:BSicon]].\n"..result
	table.remove(title, 1)
	for k, v in ipairs(title) do
		if not ((mw.ustring.match((title[2] or ''), '^set ') or mw.ustring.match((title[3] or ''), '^set ')) and title[k] == 'railway')
		and not ((mw.ustring.match((title[2] or ''), 'R([A-Z0-9]+)$') or mw.ustring.match((title[3] or ''), 'R([A-Z0-9]+)$') or mw.ustring.match((title[4] or ''), 'R([A-Z0-9]+)$') or mw.ustring.match((title[5] or ''), 'R([A-Z0-9]+)$') or title[2] == 'generic road' or title[3] == 'generic road' or title[4] == 'generic road') and title[k] == 'road')
		and (#title < 3 or k > 1 or (title[2] == 'road' and k == 1) or (title[1] ~= 'railway' and title[1] ~= 'road' and title[1] ~= 'canal'))
		and not (mw.ustring.match((title[k+1] or ''), ' quarters?$'))
		and not (title[k] == 'stations and stops' and (title[k+1] == 'interchange' or title[k+1] == 'express'))
		and not (title[k] == 'interchange' and (title[k+1] == 'CPIC'))
		and not (title[k] == 'uw' and title[k+1] == 'double')
		and not (title[k] == 'parallel lines' and mw.ustring.match((title_string or ''), '/double/'))
		and not (title[k] == 'tunnel' and (title[k+1] == 'portal' or title[k+2] == 'portal')) then
			tmp = mw.clone(title)
			tmp = 'BSicon/'..table.concat(tmp, '/')
			for _, x in ipairs(used_compounds) do
				if mw.ustring.match(x, v) then
					x = mw.ustring.gsub(x, v, '')
					x = mw.ustring.gsub(x, '/', '')
					local tmp_root = roots[x]
					if tmp_root then
						tmp = mw.ustring.gsub(tmp, tmp_root, '')
					end
				end
			end
			tmp = tmp..'/'
			tmp = mw.ustring.gsub(tmp, '/'..v..'/', '/')
			tmp = mw.ustring.gsub(tmp, '/+', '/')
			for k, v in ipairs(matches) do
				tmp = mw.ustring.gsub(tmp, compounds[v], v)
			end
			for k, v in ipairs(used_roots) do
				if mw.ustring.match(tmp, '/'..v..'/') then tmp = mw.ustring.gsub(tmp, '/'..roots[v], '/') end
			end
			if mw.ustring.match(tmp, '/junction/crossing/') then tmp = mw.ustring.gsub(tmp, '/junction/crossing/', '/crossing+junction/') end
			tmp = mw.ustring.gsub(tmp, '/$', '')
			if title2 ~= tmp then categories = categories..mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub('\n[[Category:'..tmp..'|'..mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(title[k], '^set ', '!'), '^R([A-Z0-9]+)$', '$%1'), '^generic road$', '$generic'), '^road$', '$road'), '^railway$', '!railway'), '^(.*width)$', '*%1'), '^parallel lines$', '*parallel lines'), '^one quarter$', '1'), '^two quarters$', '2'), '^three quarters$', '3'), '^four quarters$', '4'), '^five quarters$', '5'), '^six quarters$', '6'), '^seven quarters$', '7'), '^eight quarters$', '8')..']]', '/|', '|'), 'railway|!([a-z][a-z]+)', 'railway/other colors|!%1'), 'railway|!([fg])', 'railway/other colors|!%1') end
		end
	end
	categories = mw.ustring.gsub(categories, '#', '+')
	categories = mw.ustring.gsub(categories, '_', '-')
	return result..categories
end

p.categorize = makeInvokeFunction('_categorize')

-- NOT COMPLETE YET. THIS WILL NOT WORK ON A LOT OF ICONS AND NEEDS A LOT OF CATEGORY TITLE PARTS TO BE ADDED.

function p._categorize(args)
	local title = (mw.ustring.match(mw.title.getCurrentTitle().text, '^BSicon (.*)%.svg$') or 'BHF')
	local category = {['Category:BSicon'] = true}
	local titleparts
	local tmp_set
	local tmp_match = ''
	local result = {}
	if not (mw.ustring.match(title, '[^~]R[ABDEGMPRY]') or mw.ustring.match(title, '^R[ABDEGMPRY]') or mw.ustring.match(title, 'WASSER') or mw.ustring.match(title, 'WABZ')) then
		category['railway'] = true
		titleparts = mw.text.split(title, ' ')
		if titleparts[2] then 
			for k = 2, #titleparts do
				if mw.ustring.match(titleparts[k], '^[a-z]+$') then
					category['set '..titleparts[k]] = true
					category['other colors'] = true
				else
					tmp_set = mw.ustring.match(titleparts[k], '^[a-z]+')
					if tmp_set then category['set '..tmp_set] = true end
					titleparts[1] = titleparts[1]..string.sub(titleparts[k], string.len(tmp_set or '')+1)
				end
			end
		end
		titleparts = titleparts[1]
		titleparts = '|'..mw.ustring.gsub(titleparts, '.', '%0|')
		titleparts = mw.ustring.gsub(titleparts, '([A-ZÜ])|([A-ZÜ])', '%1%2')
		titleparts = mw.ustring.gsub(titleparts, '([A-ZÜ])|([A-ZÜ])', '%1%2')
		titleparts = mw.ustring.gsub(titleparts, '|n|u|m|', '|NUM|')
		titleparts = mw.ustring.gsub(titleparts, '|([A-ZÜ][A-ZÜ]+)(SPL)|', '|%1|%2|')
		titleparts = mw.ustring.gsub(titleparts, '|([A-ZÜ][A-ZÜ]+)(SHI)|', '|%1|%2|')
		titleparts = mw.ustring.gsub(titleparts, '(SHI)|([1-8])', '%1%2')
		titleparts = mw.ustring.gsub(titleparts, '(BRÜCKE)|([1-3]|[^%+])', '%1%2')
		titleparts = mw.ustring.gsub(titleparts, '(BRÜCKE)|([1-3]|)$', '%1%2')
		titleparts = mw.ustring.gsub(titleparts, '(VIADUKT)|([1-3]|[^%+])', '%1%2')
		titleparts = mw.ustring.gsub(titleparts, '(VIADUKT)|([1-3]|)$', '%1%2')
		titleparts = mw.ustring.gsub(titleparts, '(TUNNEL)|([12]|[^%+])', '%1%2')
		titleparts = mw.ustring.gsub(titleparts, '(TUNNEL)|([12]|)$', '%1%2')
		-- Other roots
		titleparts = mw.ustring.gsub(titleparts, '|?(SW)([A-HJ-ZÜ][A-HJ-ZÜ])', '|!SW|%2')
		titleparts = mw.ustring.gsub(titleparts, '|?S|%+|([A-ZÜ][A-ZÜ])', '|S+%1')
		titleparts = mw.ustring.gsub(titleparts, '|LL?([A-KMNPQS-ZÜ])', '|L|%1')
		titleparts = mw.ustring.gsub(titleparts, '|M([A-ZÜ])', '|M|%1')
		titleparts = mw.ustring.gsub(titleparts, '|M|ASK', '|MASK')
		titleparts = mw.ustring.gsub(titleparts, '|T([A-DF-QSTV-ZÜ])', '|T|%1')
		titleparts = mw.ustring.gsub(titleparts, '|K([A-LN-QS-ZÜ])', '|K|%1')
		titleparts = mw.ustring.gsub(titleparts, '|X([A-ZÜ])', '|X|%1')
		-- Other capital prefix and combining suffix searches
		-- Discard x, since it isn't used for categorization and only has one meaning
		titleparts = mw.ustring.gsub(titleparts, '|x|', '|')
		titleparts = mw.ustring.gsub(titleparts, '^([ufgelhatpnk|]*|)(c?)|?(d?)|?(b?)|', '%1%2%3%4|')
		titleparts = mw.ustring.gsub(titleparts, '|([%+%-])|([lhtnCDLM]?)|?([ck])|([1-4])|?([1-4]?)|?([1-4]?)|?([1-4]?)|', '|%1%3%4%5%6%7|%2|')
		titleparts = mw.ustring.gsub(titleparts, '|([lhtnCDLM]?)|?c|([1-4])|?([1-4]?)|?([1-4]?)|?([1-4]?)|', '|c%2%3%4%5|%1|')
		titleparts = mw.ustring.gsub(titleparts, '(|[%-~@%+])|([LRFGM]+|)', '%1%2')
		titleparts = mw.ustring.gsub(titleparts, '(|%-)([LRFGM]+|[A-ZÜ]+)', '%1|%2') -- split prefix L/F/M from parallel lines syntax
		titleparts = mw.ustring.gsub(titleparts, '|([~@])|([lrfgm])|?([lrfgm]*)|?([lrfgm]*)|?([lrfgm]*)|?([lrfgm]*)|?([lrfgm]*)|', '|%1%2%3%4%5%6|')
		titleparts = mw.ustring.gsub(titleparts, '|%(|([LRFGM]*)|?([lrfgm]*)|?([lrfgm]*)|?([lrfgm]*)|?([lrfgm]*)|?([lrfgm]*)|%)|', '|(%1%2%3%4%5%6)|')
		titleparts = mw.ustring.gsub(titleparts, '^|([ufg])|', '|#%1|')
		titleparts = mw.ustring.gsub(titleparts, '(|[%-%+]|[^A-ZÜ%-%+]+|)([fg]|.*|[A-ZÜ][A-ZÜ])', '%1#%2')
		titleparts = mw.ustring.gsub(titleparts, '(|[%-%+]|[^A-ZÜ%-%+]+|)([fg]|.*|[A-ZÜ][A-ZÜ])', '%1#%2')
		titleparts = mw.ustring.gsub(titleparts, '(|[%-%+]|[^A-ZÜ%-%+]+|)([fg]|[A-ZÜ][A-ZÜ])', '%1#%2')
		titleparts = mw.ustring.gsub(titleparts, '(|[%-%+]|[^A-ZÜ%-%+]+|)([fg]|[A-ZÜ][A-ZÜ])', '%1#%2')
		titleparts = mw.ustring.gsub(titleparts, '^(|[^A-ZÜ%-%+]+|)([fg]|.*|[A-ZÜ][A-ZÜ])', '%1#%2')
		titleparts = mw.ustring.gsub(titleparts, '^(|[^A-ZÜ%-%+]+|)([fg]|.*|[A-ZÜ][A-ZÜ])', '%1#%2')
		titleparts = mw.ustring.gsub(titleparts, '^(|[^A-ZÜ%-%+]+|)([fg]|[A-ZÜ][A-ZÜ])', '%1#%2')
		titleparts = mw.ustring.gsub(titleparts, '^(|[^A-ZÜ%-%+]+|)([fg]|[A-ZÜ][A-ZÜ])', '%1#%2')
		if mw.ustring.match(titleparts, '|m|') then
			category['set mixed'] = true
			titleparts = mw.ustring.gsub(titleparts, '|m|', '|')
		end
		titleparts = mw.ustring.gsub(titleparts, '|([%-%+])|([fg]|.*|[A-ZÜ][A-ZÜ])', '|%1|#%2')
		titleparts = mw.ustring.gsub(titleparts, '|([%-%+])|([fg]|[A-ZÜ][A-ZÜ])', '|%1|#%2')
		titleparts = mw.ustring.gsub(titleparts, '|([^%-]+)|([htCD])|([1-4lrfgm])|([ae])|$', '|%2|%1|%3|%4|')
		titleparts = mw.ustring.gsub(titleparts, '|([^%-]+)|([htCD])|([1-4lrfgm])|([ae])|([^A-Z%-]+)|', '|%2|%1|%3|%5|%4|')
		titleparts = mw.ustring.gsub(titleparts, '|%+|([LRFG]+)|', '|+%1|')
		-- remove q from KRZ because it will trip up other regexes
		titleparts = mw.ustring.gsub(titleparts, 'KRZ|q|', 'KRZ|')
		titleparts = mw.ustring.gsub(titleparts, '|ABZ|([gq]?)|?([1-4lrfgm%+])|?([1-4lrfgm%+]?)|?([1-4lrfgm%+]?)|?([1-4lrfgm%+]?)|?([1-4lrfgm%+]?)|?([1-4lrfgm%+]?)|?([1-4lrfgm%+]?)|', '|ABZ|%1%2%3%4%5%6%7%8|')
		titleparts = mw.ustring.gsub(titleparts, '|WYE|([gq]?)|?([1-4lrfgm%+])|?([1-4lrfgm%+]?)|?([1-4lrfgm%+]?)|?([1-4lrfgm%+]?)|', '|WYE|%1%2%3%4%5|')
		titleparts = mw.ustring.gsub(titleparts, '|(SHI%d)|(g?)|?([lr%+])|?([lr%+]?)|?([lr%+]?)|?([lr%+]?)|?([lr%+]?)|?(q?)|', '|%1|%2%3%4%5%6%7%8|')
		titleparts = mw.ustring.gsub(titleparts, '|(SHI%d)|(g?[lr]?[lr]?)(q?)|', '|%1|%2+%3|')
		titleparts = mw.ustring.gsub(titleparts, '|SPL|([ae])|?([lr]?)|?([lr]?)|?(%+?)|?([lr]?)|?([lr]?)|?(%+?)|?([gq]?)|', '|SPL|%1%2%3%4%5%6%7%8|')
		titleparts = mw.ustring.gsub(titleparts, '|SPL|([ae])|g|(%+?)|?([lr]?)|?([lr]?)|?(q?)|', '|SPL|%1g%2%3%4%5|')
		titleparts = mw.ustring.gsub(titleparts, '|KRW|(g?)|?([lr%+])|?([lr%+]?)|?([lr%+]?)|?([lr%+]?)|?([lr%+]?)|?(q?)|', '|KRW|%1%2%3%4%5%6%7|')
		titleparts = mw.ustring.gsub(titleparts, '|ÜWB|([lr]?)|?(%+?)|?([lr]?)|?([lr]?)|?(q?)|', '|ÜWB|%1%2%3%4%5|')
		titleparts = mw.ustring.gsub(titleparts, '|ÜWB|([lr]?)(q?)|', '|ÜWB|%1+%2|')
		titleparts = mw.ustring.gsub(titleparts, '|([A-Z][A-Z%+]+[1-3]?)|([1-4lrfgm])|?([ou]?)|?([htCD]?)|%+|([1-4lrfgm])|?([ou]?)|', '|%1|%2+%5|%3%6|%4|')
		titleparts = mw.ustring.gsub(titleparts, '|([A-Z][A-Z%+]+[1-3]?)|%+|([1-4lrfgm])|?([ou]?)|', '|%1|+%2|%3|')
		titleparts = mw.ustring.gsub(titleparts, '|([A-Z][A-Z%+]+[1-3]?)|([1-4lrfg])|?([ou]?)|', '|%1|%2+|%3|')
		titleparts = mw.ustring.gsub(titleparts, '(|[%-%+]|[^A-ZÜ%-%+]+|)(u)|', '%1#%2|')
		titleparts = mw.ustring.gsub(titleparts, '(|[%-%+]|[^A-ZÜ%-%+]+|)(u)|', '%1#%2|')
		titleparts = mw.ustring.gsub(titleparts, '^(|[^A-ZÜ%-%+]+|)(u)|', '%1#%2|')
		titleparts = mw.ustring.gsub(titleparts, '^(|[^A-ZÜ%-%+]+|)(u)|', '%1#%2|')
		titleparts = mw.ustring.gsub(titleparts, '|([%-%+])|(u)|', '|%1|#%2|')
		-- Add handling for icon names containing more than one root
		-- Affixes always treated the same
		for k, v in pairs({
			['|c|'] = 'quarter-width',
			['|d|'] = 'half-width',
			['|cd|'] = 'three-quarter-width',
			['|b|'] = 'double-width',
			['|v|'] = 'parallel lines',
			['|%-|'] = 'parallel lines',
			['|l|'] = 'legende',
			['|h|'] = 'elevated',
			['|t|'] = 'tunnel',
			['|p|'] = 'express',
			['|n|'] = 'narrow',
			['|C|'] = 'cutting',
			['|D|'] = 'embankment',
			['|k|'] = 'k',
			['|3|'] = '3',
			['|L|'] = 'interruption',
			['|M|'] = 'mask',
			['|T|'] = 'crossing',
			['|[%+%-]?k[1-4][1-4]?[1-4]?[1-4]?|'] = 'k',
			['|[%+%-]?[ck][1-4][1-4]?[1-4]?[1-4]?|'] = 'corner',
			['|#u|'] = 'set u',
			['|#f|'] = 'set f',
			['|#g|'] = 'set g',
			['|[cdbswv%|]+|e?|?#[ufg]|'] = 'set mixed',
			['|%-|e?|?#[ufg]|'] = 'set mixed',
			['|X|'] = 'interchange',
			['|[X]|'] = 'CPIC',
			['|ABZ|'] = 'junction',
			['|WYE|'] = 'wye',
			['|BHF|'] = 'BHF',
			['|HST|'] = 'HST',
			['|DST|'] = 'DST',
			['|BST|'] = 'BST',
			['|INT|'] = 'INT',
			['|ACC|'] = 'ACC',
			['|INTACC|'] = 'INTACC',
			['|HSTACC|'] = 'HSTACC',
			['|SBHF|'] = 'SBHF',
			['|S%+BHF|'] = 'S+BHF',
			['|SHST|'] = 'SHST',
			['|S%+HST|'] = 'S+HST',
			['|HUB|'] = 'hub',
			['|CONT|'] = 'continuation',
			['|ENDE|'] = 'line endings',
			['|GRZ|'] = 'border',
			['|KR[XZ]|'] = 'crossing',
			['|KRX|'] = 'uw',
			['|KMW|'] = 'milepost',
			['|SPL|'] = 'split',
			['|SHI%d|'] = 'shift',
			['|SHI1|'] = 'one quarter',
			['|SHI2|'] = 'two quarters',
			['|SHI3|'] = 'three quarters',
			['|SHI4|'] = 'four quarters',
			['|KRW|'] = 'krw',
			['|SHI5|'] = 'five quarters',
			['|SHI6|'] = 'six quarters',
			['|SHI7|'] = 'seven quarters',
			['|SHI8|'] = 'eight quarters',
			['|WSL|'] = 'loop',
			['|ZOLL|'] = 'customs',
			['|HUB|'] = 'hub',
			['|%-[LMR]+|'] = 'interchange',
			['|MASK|'] = 'mask',
			['|[uo]+|'] = 'crossing',
			
		}) do
			if mw.ustring.match(titleparts, k) then category[v] = true end
		end
		if category['set mixed'] and category['set u'] and not category['other colors'] then category['set u'] = nil end
		if category['legende'] and (category['elevated'] or category['cutting'] or category['embankment']) then
			category['legende'] = nil
			category['formations'] = true
			if category['BHF'] then
				category['BHF'] = nil
				category['stations and stops'] = true
			end
		end
		if category['3'] then
			category['parallel lines'] = nil
			if not category['junction'] then category['curve'] = true end
		elseif category['k'] then
			if not (category['junction'] or category['wye']) then
				tmp_match = mw.ustring.match(titleparts, '|[A-Z][A-Z%+]+[1-3]?|([1-4lrfgm]?%+[1-4lrfgm])|') or mw.ustring.match(titleparts, '|[A-Z][A-Z%+]+[1-3]?|([1-4lrfgm]%+[1-4lrfgm]?)|') or ''
				if mw.ustring.match(tmp_match, '[1-4]') then category['curve'] = true end
			end
		elseif category['shift'] then
			tmp_match = mw.ustring.match(titleparts, '|SHI%d|(g?[lr]?[lr]?%+?[lr]?[lr]?q?)|') or ''
			if mw.ustring.match(tmp_match, 'g') or mw.ustring.match(tmp_match, 'lr') or mw.ustring.match(tmp_match, 'rl') then
				category['junction'] = true
				if mw.ustring.match(tmp_match, 'l.*%+.*l') or mw.ustring.match(tmp_match, 'r.*%+.*r') then
					category['crossing'] = true
				end
				if category['one quarter'] and (mw.ustring.match(tmp_match, 'lr') or mw.ustring.match(tmp_match, 'rl')) then
					category['split'], category['junction'] = true, nil
				end
			elseif mw.ustring.match(tmp_match, 'l.*%+.*l') or mw.ustring.match(tmp_match, 'r.*%+.*r') then
				category['crossing'] = true
				category['curve'] = true
			elseif not category['corner'] then
				category['curve'] = true
			end
		elseif category['split'] then
			tmp_match = mw.ustring.match(titleparts, '|SPL|([^|]+)|') or ''
			if mw.ustring.match(tmp_match, '[1-4]') then
				category['uw'] = true
			elseif mw.ustring.match(tmp_match, '^a[lr]%+q') or mw.ustring.match(tmp_match, '^a%+[lr]%+g') or mw.ustring.match(tmp_match, '^e[lr]%+g') or mw.ustring.match(tmp_match, '^e%+[lr]%+q') or mw.ustring.match(tmp_match, '^[ae]g%+?[lr][lr]?q?$') or  mw.ustring.match(tmp_match, '^[ae]q?$') then
				category['shift'] = true
				category['one quarter'] = true
			end
		elseif mw.ustring.match(titleparts, '|ÜWB|') then
			if category['parallel lines'] then
				tmp_match = mw.ustring.match(titleparts, '|ÜWB|[lr]?%+([lr]?[lr]?)q?|') or ''
				if mw.ustring.match(tmp_match, '[lr]') then
					category['junction'] = true
				else
					category['curve'] = true
				end
				category['shift'] = true
				tmp_match = mw.ustring.len(mw.ustring.gsub((mw.ustring.match(titleparts, '|v|[v|?]*') or ''), '|', ''))
				if tmp_match > 1 then
					if tmp_match == 2 then
						category['four quarters'] = true
					elseif tmp_match == 3 then
						category['six quarters'] = true
					elseif tmp_match == 4 then
						category['eight quarters'] = true
					end
				else
					category['two quarters'] = true
				end
				category['crossing'] = true
			else
				category['track change'] = true
			end
		else
			if category['junction'] or category['wye'] then -- add other junctions?
				if mw.ustring.match(titleparts, '|[A-ZÜ]+|[^%-ck]*[1234]') then
					category['uw'] = true
					if mw.ustring.match(titleparts, '|u+|') then
						category['corner'] = true
					end
					if category['parallel lines'] and not (mw.ustring.match(titleparts, '|%-|')) then
						category['double'] = true
					end
				elseif category['junction'] and not category['wye'] then
					tmp_match = mw.ustring.match(titleparts, '|[A-Z][A-Z%+]+[1-3]?|([gq][lr]?[lr]?%+?[lr]?[lr]?)|') or ''
					if tmp_match ~= '' then
						if tmp_match == 'gl+l' or tmp_match == 'gr+r' or tmp_match == 'qlr' or tmp_match == 'qrl' or tmp_match == 'q+lr' or tmp_match == 'q+rl' then
							category['wye'], category['junction'] = true, nil
						end
					end
				end
			else
				tmp_match = mw.ustring.match(titleparts, '|[A-Z][A-Z%+]+[1-3]?|([1-4lrfgm]?%+[1-4lrfgm])|') or mw.ustring.match(titleparts, '|[A-Z][A-Z%+]+[1-3]?|([1-4lrfgm]%+[1-4lrfgm]?)|') or ''
				if mw.ustring.match(tmp_match, '[1-4]') then
					category['uw'] = true
					if not ((category['half-width'] and mw.ustring.match(tmp_match, 'm'))
						or ((not mw.ustring.match(titleparts, '^[ufgelhatpnk|]*|[ocdbsw]')) and (tmp_match == '3+1' or tmp_match == '2+4' or tmp_match == '1+3' or tmp_match == '4+2'))
						or mw.ustring.match(titleparts, '|K|[^|]+|[1-4]%+||$') or mw.ustring.match(titleparts, '|K|[^|]+|[1-4]%+||[~@][fglmrFGLMR]')
						or mw.ustring.match(titleparts, '|CONT|[1-4]%+||$') or mw.ustring.match(titleparts, '|CONT|[1-4]%+||[~@][fglmrFGLMR]')
						or mw.ustring.match(titleparts, '|ENDE|[1-4]%+||$') or mw.ustring.match(titleparts, '|ENDE|[1-4]%+||[~@][fglmrFGLMR]')) then
						category['curve'] = true
						if category['parallel lines'] and not (mw.ustring.match(titleparts, '|%-|')) then
							category['double'] = true
						end
					end
					if mw.ustring.match(titleparts, '|u+|') then
						category['corner'] = true
					end
				elseif mw.ustring.match(tmp_match, '^[fg]?%+[lr]$') or mw.ustring.match(tmp_match, '^[lr]%+[fg]?$') or mw.ustring.match(tmp_match, '^[lr]%+[lr]$') then
					category['curve'] = true
				elseif mw.ustring.match(tmp_match, '^[fg]%+$') and not (category['continuation'] or category['line endings']) then
					category['direction'] = true
				end
			end
		end
		if category['corner'] and not (category['3'] or category['k'] or category['shift'] or category['krw']) then category['uw'] = true end
		if category['tunnel'] and not mw.ustring.match(titleparts, '|K|') and not ((category['continuation'] or category['line endings']) and not category['uw']) and not category['split'] then
			if mw.ustring.match(titleparts, '[A-ZÜ][A-ZÜ]+[^%-%+~]*|[ae][fgae]?|') then category['portal'] = true end
		end
		
		if category['crossing'] and category['junction'] then
			category['crossing+junction'] = true
			category['crossing'], category['junction'] = nil, nil
		end
		if category['corner'] then
			if category['curve'] then
				category['curve+corner'] = true
				category['curve'], category['corner'] = nil, nil
			elseif mw.ustring.match(titleparts, '|%+[ck][1-4][1-4]?[1-4]?[1-4]?|') then
				category['straight+corner'] = true
				category['corner'] = nil
			end
		end
		
		local order = {
			'Category:BSicon',
			
			'railway',
			'road–rail',
			'road',
			'water',
			
			'set u',
			'set f',
			'set g',
			'set mixed',
			'set azure',
			'set black',
			'set blue',
			'set brown',
			'set carrot',
			'set cerulean',
			'set cyan',
			'set deepsky',
			'set denim',
			'set fuchsia',
			'set golden',
			'set green',
			'set grey',
			'set jade',
			'set lavender',
			'set lime',
			'set maroon',
			'set ochre',
			'set olive',
			'set orange',
			'set pink',
			'set purple',
			'set red',
			'set ruby',
			'set saffron',
			'set sky',
			'set steel',
			'set teal',
			'set violet',
			'set white',
			'set yellow',
			'mixed',
			'set u-f',
			'set u-g',
			'set black-orange',
			'set green-yellow',
			'set saffron-azure',
			'set yellow-blue',
			'generic road',
			'RP4',
			'RP2',
			'RP1',
			'RD',
			'RA',
			'RB',
			'RE',
			'RG',
			'RM',
			'RR',
			'RY',
			
			'quarter-width',
			'half-width',
			'three-quarter-width',
			'double-width',
			'parallel lines',
			
			'legende',
			'formations',
			'elevated',
			'tunnel',
			'portal',
			'cutting',
			'embankment',
			'interruption',
			'narrow',
			'3',
			'k',
			'uw',
			'double',
			'shift',
			'one quarter',
			'two quarters',
			'three quarters',
			'four quarters',
			'krw', -- deprecated?
			'five quarters',
			'six quarters',
			'seven quarters',
			'eight quarters',
			
			'crossing',
			'crossing+junction',
			'crossing+corner',
			'junction',
			'straight+junction',
			'split',
			'wye',
			'line endings',
			'continuation',
			'loop',
			'border', -- determine exact order
			'customs', -- determine exact order
			'milepost',
			'stations and stops',
			'suburban',
			'BHF',
			'HST',
			'DST',
			'BST',
			'INT',
			'ACC',
			'SBHF',
			'SHST',
			'S+BHF',
			'S+HST',
			'HSTACC',
			'INTACC',
			
			'hub',
			'express',
			'interchange',
			'CPIC',
			'direction',
			'curve',
			'corner',
			'straight+curve',
			'straight+corner',
			'curve+corner',
		}
		for k, v in ipairs(order) do
			if category[v] then table.insert(result, v) end
		end
		result = table.concat(result, '/')
		-- return (mw.dumpObject(result) or '')..(mw.dumpObject(titleparts) or '')..(mw.dumpObject(test) or '')
	end
	return result
end

return p