Module:BSicon
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 cnvdig = {
one=1,
two=2,
three=3,
four=4,
five=5,
six=6,
seven=7,
eight=8,
nine=9,
}
local used_compounds = {}
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 roots = {
formations = 'legende/',
BHF = 'stations/',
HST = 'stations/',
DST = 'stations/',
BST = 'stations/',
INT = 'stations/',
ACC = 'stations/',
INTACC = 'stations/',
HSTACC = 'stations/',
suburban = 'stations/',
SBHF = 'stations/',
SHST = 'stations/',
['S#BHF'] = 'stations/',
['S#HST'] = 'stations/',
['BHF#DST'] = 'stations/',
['BHF#HST'] = 'stations/',
['HST#BST'] = 'stations/',
RD = 'generic road/',
RP1 = 'generic road/',
RP2 = 'generic road/',
RP4 = 'generic road/',
['level crossing'] = 'crossing/',
fork = 'junction/',
split = 'junction/',
wye = 'junction/',
zigzag = 'junction/',
['crossing#split'] = 'junction/',
['crossing#wye'] = 'junction/',
['straight#junction'] = 'junction/',
['straight#corner'] = 'corner/',
['straight#curve'] = 'curve/',
['straight#shift'] = 'shift/',
}
for x, y in pairs(cnvdig) do
roots[x.." junction"..(y~=1 and 's' or '')] = 'junction/'
end
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)
used_compounds[k] = 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 (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 pairs(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 x, _ in pairs(used_compounds) do
tmp = mw.ustring.gsub(tmp, compounds[x], x)
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?$', cnvdig) end
if n < 1 then c, n = mw.ustring.gsub(c, '^([a-z]+) junctions?$', cnvdig) 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], "%0/set mixed")..'|!,other]]\n'
..'[[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, '|;|g', '|;|ABZ|g')
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, '|P([A-RT-ZÜ][A-ZÜ][A-ZÜ])', '|P|%1')
titleparts = mw.ustring.gsub(titleparts, '|PS([A-KM-ZÜ][A-ZÜ])', '|P|S%1')
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')
titleparts = mw.ustring.gsub(titleparts, '|X|PLT', '|X')
-- 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]*)|?([lrfgm]*)|?([lrfgm]*)|?([lrfgm]*)|%)|', '|(%1%2%3%4%5%6%7%8%9)|')
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|')
titleparts = mw.ustring.gsub(titleparts, '|;|', '|;|KRZ|')
titleparts = mw.ustring.gsub(titleparts, '|;|KRZ|([A-ZÜ][A-ZÜ])', '|;|%1')
titleparts = mw.ustring.gsub(titleparts, '|;|KRZ|([^%-]*|[A-ZÜ][A-ZÜ])', '|;|%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|')
local chunks = mw.text.split(mw.ustring.gsub(titleparts, "|([v^|]+)|%-|", "|%1|"), "|[%-%+;]|")
local titleparts_table = {}
for i, v in ipairs(chunks) do
if mw.ustring.match(v, "[^|]+") then
if i == 1 then
v = v .. "|"
elseif i == #chunks then
v = "|" .. v
else
v = "|" .. v .. "|"
end
table.insert(titleparts_table, v)
end
end
titleparts_categories = {}
if #titleparts_table < 2 then
titleparts_table = {titleparts}
else
if mw.ustring.match(titleparts, '|[v%-]|') then
category['parallel lines'] = true
end
end
for i1, v1 in ipairs(titleparts_table) do
titleparts_categories[i1] = {}
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',
['|P|'] = 'platform',
['|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',
['|PLT|'] = 'platform',
['|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(v1, k) then titleparts_categories[i1][v] = true end
end
if titleparts_categories[i1]['set mixed'] and titleparts_categories[i1]['set u'] and not titleparts_categories[i1]['other colors'] then titleparts_categories[i1]['set u'] = nil end
if titleparts_categories[i1]['legende'] and (titleparts_categories[i1]['elevated'] or titleparts_categories[i1]['cutting'] or titleparts_categories[i1]['embankment']) then
titleparts_categories[i1]['legende'] = nil
titleparts_categories[i1]['formations'] = true
if titleparts_categories[i1]['BHF'] or titleparts_categories[i1]['HST'] then
titleparts_categories[i1]['BHF'] = nil
titleparts_categories[i1]['HST'] = nil
titleparts_categories[i1]['stations'] = true
end
end
if titleparts_categories[i1]['3'] then
titleparts_categories[i1]['parallel lines'] = nil
if not titleparts_categories[i1]['junction'] then titleparts_categories[i1]['curve'] = true end
elseif titleparts_categories[i1]['k'] then
if not (titleparts_categories[i1]['junction'] or titleparts_categories[i1]['wye']) then
tmp_match = mw.ustring.match(v1, '|[A-Z][A-Z%+]+[1-3]?|([1-4lrfgm]?%+[1-4lrfgm])|') or mw.ustring.match(v1, '|[A-Z][A-Z%+]+[1-3]?|([1-4lrfgm]%+[1-4lrfgm]?)|') or ''
if mw.ustring.match(tmp_match, '[1-4]') then titleparts_categories[i1]['curve'] = true end
end
elseif titleparts_categories[i1]['shift'] then
tmp_match = mw.ustring.match(v1, '|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
titleparts_categories[i1]['junction'] = true
if mw.ustring.match(tmp_match, 'l.*%+.*l') or mw.ustring.match(tmp_match, 'r.*%+.*r') then
titleparts_categories[i1]['crossing'] = true
end
if titleparts_categories[i1]['one quarter'] and (mw.ustring.match(tmp_match, 'lr') or mw.ustring.match(tmp_match, 'rl')) then
titleparts_categories[i1]['split'], titleparts_categories[i1]['junction'] = true, nil
end
elseif mw.ustring.match(tmp_match, 'l.*%+.*l') or mw.ustring.match(tmp_match, 'r.*%+.*r') then
titleparts_categories[i1]['crossing'] = true
end
elseif titleparts_categories[i1]['split'] then
tmp_match = mw.ustring.match(v1, '|SPL|([^|]+)|') or ''
if mw.ustring.match(tmp_match, '[1-4]') then
titleparts_categories[i1]['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
titleparts_categories[i1]['shift'] = true
titleparts_categories[i1]['one quarter'] = true
end
elseif mw.ustring.match(v1, '|ÜWB|') then
if titleparts_categories[i1]['parallel lines'] then
tmp_match = mw.ustring.match(v1, '|ÜWB|[lr]?%+([lr]?[lr]?)q?|') or ''
if mw.ustring.match(tmp_match, '[lr]') then
titleparts_categories[i1]['junction'] = true
end
titleparts_categories[i1]['shift'] = true
tmp_match = mw.ustring.len(mw.ustring.gsub((mw.ustring.match(v1, '|v|[v|?]*') or ''), '|', ''))
if tmp_match > 1 then
if tmp_match == 2 then
titleparts_categories[i1]['four quarters'] = true
elseif tmp_match == 3 then
titleparts_categories[i1]['six quarters'] = true
elseif tmp_match == 4 then
titleparts_categories[i1]['eight quarters'] = true
end
else
titleparts_categories[i1]['two quarters'] = true
end
titleparts_categories[i1]['crossing'] = true
else
titleparts_categories[i1]['track change'] = true
end
else
if titleparts_categories[i1]['junction'] or titleparts_categories[i1]['wye'] then -- add other junctions?
if mw.ustring.match(v1, '|[A-ZÜ]+|[^%-ck]*[1234]') then
titleparts_categories[i1]['uw'] = true
if mw.ustring.match(v1, '|u+|') then
titleparts_categories[i1]['corner'] = true
end
if titleparts_categories[i1]['parallel lines'] and not (mw.ustring.match(v1, '|%-|')) then
titleparts_categories[i1]['double'] = true
end
elseif titleparts_categories[i1]['junction'] and not titleparts_categories[i1]['wye'] then
tmp_match = mw.ustring.match(v1, '|[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
titleparts_categories[i1]['wye'] = true
end
end
end
else
tmp_match = mw.ustring.match(v1, '|[A-Z][A-Z%+]+[1-3]?|([1-4lrfgm]?%+[1-4lrfgm])|') or mw.ustring.match(v1, '|[A-Z][A-Z%+]+[1-3]?|([1-4lrfgm]%+[1-4lrfgm]?)|') or ''
if mw.ustring.match(tmp_match, '[1-4]') then
titleparts_categories[i1]['uw'] = true
if not ((titleparts_categories[i1]['half-width'] and mw.ustring.match(tmp_match, 'm'))
or ((not mw.ustring.match(v1, '^[#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(v1, '|K|[^|]+|[1-4]%+||$') or mw.ustring.match(v1, '|K|[^|]+|[1-4]%+||[~@][fglmrFGLMR]')
or mw.ustring.match(v1, '|CONT|[1-4]%+||$') or mw.ustring.match(v1, '|CONT|[1-4]%+||[~@][fglmrFGLMR]')
or mw.ustring.match(v1, '|ENDE|[1-4]%+||$') or mw.ustring.match(v1, '|ENDE|[1-4]%+||[~@][fglmrFGLMR]')) then
titleparts_categories[i1]['curve'] = true
if titleparts_categories[i1]['parallel lines'] and not (mw.ustring.match(v1, '|%-|')) then
titleparts_categories[i1]['double'] = true
end
end
if mw.ustring.match(v1, '|u+|') and not (mw.ustring.match(v1, '|KR[XZ]|') or mw.ustring.match(v1, '|T|')) then
titleparts_categories[i1]['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
titleparts_categories[i1]['curve'] = true
elseif mw.ustring.match(tmp_match, '^[fg]%+$') and not (titleparts_categories[i1]['continuation'] or titleparts_categories[i1]['line endings']) then
titleparts_categories[i1]['direction'] = true
end
end
end
if titleparts_categories[i1]['corner'] and not (titleparts_categories[i1]['3'] or titleparts_categories[i1]['k'] or titleparts_categories[i1]['shift'] or titleparts_categories[i1]['krw']) then titleparts_categories[i1]['uw'] = true end
if titleparts_categories[i1]['tunnel'] and not mw.ustring.match(v1, '|K|') and not ((titleparts_categories[i1]['continuation'] or titleparts_categories[i1]['line endings']) and not titleparts_categories[i1]['uw']) and not titleparts_categories[i1]['split'] then
if mw.ustring.match(v1, '[A-ZÜ][A-ZÜ]+[^%-%+~]*|[ae][fgae]?|') then titleparts_categories[i1]['portal'] = true end
end
end
for i, v in ipairs(titleparts_categories) do
for k2, v2 in pairs(v) do
category[k2] = true
end
end
for i, v in pairs({
["BHF+DST"] = {"BHF", "DST"},
["BHF+HST"] = {"BHF", "HST"},
["HST+BST"] = {"HST", "BST"},
["wye"] = {"wye", "junction"},
["curve+corner"] = {"curve", "corner"},
["crossing+junction"] = {"crossing", "junction"},
["crossing+corner"] = {"crossing", "corner"},
["crossing+wye"] = {"crossing", "wye"},
}) do
if category[v[1]] and category[v[2]] then
category[i] = true
category[v[1]], category[v[2]] = nil, nil
end
end
if category['corner'] then
if 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 emerald',
'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',
'platform',
'stations',
'suburban',
'BHF',
'HST',
'DST',
'BST',
'INT',
'ACC',
'SBHF',
'SHST',
'S+BHF',
'S+HST',
'HSTACC',
'INTACC',
'BHF+DST',
'BHF+HST',
'HST+BST',
'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 '')..'<hr>'..(mw.dumpObject(titleparts) or '')..'<hr>'..(mw.dumpObject(titleparts_table) or '')
end
return result
end
return p