Module:Adjacent stations

local yesno = require("Module:Yesno") local style = { -- Style for cell ['header cell'] = 'style="width: 30%; vertical-align: middle;"|', ['header midcell'] = 'colspan="3" style="vertical-align: middle;"|', ['body cell'] = 'style="text-align: center; vertical-align: middle;"|', ['body banner'] = 'style="text-align: center; width: 8px; min-width: 8px; background-color: #', }

local function subFormat(s) return table.concat({' ', s, ' '}) end

local p = {}

function p.top return '{| class="wikitable adjacent-stations" style="max-width: 50em; margin:0.5em auto; font-size:95%; clear:both;"' end

function p.line(args, index, data) local _format local t = {} local function _subst(s1, s2) if s2 then if string.match(s2, '%[%[') then return mw.ustring.gsub(s2, '%%1', s1) else return table.concat({, s1, }) end else return s1 or '' end end local function station(var) if _format then if type(var) == 'string' then return _subst(var, (_format[var] or _format[1])) elseif type(var) == 'table' and #var > 0 then local function subst(k) return _subst(var[k], (_format[var[k]] or _format[1])) end local t = {} t[1] = subst(1) for i = 2, #var - 1 do					t[i] = table.concat({', ', subst(i)}) end if #var > 1 then t[#var] = table.concat({' or ', subst(#var)}) end if var['via'] then t[#var + 1] = table.concat({' via ', subst('via')}) end return table.concat(t) else return '' end else return var or '' end end local function circular(var) if _format then if type(var) == 'string' then return _format[var] or var elseif type(var) == 'table' and #var > 0 then local function subst(k) return _format[var[k]] or var[k] end local t = {} t[1] = subst(1) for i = 2, #var - 1 do					t[i] = table.concat({', ', subst(i)}) end if #var > 1 then t[#var] = table.concat({' or ', subst(#var)}) end if var['via'] then t[#var + 1] = table.concat({' via ', subst('via')}) end return table.concat(t) else return '' end else return var or '' end end for i, v in ipairs(index) do		if args.system[v] then -- Header row local stop_noun = data[v]['stop noun'] or 'station' table.insert(t, table.concat({'\n|-', '\n!', style['header cell'], 'Preceding ', stop_noun, '\n!', style['header midcell'], (data[v]['system title'] or (..args.system[v]..)), '\n!', style['header cell'], 'Following ', stop_noun }))			table.insert(t, '') table.insert(t, '') table.insert(t, '') end if args.line[v] or args.left[v] or args.right[v] then if not args.line[v] then args.line[v] = args.line[index[i-1]] end local _line = data[v]['lines'][args.line[v]] local left, right, colour, leftTerminus, rightTerminus, lang _format = data[v]['station format'] if data[v]['lang'] then lang = data[v]['lang'] colour = _line.colour or _line.color else if _line.colour then lang = 'en-GB' colour = _line.colour else lang = 'en-US' colour = _line.color end end local towards = (lang == 'en-GB') and 'towards ' or 'toward ' if type(_line['left terminus']) == 'string' then leftTerminus = _line['left terminus'] elseif type(_line['left terminus']) == 'table' then leftTerminus = _line['left terminus'][args.TypeL[v]] or _line['left terminus'][args.Type[v]] or _line['left terminus'][args.branch[v]] or _line['left terminus'][1] end if type(_line['right terminus']) == 'string' then rightTerminus = _line['right terminus'] elseif type(_line['right terminus']) == 'table' then rightTerminus = _line['right terminus'][args.TypeR[v]] or _line['right terminus'][args.Type[v]] or _line['right terminus'][args.branch[v]] or _line['right terminus'][1] end args.circular[v] = args.circular[v] or _line.circular if not args.left[v] then -- If no left station is defined, the station is assumed to be the left terminus local through = args.throughL[v] or args.through[v] if through then left = table.concat({"''through to ", data[v]['lines'][through]['line title']}) else left = ((args.reverseL[v] or args.reverse[v]) and "reverses direction" or "Terminus") end leftTerminus = '' else left = station(args.left[v]) if args.onewayL[v] or _line['oneway-left'] then leftTerminus = subFormat('One-way operation') elseif args.left[v] == _leftTerminus then leftTerminus = subFormat('Terminus') elseif args.circularL[v] or args.circular[v] then leftTerminus = subFormat(circular(leftTerminus)) else leftTerminus = subFormat(towards .. station(leftTerminus)) end end if not args.right[v] then -- If no right station is defined, the station is assumed to be the right terminus local through = args.throughR[v] or args.through[v] if through then right = table.concat({"''through to ", data[v]['lines'][through]['line title']}) else right = ((args.reverseR[v] or args.reverse[v]) and "reverses direction" or "Terminus") end rightTerminus = '' else right = station(args.right[v]) if args.onewayR[v] or _line['oneway-right'] then rightTerminus = subFormat('One-way operation') elseif args.right[v] == _rightTerminus then rightTerminus = subFormat('Terminus') elseif args.circularR[v] or args.circular[v] then rightTerminus = subFormat(circular(rightTerminus)) else rightTerminus = subFormat(towards .. station(rightTerminus)) end end table.insert(t, '\n|-') table.insert(t, table.concat({'\n|', style['body cell'], left, leftTerminus})) table.insert(t, table.concat({'\n|', style['body banner'], colour, '"|',				'\n|', style['body cell'], _line['line title'],				-- Branch; table key 'branches' in subpages (data type table, with strings as keys). If table does not exist then the input is displayed as the text				(args.branch[v] and table.concat({' ', (_line['branches'] and _line['branches'][args.branch[v]] or args.branch[v]), ' '}) or ),				-- Note; no table key. The input is displayed as the text				(args.note[v] and table.concat({' ', args.note[v], ' '}) or ),				-- Transfer; uses system's station link table				(args.transfer[v] and table.concat({' Transfer at: ', station(args.transfer[v]), ' '}) or ''),				'\n|', style['body banner'], colour, '"|'})) table.insert(t, table.concat({'\n|', style['body cell'], right, rightTerminus})) end end local function combine(t, i)		if t[i + 4] ~= '' and t[i + 4] == t[i] then t[i + 4] = '' -- The cell in the next row is deleted local rowspan = 2 while t[i + rowspan * 4] == t[i] do				t[i + rowspan * 4] = '' rowspan = rowspan + 1 end t[i] = mw.ustring.gsub(t[i], '\n|style="', table.concat({'\n|rowspan="', rowspan, '" style="'})) end end local Max = #t for i = 2, Max, 4 do combine(t, i) end for i = 3, Max, 4 do combine(t, i) end for i = 4, Max, 4 do combine(t, i) end return table.concat(t) end

function p.bottom return '\n|}' end

function p.main(frame) local _args = frame:getParent.args -- Raw arguments local args = { -- Processed arguments system = {}, line = {}, left = {}, right = {}, note = {}, Type = {}, TypeL = {}, TypeR = {}, through = {}, throughL = {}, throughR = {}, branch = {}, transfer = {}, onewayL = {}, onewayR = {}, reverse = {}, reverseL = {}, reverseR = {}, circular = {}, circularL = {}, circularR = {} }

local _index = {} local parameters = { -- Parameters list for mapping. Filters unknown arguments and renames some. ['strings'] = { ['system'] = 'system', ['line'] = 'line', ['left'] = 'left', ['right'] = 'right' },		['optional strings'] = { ['note-mid'] = 'note', ['type'] = 'Type', ['type-left'] = 'TypeL', ['type-right'] = 'TypeR', ['through'] = 'through', ['through-left'] = 'throughL', ['through-right'] = 'throughR', ['branch'] = 'branch', ['transfer'] = 'transfer' },		['optional boolean'] = { ['oneway-left'] = 'onewayL', ['oneway-right'] = 'onewayR', ['reverse'] = 'reverse', ['reverse-left'] = 'reverseL', ['reverse-right'] = 'reverseR', ['circular'] = 'circular', ['circular-left'] = 'circularL', ['circular-right'] = 'circularR' }	}	for k, v in pairs(_args) do -- Maps raw arguments to processed arguments using parameters list _args[k] = v:match('^%s*(.-)%s*$') if _args[k] and _args[k] ~= '' then local a = string.match(k, '^(.*%D)%d+$') or k			local b = tonumber(string.match(k, '^.*%D(%d+)$')) or 1 if parameters['strings'][a] then if args[parameters['strings'][a]][b] then return error(table.concat({'Same row number used multiple times for ', a, b})) else args[parameters['strings'][a]][b] = v					_index[b] = true end elseif args[parameters['optional strings'][a]] then args[parameters['optional strings'][a]][b] = v			elseif args[parameters['optional boolean'][a]] then args[parameters['optional boolean'][a]][b] = yesno(v) end end end

local index = {} -- A list of addresses, specified by the number suffixes in the arguments for k, v in pairs(_index) do		table.insert(index, k)	end table.sort(index) local data = {} for i, v in ipairs(index) do		if args.system[v] then -- If an address has a system input, indexes the data module data[v] = require('Module:Adjacent stations/' .. args.system[v]) --			data[v] = require('Module:Adjacent stations/' .. args.system[v] .. '/sandbox') else data[v] = data[index[i-1]] -- If an address has no system, the row uses data from the previous address. end end local rows = { -- For mixed use with S-line. Avoid this, since S-line has borders customized for Vector which don't match the ones used in Minerva and Timeless. ['all'] = table.concat({p.top, p.line(args, index, data), p.bottom}), ['top'] = table.concat({p.top, p.line(args, index, data)}), ['mid'] = table.concat({p.line(args, index, data)}), ['bottom'] = table.concat({p.line(args, index, data), p.bottom}) }	return rows[_args.rows] or rows['all'] end

return p