Module:BSicon/sandbox
Jump to navigation
Jump to search
Lua
CodeDiscussionEditHistoryLinksLink count Subpages:DocumentationTestsResultsSandboxLive code All modules
Code
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')
-- permutations of category titles that do not have the '/set ..' titleparts
-- in lexicographical order ('u','g','f','mixed' different, but also specific)
-- are filled with a category redirect if created with {{BS-category}} content
function sort_set_titleparts(cat_title, oc_cat)
local _ocols = { ins = table.insert, srt = table.sort, rm = table.remove }
cat_title:gsub("/set ([^/]+)", function (c) _ocols:ins(c) end)
_ocols:srt(function (c1, c2) return c2=="mixed"
or c2=="f" and not ("f:mixed"):find(c1,1,true)
or ("g:f"):find(c2,1,true) and not ("g:f:mixed"):find(c1,1,true)
or ("u:g:f"):find(c2,1,true) and not ("u:g:f:mixed"):find(c1,1,true)
or not ("u:g:f:mixed"):find(c2,1,true) and
not ("u:g:f:mixed"):find(c1,1,true) and c1 < c2
end
)
local um = not oc_cat and #_ocols == 2 and _ocols[1]=="u" and _ocols[2]=="mixed"
return cat_title:gsub("(/set )[^/]+", function (s) local c = _ocols:rm(1)
return um and #_ocols > 0 and '' or s..c
end)
end
function redirect(title, oc_cat)
local t = sort_set_titleparts(title, oc_cat)
local n, msg1
t, n = mw.ustring.gsub(t, '/railway(.*/level crossing)', '/road–rail%1')
if n > 0 then
msg1 = function() return '\n' .. mw.getCurrentFrame()
:expandTemplate{ title="notice", args = {
"BSicons depicting non-road, same-leveled railway crossings usually categorize into simply '../crossing', " ..
"while the term 'level crossing' binds with the more specific, but also more frequent ''road–rail level crossings'' " ..
"for the purpose of categorizing within [[:Category:BSicon/road–rail]] subtree." }
}
end
end
-- see 'level crossing' entry in roots table below, no need to have the term twice in a title
t = t:find('/level crossing',1,true) and mw.ustring.gsub(t..'/', '/crossing/', '/'):gsub('/$', '') or t
-- sometimes 'one quarter' cats are created as 'one quarters', catch and redirect..
t = mw.ustring.gsub(t, '(/one quarter)s', '%1')
if title ~= t then
return mw.getCurrentFrame()
:expandTemplate{ title="category redirect", args = { t } }
.. (msg1 and msg1() or '')
end
end
-- used to categorize BSicon graphics, it populates the category tree and
-- fills a category header with some basic information, categories by nature
-- can be monotone - to display icons from different cats on the same page
-- either BSicon/Catalogue or other _gallery_ pages can be used
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
local oc_cat = title:match("/other[^/]*colors$")
local redir = redirect(title, oc_cat)
if redir then return redir end
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 rgb_cell(x)
x = tostring(x) or 'BE2D2C'
return '<td class="rgb '..((contrast{x, 'FFF'} < 4.5) and 'light' or 'dark')..'" style="background-color: #'..x..'"><kbd>#'..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 rgb_cell(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/',
['BHF#DST'] = 'stations and stops/',
['BHF#HST'] = 'stations and stops/',
['HST#BST'] = 'stations and stops/',
RD = 'generic road/',
RP1 = 'generic road/',
RP2 = 'generic road/',
RP4 = 'generic road/',
fork = 'junction/',
wye = 'junction/',
split = 'junction/',
formations = 'legende/',
['level crossing'] = 'crossing/',
['straight#curve'] = 'curve/',
['straight#shift'] = 'shift/',
['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',
['BHF#DST'] = 'BHF/DST',
['BHF#HST'] = 'BHF/HST',
['HST#BST'] = 'HST/BST',
['curve#corner'] = 'curve/corner',
['crossing#corner'] = 'crossing/corner',
['crossing#junction'] = 'crossing/junction',
['crossing#wye'] = 'crossing/wye',
['crossing#split'] = 'crossing/split',
['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, 'water') then
result = result..'\n<tr><td>water'..rgb_cell('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'..rgb_cell('80A080')
legende_color = true
end
if mw.ustring.match(title, 'line endings') then
result = result..'\n<tr><td>line ending (open)'..rgb_cell('000')..'<tr><td>line ending (closed)'..rgb_cell('AAA')
legende_color = true
end
if mw.ustring.match(title, 'border') then
result = result..'\n<tr><td>border (active)'..rgb_cell('000')..'<tr><td>border (inactive)'..rgb_cell('AAA')
legende_color = true
end
if mw.ustring.match(title, 'platform') then
result = result..'\n<tr><td>platform (open)'..rgb_cell('888')..'<tr><td>platform (closed)'..rgb_cell('CCC')
legende_color = true
end
if mw.ustring.match(title, 'mask') then
result = result..'\n<tr><td>mask'..rgb_cell('F9F9F9')
legende_color = true
end
if mw.ustring.match(title, 'INT') then
result = result..'\n<tr><td>INT (open)'..rgb_cell('000')..'<tr><td>INT (closed)'..rgb_cell('AAA')
legende_color = true
end
if mw.ustring.match(title, 'ACC') then
result = result..'\n<tr><td>ACC (open)'..rgb_cell('034EA2')..'<tr><td>ACC (closed)'..rgb_cell('6592C5')
legende_color = true
end
if mw.ustring.match(title, 'CPIC') then
result = result..'\n<tr><td rowspan="2">cross-platform<br/>interchange'..rgb_cell('000')..'<tr>'..rgb_cell('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 (open)'..rgb_cell('006E34')..'<tr><td>S-Bahn (closed)'..rgb_cell('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') or mw.ustring.match(title, 'suburban') then
result = result..'\n<tr><td>fill'..rgb_cell('FFF')
end
legende_color = legende_color and mw.ustring.match(title, 'legende')
local r = { ins = table.insert, rm = table.remove }
r:ins(mw.getCurrentFrame():expandTemplate{ title = 'BS-set' })
if oc_cat then
r:ins("<div style=\"clear:right;float:right;padding-left:1.5em\">\n")
r:ins(mw.getCurrentFrame():expandTemplate{ title = 'Collapse', args = {
"\n" .. mw.getCurrentFrame():expandTemplate{ title = 'BS-colorlist', args = {} },
title = 'BSicon color list '
}
})
r:ins("\n</div>\n")
end
r:ins("These [[BSicon]]s are to be used with '''route diagram templates'''. ")
r:ins("For an overview, see [[:Category:BSicon]].\n")
r:ins('<table class="wikitable"><tr><th>Colour<th>RGB hex triplet')
title_string = mw.clone(title)..'/'
title = mw.text.split(title, '/')
if title[1] ~= 'BSicon' then return '' end
if title[2] == 'railway' and (not used_roots['formations']) and (not legende_color) then
-- 'set mixed' assumes {'', 'u'} combination, while a 2nd color (cp. "/set mixed/set azure") may override 'u'
-- if 'set mixed' follows, then '' (default color) is combined with previous one (cp. "/set azure/set mixed")
-- {{BS-category}} produces color table for line(s) as long as at least one set spec is in category title
-- caveat: the interpretation of 'set mixed' is closely oriented on its use in icon title codes, which by definition
-- mixes default color with color u ('/set mixed' and '/set u/set mixed' are ident in that respect), while
-- the term 'set mixed' within BSicon scope implies 'mixed colors', not all 'mixed colors' icons are in 'set mixed'
local i = ((title[3] ~= 'road') and (title[3] ~= 'water')) and 3 or 4
local ci = 1
while i <= #title do
local c = title[i] and mw.ustring.match(title[i], '^set (.+)$')
if c then
for _c in mw.text.gsplit(c, '–') do
if _c == 'mixed'
then color[ci] = '' if ci == 1 and not oc_cat then color[2] = 'u' end
else color[ci] = _c
end
ci = ci + 1
end
end
i = i + 1
end
r:ins('\n<tr><td rowspan="') r:ins(tostring(#color))
r:ins('">open line'..(#color>1 and 's' or ''))
for _, c in ipairs(color) do r:ins(hex(c)) r:ins('<tr>') end r:rm()
r:ins('\n<tr><td rowspan="') r:ins(tostring(#color))
r:ins('">closed line'..(#color>1 and 's' or ''))
for _, c in ipairs(color) do r:ins(hex(c, true)) r:ins('<tr>') end r:rm()
if title[3] == 'road' then
local generic = { "RD", "RP1", "RP2", "RP4" }
local classed = { "RA", "RM", "RR", "RB", "RG", "RE", "RY" }
local _g, _c = { }, { }
for _, v in ipairs(title) do
for _, vg in ipairs(generic) do if v == vg then _g[#_g+1] = v end end
for _, vc in ipairs(classed) do if v == vc then _c[#_c+1] = v end end
if v == "set f" then
for i, vs in ipairs(r) do
if tostring(vs):find(' line',1,true) then
r[i] = r[i] .. ' or footpath'
end
end
end
if v:sub(1,7) == "generic" then
classed, _c = { }, { }
end
end
if #_g > 0 or #_c > 0 then generic, classed = _g, _c end
if #generic > 0 then
r:ins('\n<tr><td>generic road<td>')
for _, v in ipairs(generic) do r:ins('[[File:BSicon '..v:gsub('RD','RD1')..'.svg|x30px| ]] ') end
end
if #classed > 0 then
r:ins('\n<tr><td>classed road<td>')
for _, v in ipairs(classed) do r:ins('[[File:BSicon '..v..'.svg|x30px| ]] ') end
end
for i, v in ipairs(r) do -- adjust header to accomodate road rows
if v:find('<table',1,true) then
r[i] = r[i]:gsub('<th>([^<]*)<th>([^<]*)', '<th>Object<th>%2 or prototype'
..((#generic + #classed)>1 and 's' or ''))
end
end
end
end
if mw.ustring.match(result, '<t[rd]>') then r:ins(result) end
if r[#r]:find("<th>",1,true) -- if only html table skeleton, remove it
then for i = 1, #r do if r:rm():find("<table>",1,true) then i = #r end end
else r:ins("</table>")
end
if oc_cat then
r:ins("\nThis category is on categories holding icons ")
r:ins(title[#title]:find('mixed',1,true) -- if '/other mixed colors' ..
and 'combining the set color(s) given above with an additional one'
or 'replacing the set color given above with one'
)
r:ins(" from [[:Template:BS-colorlist|BSicon color list]].")
end
local set_subkey = '0'
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 not ((title_string or ''):find('/level crossing',1,true) and title[k] == 'road' and title[k-1] and title[k-1] == 'railway')
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 (title[k] == 'shift' and mw.ustring.match((title[k+1] or ''), ' quarters?$'))
and not (title[k] == 'stations and stops' and (title[k+1] == 'interchange' or title[k+1] == 'terminus' or title[k+1] == 'limited'))
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 ((title_string or ''):find('/straight#shift',1,true) or (title_string or ''):find('/double/',1,true)))
and not (title[k] == 'tunnel' and (title[k+1] == 'portal' or title[k+2] == 'portal'))
and not (oc_cat and title[#title] ~= v) 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
tmp = mw.ustring.gsub(tmp, '/junction/crossing/', '/crossing+junction/')
tmp = mw.ustring.gsub(tmp, '/$', '')
if title2 ~= tmp then
local tmpsub, omctitle = '', '/other mixed colors'
local c, n = mw.ustring.gsub(title[k], '^set ([ufg])$', '!++++'..set_subkey..'%1')
if n < 1 then c, n = mw.ustring.gsub(c, '^set mixed', '!+++'..set_subkey) end
if n < 1 then c, n = mw.ustring.gsub(c, '^set ex', '!+'..set_subkey) end
if n < 1 then c, n = mw.ustring.gsub(c, '^set ', '!++'..set_subkey) end
if n > 0 then
set_subkey = string.char(set_subkey:byte() + 1)
if title[1] == 'railway' and title[k] ~= 'set u' and title[k] ~= 'set mixed' then
tmpsub = tmp:find('/set ',1,true) and omctitle or '/other colors'
end
end
if n < 1 then c, n = mw.ustring.gsub(c, '^other.*colors$', '!,other') end
if n < 1 then c, n = mw.ustring.gsub(c, '^railway$', '!'..'%0') end
if n < 1 then c, n = mw.ustring.gsub(c, '^R([A-Z0-9]+)$', '$'..'%1') end
if n < 1 then c, n = mw.ustring.gsub(c, '^(generic) road$', '$'..'%1') end
if n < 1 then c, n = mw.ustring.gsub(c, '^road$', '$'..'%0') end
if n < 1 then c, n = mw.ustring.gsub(c, '^(.*width)$', '*'..'%1') end
if n < 1 then c, n = mw.ustring.gsub(c, '^parallel lines$', '*'..'%0') end
if n < 1 then c, n = mw.ustring.gsub(c, '^([a-z]+) quarters?$',
{ one=1, two=2, three=3, four=4, five=5, six=6, seven=7, eight=8 })
end
categories = categories..'\n[[Category:'..tmp..tmpsub..'|'..c..']]'
if oc_cat then
c, n = categories, 0
for _, v in ipairs(color) do
if v=='u' then n = n + 1 end
if v=='' then n = n + 2 end
end
if n > 2 then
categories = c:gsub('/set u([^|]*)', '%1'..omctitle)
.. c:gsub('/set mixed([^|]*)', '%1'..omctitle)
elseif n == 1 then
categories = c .. c:gsub('/set u', '/set mixed')
elseif n == 2 then
if #color == 1 then
r:ins("<br />It also contains the (indirect) categories on mixing non-default set colors with another one from this list.")
end
elseif n == 0 then
if #color == 1 then
categories = '[[Category:'..tmp:gsub("/set "..color[1], "/set mixed")..omctitle..'|'
.. (#color[1] < 2 and '@' or (color[1]:sub(1,2) == 'ex' and '\\' or '')) .. color[1]:upper()
..']]\n' .. c
end
end
end
end
end
end
categories = mw.ustring.gsub(categories, '#', '+')
categories = mw.ustring.gsub(categories, '_', '-')
return table.concat(r)..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/KRX because it will trip up other regexes
titleparts = mw.ustring.gsub(titleparts, '(KR[XZ])|q|', '%1|')
titleparts = mw.ustring.gsub(titleparts, '|([CDW])ABZ|', '|%1|ABZ|')
titleparts = mw.ustring.gsub(titleparts, '|([CDW])WYE|', '|%1|WYE|')
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, '(KR[ZX])([CDLMW])|', '%1|%2|')
titleparts = mw.ustring.gsub(titleparts, '|([CDW])(KR[ZX])|', '|%1|%2|')
titleparts = mw.ustring.gsub(titleparts, '|([CDW])STR|', '|%1|STR|')
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|'] = 'limited',
['|n|'] = 'narrow',
['|C|'] = 'cutting',
['|D|'] = 'embankment',
['|k|'] = 'k',
['|3|'] = '3',
['|L|'] = 'interruption',
['|M|'] = 'mask',
['|T|'] = 'crossing',
['|W|'] = 'water',
['|[%+%-]?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',
['|K|X|'] = 'terminus',
['|K|BHF|'] = 'terminus',
['|K|HST|'] = 'terminus',
['|K|DST|'] = 'terminus',
['|K|BST|'] = 'terminus',
['|K|INT|'] = 'terminus',
['|K|ACC|'] = 'terminus',
['|K|INTACC|'] = 'terminus',
['|K|HSTACC|'] = 'terminus',
['|K|SBHF|'] = 'terminus',
['|K|S%+BHF|'] = 'terminus',
['|K|SHST|'] = 'terminus',
['|K|S%+HST|'] = 'terminus',
['|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',
['|WASSER|'] = 'water',
['|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'] or category['HST'] then
category['BHF'] = nil
category['HST'] = 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
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+|') and not (mw.ustring.match(titleparts, '|KR[XZ]|') or mw.ustring.match(titleparts, '|T|')) 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',
'mask',
'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',
'limited',
'interchange',
'CPIC',
'terminus',
'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 '').."\n\n"..(mw.dumpObject(titleparts) or '').."\n\n"..(mw.dumpObject(test) or '')
end
return result
end
return p