Module:Routemap

local i18n = {

	errors = {

		"parameter-missing" = "引數缺失！",

		"collapsible-block-not-closed" = "摺疊單元未閉合！",

		"collapsible-block-not-open" = "找不到摺疊單元開始標記！",

		"collapsible-block-empty" = "摺疊單元不能為空！",

		"collapsible-block-no-first-row" = "摺疊單元沒有第一行！",

		"collapsible-block-no-replacement" = "折單元沒有替代！",

	},

	"error-categories" = {

		default = ,

		"text-images" = ,

		"separate-navbar" = ,

		"missing-text-values" = ,

		"br-tags" = ,

		"srws" = ,

		"rmr-error" =

	},

	text = {

		navbar_mini = true, -- for navbar pos 2 only

		navbar_text = ' 此線路圖：', -- for navbar pos 2 only

		legend_text = ' 圖例 ',

		legend = {

			default = 'Template:Railway line legend',

			track = 'Template:Railway track legend',

			bus = 'Template:Bus route legend',

			canal = 'Template:Waterways legend',

			water = 'Template:Waterways legend',

			waterway = 'Template:Waterways legend',

			foot = 'Template:Trails legend',

			footpath = 'Template:Trails legend',

			walkway = 'Template:Trails legend'

		}

	},

	html = {

		"cell-icon-fmt" = '\

% s',

		"cell-overlapicon-fmt" = ,

		"cell-icon-fmt-with-overlap" = '\

% s% s',

		"cell-text-fmt" = '\

% s% s% s% s',

		"cell-overlaptext-fmt" = '% s% s% s',

		"cell-text-fmt-with-overlap" = '\

% s% s% s% s% s',

		"cell-empty-fmt" = '\n|style="padding:0;width:% s;min-width:% s;height:20px;min-height:20px% s"|% s',

		"cell-empty-fmt-with-overlap" = '\n|style="padding:0% s"|% s% s',

		"cell-filler-fmt" = '\n|style="padding:0; width:8px"|

',

		"cell-filler-empty-fmt" = '\n|style="padding:0; width:20px"|',

		"row-linfo4-fmt" = '\

% s',-- parameters:linfo4-width, linfo4

		"row-linfo3-fmt" = '% s ',

		"row-rinfo3-fmt" = ' % s',

		"row-rinfo4-fmt" = '\

% s',-- parameters:rinfo4-width, rinfo4

		"row-general-fmt" = '\

% s\

% s\

\

\

% 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" = '\

\

',

		"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" = '\

',-- parameters: linfo4, linfo3+2

		"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" = '\

',-- parameters: rinfo2+3, linfo4

		"row-collapsible-right-button-fmt" = '\n| style="width:auto;vertical-align:middle;padding-left:3px;font-size:90%%;min-width:% s;% s" |',--parameters: right-button-width, rinfo4-width

		"row-collapsible-replace-begin-fmt" = '\

\

',

		"empty-row-fmt" = '\n

		}

}

local p,q=,

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 pfuncName (args)

	end

end

local function formaterror (key,param)

	local result = string.format (i18n.html'colspan-fmt', , , , , .. string.format (i18n.errorskey or (tostring (key) .. ' % s'),

		tostring (param or)) .. )

	if mw.site.namespacesmw.title.getCurrentTitle .namespace.isContent then result = result .. (i18n'error-categories'key or i18n'error-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',

		olive = '837902', ex_olive = 'B2AC64',

		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 colorscode or colors.bahn

end

p.RGBbyCode = makeInvokeFunction ('_RGBbyCode')

function p._RGBbyCode (args)

	return RGBbyCode (args1)

end

local function properties (str)

--str is a combination of properties with following syntax:

--property name=value,property name1=value1,property name2=value2 and so on

	local result =

	for i, v in ipairs (mw.text.split (str, ',')) do

		if v then

			local t = mw.text.split (v, '=')

			if string.find (v, '=') then

				t1 = mw.text.trim (t1) --trim parameter names

				table.insert (result, t1)

				resultt1 = t2 or --fill table with pairs "property"="value"

			elseif resultresulti - 1 then

				table.insert (result, resulti - 1)

				resultresulti = resultresulti..','..t1 --if no equals sign then tack t1 onto the previous result

			else

				table.insert (result, '~~')

			end

		else

			table.insert (result, '~~')

		end

	end

	return result

end

local function positive (x)

	if not x then return nil else x = string.lower (x) end

	if x == 'yes' or x == 'y' or x == '1' or x == 'true' then return 1 end

end

local function negative (x)

	if not x then return nil else x = string.lower (x) end

	if x == 'no' or x == 'n' or x == '0' or x == 'false' then return 0 end

end

local function alignment (x, y, z)

	if not x then return nil end

	local directions = {'inherit-left' = {'l', 'left',},

		'inherit-right' = {'r', 'right',},

		'top-inherit' = {'a', 't', 'top',},

		'bottom-inherit' = {'e', 'b', 'bottom',},

		'top-left' = {'la', 'tl', 'c4', 'nw', 'top-left', 'topleft',},

		'top-right' = {'ra', 'tr', 'c1', 'ne', 'top-right', 'topright',},

		'bottom-left' = {'le', 'bl', 'c3', 'sw', 'bottom-left', 'bottomleft',},

		'bottom-right' = {'re', 'br', 'c2', 'se', 'bottom-right', 'bottomright',},

		'inherit-center' = {'c', 'center', 'centre',},

		'middle-inherit' = {'m', 'middle',},

		'top-center' = {'ma', 'tc', 'top-center', 'top-centre', 'topcenter', 'topcentre',},

		'bottom-center' = {'me', 'bc', 'bottom-center', 'bottom-centre', 'bottomcenter', 'bottomcentre',},

		'middle-left' = {'lm', 'ml', 'middle-left', 'middleleft',},

		'middle-right' = {'rm', 'mr', 'middle-right', 'middleright',},

		'middle-center' = {'cm', 'mc', 'middle-center', 'middle-centre', 'middlecenter', 'middlecentre',},

	}

	for k, v in pairs (directions) do

		for _, name in ipairs (v) do

			if x:lower == name then

				local values = mw.text.split (k, '-')

				if values1 == 'inherit' then values1 = y end

				if values2 == 'inherit' then values2 = z end

				return values

			end

		end

	end

	return {y, z}

end

local function widths (p)

	local values = {

		'w' = 160, '+bs' = 140, 'bs' = 120, '+s' = 100,

		's' = 80, '+db' = 70, '+b' = 60, 'db' = 50,

		'b' = 40, '+cd' = 35, '+d' = 30, '+c' = 25,

		 = 20, 'ocd' = 17.5, 'cd' = 15, 'od' = 12.5,

		'd' = 10, 'oc' = 7.5, 'c' = 5, 'o' = 2.5

	} -- in px

	return valuesp

end

local function cell (icon,overlapIcons,rowProps)--

Icon handling. Each icon is defined as in the following example:

icon ID!~overlap icon ID!@image link target

Values for an icon ID containing "" are treated as text, with the letter (s) before "" as width prefix (es).

No limit on overlapping icons or text; just separate them by "!~".

Parameters can be added after every object, separated to the left by "!_". This, if there is a link, must be after the link.

Parameters for individual objects in an overlapping stack can also be added, separated to the left by "".

Unless a link is provided, each cell will have mouseover text indicating its contents.

	local tmp, tmp2, cellProps, iconProps, overlapProps, tmp_sep, link, tracking, icontext, iconpre, width = , , , , , , ,

	if #overlapIcons > 0 then

		tmp = mw.text.split (overlapIcons#overlapIcons, '!_')

		if #tmp > 1 then overlapIcons#overlapIcons, cellProps = tmp1, properties (tmp2) end

		tmp = mw.text.split (overlapIcons#overlapIcons, '!@')

		overlapIcons#overlapIcons = tmp1

		if #tmp > 1 then link = tmp2 end	

		tmp = mw.text.split (icon, '')

		icon = tmp1

		if #tmp > 1 then iconProps = properties (tmp2) end

		for i, v in ipairs (overlapIcons) do

			tmp = mw.text.split (v, '')

			overlapIconsi = mw.text.trim (tmp1)

			if #tmp > 1 then overlapPropsi = properties (tmp2) else overlapPropsi = end

		end

	else

		tmp = mw.text.split (icon, '!_')

		if #tmp > 1 then icon, cellProps = tmp1, properties (tmp2) end

		tmp = mw.text.split (icon, '!@')

		icon = mw.text.trim (tmp1)

		if #tmp > 1 then link = tmp2 end

	end

	if #overlapIcons > 0 and icon ~= then tmp_sep = '; ' end

	local icontip = mw.text.nowiki (mw.text.unstripNoWiki (icon..tmp_sep..table.concat (overlapIcons, '; ')))

	local textspl = string.find (icon, '%*')

	if textspl then

		icontext = mw.text.trim (mw.ustring.sub (icon, textspl + 1))

		if textspl ~= 1 then iconpre = mw.text.trim (mw.ustring.sub (icon, 1, textspl - 1)) end

	end

	if cellProps.style then cellProps.style = ';'..cellProps.style else cellProps.style = end

	cellProps.bg = cellProps.bg or cellProps.background or cellProps.bgcolor

	if cellProps.bg then cellProps.style = cellProps.style..';background:'..cellProps.bg end

	if #overlapIcons > 0 or icontext then

		cellProps.before, cellProps.after = rowProps.before or , rowProps.after or

		cellProps.color = cellProps.color or cellProps.colour ; cellProps.bold = cellProps.bold or cellProps.b ; cellProps.italic = cellProps.italic or cellProps.i or cellProps.it

		if cellProps.color then cellProps.style = cellProps.style..';color:'..cellProps.color end

		if positive (cellProps.italic) then cellProps.style = cellProps.style..';font-style:italic' end

		if positive (cellProps.bold) then cellProps.style = cellProps.style..';font-weight:bold' end

		if not cellProps.fontsize or rowProps.fontsize or cellProps.fontsize == 'info' then

		elseif cellProps.fontsize == 'cmt' or cellProps.fontsize == 'comment' then

			cellProps.before, cellProps.after = ,

		else

			cellProps.style = cellProps.style..';font-size:'..cellProps.fontsize

		end

		if cellProps.align or rowProps.align then

			rowProps.align = rowProps.align or {'middle', 'center'}

			cellProps.align = alignment (cellProps.align, rowProps.align1, rowProps.align2) or rowProps.align

			cellProps.style, cellProps.textfmt = cellProps.style..';vertical-align:'..cellProps.align1..';text-align:'..cellProps.align2, ';vertical-align:'..cellProps.align1

		else

			cellProps.style, cellProps.textfmt = cellProps.style..';vertical-align:middle;text-align:center', ';vertical-align:middle;text-align:center'

		end

	end

	if #overlapIcons > 0 then

		tmp =

		for i, v in ipairs (overlapIcons) do

			if i ~= #overlapIcons then link = end

			if link and link ~= then icontip = link end

			if overlapPropsi.style then overlapProps.style = ';'..overlapPropsi.style else overlapProps.style = end

			overlapPropsi.bg = overlapPropsi.bg or overlapPropsi.background or overlapPropsi.bgcolor

			if overlapPropsi.bg then overlapProps.style = overlapProps.style..';background:'..overlapPropsi.bg end

			local tmp_textspl = string.find (v, '%*')

			if tmp_textspl then

				local tmpicontext, tmpiconpre = mw.text.trim (mw.ustring.sub (v, tmp_textspl + 1)),

				if tmptextspl ~= 1 then tmpiconpre = mw.text.trim (mw.ustring.sub (v, 1, tmp_textspl - 1)) end

				overlapProps.before, overlapProps.after = cellProps.before, cellProps.after

				overlapPropsi.color = overlapPropsi.color or overlapPropsi.colour ; overlapPropsi.bold = overlapPropsi.bold or overlapPropsi.b ; overlapPropsi.italic = overlapPropsi.italic or overlapPropsi.i or overlapPropsi.it

				if overlapPropsi.color then overlapProps.style = overlapProps.style..';color:'..overlapPropsi.color end

				if positive (overlapPropsi.italic) then overlapProps.style = overlapProps.style..';font-style:italic' end

				if positive (overlapPropsi.bold) then overlapProps.style = overlapProps.style..';font-weight:bold' end

				if rowProps.fontsize or cellProps.fontsize or overlapPropsi.fontsize == 'info' then

				elseif not overlapPropsi.fontsize then

					overlapProps.style = overlapProps.style..';font-size:10px'

				elseif overlapPropsi.fontsize == 'cmt' or overlapPropsi.fontsize == 'comment' then

					overlapProps.before, overlapProps.after = ,

				else

					overlapProps.style = overlapProps.style..';font-size:'..overlapPropsi.fontsize

				end

				if overlapPropsi.align or cellProps.align then

					cellProps.align = cellProps.align or {'middle', 'center'}

					overlapProps.align = alignment (overlapPropsi.align, cellProps.align1, cellProps.align2) or cellProps.align

					overlapProps.style, overlapProps.textfmt = overlapProps.style..';vertical-align:'..overlapProps.align1..';text-align:'..overlapProps.align2, ';vertical-align:'..overlapProps.align1

				else

					overlapProps.style, overlapProps.textfmt = overlapProps.style..';vertical-align:middle;text-align:center', ';vertical-align:middle;text-align:center'

				end

				if overlapPropsi.abbr then

					overlapProps.tag = {'abbr title="'..string.gsub (overlapPropsi.abbr, '"', '"')..'"', 'abbr'}

				else

					overlapProps.tag = {'div', 'div'}

				end

				width = (widths (tmp_iconpre) or 20)..'px'

				table.insert (tmp, string.format (i18n.html'cell-overlaptext-fmt', width, width, overlapProps.style, icontip, overlapProps.tag1, overlapProps.textfmt, overlapProps.before, tmpicontext, overlapProps._after, overlapProps.tag2))

			else

				v = mw.text.trim (v)

				if string.find (v, 'num') then

					if not string.find (v, 'numN% d+') then tracking = tracking..(i18n'error-categoriestext-images' or i18n'error-categories'.default) end

				end

				table.insert (tmp, string.format (i18n.html'cell-overlapicon-fmt', overlapProps.style, v, link, icontip))

			end

		end

		if iconProps.style then tmp21 = true else iconProps.style = end

		iconProps.bg = iconProps.bg or iconProps.background or iconProps.bgcolor

		if iconProps.bg then iconProps.style = iconProps.style..';background:'..iconProps.bg end

		width = widths (icon)

		if width then

			if tmp21 then iconProps.style = ';'..iconProps.style end

			return string.format (i18n.html'cell-empty-fmt-with-overlap', cellProps.style, mw.text.trim (table.concat (tmp)), width..'px', width..'px', iconProps.style, tracking)

		elseif icontext then

			iconProps.before, iconProps.after = cellProps.before, cellProps.after

			iconProps.color = iconProps.color or iconProps.colour ; iconProps.bold = iconProps.bold or iconProps.b ; iconProps.italic = iconProps.italic or iconProps.i or iconProps.it

			if iconProps.color then iconProps.style = iconProps.style..';color:'..iconProps.color end

			if positive (iconProps.italic) then iconProps.style = iconProps.style..';font-style:italic' end

			if positive (iconProps.bold) then iconProps.style = iconProps.style..';font-weight:bold' end

			if rowProps.fontsize or cellProps.fontsize or iconProps.fontsize == 'info' then

			elseif not iconProps.fontsize then

				iconProps.style = iconProps.style..';font-size:10px'

			elseif iconProps.fontsize == 'cmt' or iconProps.fontsize == 'comment' then

				iconProps.before, iconProps.after = ,

			else

				iconProps.style = iconProps.style..';font-size:'..iconProps.fontsize

			end

			if iconProps.align or cellProps.align then

				cellProps.align = cellProps.align or {'middle', 'center'}

				iconProps.align = alignment (iconProps.align, cellProps.align1, cellProps.align2) or cellProps.align

				iconProps.style, iconProps.textfmt = iconProps.style..';vertical-align:'..iconProps.align1..';text-align:'..iconProps.align2, ';vertical-align:'..iconProps.align1

			else

				iconProps.style, iconProps.textfmt = iconProps.style..';vertical-align:middle;text-align:center', ';vertical-align:middle;text-align:center'

			end

			if iconProps.abbr then

				iconProps.tag = {'abbr title="'..string.gsub (iconProps.abbr, '"', '"')..'"', 'abbr'}

			else

				iconProps.tag = {'div', 'div'}

			end

			if tmp21 then iconProps.style = ';'..iconProps.style end

			width = (widths (iconpre) or 20)..'px'

			return string.format (i18n.html'cell-text-fmt-with-overlap', cellProps.style, mw.text.trim (table.concat (tmp)), width, width, iconProps.style, icontip, iconProps.tag1, iconProps.textfmt, iconProps.before, icontext, iconProps.after, tracking, iconProps.tag2)

		else

			if iconProps.style ~= then iconProps.style = string.gsub (' style="'..iconProps.style..'"', '";', '"', 1) end

			if string.find (icon, 'num') then

				if not string.find (icon, 'numN% d+') then tracking = tracking..(i18n'error-categoriestext-images' or i18n'error-categories'.default) end

			end

			return string.format (i18n.html'cell-icon-fmt-with-overlap', cellProps.style, mw.text.trim (table.concat (tmp)), iconProps.style, icon, icontip, tracking)

		end

	end

	width = widths (icon)

	if width then

		return string.format (i18n.html'cell-empty-fmt', width..'px', width..'px', cellProps.style, tracking)

	else

		if link and link ~= then icontip = link end

		if icontext then

			if not cellProps.fontsize and not rowProps.fontsize then cellProps.style = cellProps.style..';font-size:10px' end

			if cellProps.abbr then

				cellProps.tag = {'abbr title="'..string.gsub (cellProps.abbr, '"', '"')..'"', 'abbr'}

			else

				cellProps.tag = {'div', 'div'}

			end

			width = (widths (iconpre) or 20)..'px'

			return string.format (i18n.html'cell-text-fmt', width, width, cellProps.style, icontip, cellProps.tag1, cellProps.textfmt, cellProps.before, icontext, cellProps.after, tracking, cellProps.tag2)

		else

			if string.find (icon, 'num') then

				if not string.find (icon, 'numN% d+') then tracking = tracking..(i18n'error-categoriestext-images' or i18n'error-categories'.default) end

			end

			return string.format (i18n.html'cell-icon-fmt', cellProps.style, icon, link, icontip, tracking)

		end

	end

end

local function fillercell (code)

--Creates a 5px-high row.

--Values in icon pattern can only be blank, d, BSicon color or #hex triplet.

	if code == then

		return string.format (i18n.html'cell-filler-empty-fmt', '20px', '20px')

	elseif code == 'd' then

		return string.format (i18n.html'cell-filler-empty-fmt', '10px', '10px')

	elseif mw.ustring.sub (code,1,1) == '#' then

		return string.format (i18n.html'cell-filler-fmt', code)

	else

		return string.format (i18n.html'cell-filler-fmt', '#'..RGBbyCode (code))

	end

end

local function row (pattern,noformatting,filler)--

Row handling. Each row looks like the following:

row properties~~linfo4~~linfo3~~linfo2~~linfo1! !(icon pattern)~~rinfo1~~rinfo2~~rinfo3~~rinfo4~~row properties

	local result = {'linfo4' = , 'linfo3+2' = , 'linfo1' = , rowstyle = , 'cells' = , 'rinfo1' = , 'rinfo2+3' = , 'rinfo4' = , 'rowProp' = }

	local lcolspan, rcolspan, linfo4fmt, rinfo4fmt = '2', '2', ,

	local left, right, icons, overlapIcons, tmp = , , , , mw.text.split (pattern, '! !')

	if #tmp > 1 then--splitting the pattern by '! !'

		left = tmp1 ; right = tmp2

	else

		left = ; right = tmp1 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' = string.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 = string.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 (tmp1)

	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 (tmp2))

		result'rinfo2+3' = mw.text.trim (tmp3)

		if #tmp > 3 then

			tmp4 = mw.text.trim (tmp4)

			if tmp4 ~= then result'rinfo2+3' = result'rinfo2+3' .. string.format (i18n.html'row-rinfo3-fmt', tmp4) end

			if #tmp > 4 then

				tmp5 = mw.text.trim (tmp5)

				if tmp5 ~= then

					result'rinfo4' = mw.getCurrentFrame :preprocess (tmp5)

					rcolspan = '1'

					rinfo4_fmt = string.format (i18n.html'row-rinfo4-fmt', , result'rinfo4')

				end

				if #tmp > 5 then result.rowProp = properties (mw.text.trim (tmp6)) end

			end

		end

	else--assume only rinfo2 was provided.

		result'rinfo2+3' = mw.text.trim (tmp2 or)

	end

	result'rinfo2+3' = mw.getCurrentFrame :preprocess (result'rinfo2+3')

-- The below parameter functions are passed through to the cells.

	if result.rowProp.fontsize == 'cmt' or result.rowProp.fontsize == 'comment' then

		result.rowProp.before, result.rowProp.after = ,

	end

	if result.rowProp.align then

		result.rowProp.align = alignment (result.rowProp.align, 'middle', 'center') or {'middle', 'center'}

	end

	icons = mw.text.split (tmp1, '\\')--splitting the string of icons first by "\"

	if type (filler) == 'string' then

		result.style = ';font-size:0px'

		result.rowstyle = 'height:' .. filler .. ';min-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

		result.style =

		for i, v in ipairs (icons) do

			tmp = mw.text.split (v, '!~')

			iconsi = tmp1

			table.remove (tmp, 1)

			table.insert (overlapIcons, tmp)

		end

		for i, v in ipairs (icons) do table.insert (result'cells', cell (v, overlapIconsi, result.rowProp)) end

	end

	result'cells' = table.concat (result'cells')

	if result.rowProp.style then result.style = result.style..';'..result.rowProp.style end

	result.rowProp.bg = result.rowProp.bg or result.rowProp.background or result.rowProp.bgcolor ; result.rowProp.color = result.rowProp.color or result.rowProp.colour ; result.rowProp.bold = result.rowProp.bold or result.rowProp.b ; result.rowProp.italic = result.rowProp.italic or result.rowProp.i or result.rowProp.it

	if result.rowProp.bg then result.style = result.style..';background:'..result.rowProp.bg end

	if result.rowProp.color then result.style = result.style..';color:'..result.rowProp.color end

	if positive (result.rowProp.italic) then result.style = result.style..';font-style:italic' end

	if positive (result.rowProp.bold) then result.style = result.style..';font-weight:bold' end

	if result.rowProp.align then

		result.style = result.style..';vertical-align:'..result.rowProp.align1..';text-align:'..result.rowProp.align2

	else

		result.style = result.style..';vertical-align:middle;text-align:center'

	end

	if result.rowProp.fontsize and result.rowProp._after == and result.rowProp.fontsize ~= 'info' then

		result.style = result.style..';font-size:'..result.rowProp.fontsize

	end

	if noformatting then

		return result

	else

		return string.format (i18n.html'row-general-fmt', linfo4fmt, lcolspan, , result'linfo3+2', q.linfo1pad, , result'linfo1', result.style, result.rowstyle,

			result'cells', q.rinfo1pad, , result'rinfo1', rcolspan, , result'rinfo2+3', rinfo4fmt)

	end

end

--↓ This table handles diagram rows beginning with a hyphen ("-").

q = {collapsibles = -1, textwidth = {, , , , ,}, linfo1pad = '3px', rinfo1_pad = '3px', bg = '#F9F9F9'}

q.isKeyword = function (pattern, i, rows, justTest)

	if mw.ustring.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 (qtmp1)=="function" and tmp1 ~= 'isKeyword' then

		if justTest then return tmp1 else return qtmp1 (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 (rowsi, 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 (rowsi, 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 = params2, params3 or , properties (table.concat (params, '-', 4))--params1 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 = ; 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 = string.format (i18n.html"row-collapsible-begin-fmt", props.bg, mode, collapsed, float)

	tmp = row (rowsi, true, nil)

	local linfo432fmt, rinfo234fmt = ,

	if q.rinfo1_pad == then

		if tmp'linfo4' ~= or tmp'linfo3+2' ~= then linfo432_fmt = string.format (i18n.html'row-collapsible-left-linfo4+3+2-fmt', tmp'linfo4', tmp'linfo3+2') end

		result = result .. string.format (i18n.html'row-general-fmt', string.format (i18n.html'row-collapsible-left-button-fmt', i18n.html'row-collapsible-left-button-width', q.text_width1),

			'1', q.textwidth2, linfo432fmt, q.linfo1pad, q.textwidth3, tmp'linfo1', tmp.style, , tmp'cells', , , , '1', , , string.format (i18n.html'row-rinfo4-fmt', ,))

	else

		if tmp'rinfo4' ~= or tmp'rinfo2+3' ~= then rinfo234_fmt = string.format (i18n.html'row-collapsible-right-rinfo2+3+4-fmt', tmp'rinfo2+3', tmp'rinfo4') end

		result = result .. string.format (i18n.html'row-general-fmt', string.format (i18n.html'row-linfo4-fmt', q.text_width1, tmp'linfo4'),

			'1', q.textwidth2, tmp'linfo3+2', q.linfo1pad, q.textwidth3, tmp'linfo1', tmp.style, , tmp'cells', q.rinfo1pad, q.text_width4, tmp'rinfo1',

			'1', q.textwidth5, rinfo234fmt, string.format (i18n.html'row-collapsible-right-button-fmt', i18n.html'row-collapsible-right-button-width', q.text_width6))

	end

	if replace ~= then

		if q.isKeyword (rowsi + 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 (rowsi, 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 .. string.format (i18n.html'row-collapsible-replace-begin-fmt', padding, right, props'bg-replace')

		linfo432fmt = ; rinfo234fmt =

		if q.rinfo1_pad == then

			if tmp'linfo4' ~= or tmp'linfo3+2' ~= then linfo432_fmt = string.format (i18n.html'row-collapsible-left-linfo4+3+2-fmt', tmp'linfo4', tmp'linfo3+2') end

			result = result .. string.format (i18n.html'row-general-fmt', string.format (i18n.html'row-linfo4-fmt', ,), '1', q.textwidth2, linfo432fmt,

				q.linfo1pad, q.textwidth3, tmp'linfo1', tmp.style, , tmp'cells', , , , '1', , , string.format (i18n.html'row-rinfo4-fmt', ,))

		else

			if tmp'rinfo4' ~= or tmp'rinfo2+3' ~= then rinfo234_fmt = string.format (i18n.html'row-collapsible-right-rinfo2+3+4-fmt', tmp'rinfo2+3', tmp'rinfo4') end

			result = result .. string.format (i18n.html'row-general-fmt', string.format (i18n.html'row-linfo4-fmt', q.textwidth1, tmp'linfo4'), '1', q.textwidth2,

				tmp'linfo3+2', q.linfo1pad, q.textwidth3, tmp'linfo1', tmp.style, , tmp'cells', q.rinfo1pad, q.textwidth4, tmp'rinfo1', '1', q.text_width5,

				rinfo234_fmt, string.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

	if params2 == 'end' then return end

	local tmp, j, nrows, props = , 0, tonumber (params2), 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 0 then table.insert (tmp, formaterror ('collapsible-block-not-closed') .. q'endCollapsible') end

	if q.collapsibles ~= -1 then

		if q.rinfo1_pad == then

			q.textwidth1 = q.textwidth1 .. 'min-width:' .. i18n.html'row-collapsible-left-button-width' .. ';'

		else

			q.textwidth6 = q.textwidth6 .. '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, string.format (i18n.html'empty-row-fmt', q.textwidth1, q.textwidth2, q.linfo1pad, q.textwidth3, q.rinfo1pad, q.textwidth4, q.textwidth5, q.textwidth6))

	return table.concat (tmp)

end

function p.route (frame)

	local rows = frame.args.pattern or

	local tw = frame.args'text-width' or frame.args.tw or frame.args'text width' or

	local bg = frame.args.bg or

	return localroute (rows,tw,bg)

end

local function getArgNums (prefix, args)

-- Copied from Module:Infobox on enwiki.

-- Returns a table containing the numbers of the arguments that exist

-- for the specified prefix. For example, if the prefix were 'data', and

-- 'data1', 'data2', and 'data5' existed, this would return {1, 2, 5}.

local nums =

for k, v in pairs (args) do

local num = tostring (k):match ('^' .. prefix .. '(1-9% d*)$')

if num then table.insert (nums, tonumber (num)) end

end

table.sort (nums)

return nums

end

local greatercontrast = require ('Module:Color contrast')._greatercontrast

local rgb_black = 'var (--color-base,#252525)' -- class .mw-body in Mediawiki:Common.css

p.infobox = makeInvokeFunction ('_infobox')

function p._infobox (args) -- Creates a pretty box.

	args.map1, args.tw, args'map1-title', args'map1-collapsible', args'map1-collapse' = args.map1 or args.map, args.tw or args'text-width' or args'text width', args'map1-title' or args'map-title', args'map1-collapsible' or args'map-collapsible', args'map1-collapse' or args'map1-collapsed' or args'map-collapse' or args'map-collapsed'

	local function map_prefix (x) return 'map'..x end

	local mapnums, prefix =

	if args1 and args1 ~= then

		prefix = tonumber

		for k, v in pairs (args) do

			if type (k) == 'number' then table.insert (mapnums, k) end

		end

	else

		prefix = map_prefix

		mapnums = getArgNums ('map', args)

		table.sort (mapnums)

	end

	args'title bg color' = args'title bg color' or args'title bg' or args'title-bg' or 'var (--background-color-progressive--active,#27404E)'

	args'title color' = args'title color' or args'title-color' or greatercontrast {args'title bg color', 'var (--color-inverted,#FFF)', rgb_black}

	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 = i18n.text.navbarmini, text = i18n.text.navbartext}

		args.navbar = navbar (navtable)

	else

		args.navbar =

	end

	local result =

	if args.inline then result = result..' \n' end

	result = result..

				if positive (args'map'..v..'-collapse') or positive (args'map'..v..'-collapsed') then args.mapcollapsed = ' mw-collapsed autocollapse' else args.mapcollapsed = end

				result = result..'\n

	end

	args.bottom = args.bottom or args.footnote

	if args.bottom then

		if args.inline then args.padding2 = '6px' else args.padding2 = '0px' end

		result = result .. '\n

		if string.find (args.bottom, '&action=edit') then result = result .. (i18n'error-categoriesseparate-navbar' or i18n'error-categories'.default) end

	end

	if args.navbar ~= and args'navbar pos' == '2' then

		if negative (args'navbar long') or positive (args'navbar mini') then navtable.mini = true; args.navbar = navbar (navtable) end

		if args.inline and not args.bottom then args.padding2 = '6px' else args.padding2 = '0px' end

		result = result .. '\n

	end

	return result .. '\n

end

local function base (t1,t2,link,stn,italic,it,it2,bold,align,style,bg1,bg2,line,fs1,fs2,lh,v1,swap,inp,bs)

--Creates an inline table with two rows of text. Can be used in any Routemap text cell.

--Implemented in the BSsplit, BSto, BSsrws and BScvt templates.

	if not align then

		if bs == 'cvt' then

			align = 'right'

		else

			align = 'inherit'

		end

	end

	style = style or

	local result = ' '..rowstart..lh

	if line then result = result..';border-bottom:1px solid gray' end

	local bgpad = ';padding-left:0.5em;padding-right:0.5em'

	local function bgtext (v)

		return ';color:'..greatercontrast {v, 'var (--color-inverted,#FFF)', rgb_black}

	end

	if bg1 then

		result = result..bgpad..bgtext (bg1)..';background-color:'..bg1

	elseif bg2 then

		result = result..bgpad

	end

	if fs1 then result = result..';font-size:'..fs1 end

	result = result..'">'

	if not t1 or string.find (t1, '^% s*$') then

		if not t2 then t2 = end

		if string.find (t2,) == nil then t1 = '' end

	end

	if not t2 or string.find (t2, '^% s*$') then

		if not t1 then t1 = end

		if string.find (t1,) == nil then t2 = '' end

	end

	if bs == 'srws' then

		if stn then

			link = t1..' '..t2..' '..stn

		else

			link = t1..' '..t2..' railway station'

		end

	elseif bs == 'cvt' then

		local split, floor, outp, v2, mult = mw.text.split, math.floor

		local function trim (x)

			return string.gsub (x, '% s',)

		end

		local cvt = { -- conversion values

			'mi' = 1.609344,

			'ch' = 20.1168,

			'mi;ch' = 80,

			'm' = 1 / 0.9144,

			'yd' = 0.9144,

			'ft' = 0.3048,

		}

		local sf = {-- 10 ^ floor (log10 (cvtinp) + 0.5); or 10 ^ floor (log10 (cvtinps1 * cvtinp) + 0.5) for dual-unit inputs. this corrects the accuracy of result so that it usually has same significant figures

			'mi' = 1,

			'ch' = 10,

			'mi;ch' = 1/100,

			'm' = 1,

			'yd' = 1,

			'ft' = 1/10,

		}

		if not inp then inp = 'mi' end

		inp = string.lower (trim (inp))

		if inp == 'ch' or inp == 'yd' or inp == 'ft' then -- output unit

			outp = 'm'

		elseif inp == 'm' then

			outp = 'yd'

		else

			if inp ~= 'mi;ch' then inp = 'mi' end

			outp = 'km'

		end

		local pos = string.find (v1, '%.')

		if not pos then

			mult = 1

		else

			mult = 10 ^ (string.len (v1) - pos)

		end

		local inps = string.find (inp, ';')

		if inps then

			inps = split (inp, ';')

			if swap then

				t1 = v1..''..outp

				v1 = tonumber (v1)

				t2 = floor (v1 /cvtinps1)..''..inps1..''..floor (v1 % cvtinps1 /cvtinps1 cvtinp mult * sfinp + 0.5) /mult/sfinp..''..inps2

			else

				v1 = split (trim (v1), ';')

				t1 = v11..''..inps1..''..v12..''..inps2

				t2 = floor ((tonumber (v11) cvtinps1 + tonumber (v12) cvtinps1 /cvtinp) mult /sfinp + 0.5) /mult sfinp..''..outp

			end

		else

			if swap then

				v2 = floor (tonumber (v1) /cvtinp mult sfinp + 0.5) /mult/sfinp

				inp, outp = outp, inp

			else

				v2 = floor (tonumber (v1) cvtinp mult /sfinp + 0.5) /mult * sfinp

			end

			t1 = v1..''..inp

			t2 = v2..''..outp

		end

	end

	if t1 then

		if link then

			result = result....t1..

		else

			result = result..t1

		end

	end

	local rowend =

	result = result..rowend..rowstart..lh

	if bg2 then

		result = result..bgpad..bgtext (bg2)..';background-color:'..bg2

	elseif bg1 then

		result = result..bgpad

	end

	if fs2 then result = result..';font-size:'..fs2 end

	if (it ~= 'off' and bs == 'to') or it2 == 'italic' then

		result = result..';font-style:italic'

	elseif it == 'off' then

		result = result..';font-style:normal'

	end

	result = result..'">'

	if t2 then

		if link then

			result = result....t2..

		else

			result = result..t2

		end

	end

	result = result..rowend..' '

	if bs == 'to' or bs == 'srws' then

		if t1 == '' or t2 == '' then result = result..(i18n'error-categoriesmissing-text-values' or i18n'error-categories'.default) end

	end

	if string.find (t1,) ~= nil or string.find (t2,) ~= nil then result = result..(i18n'error-categoriesbr-tags' or i18n'error-categories'.default) end

	if bs == 'split' then

		if link and t1 and t2 then

			if string.find (link, '^'..t1..' '..t2..' ') then result = result..(i18n'error-categoriessrws' or i18n'error-categories'.default) end

		end

	end

	return result

end

p.BSto = makeInvokeFunction ('_BSto')

function p._BSto (args)

	args3 = args3 or args.L

	args4 = args4 or args.it or args.i

	args5 = args5 or args.b

	return base (args1,args2,args3,nil,nil,args4,nil,args5,args.align,args.style,args.bg1,args.bg2,args.line,'105%','92%','0.9',nil,nil,nil,'to')

end

p.BSsplit = makeInvokeFunction ('_BSsplit')

function p._BSsplit (args)

	args3 = args3 or args.L

	args4 = args4 or args.it or args.i

	args5 = args5 or args.b

	return base (args1,args2,args3,nil,args4,nil,nil,args5,args.align,args.style,args.bg1,args.bg2,args.line,'inherit','inherit','0.9',nil,nil,nil,'split')

end

p.BSsrws = makeInvokeFunction ('_BSsrws')

function p._BSsrws (args)

	args3 = args3 or args.S

	args4 = args4 or args.it or args.i

	args5 = args5 or args.b

	return base (args1,args2,nil,args3,args4,nil,nil,args5,args.align,args.style,args.bg1,args.bg2,args.line,'inherit','inherit','0.9',nil,nil,nil,'srws')

end

p.BScvt = makeInvokeFunction ('_BScvt')

function p._BScvt (args)

	return base (nil,nil,nil,nil,nil,nil,args.alt,nil,args.align,args.style,args.bg1,args.bg2,args.line,'inherit','inherit','0.9',args1,args2,args'in','cvt')

end

p.rmri = makeInvokeFunction ('_rmri')

function p._rmri (args)--

Displays a blue arrow pointing in one of eight directions.

Implemented in the RoutemapRouteIcon template.

	local directions, result = {'Up' = {'u', 'up'},

	'Down' = {'d', 'dn', 'down'},

	'Left' = {'l', 'left'},

	'Right' = {'r', 'right'},

	'UpperRight' = {'ur', 'ne', 'c1', 'upperright'},

	'LowerRight' = {'lr', 'se', 'c2', 'lowerright'},

	'LowerLeft' = {'ll', 'sw', 'c3', 'lowerleft'},

	'UpperLeft' = {'ul', 'nw', 'c4', 'upperleft'},

	}

	local d, link, size = args1, args2, args3

	if not d then

		if args4 ~= ' ' then d, link, size = args2, args3, 's' end

	end

	for k, v in pairs (directions) do

		for _, name in ipairs (v) do

			if d:lower == name then

				if size == 's' then

					size = '7'

				elseif not size then

					size = '10'

				end

				if not link then link = end

				result =

			end

		end

	end

	if not result then

		return 'Invalid Template:RoutemapRoute arrow value "'..d..'".'..(i18n'error-categoriesrmr-error' or i18n'error-categories'.default)

	else

		return result

	end

end

p.rmr = makeInvokeFunction ('_rmr')

function p._rmr (args)--

Displays text between two blue arrows (or to the left/right side of one).

Text can be split with an en dash if entered in both first and second numbered parameters.

Implemented in the RoutemapRoute template.

	args.l = args.l or args.Licon or args.licon or args.L

	args.r = args.r or args.Ricon or args.ricon or args.R

	if args.l then args.l = p._rmri {args.l,args.llink,(args.lsize or args.size),' '}..'' else args.l = end

	if args.r then args.r = ''..p._rmri {args.r,args.rlink,(args.rsize or args.size),' '} else args.r = end

	if args1 then

		if args2 then args1 = args1..'–'..args2 end

	else

		args1 = args2 or

	end

	if positive (args.enclosed) then

		args.b1, args.b2 = '(', ')'

	else

		args.b1, args.b2 = ,

	end

	return args.b1..args.l..args1..args.r..args.b2

end

function p.BSrow (frame)

	local args = getArgs (frame, {

		parentOnly = true,

		removeBlanks = false,

	})

	return p._BSrow (args)

end

function p._BSrow (args)--

Creates Routemap syntax for a diagram row based on parameters.

Implemented in the RDTr template.

	args.n = tonumber (args.n or)

	if not args.n then

		local icontotal = getArgNums (, args)

		table.sort (icontotal)

		args.n = icontotal#icontotal or 1

	end

	local count, icons, overlaps, overlapCalc = tonumber (args'$count') or 1, , , math.log10 (args.n)

	local text = (args.text and '*') or

	if overlapCalc == math.floor (overlapCalc) then overlapCalc = 10^(overlapCalc) else overlapCalc = 10^(math.floor (overlapCalc) + 1) end

	while count ....

	return ..

		mw.text.nowiki (text) ..

		(text:sub (-1) == '\n' and or '\n') ..

		'\n'

end

function p.convertbs (frame)--

Converts a legacy route diagram into Routemap format.

Code to be used is displayed in preview mode or after saving the page, above the original code.

	local org = mw.text.unstripNoWiki (frame.args1) or ' 在標記之間貼上傳統線路圖模板 '

	local res = org

	res = string.gsub (res, '}')) then

						local bg2 = string.match (restablei+1, '|% sbg% s=% s(#?a-zA-Z0-9+)') or string.find (restablei+1, '|% sbg% s=% s(#?)% s|') or string.find (restablei+1, '|% sbg% s=% s(#?)% s*}}')

						if bg2 == bg then restablei+1 = string.gsub (restablei, '|% sbg% s=% s*'..bg2,) end

					end

				end

			end

		end

		res = table.concat (restable, '\n')

	end

	return "\nSafe substitution:\n" .. preblock (res) .. "Original:\n" .. preblock (org)

end

return p