「模組:Adjacent stations」:修訂間差異
Sinensisbegonie(留言 | 貢獻) 建立內容為「<includeonly><templatestyles src="Adjacent stations/styles.css"/>{{safesubst:#invoke:Adjacent stations|main}}</includeonly><noinclude> {{documentation}} </noinclude>」的新頁面 標籤:行動版編輯 行動版網頁編輯 |
Sinensisbegonie(留言 | 貢獻) 無編輯摘要 |
||
| (未顯示同一使用者於中間所作的 3 次修訂) | |||
| 第1行: | 第1行: | ||
< | require('strict') | ||
{{ | |||
</ | local p = {} | ||
local lang = 'zh-CN' -- 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 line[k] = line[k] 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 .. ' 曾是 "' .. key .. '" 但既没有找到它的条目,也没有找到默认值。选择是: ' .. table.concat(formatKeys, ', ') | |||
end | |||
local function getStation(station, _Format) | |||
if type(_Format) == 'table' then | |||
local lineNformats = _Format | |||
_Format = lineNformats[lineN] or lineNformats[1] | |||
if not _Format then | |||
error(somethingMissing('lineN', lineN, lineNformats)) | |||
elseif type(_Format) == 'table' then | |||
local typeNformats = _Format | |||
_Format = typeNformats[typeN] or typeNformats[1] | |||
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, (Format[var1] or Format[1])) | |||
or type(var1) == 'table' and #var1 > 0 and getStation(var1[var2], (Format[var1[var2]] or Format[1])) | |||
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 | |||
t[i] = i18n[lang]['comma'](subst(var, i)) | |||
end | |||
if #var > 1 then t[#var] = i18n[lang]['or'](subst(var, #var)) end | |||
if var['via'] then | |||
if i18n[lang]['via-first'] then | |||
table.insert(t, 1, i18n[lang]['via'](subst(var, 'via'))) | |||
else | |||
table.insert(t, i18n[lang]['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 | |||
_args[k] = v:match('^%s*(.-)%s*$') | |||
if _args[k] and _args[k] ~= '' 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 boolean[a] then | |||
v = yesno(v) | |||
end | |||
if not args[b] then | |||
args[b] = {[a] = v} | |||
table.insert(index, b) | |||
elseif args[b][a] then | |||
return error(i18n[lang]['error_duplicate'](a .. b)) | |||
else | |||
args[b][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 notheme" 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 | |||
data[v] = args[v]['system'] and getData(args[v]['system']) | |||
-- If an address has no system, the row uses data from the previous address | |||
or data[index[i - 1]] | |||
or (args[v]['header'] and getData(args[index[i+1]]['system'])) | |||
or error(i18n[lang]['error_unknown'](args[v]['system'])) | |||
local lang = data[v]['lang'] or lang | |||
if args[v]['system'] and not args[v]['hide-system'] then -- Header row | |||
local stop_noun = data[v]['header stop noun'] or i18n[lang]['stop_noun'] | |||
table.insert(wikitable, table.concat({'\n|-', | |||
'\n! scope="col" ', style['header cell'], i18n[lang]['preceding'](stop_noun), | |||
'\n! scope="col" ', style['header midcell'], (data[v]['system icon'] and data[v]['system icon'] .. ' ' or ''), (data[v]['system title'] or ('[['.. args[v]['system'] ..']]')), | |||
'\n! scope="col" ', style['header cell'], i18n[lang]['following'](stop_noun) | |||
})) | |||
table.insert(wikitable, '') | |||
table.insert(wikitable, '') | |||
table.insert(wikitable, '') | |||
end | |||
if args[v]['header'] then -- Subheader | |||
table.insert(wikitable, '\n|-\n!colspan="5" class="hmA"|'.. args[v]['header']) | |||
table.insert(wikitable, '') | |||
table.insert(wikitable, '') | |||
table.insert(wikitable, '') | |||
end | |||
if args[v]['line'] or args[v]['left'] or args[v]['right'] or args[v]['nonstop'] then | |||
if not args[v]['line'] and i > 1 and not args[v]['system'] then | |||
args[v]['line'] = args[index[i - 1]]['line'] | |||
end | |||
lineN = args[v]['line'] or '_default' | |||
typeN = args[v]['type'] | |||
if data[v]['aliases'] then | |||
lineN = data[v]['aliases'][mw.ustring.lower(lineN)] or lineN | |||
if typeN then typeN = data[v]['aliases'][mw.ustring.lower(typeN)] or typeN end | |||
end | |||
-- get the line table | |||
local line = data[v]['lines'] and (mw.clone(data[v]['lines'][lineN]) or error(i18n[lang]['error_unknown'](args[v]['line']))) or error(i18n[lang]['error_line']) | |||
local default = data[v]['lines']['_default'] or {} | |||
line['title'] = line['title'] or default['title'] or '' | |||
line['title'] = mw.ustring.gsub(line['title'], '%%1', lineN) | |||
-- cell across row for non-stop service | |||
if args[v]['nonstop'] then | |||
table.insert(wikitable, | |||
table.concat({'\n|-\n|colspan="5" ', | |||
style['body cell'], | |||
((args[v]['nonstop'] == 'former') and i18n[lang]['nonstop_past'] or i18n[lang]['nonstop_present'])(p._box({data = data[v], line = lineN, Type = typeN, inline = 'yes'})) | |||
}) | |||
) | |||
table.insert(wikitable, '') | |||
table.insert(wikitable, '') | |||
table.insert(wikitable, '') | |||
else | |||
local Format = data[v]['station format'] or i18n[lang]['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 args[v][b] then -- If no station is given on one side, the station is assumed to be the terminus on that side | |||
local _through = args[v]['through-' .. b] or args[v]['through'] | |||
local _through_data = getLine(data[v], _through) | |||
if _through_data then _through = _through_data['title'] or _through end | |||
sideCell[i] = _through and "''" .. i18n[lang]['through'](trimq(_through)) .. "''" | |||
or "''" .. trimq((args[v]['reverse-' .. b] | |||
or args[v]['reverse']) and i18n[lang]['reverse'] | |||
or i18n[lang]['terminus']) .. "''" | |||
else | |||
local terminusT | |||
local terminusN = Type and Type[b .. ' terminus'] or line[b .. ' 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 (terminusN[2] or terminusN['via'])) then | |||
if args[v]['to-' .. b] then | |||
terminusT = args[v]['to-' .. b] | |||
local _or = mw.ustring.match(terminusT, i18n[lang]['or-format']) | |||
if _or then | |||
terminusT = mw.ustring.gsub(terminusT, i18n[lang]['or-format'], '\127_OR_\127') | |||
terminusT = mw.ustring.gsub(terminusT, i18n[lang]['comma-format'], '\127_OR_\127') | |||
end | |||
local _via = (mw.ustring.match(terminusT, i18n[lang]['via-format'])) | |||
if _via then | |||
terminusT = mw.ustring.gsub(terminusT, i18n[lang]['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 = terminusN[args[v]['to-' .. b]] or terminusN[args[v]['to']] or terminusN[1] | |||
end | |||
local mainText = args[v]['note-' .. b] and getTerminusText(args[v][b], Format) .. small(args[v]['note-' .. b]) or getTerminusText(args[v][b], Format) | |||
local subText = (args[v]['oneway-' .. b] or line['oneway-' .. b]) and i18n[lang]['oneway'] | |||
or args[v][b] == terminusT and i18n[lang]['terminus'] | |||
or circular and terminusT | |||
or i18n[lang]['towards'](getTerminusText(terminusT, Format)) | |||
subText = small(subText, true) | |||
sideCell[i] = mainText .. subText | |||
end | |||
end | |||
table.insert(wikitable, '\n|-') | |||
table.insert(wikitable, '\n|' .. style['body cell'] .. sideCell[1]) | |||
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 | |||
((args[v]['note-mid'] and small(args[v]['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 | |||
(args[v]['transfer'] and small('于' .. getTerminusText(args[v]['transfer'], Format) .. '换乘', true) or ''), | |||
'\n|', style['body banner'], color_2, '"|'})) | |||
table.insert(wikitable, '\n|' .. style['body cell'] .. sideCell[2]) | |||
end | |||
end | |||
if args[v]['note-row'] then -- Note | |||
if args[v]['note-row']:match('^%s*< tr') or args[v]['note-row']:match('^%s*%|%-') then | |||
table.insert(wikitable, '\n' .. args[v]['note-row']) | |||
else | |||
table.insert(wikitable, '\n|-\n|colspan="5" ' .. style['body cell'] .. args[v]['note-row']) | |||
end | |||
table.insert(wikitable, '') | |||
table.insert(wikitable, '') | |||
table.insert(wikitable, '') | |||
end | |||
end | |||
local function combine(t, n) | |||
if t[n + 4] ~= '' and t[n + 4] == t[n] then | |||
t[n + 4] = '' -- The cell in the next row is deleted | |||
local rowspan = 2 | |||
while t[n + rowspan * 4] == t[n] do | |||
t[n + rowspan * 4] = '' | |||
rowspan = rowspan + 1 | |||
end | |||
t[n] = mw.ustring.gsub(t[n], '\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 p[funcName](args, frame) | |||
end | |||
end | |||
local function makeTemplateFunction(funcName) | |||
-- makes a function that can be returned from #invoke, using | |||
-- [[Module:Arguments]] | |||
return function (frame) | |||
local args = getArgs(frame, {frameOnly = true}) | |||
return p[funcName](args, frame) | |||
end | |||
end | |||
p.main = makeInvokeFunction('_main') | |||
function p._color(args, frame) | |||
local data = args.data | |||
if args[1] or data then | |||
data = data or getData(args[1], true) | |||
if not data then return getColor(nil, args[1], args[2], args[3], frame) end | |||
return getColor(data, nil, args[2], args[3]) | |||
end | |||
end | |||
p.color = makeInvokeFunction('_color') | |||
function p._box(args, frame) | |||
local system = args[1] or args.system | |||
lineN = args[2] or args.line | |||
if not (system or lineN) then return '' end | |||
local line, Type, line_data | |||
local inline = args[3] 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(i18n[lang]['error_unknown'](lineN)) | |||
line = line_data['title'] or default['title'] or error(i18n[lang]['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(i18n[lang]['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;color:inherit;background-color:#' .. color .. '"> </span> ' .. line .. result .. '</div>' | |||
elseif inline == 'yes' then | |||
result = '<span style="color:inherit;background-color:#' .. color .. ';border:1px solid #000"> </span> ' .. line .. result | |||
elseif inline == 'syes' then | |||
result = '<span style="color:inherit;background-color:#' .. color .. ';border:1px solid #000"> </span> ' .. '[[' .. system .. ']]' .. line .. result | |||
elseif inline == 'box' then | |||
result = '<span style="color:inherit;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="color:inherit;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> ' .. line .. result | |||
elseif inline == 'ssquare' then | |||
result = '<span style="color:#' .. color .. ';line-height:initial">■</span> ' .. '[[' .. system .. ']]' .. 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 == 'sdot' then | |||
result = '<span style="color:#' .. color .. ';line-height:initial">●</span> ' .. '[[' .. system .. ']]' .. 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="color:inherit;background-color:#' .. color .. '"> </span>' .. ' ' .. line .. result | |||
elseif inline == 'ssmall' then | |||
result = '<span style="color:inherit;background-color:#' .. color .. '"> </span>' .. ' ' .. '[[' .. system .. ']]' .. line .. result | |||
elseif inline == 'lsmall' then | |||
local link = args.link or mw.ustring.match(line, '%[%[([^%[:|%]]+)[|%]]') | |||
if link then | |||
result = '[[' .. link .. '|<span style="color:inherit;background-color:#' .. color .. '"> </span>]]' | |||
else | |||
result = '<span style="color:inherit;background-color:#' .. color .. '"> </span>' | |||
end | |||
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 args[4] 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 args[4] 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="color:inherit;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="color:inherit;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="color:inherit;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="color:inherit;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;color:inherit;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 = args[1] 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, args[2] or args.line) | |||
local icon | |||
local icon_format | |||
if line then | |||
local line_type = args[3] or args.type | |||
if line_type then | |||
line_type = data.aliases and data.aliases[mw.ustring.lower(line_type)] or line_type | |||
line_type = line.types and line.types[line_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] = (args[2] or args.line), [3] = icon_format, type = (args[3] 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, regex[1]) then | |||
icon = mw.ustring.gsub(icon, regex[1], 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') | |||
p['rail icon'] = makeTemplateFunction('_icon') | |||
function p._line(args, frame) | |||
local system = args[1] or args.system | |||
local line = args[2] or args.line | |||
if not line then return '' end | |||
local Type = args[3] 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(i18n[lang]['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(i18n[lang]['error_missing']('title')) | |||
else | |||
line = frame:expandTemplate{ title = system .. ' lines', args = {line, ['branch'] = Type} } | |||
if mw.text.trim(line) == '' then return error(i18n[lang]['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 = args[1] or args.system | |||
lineN = args[2] 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(i18n[lang]['error_unknown'](lineN)) | |||
line = line_data['title'] or default['title'] or error(i18n[lang]['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(i18n[lang]['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 = args[1] or args.system | |||
local station = args[2] or args.station | |||
if not station then return '' end | |||
lineN = args[3] or args.line | |||
typeN = args[4] 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') | |||
p['station link'] = makeTemplateFunction('_station') | |||
function p._terminusTable(args, frame) | |||
local system = args[1] or args.system | |||
lineN = args[2] or args.line | |||
local side = mw.ustring.sub(mw.ustring.lower(args[3] 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(i18n[lang]['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 Type[prefix .. ' terminus'] or line[prefix .. ' terminus'], data['station format'] or i18n[lang]['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 = args[1] or args.style | |||
local system = args[2] or args.system | |||
local line = args[3] or args.line | |||
local station = args[4] or args.station | |||
local result = {} | |||
local data = args.data | |||
local default = 'color:inherit;background-color:#efefef' -- Default text and 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 = var[line] or var[1] | |||
if type(var) == 'table' then | |||
var = var[station] or var[1] | |||
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(args[1], '^%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 | |||
code[i] = mw.ustring.gsub(code[i], '\n', ' ') | |||
local template = mw.ustring.lower(mw.text.trim(mw.ustring.match(code[i], '^[^|]+'))) | |||
code[i] = mw.ustring.match(code[i], '(|.+)$') | |||
if (mw.ustring.match(code[i] or '', 'noclear%s*=%s*[a-z]')) then | |||
noclear = true | |||
end | |||
if template == 's-line' then | |||
data[i] = {} | |||
local this_system = mw.text.trim(mw.ustring.match(code[i], '|%s*system%s*=([^|]+)')) | |||
code[i] = mw.text.split(code[i], '%s*|%s*') | |||
for m, n in ipairs(code[i]) do | |||
local tmp = mw.text.split(n, '%s*=%s*') | |||
if tmp[3] then | |||
tmp[2] = mw.ustring.gsub(n, '^.-%s*=', '') | |||
end | |||
tmp[1] = replace[tmp[1]] or tmp[1] | |||
if tmp[2] then | |||
-- checks for matching brackets | |||
local curly = select(2, mw.ustring.gsub(tmp[2], "{", ""))-select(2, mw.ustring.gsub(tmp[2], "}", "")) | |||
local square = select(2, mw.ustring.gsub(tmp[2], "%[", ""))-select(2, mw.ustring.gsub(tmp[2], "%]", "")) | |||
if not (curly == 0 and square == 0) then | |||
local count = mw.clone(m)+1 | |||
while not (curly == 0 and square == 0) do | |||
tmp[2] = tmp[2]..'|'..code[i][count] | |||
curly = curly+select(2, mw.ustring.gsub(code[i][count], "{", ""))-select(2, mw.ustring.gsub(code[i][count], "}", "")) | |||
square = square+select(2, mw.ustring.gsub(code[i][count], "%[", ""))-select(2, mw.ustring.gsub(code[i][count], "%]", "")) | |||
code[i][count] = '' | |||
count = count+1 | |||
end | |||
end | |||
data[i][tmp[1]] = tmp[2] | |||
end | |||
end | |||
if (this_system ~= system) or (not system) then | |||
system = this_system | |||
data[i]['system'] = system | |||
else | |||
data[i]['system'] = nil | |||
end | |||
local last = data[i-1] or data[i-2] or data[i-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 data[i][r] then | |||
for m, n in ipairs(s) do | |||
if not data[i][n] then | |||
data[i][n] = last[n] | |||
end | |||
end | |||
end | |||
end | |||
end | |||
code[i] = {} | |||
local X = '|' | |||
local Y = (i+group)..'=' | |||
if data[i]['system'] then | |||
table.insert(code[i], '|system') | |||
table.insert(code[i], Y) | |||
table.insert(code[i], data[i]['system']) | |||
table.insert(code[i], '\n') | |||
end | |||
for m, n in ipairs(order) do | |||
if data[i][n] then | |||
table.insert(code[i], X) | |||
table.insert(code[i], n) | |||
table.insert(code[i], Y) | |||
table.insert(code[i], data[i][n]) | |||
end | |||
end | |||
code[i] = table.concat(code[i]) | |||
elseif template == 's-note' then | |||
code[i] = mw.ustring.gsub(code[i], '|%s*text%s*=', '|header'..i+group..'=') | |||
code[i] = mw.ustring.gsub(code[i], '|%s*wide%s*=[^|]*', '') | |||
elseif template == 's-text' then | |||
code[i] = mw.ustring.gsub(code[i], '|%s*text%s*=', '|note-row'..i+group..'=') | |||
elseif delete[template] then | |||
code[i] = '' | |||
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=') | |||
t[2] = '}}' | |||
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 t[1]..code..t[2] | |||
end | |||
return p | |||