Permanently protected module
From Wikipedia, the free encyclopedia


require('strict')



local p = {}



local lang = 'en-GB' -- local default language



--	Below these comments: Internationalization table

--	How to translate this module (for languages without variants):

--	• Characters inside single and double quotation marks are called strings.

--	  The strings in this i18n table are used as output.

--	• Strings within square brackets are keys.

--	• Strings are concatenated (joined) with two dots.

--	• Set the string after «local lang =» to your language's code.

--	  Change the first key after "i18n" (usually "en-GB") to the same thing.

--	• For each string which is not inside a function, translate it directly.

--	• Strings with keys named "format" are Lua regular expressions.

--	  «()» is a match; «.+» means all characters; «%s+» means all spaces.

--	• For each string which is concatenated to the variable «var»,

--	  translate the phrase assuming that «var» will be a noun.

--	• Remove any unnecessary translations.



local i18n = require("Module:Adjacent stations/i18n")

local function getData(system, verify)

	if verify then

		local title = mw.title.new('Module:Adjacent stations/' .. system -- .. '/sandbox'

			)

		if not (title and title.exists) then return nil end

	end

	return require('Module:Adjacent stations/' .. system -- .. '/sandbox'

		)

end



local function getLine(data, lineN)

	if lineN then

		if data'aliases' then

			lineN = data'aliases'][mw.ustring.lower(lineN)] or lineN

		end

		local default = data'lines']['_default' or {}

		local line = data'lines'][lineN or {}

		for k, v in pairs(default) do

			if v then linek = linek or v end

		end

		line'title' = line'title' and mw.ustring.gsub(line'title'], '%%1', lineN)

		return line, lineN

	end

end



local function getColor(data, system, line, Type, frame)

	if system then

		if line then return frame:expandTemplate{ title = system .. ' color', args = {line, 'branch' = Type} } end

		return frame:expandTemplate{ title = system .. ' color' }

	else

		line = (getLine(data, line))

		local default = data'lines']['_default'

		if line or default then

			default = default or {}

			if not line then line = mw.clone(default) end

			local color = line'color' or line'background color' or default'color' or default'background color' or data'system color'

			local Type_value = Type and line'types' and (line'types'][Type and line'types'][Type]['color'])

			if Type_value then color = Type_value end

			return color

		end

		return (default and (default'color' or default'background color']) or data'system color' or '')

	end

end



local lineN, typeN



local function somethingMissing(name, key, formats)

	local formatKeys = {}

	for k in pairs(formats) do

		table.insert(formatKeys, k)

	end

	return name .. ' was "' .. key .. '" but neither an entry for it nor a default was found. Choices were: ' .. table.concat(formatKeys, ', ')

end



local function getStation(station, _Format)

	if type(_Format) == 'table' then

		local lineNformats = _Format

		_Format = lineNformatslineN or lineNformats1

		if not _Format then

			error(somethingMissing('lineN', lineN, lineNformats))

		elseif type(_Format) == 'table' then

			local typeNformats = _Format

			_Format = typeNformatstypeN or typeNformats1

			if not _Format then

				error(somethingMissing('typeN', typeN, typeNformats))

			end

		end

	end

	if typeN then _Format = mw.ustring.gsub(_Format, '%%3', typeN) end

	if lineN then _Format = mw.ustring.gsub(_Format, '%%2', lineN) end

	return (mw.ustring.match(_Format, '%[%[.+%]%]')) and (mw.ustring.gsub(_Format, '%%1', station)) or table.concat({'[[', mw.ustring.gsub(_Format, '%%1', station), '|', station, ']]'})

end



local function getTerminusText(var, Format)

	local function subst(var1, var2)

		-- var1 is the terminus or table of termini; var2 is the key for the table of termini

		return type(var1) == 'string' and getStation(var1, (Formatvar1 or Format1]))

		or type(var1) == 'table' and #var1 > 0 and getStation(var1var2], (Formatvar1var2]] or Format1]))

		or ''

	end



	if Format then

		if type(var) == 'string' then

			return subst(var)

		elseif type(var) == 'table' and #var > 0 then

			local t = {subst(var, 1)}



			for i = 2, #var - 1 do

				ti = i18nlang]['comma'](subst(var, i))

			end



			if #var > 1 then t#var = i18nlang]['or'](subst(var, #var)) end

			if var'via' then

				if i18nlang]['via-first' then

					table.insert(t, 1, i18nlang]['via'](subst(var, 'via')))

				else

					table.insert(t, i18nlang]['via'](subst(var, 'via')))

				end

			end



			return table.concat(t)

		else

			return ''

		end

	else

		return var or ''

	end

end



function p._main(_args) -- Arguments are processed here instead of the main function



	local yesno = require('Module:Yesno')

	local trimq = require('Module:Trim quotes')._trim



	local boolean = {

		'oneway-left' = true,

		'oneway-right' = true,

		'reverse' = true,

		'reverse-left' = true,

		'reverse-right' = true

	}



	local args = {} -- Processed arguments

	local index = {} -- A list of addresses corresponding to number suffixes in the arguments



	for k, v in pairs(_args) do -- Maps each raw argument to processed arguments by string matching

		_argsk = v:match('^%s*(.-)%s*$')

		if _argsk and _argsk ~= '' then

			local a = mw.ustring.match(k, '^(.*%D)%d+$') or k -- The parameter; address 1 can be omitted

			local b = tonumber(mw.ustring.match(k, '^.*%D(%d+)$')) or 1 -- The address for a given argument; address 1 can be omitted



			if booleana then

				v = yesno(v)

			end



			if not argsb then

				argsb = {[a = v}

				table.insert(index, b)

			elseif argsb][a then

				return error(i18nlang]['error_duplicate'](a .. b))

			else

				argsb][a = v

			end

		end

	end

	table.sort(index)



	local function small(s, italic)

		return italic and '<div class="isA">' .. s .. '</div>'

			or '<div class="smA">' .. s .. '</div>'

	end



	local style = { -- Style for each cell type

		'header cell' = 'class="hcA"|',

		'header midcell' = 'colspan="3" class="hmA"|',

		'body cell' = 'class="bcA"|',

		'body banner' = 'class="bbA" style="color:inherit;background-color:#',

	}



	local function rgb(var)

		if var:len() == 3 then

			return {tonumber(var:sub(1, 1), 16) * 17, tonumber(var:sub(2, 2), 16) * 17, tonumber(var:sub(2, 2), 16) * 17}

		elseif var:len() == 6 then

			return {tonumber(var:sub(1, 2), 16), tonumber(var:sub(3, 4), 16), tonumber(var:sub(5, 6), 16)}

		end

		return {}

	end



	local data = {} -- A table of data modules for each address

	local noclearclass = (((_args.noclear or '') ~= '') and ' adjacent-stations-noclear' or '')

	local wikitable = {'{| class="wikitable adjacent-stations' .. noclearclass .. '"'}



	for i, v in ipairs(index) do

		-- If an address has a system argument, indexes the data module

		datav = argsv]['system' and getData(argsv]['system'])

		-- If an address has no system, the row uses data from the previous address

			or dataindexi - 1]]

			or (argsv]['header' and getData(argsindexi+1]]['system']))

			or error(i18nlang]['error_unknown'](argsv]['system']))



		local lang = datav]['lang' or lang



		if argsv]['system' and not argsv]['hide-system' then -- Header row

			local stop_noun = datav]['header stop noun' or i18nlang]['stop_noun'

			table.insert(wikitable, table.concat({'\n|-',

				'\n! scope="col" ', style'header cell'], i18nlang]['preceding'](stop_noun),

				'\n! scope="col" ', style'header midcell'], (datav]['system icon' and datav]['system icon' .. ' ' or ''), (datav]['system title' or ('[['.. argsv]['system' ..']]')),

				'\n! scope="col" ', style'header cell'], i18nlang]['following'](stop_noun)

			}))

			table.insert(wikitable, '')

			table.insert(wikitable, '')

			table.insert(wikitable, '')

		end



		if argsv]['header' then -- Subheader

			table.insert(wikitable, '\n|-\n!colspan="5" class="hmA"|'.. argsv]['header'])

			table.insert(wikitable, '')

			table.insert(wikitable, '')

			table.insert(wikitable, '')

		end



		if argsv]['line' or argsv]['left' or argsv]['right' or argsv]['nonstop' then

			if not argsv]['line' and i > 1 and not argsv]['system' then

				argsv]['line' = argsindexi - 1]]['line'

			end



			lineN = argsv]['line' or '_default'

			typeN = argsv]['type'

			if datav]['aliases' then

				lineN = datav]['aliases'][mw.ustring.lower(lineN)] or lineN

				if typeN then typeN = datav]['aliases'][mw.ustring.lower(typeN)] or typeN end

			end



			-- get the line table

			local line = datav]['lines' and (mw.clone(datav]['lines'][lineN]) or error(i18nlang]['error_unknown'](argsv]['line']))) or error(i18nlang]['error_line'])

			local default = datav]['lines']['_default' or {}

			line'title' = line'title' or default'title'

			line'title' = mw.ustring.gsub(line'title'], '%%1', lineN)



			-- cell across row for non-stop service

			if argsv]['nonstop' then

				table.insert(wikitable,

					table.concat({'\n|-\n|colspan="5" ',

						style'body cell'],

						((argsv]['nonstop' == 'former') and i18nlang]['nonstop_past' or i18nlang]['nonstop_present'])(p._box({data = datav], line = lineN, Type = typeN, inline = 'yes'}))

					})

				)

				table.insert(wikitable, '')

				table.insert(wikitable, '')

				table.insert(wikitable, '')

			else

				local Format = datav]['station format' or i18nlang]['error_format'



				local color, color_2, background_color, circular

				local Type = line'types' and line'types'][typeN -- get the line type table



				if Type then

					if Type'color' then

						-- line color is used as background if there is no background color in the line type table

						background_color = Type'background color' or line'color'

						color = Type'color'

						color_2 = Type'color2' or color

					else

						background_color = Type'background color' or line'background color'

						color = line'color' or default'color' or ''

						color_2 = line'color2' or color

					end

					if Type'circular' then

						-- Type may override the circular status of the line

						circular = Type'circular'

					end

				else

					background_color = line'background color'

					color = line'color' or default'color' or ''

					color_2 = line'color2' or color

					circular = line'circular'

				end



				-- Alternate termini can be specified based on type

				local sideCell = {true, true}

				for i, b in ipairs({'left', 'right'}) do

					if not argsv][b then -- If no station is given on one side, the station is assumed to be the terminus on that side

						local _through = argsv]['through-' .. b or argsv]['through'

						local _through_data = getLine(datav], _through)

						if _through_data then _through = _through_data'title' or _through end

						sideCelli = _through and "''" .. i18nlang]['through'](trimq(_through)) .. "''"

							or "''" .. trimq((argsv]['reverse-' .. b

							or argsv]['reverse']) and i18nlang]['reverse'

							or i18nlang]['terminus']) .. "''"

					else

						local terminusT

						local terminusN = Type and Typeb .. ' terminus' or lineb .. ' terminus'



						-- If the terminus table has more than one numbered key or has the via key then the table shows only the default termini, since terminusN[2] cannot be used and terminusN[via] is reserved

						if type(terminusN) == 'string' or (type(terminusN) == 'table' and (terminusN2 or terminusN'via'])) then

							if argsv]['to-' .. b then

								terminusT = argsv]['to-' .. b

								local _or = mw.ustring.match(terminusT, i18nlang]['or-format'])

								if _or then

									terminusT = mw.ustring.gsub(terminusT, i18nlang]['or-format'], '\127_OR_\127')

									terminusT = mw.ustring.gsub(terminusT, i18nlang]['comma-format'], '\127_OR_\127')

								end

								local _via = (mw.ustring.match(terminusT, i18nlang]['via-format']))

								if _via then

									terminusT = mw.ustring.gsub(terminusT, i18nlang]['via-format'], '')

									terminusT = mw.text.split(terminusT, '\127_OR_\127')

									terminusT'via' = _via

								elseif _or then

									terminusT = mw.text.split(terminusT, '\127_OR_\127')

								end

							else

								terminusT = terminusN

							end

						elseif type(terminusN) == 'table' then

							terminusT = terminusNargsv]['to-' .. b]] or terminusNargsv]['to']] or terminusN1

						end



						local mainText = argsv]['note-' .. b and getTerminusText(argsv][b], Format) .. small(argsv]['note-' .. b]) or getTerminusText(argsv][b], Format)



						local subText = (argsv]['oneway-' .. b or line'oneway-' .. b]) and i18nlang]['oneway'

							or argsv][b == terminusT and i18nlang]['terminus'

							or circular and terminusT

							or i18nlang]['towards'](getTerminusText(terminusT, Format))

						subText = small(subText, true)



						sideCelli = mainText .. subText

					end

				end



				table.insert(wikitable, '\n|-')

				table.insert(wikitable, '\n|' .. style'body cell' .. sideCell1])

				table.insert(wikitable, table.concat({'\n|', style'body banner'], color, '"|',

					'\n|', (background_color and 'class="bcA" style="background-color:rgba(' .. table.concat(rgb(background_color), ',') .. ',.2)"|' or style'body cell']), line'title'],



					-- Type; table key 'types' in subpages (datatype table, with strings as keys). If table does not exist then the input is displayed as the text

					(typeN and '<div>' .. (Type and Type'title' or typeN) .. '</div>' or ''),



					-- Note-mid; table key 'note-mid' in subpages. Overridden by user input

					((argsv]['note-mid' and small(argsv]['note-mid'])) or (Type and Type'note-mid' and small(Type'note-mid'])) or (line'note-mid' and small(line'note-mid'])) or ''),



					-- Transfer; uses system's station link table

					(argsv]['transfer' and small('transfer at ' .. getTerminusText(argsv]['transfer'], Format), true) or ''),



					'\n|', style'body banner'], color_2, '"|'}))

				table.insert(wikitable, '\n|' .. style'body cell' .. sideCell2])

			end

		end



		if argsv]['note-row' then -- Note

			if argsv]['note-row']:match('^%s*<tr') or argsv]['note-row']:match('^%s*%|%-') then

				table.insert(wikitable, '\n' .. argsv]['note-row'])

			else

				table.insert(wikitable, '\n|-\n|colspan="5" ' .. style'body cell' .. argsv]['note-row'])

			end

			table.insert(wikitable, '')

			table.insert(wikitable, '')

			table.insert(wikitable, '')

		end

	end



	local function combine(t, n)

		if tn + 4 ~= '' and tn + 4 == tn then

			tn + 4 = '' -- The cell in the next row is deleted

			local rowspan = 2

			while tn + rowspan * 4 == tn do

				tn + rowspan * 4 = ''

				rowspan = rowspan + 1

			end

			tn = mw.ustring.gsub(tn], '\n|class="', '\n|rowspan="' .. rowspan .. '" class="')

		end

	end



	local M = #wikitable

	for i = 3, M, 4 do combine(wikitable, i) end

	for i = 4, M, 4 do combine(wikitable, i) end

	for i = 5, M, 4 do combine(wikitable, i) end



	table.insert(wikitable, '\n|}')



	return table.concat(wikitable)

end



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, frame)

	end

end



p.main = makeInvokeFunction('_main')



function p._color(args, frame)

	local data = args.data

	if args1 or data then

		data = data or getData(args1], true)

		if not data then return getColor(nil, args1], args2], args3], frame) end

		return getColor(data, nil, args2], args3])

	end

end



p.color = makeInvokeFunction('_color')



function p._box(args, frame)

	local system = args1 or args.system

	lineN = args2 or args.line

	if not (system or lineN) then return '' end

	local line, Type, line_data

	local inline = args3 or args.inline

	typeN = args.type

	local data = args.data

	if system or data then

		data = data or getData(system, true)

		local color

		if data then

			local default = data'lines']['_default' or {}

			line, lineN = getLine(data, lineN)

			if typeN then

				typeN = data'aliases' and data'aliases'][mw.ustring.lower(typeN)] or typeN

				Type = line'types' and line'types'][typeN and line'types'][typeN]['title' or typeN

			end

			color = getColor(data, nil, lineN, typeN)

			if inline ~= 'box' then

				line_data = line or error(i18nlang]['error_unknown'](lineN))

				line = line_data'title' or default'title' or error(i18nlang]['error_missing']('title'))

				line = mw.ustring.gsub(line, '%%1', lineN)

			end

		else

			color = getColor(nil, system, lineN, typeN, frame)

			if inline ~= 'box' then

				line = frame:expandTemplate{ title = system .. ' lines', args = {lineN, 'branch' = typeN} }

				if mw.text.trim(line) == '' then return error(i18nlang]['error_unknown'](lineN)) end

			end

			Type = typeN

		end



		local result



		if Type and Type ~= '' and inline ~= 'box' then

			if line == '' then

				line = Type

			else

				result = ' – ' .. Type

			end

		end

		if args.note then result = (result or '') .. ' ' .. args.note end

		result = result or ''



		if not inline then -- [[Template:Legend]]

			result = '<div class="legend" style="page-break-inside:avoid;break-inside:avoid-column"><span class="legend-color" style="display:inline-block;min-width:1.25em;height:1.25em;line-height:1.25;margin:1px 0;border:1px solid black;background-color:#' .. color .. '"> </span> ' .. line .. result .. '</div>'

		elseif inline == 'yes' then

			result = '<span style="background-color:#' .. color .. ';border:1px solid #000">    </span>&nbsp;' .. line .. result

		elseif inline == 'box' then

			result = '<span style="background-color:#' .. color .. ';border:1px solid #000">    </span>' .. result

		elseif inline == 'link' then

			local link = args.link or mw.ustring.match(line, '%[%[([^%[:|%]]+)[|%]]')

			if link then

				result = '[[' .. link .. '|<span style="background-color:#' .. color .. ';border:1px solid #000">    </span>]]' .. result

			else

				result = '<span style="background-color:#' .. color .. ';border:1px solid #000">    </span>' .. result

			end

		elseif inline == 'square' then

			result = '<span style="color:#' .. color .. ';line-height:initial">■</span>&nbsp;' .. line .. result

		elseif inline == 'lsquare' then

			local link = args.link or mw.ustring.match(line, '%[%[([^%[:|%]]+)[|%]]')

			if link then

				result = '[[' .. link .. '|<span style="color:#' .. color .. ';line-height:initial">■</span>]]'

			else

				result = '<span style="color:#' .. color .. ';line-height:initial">■</span>'

			end

		elseif inline == 'dot' then

			result = '<span style="color:#' .. color .. ';line-height:initial">●</span> ' .. line .. result

		elseif inline == 'ldot' then

			local link = args.link or mw.ustring.match(line, '%[%[([^%[:|%]]+)[|%]]')

			if link then

				result = '[[' .. link .. '|<span style="color:#' .. color .. ';line-height:initial">●</span>]]'

			else

				result = '<span style="color:#' .. color .. ';line-height:initial">●</span>'

			end

		elseif inline == 'small' then

			result = '<span style="background-color:#' .. color .. '"> </span>' .. ' ' .. line .. result

		else

			local yesno = require("Module:Yesno")

			local link = args.link or mw.ustring.match(line, '%[%[([^%[:|%]]+)[|%]]')

			local border_color, text_color

			local color_box = data'color box format' or data'rail box format' or {}

			if line_data then

				if line_data'types' and line_data'types'][typeN then

					local Type_data = line_data'types'][typeN

					border_color = Type_data'border color' or line_data'border color' or color

					text_color = Type_data'text color' or line_data'text color'

					if color_box == 'title' and not args4 then

						lineN = Type_data'short name' or line_data'short name' or require('Module:Delink')._delink{line}

					else

						lineN = Type_data'short name' or line_data'short name' or lineN

					end

				else

					border_color = line_data'border color' or color

					text_color = line_data'text color'

					if color_box == 'title' and not args4 then

						lineN = line_data'short name' or require('Module:Delink')._delink{line}

					else

						lineN = line_data'short name' or lineN

					end

				end

			else

				border_color = color

			end

			text_color = text_color and '#' .. text_color or require('Module:Color contrast')._greatercontrast{color}

			local bold = ';font-weight:bold'

			if (yesno(args.bold) == false) then bold = '' end

			if inline == 'route' then -- [[Template:RouteBox]]

				if link then

					result = '<span style="background-color:#' .. color .. ';border:.075em solid #' .. border_color .. ';padding:0 .3em">[[' .. link .. '|<span style="color:' .. text_color .. bold .. ';font-size:inherit;white-space:nowrap">' .. lineN .. '</span>]]</span>'

				else

					result = '<span style="background-color:#' .. color .. ';border:.075em solid #' .. border_color .. ';padding:0 .3em;color:' .. text_color .. bold .. ';font-size:inherit;white-space:nowrap">' .. lineN .. '</span>'

				end

			elseif inline == 'croute' then -- [[Template:Bahnlinie]]

				if link then

					result = '<span style="background-color:#' .. color .. ';border:.075em solid #' .. border_color .. ';border-radius:.5em;padding:0 .3em">[[' .. link .. '|<span style="color:' .. text_color .. bold .. ';font-size:inherit;white-space:nowrap">' .. lineN .. '</span>]]</span>'

				else

					result = '<span style="background-color:#' .. color .. ';border:.075em solid #' .. border_color .. ';border-radius:.5em;padding:0 .3em;color:' .. text_color .. bold .. ';font-size:inherit;white-space:nowrap">' .. lineN .. '</span>'

				end

			elseif inline == 'xroute' then -- [[Template:Bahnlinie]]

				if link then

					result = '<span style="border:.075em solid #' .. border_color .. ';border-radius:.5em;padding:0 .3em">[[' .. link .. '|<span style="color:#' .. color .. bold .. ';font-size:inherit;white-space:nowrap">' .. lineN .. '</span>]]</span>'

				else

					result = '<span style="border:.075em solid #' .. border_color .. ';border-radius:.5em;padding:0 .3em;color:#' .. color .. bold .. ';font-size:inherit;white-space:nowrap">' .. lineN .. '</span>'

				end

			elseif inline == 'broute' then

				if link then

					result = '<span style="background-color:#' .. color .. ';border:.075em solid #000;padding:0 .3em">[[' .. link .. '|<span style="color:' .. text_color .. bold .. ';font-size:inherit;white-space:nowrap">' .. lineN .. '</span>]]</span>'

				else

					result = '<span style="background-color:#' .. color .. ';border:.075em solid #000;padding:0 .3em;color:' .. text_color .. bold .. ';font-size:inherit;white-space:nowrap">' .. lineN .. '</span>'

				end

			else -- [[Template:Legend]] (fallback; duplication to simplify logic)

				result = '<div class="legend" style="page-break-inside:avoid;break-inside:avoid-column"><span class="legend-color" style="display:inline-block;min-width:1.25em;height:1.25em;line-height:1.25;margin:1px 0;border:1px solid black;background-color:#' .. color .. '"> </span> ' .. line .. result .. '</div>'

			end

		end



		result = mw.ustring.gsub(result, ':%s*#transparent', ':transparent')



		return result

	end

end



p.box = makeInvokeFunction('_box')



function p._icon(args, frame)

	local system = args1 or args.system

	local data = args.data



	if not system and not data then

		return

	end



	data = data or getData(system)



	local line, line_name = getLine(data, args2 or args.line)



	local icon

	local icon_format



	if line then

		local line_type = args3 or args.type

		if line_type then

			line_type = data.aliases and  data.aliasesmw.ustring.lower(line_type)] or line_type

			line_type = line.types and line.typesline_type -- If there's no type table or entry for this type, then it can't have its own icon

			icon_format = line_type'icon format' or data'type icon format'



			if line_type.icon then

				icon = line_type.icon

			end

		end



		if not icon then

			icon = line.icon

		end



		-- Only if there is no icon use the icon_format.

		if not icon and not icon_format then

			icon_format = line'icon format' or data'line icon format'

		end



		local default = data.lines._default or {}

		if icon and string.find(icon, "%%1") and default and default.icon then

			icon = mw.ustring.gsub(default.icon, '%%1', line_name)

		end



	end



	if not icon then

		icon = data'system icon'

	end



	if not icon_format then

		icon_format = data'system icon format'

	end



	if icon_format then

		if icon_format ~= 'image' then

			icon = p._box({data = data, 2 = (args2 or args.line), 3 = icon_format, type = (args3 or args.type), bold = args.bold, link = args.link}, frame)



			if args.name then

				if line and line.title then

					return icon .. " " .. line.title

				end

				return icon .. " " .. data"system title"

			end

		end

	end



	local size = args.size

	if size then

		if mw.ustring.match(size, '%d$') then

			size = '|' .. size .. 'px'

		else

			size = '|' .. size

		end

		-- Upright values are to be disabled until there is use of upright scaling in subpages; doesn't seem to work anyway as of 2018-08-10

		local regex = {

			'|%s*%d*x?%d+px%s*([%]|])', -- '|%s*upright=%d+%.?%d*%s*([%]|])', '|%s*upright%s*([%]|])'

		}

		if mw.ustring.match(icon, regex1]) then

			icon = mw.ustring.gsub(icon, regex1], size .. '%1')

	--	elseif mw.ustring.match(icon, regex[2]) then

	--		icon = gsub(icon, regex[2], size .. '%1')

	--	elseif mw.ustring.match(icon, regex[3]) then

	--		icon = gsub(icon, regex[3], size .. '%1')

		else

			icon = mw.ustring.gsub(icon, '(%[%[[^%]|]+)([%]|])', '%1' .. size .. '%2')

		end

	end



	local link = args.link

	if link then

		if mw.ustring.match(icon, '|%s*link=[^%]|]*[%]|]') then

			icon = mw.ustring.gsub(icon, '|%s*link=[^%]|]*([%]|])', '|link=' .. link .. '%1')

		else

			icon = mw.ustring.gsub(icon, '(%[%[[^%]|]+)([%]|])', '%1|link=' .. link .. '%2')

		end

	end



	local alt = args.alt or link

	if alt then

		if mw.ustring.match(icon, '|%s*alt=[^%]|]*[%]|]') then

			icon = mw.ustring.gsub(icon, '|%s*alt=[^%]|]*([%]|])', '|alt=' .. alt .. '%1')

		else

			icon = mw.ustring.gsub(icon, '(%[%[[^%]|]+)([%]|])', '%1|alt=' .. alt .. '%2')

		end

	end



	if args.name then

		if line and line.title then

			return icon .. " " .. line.title

		end

		return icon .. " " .. data"system title"

	end

	return icon

end



p.icon = makeInvokeFunction('_icon')



function p._line(args, frame)

	local system = args1 or args.system

	local line = args2 or args.line

	if not line then return '' end

	local Type = args3 or args.type

	local data = args.data

	if system or data then

		data = data or getData(system, true)

		if data then

			line = (getLine(data, line)) or error(i18nlang]['error_unknown'](line))

			if Type then

				Type = data'aliases' and data'aliases'][mw.ustring.lower(Type)] or Type

				Type = line'types' and line'types'][Type and line'types'][Type]['title' or Type

			end

			line = line'title' or error(i18nlang]['error_missing']('title'))

		else

			line = frame:expandTemplate{ title = system .. ' lines', args = {line, 'branch' = Type} }

			if mw.text.trim(line) == '' then return error(i18nlang]['error_unknown'](lineN)) end

		end



		if Type and Type ~= '' then

			if line == '' then

				line = Type

			else

				line = line .. ' – ' .. Type

			end

		end

		return line

	end

end



p.line = makeInvokeFunction('_line')



function p._shortline(args, frame)

	local system = args1 or args.system

	lineN = args2 or args.line

	if not (system or lineN) then return '' end

	local line, Type, line_data

	typeN = args.type

	local data = args.data

	if system or data then

		data = data or getData(system, true)

		if data then

			local default = data'lines']['_default' or {}

			line, lineN = getLine(data, lineN)

			if typeN then

				typeN = data'aliases' and data'aliases'][mw.ustring.lower(typeN)] or typeN

				Type = line'types' and line'types'][typeN and line'types'][typeN]['title' or typeN

			end

			line_data = line or error(i18nlang]['error_unknown'](lineN))

			line = line_data'title' or default'title' or error(i18nlang]['error_missing']('title'))

			line = mw.ustring.gsub(line, '%%1', lineN)

		else

			line = frame:expandTemplate{ title = system .. ' lines', args = {lineN, 'branch' = typeN} }

			if mw.text.trim(line) == '' then return error(i18nlang]['error_unknown'](lineN)) end

			Type = typeN

		end



		local result



		if Type and Type ~= '' then

			if line == '' then

				line = Type

			else

				result = ' – ' .. Type

			end

		end

		if args.note then result = (result or '') .. ' ' .. args.note end

		result = result or ''



		local link = args.link or mw.ustring.match(line, '%[%[([^%[:|%]]+)[|%]]')

		if line_data then

			if line_data'types' and line_data'types'][typeN then

				local Type_data = line_data'types'][typeN

				lineN = Type_data'short name' or line_data'short name' or lineN

			else

				lineN = line_data'short name' or lineN

			end

		end

			

		if link then

			result = '[[' .. link .. '|' .. lineN .. ']]'

		else

			result = lineN

		end

	

		result = mw.ustring.gsub(result, ':%s*#transparent', ':transparent')



		return result

	end

end



p.shortline = makeInvokeFunction('_shortline')



function p._station(args, frame)

	local system = args1 or args.system

	local station = args2 or args.station

	if not station then return '' end

	lineN = args3 or args.line

	typeN = args4 or args.type

	local data = args.data

	if system or data then

		data = data or getData(system, true)

		if data then

			local _Format = data'station format'][station or data'station format'][1

			if _Format then

				if data'aliases' then

					if lineN then

						lineN = data'aliases'][mw.ustring.lower(lineN)] or lineN

					end

					if typeN then

						typeN = data'aliases'][mw.ustring.lower(typeN)] or typeN

					end

				end

				station = getStation(station, _Format)

			else

				station = station or ''

			end

		else

			station = frame:expandTemplate{ title = system .. ' stations', args = {['station' = station, 'line' = lineN, 'branch' = typeN} }

		end



		return station

	end

end



p.station = makeInvokeFunction('_station')



function p._terminusTable(args, frame)

	local system = args1 or args.system

	lineN = args2 or args.line

	local side = mw.ustring.sub(mw.ustring.lower(args3 or args.side or ''), 1, 1)

	typeN = args.type

	local prefix = (side == 'r') and 'right' or 'left'

	local data = args.data



	if system or data then

		data = data or getData(system, true)

	end

	if data then

		local line = getLine(data, lineN) or error(i18nlang]['error_unknown'](lineN))

		if typeN and data and data'aliases' then typeN = data'aliases'][mw.ustring.lower(typeN)] or typeN end

		local Type = line'types' and line'types'][typeN



		local circular

		if Type then

			if Type'circular' then

				-- Type may override the circular status of the line

				circular = Type'circular'

			end

		else

			circular = line'circular'

		end



		return Type and Typeprefix .. ' terminus' or lineprefix .. ' terminus'], data'station format' or i18nlang]['error_format'], circular

	else

		local terminus = frame:expandTemplate{ title = 'S-line/' .. system .. ' ' .. prefix .. '/' .. lineN }

		return mw.ustring.gsub(terminus, '{{{type}}}', typeN)

	end

end



function p._terminus(args, frame)

	local var, Format, circular = p._terminusTable(args, frame)



	return circular and var or getTerminusText(var, Format)

end



p.terminus = makeInvokeFunction('_terminus')



function p._style(args, frame)

	local style = args1 or args.style

	local system = args2 or args.system

	local line = args3 or args.line

	local station = args4 or args.station

	local result = {}

	local data = args.data

	local default = 'background-color:#efefef' -- Default background color for {{Infobox station}}

	if system or data then

		data = data or getData(system, true)

	end

	if data then

		local function getValue(var)

			if type(var) == 'table' then

				var = varline or var1

				if type(var) == 'table' then

					var = varstation or var1

				end

			end

			if var ~= '' then return var end

		end



		if style == 'header' then

			local tmp = data'name format' and getValue(data'name format'])

			if tmp then table.insert(result, tmp) end

		elseif style == 'subheader' then

			local tmp = data'header background color' and getValue(data'header background color'])

			if tmp then

				table.insert(result, 'background-color:#' .. tmp)

				local color = data'header text color' and getValue(data'header text color'])

				if color then

					table.insert(result, 'color:#' .. color)

				else

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

					if greatercontrast{tmp} == '#FFFFFF' then table.insert(result, 'color:#FFFFFF') end

				end

			else

				table.insert(result, default)

				local color = data'header text color' and getValue(data'header text color'])

				if color then table.insert(result, 'color:#' .. color) end

			end

		end

		result = table.concat(result, ';')

	elseif system then

		local title = 'Template:' .. system .. ' style'

		local titleObj = mw.title.new(title)

		if titleObj and titleObj.exists then

			local tmp

			if style == 'header' then

				tmp = frame:expandTemplate{ title = title, args = {'name_format', line, station} }

				if tmp ~= '' then table.insert(result, tmp) end

			elseif style == 'subheader' then

				tmp = frame:expandTemplate{ title = title, args = {'thbgcolor', line, station} }

				if tmp ~= '' then

					table.insert(result, 'background-color:#' .. tmp)

					local color = frame:expandTemplate{ title = title, args = {'thcolor', line, station} }

					if color ~= '' then

						table.insert(result, 'color:#' .. color)

					else

						local ratio = require('Module:Color contrast')._ratio

						if ratio{tmp, '222222'} < 4.5 then table.insert(result, 'color:#FFFFFF') end -- 222222 is the default text color in Vector

					end

				else

					table.insert(result, default)

					tmp = frame:expandTemplate{ title = title, args = {'thcolor', line, station} }

					if tmp ~= '' then

						table.insert(result, 'color:#' .. tmp)

					end

				end

			end

			result = table.concat(result, ';')

		else

			if style == 'subheader' then

				result = default

			else

				result = ''

			end

		end

	else

		if style == 'subheader' then

			result = default

		else

			result = ''

		end

	end



	return result

end



function p.style(frame)

	local args = getArgs(frame, {frameOnly = true})

	return p._style(args, frame)

end



function p.convert(frame)

	local args = frame.args

	local code = mw.text.split(mw.ustring.gsub(args1], '^%s*{{(.*)}}%s*$', '%1'), '%s*}}%s*{{%s*')

	local system

	local group = tonumber(args.offset or 0) or 0

	local firstgroup = group + 1

	local delete = {

		's-rail' = true,

		's-rail-next' = true,

		's-rail-national' = true,

		's-start' = true,

		's-rail-start' = true,

		'start' = true,

		's-end' = true,

		'end' = true

	}

	local order = {

		'line', 'left', 'right', 'to-left', 'to-right',

		'oneway-left', 'oneway-right', 'through-left', 'through-right',

		'reverse', 'reverse-left', 'reverse-right',

		'note-left', 'note-mid', 'note-right', 'transfer'

		-- circular: use module subpage

		-- state: not implemented

	}

	local replace = {

		'previous' = 'left',

		'next' = 'right',

		'type' = 'to-left',

		'type2' = 'to-right',

		'branch' = 'type',

		'note' = 'note-left',

		'notemid' = 'note-mid',

		'note2' = 'note-right',

		'oneway1' = 'oneway-left',

		'oneway2' = 'oneway-right',

		'through1' = 'through-left',

		'through2' = 'through-right'

	}

	local remove_rows = {}

	local data = {}

	local noclear = false

	for i, v in ipairs(code) do

		codei = mw.ustring.gsub(codei], '\n', ' ')

		local template = mw.ustring.lower(mw.text.trim(mw.ustring.match(codei], '^[^|]+')))

		codei = mw.ustring.match(codei], '(|.+)$')

		if (mw.ustring.match(codei or '', 'noclear%s*=%s*[a-z]')) then

			noclear = true

		end

		if template == 's-line' then

			datai = {}

			local this_system = mw.text.trim(mw.ustring.match(codei], '|%s*system%s*=([^|]+)'))

			codei = mw.text.split(codei], '%s*|%s*')

			for m, n in ipairs(codei]) do

				local tmp = mw.text.split(n, '%s*=%s*')

				if tmp3 then

					tmp2 = mw.ustring.gsub(n, '^.-%s*=', '')

				end

				tmp1 = replacetmp1]] or tmp1

				if tmp2 then

					-- checks for matching brackets

					local curly = select(2, mw.ustring.gsub(tmp2], "{", ""))-select(2, mw.ustring.gsub(tmp2], "}", ""))

					local square = select(2, mw.ustring.gsub(tmp2], "%[", ""))-select(2, mw.ustring.gsub(tmp2], "%]", ""))

					if not (curly == 0 and square == 0) then

						local count = mw.clone(m)+1

						while not (curly == 0 and square == 0) do

							tmp2 = tmp2..'|'..codei][count

							curly = curly+select(2, mw.ustring.gsub(codei][count], "{", ""))-select(2, mw.ustring.gsub(codei][count], "}", ""))

							square = square+select(2, mw.ustring.gsub(codei][count], "%[", ""))-select(2, mw.ustring.gsub(codei][count], "%]", ""))

							codei][count = ''

							count = count+1

						end

					end

					datai][tmp1]] = tmp2

				end

			end

			if (this_system ~= system) or (not system) then

				system = this_system

				datai]['system' = system

			else

				datai]['system' = nil

			end

			local last = datai-1 or datai-2 or datai-3

			if last then

				for r, s in pairs({

					'hide1' = {'left', 'to-left', 'note-left', 'oneway-left'},

					'hide2' = {'right', 'to-right', 'note-right', 'oneway-right'},

					'hidemid' = {'type', 'note-mid'}

					}) do

					if datai][r then

						for m, n in ipairs(s) do

							if not datai][n then

								datai][n = lastn

							end

						end

					end

				end

			end

			codei = {}

			local X = '|'

			local Y = (i+group)..'='

			if datai]['system' then

				table.insert(codei], '|system')

				table.insert(codei], Y)

				table.insert(codei], datai]['system'])

				table.insert(codei], '\n')

			end

			for m, n in ipairs(order) do

				if datai][n then

					table.insert(codei], X)

					table.insert(codei], n)

					table.insert(codei], Y)

					table.insert(codei], datai][n])

				end

			end

			codei = table.concat(codei])

		elseif template == 's-note' then

			codei = mw.ustring.gsub(codei], '|%s*text%s*=', '|header'..i+group..'=')

			codei = mw.ustring.gsub(codei], '|%s*wide%s*=[^|]*', '')

		elseif template == 's-text' then

			codei = mw.ustring.gsub(codei], '|%s*text%s*=', '|note-row'..i+group..'=')

		elseif deletetemplate then

			codei = ''

			table.insert(remove_rows, 1, i) -- at the start, so that the rows are deleted in reverse order

			group = group-1

		end

	end

	for i, v in ipairs(remove_rows) do

		table.remove(code, v)

	end

	code = table.concat(code, '\n')

	local t = {'{{Adjacent stations' .. (noclear and '|noclear=y\n' or ''), '\n}}'}

	system = mw.ustring.match(code, '|system(%d*)=')

	code = mw.ustring.gsub(code, '\n\n+', '\n')

	if tonumber(system) > firstgroup then

		-- If s-line isn't the first template then the system will have to be moved to the top

		system = mw.ustring.match(code, '|system%d*=([^|]*[^|\n])')

		code = mw.ustring.gsub(code, '|system%d*=[^|]*', '')

		code = '\n|system'..firstgroup..'='..system..code

	elseif not mw.ustring.match(code, '^[^{%[]*|[^=|]+2=') then

		-- If there's only one parameter group then there's no need to have line breaks

		code = mw.ustring.gsub(code, '\n', '')

		code = mw.ustring.gsub(code, '(|[^=|]+)1=', '%1=')

		t2 = '}}'

		if not mw.ustring.match(code, '[%[{]') then

			code = mw.ustring.gsub(code, '|[^=|]*=$', '')

			code = mw.ustring.gsub(code, '|[^=|]*$', '')

		end

	end

	if not mw.ustring.match(code, '[%[{]') then

		code = mw.ustring.gsub(code, '|[^=|]*=|', '|')

		code = mw.ustring.gsub(code, '|[^=|]*|', '|')

		code = mw.ustring.gsub(code, '|[^=|]*=\n', '\n')

		code = mw.ustring.gsub(code, '|[^=|]*\n', '\n')

	end

	return t1..code..t2

end



return p