Module:Routemap

local i18n = { errors = { ["parameter-missing"] = "Missing parameter!", ["collapsible-block-not-closed"] = "Collapsible section not closed properly!", ["collapsible-block-not-open"] = "Missing start-Collapsible markup!", ["collapsible-block-empty"] = "Collapsible section must not be empty!", ["collapsible-block-no-first-row"] = "Invalid first row of collapsible section!", ["collapsible-block-no-replacement"] = "Invalid collapsible replacement row!", ["colspan-less-rows-than-set"] = "Invalid colspan set!", },	["error-categories"] = { default = '' },	html = { ["cell-icon-fmt"] = '\ ["cell-overlapicon-fmt"] = ' ', ["cell-icon-fmt-with-overlap"] = '\ ["cell-filler-fmt"] = '\n|style="width:8px"| ||style="width:4px; background-color:%s"| ||style="width:8px"|', ["cell-filler-empty-fmt"] = '\n|style="width:%s;min-width:%s"|',
 * BSicon_%s.svg',
 * %s BSicon_%s.svg',

["row-linfo4-fmt"] = '\ ["row-linfo3-fmt"] = ' %s ', ["row-rinfo3-fmt"] = ' %s ', ["row-rinfo4-fmt"] = '\
 * style="padding-right:3px;text-align:left;%s"| %s ',-- parameters:linfo4-width, linfo4
 * style="padding-left:3px;text-align:right;%s"| %s ',-- parameters:rinfo4-width, rinfo4

["row-general-fmt"] = '\
 * -%s\
 * colspan="%s" style="text-align:right;%s"|%s\
 * style="text-align:left;padding:0 %s;%s"| %s \
 * style="padding:0;background-color:%s"| \
 * style="text-align:right;padding:0 %s;%s"| %s \
 * colspan="%s" style="text-align:left;%s"|%s%s',-- parameters: linfo4-fmt, colspan-left, linfo3+2-width, linfo3+2, linfo1-pad, linfo1-width, linfo1, bg, cells, rinfo1-pad, rinfo1-width, rinfo1, colspan-right, rinfo2+3-width, rinfo2+3, rinfo4-fmt

["row-collapsible-begin-fmt"] = '\ {|class="%s%s" cellpadding="0" cellspacing="0" style="%s padding:0 !important;vertical-align:middle;margin:none !important;white-space:nowrap"',-- parameters: bg, "collapsible "/"mw-collapsible mw-", collapse-state, "float:right;" / ""
 * colspan="7" style="padding:0 !important;background-color:%s"|\
 * colspan="7" style="padding:0 !important;background-color:%s"|\

["row-collapsible-end-fmt"] = '\n|}',

["row-collapsible-left-button-width"] = '45px',-- 50px is the minimal width for [показать] / [скрыть] button. Use 40px for [show] / [hide] ["row-collapsible-left-button-fmt"] = '\n! style="padding-right:3px;min-width:%s;%s" |',--parameters: left-button-width, linfo4-width ["row-collapsible-left-linfo4+3+2-fmt"] = '\ ["row-collapsible-right-button-width"] = '45px',-- 72px is the minimal width for [развернуть] / [свернуть] button at 90%. Use 58px for [expand] / [collapse] ["row-collapsible-right-rinfo2+3+4-fmt"] = '\ ["row-collapsible-right-button-fmt"] = '\n| style="padding-left:3px;font-size:90%%;min-width:%s;%s" |',--parameters: right-button-width, rinfo4-width

["row-collapsible-replace-begin-fmt"] = '\ {| cellspacing="0" cellpadding="0" style="position:absolute;bottom:0px;%s vertical-align:middle;white-space:nowrap;background-color:%s"',-- parameters: "right-button-width 0 0" / "0 0 left-button-width", "right:0px" / "", bg		["row-collapsible-replace-end-fmt"] = '\n|} ',
 * colspan="7" style="padding:0 %s"| \
 * colspan="7" style="padding:0 %s"| \

["colspan-fmt"] = '%s\n|-\n| colspan="7" style="background-color:%s;text-align:%s;%s"|\n%s', ["empty-row-fmt"] = '\n|-\n| style="padding-right:3px;%s" |\n| style="%s" |\n| style="padding:0 %s;%s" |\n|\n| style="padding:0 %s;%s" |\n| style="%s" |\n| style="padding-left:3px;%s" |' } } local p,q={},{}

local function formaterror(key,param) local result = mw.ustring.format(i18n.html['colspan-fmt'], , , , , ' ' .. mw.ustring.format(i18n.errors[key] or (tostring(key) .. ' %s'), tostring(param or '')) .. ' ') if mw.site.namespaces[mw.title.getCurrentTitle.namespace].isContent then result = result .. (i18n['errors-categories'][key] or i18n['errors-categories'].default or '') end return result end

local function RGBbyCode(code)-- RGB codes for BSicon sets at Commons:Category:Icons for railway descriptions/other colors local colors = {--      Any changes should be discussed at Commons:Talk:BSicon/Colors bahn    = 'BE2D2C', ex          = 'D77F7E', u       = '003399', uex         = '6281C0', f       = '008000', fex         = '64B164', g       = '2CA05A', gex         = '7EC49A', azure   = '3399FF', ex_azure    = '99CCFF', black   = '000000', ex_black    = '646464', blue    = '0078BE', ex_blue     = '64ACD6', brown   = '8D5B2D', ex_brown    = 'B89A7F', cerulean = '1A8BB9', ex_cerulean = '73B7D3', cyan    = '40E0D0', ex_cyan     = '8AEAE1', denim   = '00619F', ex_denim    = '649EC3', fuchsia = 'B5198D', ex_fuchsia  = 'D173B8', golden  = 'D7C447', ex_golden   = 'E5DA8E', green   = '2DBE2C', ex_green    = '7FD67E', grey    = '999999', ex_grey     = 'C0C0C0', jade    = '53B147', ex_jade     = '95CE8E', lavender = '9999FF', ex_lavender = 'C0C0FF', lime    = '99CC00', ex_lime     = 'D1E681', maroon  = '800000', ex_maroon   = 'B16464', ochre   = 'CC6600', ex_ochre    = 'DEA164', orange  = 'FF6600', ex_orange   = 'FF9955', pink    = 'F0668D', ex_pink     = 'F4A1B8', purple  = '8171AC', ex_purple   = 'B1A8CB', red     = 'EF161E', ex_red      = 'F37176', ruby    = 'CC0066', ex_ruby     = 'DE64A1', saffron = 'FFAB2E', ex_saffron  = 'FFC969', sky     = '069DD3', ex_sky      = '67C2E3', steel   = 'A1B3D4', ex_steel    = 'C4CFE3', teal    = '339999', ex_teal     = '82C0C0', violet  = '800080', ex_violet   = 'B164B1', yellow  = 'FFD702', ex_yellow   = 'FFEB81', }	return colors[code] or colors.bahn end

local function cell(icon,overlapIcons) --Icon handling. Each icon is defined as in the following example: --icon ID!~overlap icon ID!@image link target --No limit on overlap icons, just separate them by "!~". local tmp,link={},'' if #overlapIcons>0 then tmp = mw.text.split(overlapIcons[#overlapIcons], '!@') overlapIcons[#overlapIcons] = tmp[1] if #tmp > 1 then link = tmp[2] end tmp = {} for i,v in ipairs(overlapIcons) do			if i==#overlapIcons then local link=link else local link='' end table.insert(tmp,mw.ustring.format(i18n.html['cell-overlapicon-fmt'],mw.text.trim(v),link))end return mw.ustring.format(i18n.html['cell-icon-fmt-with-overlap'],mw.text.trim(table.concat(tmp)),icon) end tmp = mw.text.split(icon, '!@') icon = mw.text.trim(tmp[1]) if #tmp > 1 then link = tmp[2] end if icon ~= '' then return mw.ustring.format(i18n.html['cell-icon-fmt'], icon, link) else return mw.ustring.format(i18n.html['cell-filler-empty-fmt'], '20px', '20px') end end local function fillercell(code) if code == '' then return mw.ustring.format(i18n.html['cell-filler-empty-fmt'], '20px', '20px') elseif code == 'd' then return mw.ustring.format(i18n.html['cell-filler-empty-fmt'], '10px', '10px') elseif string.sub(code,1,1) == '#' then return mw.ustring.format(i18n.html['cell-filler-fmt'], code) else return mw.ustring.format(i18n.html['cell-filler-fmt'],'#' .. RGBbyCode(code)) end end local function properties(str) --str is a combination of properties with following syntax: --[property name=value[!@property name1=value1[!@property name1=value1]]] and so on	local result = {} for i, v in ipairs(mw.text.split(str, '!@')) do		if v ~= '' then local t = mw.text.split(v, '=') table.insert(result, t[1]) result[t[1]] = table.concat(t, '=', 2) or ''--fill table with pairs "property"="value" end end return result end

local function row(pattern,noformatting,filler) --Row handling. Each row looks like the following: --row propertieslinfo4linfo3linfo2linfo1! !(icon pattern)rinfo1rinfo2rinfo3rinfo4row properties local result = {['linfo4'] = , ['linfo3+2'] = , ['linfo1'] = , ['cells'] = {}, ['rinfo1'] = , ['rinfo2+3'] = , ['rinfo4'] = , ['rowProp'] = {}} local lcolspan, rcolspan, linfo4_fmt, rinfo4_fmt = '2', '2', ,  local left, right, icons, overlapIcons, tmp = {}, {}, {}, {}, mw.text.split(pattern, '! !') if #tmp > 1 then--splitting the pattern by '! !'		left = tmp[1] ; right = tmp[2] else left =  ; right = tmp[1] or  end

tmp = mw.text.split(left, '')--analysing the left part if #tmp > 1 then--if there are several result['linfo1'] = mw.getCurrentFrame:preprocess(mw.text.trim(tmp[#tmp])) result['linfo3+2'] = mw.text.trim(tmp[#tmp - 1]) if #tmp > 2 then tmp[#tmp - 2] = mw.text.trim(tmp[#tmp - 2]) if tmp[#tmp - 2] ~= '' then result['linfo3+2'] = mw.ustring.format(i18n.html['row-linfo3-fmt'], tmp[#tmp - 2]) .. result['linfo3+2'] end if #tmp > 3 then tmp[#tmp - 3] = mw.text.trim(tmp[#tmp - 3]) if tmp[#tmp - 3] ~= '' then result['linfo4'] = mw.getCurrentFrame:preprocess(tmp[#tmp - 3]) lcolspan = '1' linfo4_fmt = mw.ustring.format(i18n.html['row-linfo4-fmt'], '', result['linfo4']) end if #tmp > 4 then result['rowProp'] = properties(mw.text.trim(tmp[#tmp - 4])) end end end else--assume only linfo2 was provided. result['linfo3+2'] = mw.text.trim(tmp[1]) end result['linfo3+2'] = mw.getCurrentFrame:preprocess(result['linfo3+2'])--expand possible templates in info.

tmp = mw.text.split(right, '')--analysing the right part if #tmp > 2 then result['rinfo1'] = mw.getCurrentFrame:preprocess(mw.text.trim(tmp[2])) result['rinfo2+3'] = mw.text.trim(tmp[3]) if #tmp > 3 then tmp[4] = mw.text.trim(tmp[4]) if tmp[4] ~= '' then result['rinfo2+3'] = result['rinfo2+3'] .. mw.ustring.format(i18n.html['row-rinfo3-fmt'], tmp[4]) end if #tmp > 4 then tmp[5] = mw.text.trim(tmp[5]) if tmp[5] ~= '' then result['rinfo4'] = mw.getCurrentFrame:preprocess(tmp[5]) rcolspan = '1' rinfo4_fmt = mw.ustring.format(i18n.html['row-rinfo4-fmt'], '', result['rinfo4']) end if #tmp > 5 then result['rowProp'] = properties(mw.text.trim(tmp[6])) end end end else--assume only rinfo2 was provided. result['rinfo2+3'] = mw.text.trim(tmp[2] or '') end result['rinfo2+3'] = mw.getCurrentFrame:preprocess(result['rinfo2+3'])

icons = mw.text.split(tmp[1], '\\')--splitting the string of icons first by "\" if type(filler) == 'string' then result['cells'][1] = 'style="height:' .. filler .. '"'--row parameter before any cells for i, v in ipairs(icons) do table.insert(result['cells'], fillercell(v)) end--no !@ or !~ for filler row else for i, v in ipairs(icons) do			tmp = mw.text.split(v, '!~') icons[i] = tmp[1] table.remove(tmp, 1) table.insert(overlapIcons, tmp) end for i, v in ipairs(icons) do table.insert(result['cells'], cell(v, overlapIcons[i])) end end result['cells'] = table.concat(result['cells']) if result['rowProp']['bg'] == nil or result['rowProp']['bg'] == '' then result['rowProp']['bg'] = 'transparent' end

if noformatting then return result else return mw.ustring.format(i18n.html['row-general-fmt'], linfo4_fmt, lcolspan, , result['linfo3+2'], q.linfo1_pad, , result['linfo1'], result['rowProp']['bg'],			result['cells'], q.rinfo1_pad, , result['rinfo1'], rcolspan, , result['rinfo2+3'], rinfo4_fmt) end end

q = {collapsibles = -1, text_width = {, , , , , }, linfo1_pad = '3px', rinfo1_pad = '3px', bg = '#f9f9f9'} q.isKeyword = function(pattern, i, rows, justTest) if string.sub(pattern, 1, 1) ~= '-' then if justTest then return false else return nil end end--not a valid keyword local tmp = mw.text.split(string.sub(pattern, 2), '%-') if type(q[tmp[1]])=="function" and tmp[1] ~= 'isKeyword' then if justTest then return tmp[1] else return q[tmp[1]](tmp, i, rows) end--valid keyword else if justTest then return false else return nil end end end q['startCollapsible'] = function(params, i, rows) table.remove(rows, i)	local tmp = q.isKeyword(rows[i], i, rows, true) if tmp then if tmp == 'endCollapsible' then return formaterror('collapsible-block-empty') else return formaterror('collapsible-block-no-first-row') .. q.isKeyword(rows[i], i, rows) --no valid keywords that can follow "startCollapsible" end end if q.collapsibles == -1 then q.collapsibles = 1 else q.collapsibles = q.collapsibles + 1 end--q.collapsibles == -1 means there are no collapsibles at all; 0 - all closed; >0 - some not closed local collapsed, replace, props = params[2], params[3] or '', properties(table.concat(params, '-', 4))--params[1] is the keyword name so all indices are shifted by one. if collapsed == nil or collapsed == '' then collapsed = 'collapsed' end if props['bg'] == nil or props['bg'] == '' then props['bg'] = 'transparent' ; props['bg-replace'] = q.bg else props['bg-replace'] = props['bg'] end local mode, float, result if q.rinfo1_pad == '' then mode = 'collapsible ' ; float = 'float:right;' else mode = 'mw-collapsible mw-' ; float = '' end result = mw.ustring.format(i18n.html["row-collapsible-begin-fmt"], props['bg'], mode, collapsed, float) tmp = row(rows[i], true, nil) local linfo4_3_2_fmt, rinfo2_3_4_fmt = ,  if q.rinfo1_pad == '' then if tmp['linfo4'] ~=  or tmp['linfo3+2'] ~=  then linfo4_3_2_fmt = mw.ustring.format(i18n.html['row-collapsible-left-linfo4+3+2-fmt'], tmp['linfo4'], tmp['linfo3+2']) end result = result .. mw.ustring.format(i18n.html['row-general-fmt'], mw.ustring.format(i18n.html['row-collapsible-left-button-fmt'], i18n.html['row-collapsible-left-button-width'], q.text_width[1]),			'1', q.text_width[2], linfo4_3_2_fmt, q.linfo1_pad, q.text_width[3], tmp['linfo1'], tmp['rowProp']['bg'], tmp['cells'], , , , '1', , , mw.ustring.format(i18n.html['row-rinfo4-fmt'], , '')) else if tmp['rinfo4'] ~=  or tmp['rinfo2+3'] ~=  then rinfo2_3_4_fmt = mw.ustring.format(i18n.html['row-collapsible-right-rinfo2+3+4-fmt'], tmp['rinfo2+3'], tmp['rinfo4']) end result = result .. mw.ustring.format(i18n.html['row-general-fmt'], mw.ustring.format(i18n.html['row-linfo4-fmt'], q.text_width[1], tmp['linfo4']),			'1', q.text_width[2], tmp['linfo3+2'], q.linfo1_pad, q.text_width[3], tmp['linfo1'], tmp['rowProp']['bg'], tmp['cells'], q.rinfo1_pad, q.text_width[4], tmp['rinfo1'],			'1', q.text_width[5], rinfo2_3_4_fmt, mw.ustring.format(i18n.html['row-collapsible-right-button-fmt'], i18n.html['row-collapsible-right-button-width'], q.text_width[6])) end if replace ~= '' then if q.isKeyword(rows[i + 1], i, rows, true) then return result .. formaterror('collapsible-block-no-replacement') end--a plain row needed for replacement table.remove(rows, i)		tmp = row(rows[i], true, nil) local padding, right = i18n.html['row-collapsible-right-button-width'] .. ' 0 0', 		if q.rinfo1_pad ==  then padding = '0 0 ' .. i18n.html['row-collapsible-left-button-width'] ; right = 'right:0px;' end result = result .. mw.ustring.format(i18n.html['row-collapsible-replace-begin-fmt'], padding, right, props['bg-replace']) linfo4_3_2_fmt =  ; rinfo2_3_4_fmt =  if q.rinfo1_pad == '' then if tmp['linfo4'] ~=  or tmp['linfo3+2'] ~=  then linfo4_3_2_fmt = mw.ustring.format(i18n.html['row-collapsible-left-linfo4+3+2-fmt'], tmp['linfo4'], tmp['linfo3+2']) end result = result .. mw.ustring.format(i18n.html['row-general-fmt'], mw.ustring.format(i18n.html['row-linfo4-fmt'], , ), '1', q.text_width[2], linfo4_3_2_fmt,				q.linfo1_pad, q.text_width[3], tmp['linfo1'], tmp['rowProp']['bg'], tmp['cells'], , , , '1', , , mw.ustring.format(i18n.html['row-rinfo4-fmt'], , '')) else if tmp['rinfo4'] ~=  or tmp['rinfo2+3'] ~=  then rinfo2_3_4_fmt = mw.ustring.format(i18n.html['row-collapsible-right-rinfo2+3+4-fmt'], tmp['rinfo2+3'], tmp['rinfo4']) end result = result .. mw.ustring.format(i18n.html['row-general-fmt'], mw.ustring.format(i18n.html['row-linfo4-fmt'], q.text_width[1], tmp['linfo4']), '1', q.text_width[2],				tmp['linfo3+2'], q.linfo1_pad, q.text_width[3], tmp['linfo1'], tmp['rowProp']['bg'], tmp['cells'], q.rinfo1_pad, q.text_width[4], tmp['rinfo1'], '1', q.text_width[5],				rinfo2_3_4_fmt, mw.ustring.format(i18n.html['row-rinfo4-fmt'], , )) end result = result .. i18n.html['row-collapsible-replace-end-fmt'] end return result end q['endCollapsible'] = function(params, i, rows) if q.collapsibles > 0 then q.collapsibles = q.collapsibles - 1 return i18n.html['row-collapsible-end-fmt'] else return formaterror('collapsible-block-not-open') end end q['colspan'] = function(params, i, rows) if params[2] == 'end' then return '' end local tmp, j, nrows, props = {}, 0, tonumber(params[2]), properties(table.concat(params, '-', 3)) if nrows ~= 0 then table.remove(rows, i) end if nrows == nil then nrows = #rows - i + 1 end while j < nrows and i <= #rows do		j = j + 1 if rows[i] == '-colspan-end' then j = nrows else table.insert(tmp, rows[i]) end if nrows ~= j or i == #rows then table.remove(rows, i) end end if j < nrows then j = formaterror('colspan-less-rows-than-set',j) else j = '' end return mw.ustring.format(i18n.html['colspan-fmt'], j, props['bg'] or , props['align'] or , props['style'] or '', mw.getCurrentFrame:preprocess(table.concat(tmp, '\n'))) end q['filler'] = function(params, i, rows) local tmp, height = table.concat(params, '-', 3), '5px' if #params < 3 or tmp == '' then return formaterror('parameter-missing') end--TODO: указать имя нужного параметра. if params[2] ~= '' then height = params[2] end return row(tmp, nil, height) end

function p.RGBbyCode(frame) return RGBbyCode(mw.text.trim(frame.args[1] or '')) end

local function localroute(pattern,ptw,pbg) local tmp = {} if mw.text.trim(pbg) ~= '' then q.bg = pbg end tmp = mw.text.split(mw.text.trim(ptw), ',') if #tmp == 6 then for i = 1, 6 do			if tmp[i] ~= '' then if tonumber(string.sub(tmp[i],-1)) then q.text_width[i] = 'width:' .. tmp[i] .. 'px;' else q.text_width[i] = 'width:' .. tmp[i] .. ';'				end end end if tmp[4] ==  and tmp[5] ==  and tmp[6] == '' then q.rinfo1_pad = ''--padding for rinfo1 column = 0, not 3px elseif tmp[1] ==  and tmp[2] ==  and tmp[3] == '' then q.linfo1_pad = '' end--padding for linfo1 column = 0, not 3px elseif #tmp == 3 then for i = 1, 3 do			if tmp[i] ~= '' then if tonumber(string.sub(tmp[i],-1)) then q.text_width[i + 3] = 'width:' .. tmp[i] .. 'px;' else q.text_width[i + 3] = 'width:' .. tmp[i] .. ';'				end end end q.linfo1_pad = '' elseif #tmp == 1 and tmp[1]~='' then if tonumber(string.sub(tmp[1],-1)) then q.text_width[5] = 'width:' .. tmp[1] .. 'px;' else q.text_width[5] = 'width:' .. tmp[1] .. ';'		end q.linfo1_pad = '' end tmp = {} local index = 0 local rows = {} for item in pattern:gmatch('([^\n]*)\n?') do		item = mw.text.trim(item) if item ~= '' then index = index + 1 rows[index] = item end end if index == 0 then return formaterror('parameter-missing') end for i, v in ipairs(rows) do		local keyword = q.isKeyword(v, i, rows) if type(keyword) ~= "string" then table.insert(tmp, row(v, nil, nil)) else table.insert(tmp, keyword) end end

if q.collapsibles > 0 then table.insert(tmp, formaterror('collapsible-block-not-closed') .. q['endCollapsible']) end if q.collapsibles ~= -1 then if q.rinfo1_pad == '' then q.text_width[1] = q.text_width[1] .. 'min-width:' .. i18n.html['row-collapsible-left-button-width'] .. ';'		else q.text_width[6] = q.text_width[6] .. 'min-width:' .. i18n.html['row-collapsible-right-button-width'] .. ';' 		end end -- ↓ empty row to set column widths; ↑ if q.collapsibles ≠ -1 and there are collapsible sections, leftmost or rightmost column should be wide enough to accomodate the button table.insert(tmp, mw.ustring.format(i18n.html['empty-row-fmt'], q.text_width[1], q.text_width[2], q.linfo1_pad, q.text_width[3], q.rinfo1_pad, q.text_width[4], q.text_width[5], q.text_width[6])) return table.concat(tmp) end

function p.route(frame) local rows = frame.args.pattern or '' local tw = frame.args['text-width'] or '' local bg = frame.args.bg or '' return localroute(rows,tw,bg) end

local getArgs

function p._infobox(args) args[1] = args[1] or args.map or '' args.tw = args.tw or args['text width'] or args['text-width'] or '' args['title color'] = args['title color'] or args['title-color'] or '#fff' args['title bg color'] = args['title bg color'] or args['title-bg'] or '#27404E' args.legend = args.legend or '' local navbar = require('Module:Navbar').navbar local navtable = {} args.navbar = args.navbar or args.tnavbar if args.navbar then navtable = {args.navbar, mini = true} args.navbar = navbar(navtable) else args.navbar = '' end local result = '' if args.inline then result = result..'&#32;\n' end result = result..'{|' args.collapse = args.collapse or args.collapsed if args.collapsible == 'no' or args.collapsible == '0' then args.collapsible = '0' else result = result .. 'class="collapsible '		if args.collapse then result = result .. 'collapsed' end		result = result .. '" ' end args.float = args.float or 'right' if args.float == 'right' then args.margin = 'margin-left:1em;' elseif args.float == 'left' then args.margin = 'margin-right:1em;' else args.margin = '' end if args.inline then args.inline2 = 'float:none;width:100%;margin:0;border:none;' args.fontsize = '100%' else args.inline2 = 'box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12), 0 3px 1px -2px rgba(0, 0, 0, 0.2); -moz-box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12), 0 3px 1px -2px rgba(0, 0, 0, 0.2); -webkit-box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12), 0 3px 1px -2px rgba(0, 0, 0, 0.2);' args.fontsize = '85%' end args.bg = args.bg or '#F9F9F9' args.style = args.style or '' result = result .. 'cellspacing="0" cellpadding="0" style="float:' .. args.float .. ';clear:' .. args.float .. ';margin-top:0;margin-bottom:1em;' .. args.margin .. ';empty-cells:show;border-collapse:collapse;font-size:' .. args.fontsize .. ';background:' .. args.bg .. ';' .. args.inline2 .. args.style .. '"' args.title = args.title or '' if args.inline or args.title == 'no' or args.title == '0' then else result = result .. '\n! style="color:' .. args['title color'] .. ';background-color:' .. args['title bg color'] .. ';text-align:center;padding:5px"|' if args['navbar pos'] then result = result .. ' '		else if args.navbar ~= '' then navtable.brackets = true navtable.style = 'float:left;margin-right:5px' navtable.fontstyle = 'font-size:115%;color:' .. args['title color'] args.navbar = navbar(navtable) .. ' '			else args.navbar = ' ' end result = result .. args.navbar end result = result .. '' .. args.title .. ' '	end args.top = args.top or args['on top'] if args.top then result = result .. '\n|-\n|style="padding:0px 5px;text-align:center"|' .. args.top end result = result .. '\n|-\n|style="line-height:normal;padding:4px 5px"|' if args.navbar ~= '' and args['navbar pos'] == '1' then result = result .. ' ' .. args.navbar .. ' '	end args.legend2 = args.legend:lower if args.legend2 == 'no' or args.legend2 == '0' then else local legtable = {['water']={'canal','water','waterway'},['foot']={'foot','footpath','walkway'}} for k, v in pairs(legtable) do			for _, name in ipairs(v) do				if args.legend2 == name then args.legend2 = k end end end if args.legend2 == 'water' then args.legend =  .. args['legend alt'] .. 		else			args.legend = args.legend .. '|Legend]]'		end		result = result .. ' ' .. args.legend .. ' '	end	result = result .. '\n|-\n|style="padding:0px"|\n{|cellpadding="0" cellspacing="0" class="nogrid" style="padding:0px;border:0px;background:transparent;white-space:nowrap;line-height:1.2;font-size:110%;margin:'	if args.inline then		result = result..'auto'	else		result = result..'0px 6px 6px'	end	result = result..'"\n' .. localroute(args[1],args.tw,args.bg) .. '\n|}'	args.bottom = args.bottom or args.footnote	if args.bottom then		result = result .. '\n|-\n|style="line-height:normal;text-align:right;padding:0px 5px 5px"|' .. args.bottom	end	if args.navbar ~= '' and args['navbar pos'] == '2' then		result = result .. '\n|-\n|style="line-height:normal;padding:0px 5px 3px;text-align:center"|' .. args.navbar .. ' '	end	return result .. '\n|}' end

function p.infobox(frame) if not getArgs then getArgs = require('Module:Arguments').getArgs end return p._infobox(getArgs(frame)) end

local function pre_block(text) -- Pre tags returned by a module do not act like wikitext ... .	return ' ' .. mw.text.nowiki(text) .. (text:sub(-1) == '\n' and '' or '\n') .. ' \n' end

function p.convertbs(frame) local org = mw.text.unstripNoWiki(frame.args[1]) or 'Paste legacy RDT markups between the nowiki tags' local res = org res = string.gsub(res, '{{%a%a%-map', '{{Routemap') -- "%-" is an escape for hyphen which is used as "between" in pattern. res = string.gsub(res, '{|{{%aailway line header}}', '{{Routemap') -- string.gsub cannot ignore letter case of specific alphabet in pattern, hence "BS" is replaced by "%a%a" as "any alphabet". res = string.gsub(res, '{{%a%a%-header%d?|', '{{safesubst:BS-header/safesubst|') -- "%d?" means optional digit in case use of variant template like BS-header3. res = string.gsub(res, '{{%a%a%-table%d?}}', '|map=') res = string.gsub(res, '{{%a%a|', '{{safesubst:BS|') res = string.gsub(res, '{{%a%a(%d)|', '{{safesubst:BS%1|') res = string.gsub(res, '{{%a%a(%d)(%d)|', '{{safesubst:BS%1%2|') res = string.gsub(res, '{{%a%a%-2|', '{{safesubst:BS-2|') res = string.gsub(res, '{{%a%a(%d)%-2|', '{{safesubst:BS%1-2|') res = string.gsub(res, '{{%a%a(%d)(%d)%-2|', '{{safesubst:BS%1%2-2|') res = string.gsub(res, '{{%a%akm|', '{{BSkm{{!}}') res = string.gsub(res, '{{%a%ato|', '{{BSto{{!}}') res = string.gsub(res, '{{%a%asplit|', '{{BSsplit{{!}}') res = string.gsub(res, '{{%a%acvt|', '{{BScvt{{!}}') res = string.gsub(res, '|}\n|}', '}}') -- Replace ending of Railway line header map setup. return "\nSafe substitution:\n" .. pre_block(res) .. "Original:\n" .. pre_block(org) end

return p

--[[for testing in console:

print(p.route({['args={['text-width='',['pattern=[=[ STR STR]=]}}))

]]