local getArgs = require('Module:Arguments').getArgs;
local yesno = require('Module:Yesno');
local tools = require 'Module:TableTools'
local railSystems = require 'Module:RailSystems'
local p = {}
local term = '落客-{zh-hans:站台;zh-hant:月台}-'
local dest = '終點站'
local function round(text, bg, fg)
return text and string.format([[<span style="display: inline-block; position: relative; width: 1.5em; height: 1.5em; line-height: 1.5em; vertical-align: middle; text-align: center; border-radius: 50%%; background-color:%s; color:%s; font-size: 1em; font-weight: bold;"><span style="display: inline-block; position: absolute; top: 0; left: 0; width: 100%%; height: 100%%;">%s</span></span>]], bg or 'black', fg or 'white', text or '') or nil
end
local function warn(content, value)
if value then
mw.addWarning(content)
end
return value
end
local function stationLink(stn, system)
if stn == '' or not stn then return nil end
if stn == 'dest' or mw.ustring.match(stn, '[終终][点點]站') then return dest end
if stn == 'term' or mw.ustring.match(stn, '落客[站月]台' ) then return term end
-- if format then
local str = system and system:getStation(stn):getLink() or stn
return str
-- else return stn
-- end
end
function p.main(frame)
local args = getArgs(frame, {removeBlanks = false})
return p._main(args, frame)
end
function p._main(args, frame)
frame = frame or mw.getCurrentFrame()
local numData = tools.numData(args)
local astyle = args.style
local out = mw.html.create 'table'
:addClass( "platform-config" )
-- 都有class了还css干啥
:cssText(nil and "border-collapse:collapse;width:99.5%;white-space:nowrap;box-sizing:border-box;")
-- @nullable
local systemName = args.system or args.s or args['系統'] or args['系统'] or nil
-- @nullable
local system = systemName and railSystems.System.load(systemName) or nil
local complex = yesno(args.complex or args['進階'] or args['进阶'])
if complex then
out:addClass 'platform-config-complex' end
for k, v in pairs(numData.other or {}) do
local prefix, i, suffix = mw.ustring.match(k, '^(.-)(%d+)(.+)$')
i = tonumber(i)
if prefix and suffix and i then
numData[i] = numData[i] or {}
numData[i][prefix .. '|' .. suffix] = v
end
end
-- 是否存在某一行有 pre 和 ap。
-- 这两个变量会对所有的行生效。
local prepend, append = false, false
for i, item in tools.sparseIpairs(numData) do
if item.pre then prepend = true end
if item.ap then append = true end
end
for i, item in tools.sparseIpairs(numData) do
local g = item.g or item['組'] or item['组'] or nil
local group = nil
if not g then
elseif mw.ustring.match(g, '[p台]') then group = "platform"
elseif mw.ustring.match(g, '[t軌轨]') then group = "track"
elseif mw.ustring.match(g, '[x文]') then group = "text" end
local style = item.style or nil
local future = g and mw.ustring.match(g, '[f來来]')
local tr = out:tag 'tr'
:addClass ('platform-config-row platform-config-group-' .. tostring(group))
if complex then
tr:addClass 'platform-config-row-complex' end
if future then tr:css('opacity', '50%') end
local border = {
plt = 'solid 2px black';
pltx = 'solid 2px black';
-- trk = 'solid 1px lightgray'
}
local text = item.text or item.t or item['文'] or nil
-- 代表这一行的 pre 和 ap 的单元格。
-- 如果所有行都没有 pre 和 ap,那么它们不存在。
-- 如果有某一行有 pre 或者 ap,即使这一行没有指定,这个单元格仍会存在。
-- 它们是 mw.html 对象,直接使用 :node 添加。
-- 在 platform 和 text 模式下,即使 prepend 和 append 为 true,它们也不一定被添加,有可能被相邻单元格合并。
-- 在 track 模式下,只要有 prepend 和 append,那么这两个单元格一定被添加。
local td_pre, td_ap
local prep = item.pre or nil
local pres = item['pre|s'] or nil
if prepend then
td_pre = mw.html.create 'td':cssText( pres ) :wikitext( prep ):addClass 'platform-config-prep'
end
local app = item['ap']
local aps = item['ap|s']
if append then
td_ap = mw.html.create 'td':cssText( aps ) :wikitext( app ):addClass 'platform-config-ap'
end
if group == "platform" then
local should_span_to_pre = (prepend and not item.pre) and 1 or 0
local should_span_to_ap = (append and not item.ap) and 1 or 0
local colspan = 1 + should_span_to_pre + should_span_to_ap
if item.pre then
tr:node(td_pre)
end
local bg = item.bg or item.background or item['背']
local p = item.type or item.p or item[ '類' ] or item[ '类' ] or nil
local type
if not p then
type = p
elseif string.find(p, 'side')
or mw.ustring.match(p, '[側侧]') then type = "side"
elseif string.find(p, 'split')
or mw.ustring.match(p, '[離离]') then type = "split"
elseif string.find(p, 'narrow')
or mw.ustring.match(p, '[窄]') then type = "narrow"
elseif string.find(p, 'island')
or mw.ustring.match(p, '[島岛]') then type = "island"
elseif string.find(p, 'bay')
or mw.ustring.match(p, '[灣湾]') then type = "bay"
elseif string.find(p, 'wide')
or mw.ustring.match(p, '[闊阔]') then type = "wide"
else type = nil end
if not type then
if g:find 's' then type = 'side'
elseif g:find 'n' then type = 'narrow'
elseif g:find 'i' then type = 'island'
elseif g:find 'b' then type = 'bay'
elseif g:find 'w' then type = 'wide'
else type = p
end
end
tr:addClass ('platform-type-' .. tostring(type))
local bt = item['border-top'] or item.bt or item[ '上邊框' ] or item[ '上边框' ]
local bb = item['border-bottom'] or item.bb or item[ '下邊框' ] or item[ '下边框' ]
local bl = item['border-left'] or item.bl or item[ '左邊框' ] or item[ '左边框' ]
local br = item['border-right'] or item.br or item[ '右邊框' ] or item[ '右边框' ]
local maintd = mw.html.create 'td'
:addClass 'platform-main-td'
local maintdwkt = {}
if type == "island" or type == "bay" or type == "wide" then
if complex then
tr:tag 'td'
:css( 'border-top' , bt )
:css( 'border-left' , bl )
:css( 'border-bottom', bb )
:css( 'background' , bg )
local prevItem = numData[i - 1] or {}
-- @nullable
local line_u = system and system:getLine(prevItem.l
or prevItem['綫'] or prevItem['線'] or prevItem['线']) or nil
local pu = prevItem['pd'] or prevItem['下台']
local color_u = line_u and line_u:getColor(true) or '#000000'
local rgb_u = { tonumber(string.sub(color_u, 2, 3), 16), tonumber(string.sub(color_u, 4, 5), 16), tonumber(string.sub(color_u, 6, 7), 16) }
local text_color_u = (rgb_u[1]*0.299 + rgb_u[2]*0.587 + rgb_u[3]*0.114) > 186 and "black" or "white"
-- out = out .. '<!-- ' .. rgb[1] .. ',' .. rgb[2] .. ',' .. rgb[3] .. ' -->'
local pno_u = round(pu, color_u, text_color_u)
local nextItem = numData[i + 1] or {}
local line_d = system and system:getLine(nextItem.l
or nextItem['綫'] or nextItem['線'] or nextItem['线']) or nil
local pd = nextItem.pd or nextItem['上台']
local line_color_d = line_d:getColor(true) or '#000000'
local rgb_d = { tonumber(string.sub(line_color_d, 2, 3), 16), tonumber(string.sub(line_color_d, 4, 5), 16), tonumber(string.sub(line_color_d, 6, 7), 16) }
local text_color_d = (rgb_d[1]*0.299 + rgb_d[2]*0.587 + rgb_d[3]*0.114) > 186 and "black" or "white"
-- out = out .. '<!-- ' .. rgb[1] .. ',' .. rgb[2] .. ',' .. rgb[3] .. ' -->'
local pno_d = round(pd, line_color_d, text_color_d)
tr:tag 'td'
:css( 'border-top' , bt )
:css( 'border-bottom', bb )
:css( 'background' , bg )
:wikitext( pno_u .. '<br/>' .. pno_d )
else
colspan = colspan + 4
end
(complex and maintd or tr)
-- :cssText( 'font-size:smaller;text-align:center;' )
:css ( 'background' , bg )
:css ( 'border-top' , bt )
:css ( 'border-bottom', bb )
:css ( 'border-left' , complex and 'none' or bl )
:css ( 'border-right' , complex and 'none' or br )
-- :css ( 'height' , complex and '3rem' or '2rem' )
if type ~= "wide" and future then
table.insert( maintdwkt , "未啟用" )
end
if type == "island" then
table.insert( maintdwkt , '[[島式月台|島式-{zh-cn:站台;zh-tw:月台;zh-hk:月台}-]]' )
elseif type == "bay" then
table.insert( maintdwkt , '[[港灣式月台|港灣式-{zh-cn:站台;zh-tw:月台;zh-hk:月台}-]]' ) end
elseif type == "side" or type == "split" or type == "narrow" then
local dir
-- dir = dn
if p and (string.find(p, 'dn') or string.find(p, 'down') or string.find(p, '下')) then
dir = 'down'
tr:addClass 'platform-dir'
bt = (bt or 'none')
bb = (bb or border.plt)
-- dir = up
elseif p and (string.find(p, 'up') or mw.ustring.find(p, '上')) then
dir = 'up'
tr:addClass 'platform-dir-up'
bt = (bt or border.plt)
bb = (bb or 'none')
else
tr:addClass 'platform-dir-none'
end
if complex then
tr:tag 'td'
:css( 'border-top' , bt )
:css( 'border-left' , bl )
:css( 'border-bottom', bb )
:css( 'background' , bg )
if dir == 'down' then
-- @nullable
local nextItem = numData[i + 1]
-- @nullable
local line_d = system and system:getLine(nextItem.l
or nextItem['綫'] or nextItem['線'] or nextItem['线']) or nil
local pd = nextItem.pu or nextItem['上台']
local line_color_d = '#' .. (line_d:getColor() or '000000')
local rgb_d = { tonumber(string.sub(line_color_d, 2, 3), 16), tonumber(string.sub(line_color_d, 4, 5), 16), tonumber(string.sub(line_color_d, 6, 7), 16) }
local text_color_d = (rgb_d[1]*0.299 + rgb_d[2]*0.587 + rgb_d[3]*0.114) > 186 and "black" or "white"
-- out = out .. '<!-- ' .. rgb[1] .. ',' .. rgb[2] .. ',' .. rgb[3] .. ' -->'
tr:tag 'td'
:css( 'border-top' , bt )
:css( 'border-bottom', bb )
:css( 'background' , bg )
:wikitext( round(pd, line_color_d, text_color_d) )
elseif dir == 'up' then
local prevItem = numData[i - 1]
local line_u = system and system:getLine(args.l
or prevItem['綫'] or prevItem['線'] or prevItem['线']) or nil
local pu = prevItem.pd or prevItem['下台']
local line_color_u = '#' .. (line_u:getColor() or '000000')
local rgb_u = { tonumber(string.sub(line_color_u, 2, 3), 16), tonumber(string.sub(line_color_u, 4, 5), 16), tonumber(string.sub(line_color_u, 6, 7), 16) }
local text_color_u = (rgb_u[1]*0.299 + rgb_u[2]*0.587 + rgb_u[3]*0.114) > 186 and "black" or "white"
-- out = out .. '<!-- ' .. rgb[1] .. ',' .. rgb[2] .. ',' .. rgb[3] .. ' -->'
tr:tag 'td'
:css( 'border-top' , bt )
:css( 'border-bottom', bb )
:css( 'background' , bg )
:wikitext( round(pu, line_color_u, text_color_u) )
end
else
colspan = colspan + 4
end
(complex and maintd or tr)
:cssText( 'height:1.25rem;font-size:smaller;text-align:center;' )
:css ( 'background' , bg )
:css ( 'border-top' , bt )
:css ( 'border-bottom', bb )
:css ( 'border-left' , complex and 'none' or bl )
:css ( 'border-right' , complex and 'none' or br )
if type ~= "narrow" and future then
table.insert( maintdwkt , "未啟用" )
end
if type == "side" then
table.insert( maintdwkt , '[[側式月台|側式-{zh-cn:站台;zh-tw:月台;zh-hk:月台}-]]' )
elseif type == "split" then
table.insert( maintdwkt ,'[[分離式月台|分離式-{zh-cn:站台;zh-tw:月台;zh-hk:月台}-]]' ) end
else
colspan = colspan + 4
end
local d = item.door or item.d or item['門'] or item['门']
if d == "左" or d == 'left' or d == 'L' or g:find 'l' then
maintdwkt[#maintdwkt + 1] = ',开左边门'
elseif d == "右" or d == 'right' or d == 'R' or g:find 'r' then
maintdwkt[#maintdwkt + 1] = ',开右边门'
elseif d and mw.ustring.match( d, "上[左右]下[左右]" ) then
local str = mw.ustring.gsub( d, "上([左右])下([左右])",
' <div style="display:inline-block;vertical-align:middle;line-height:1.2em;">↑開%1邊门<br/>↓開%2邊门</div>'
)
maintdwkt[#maintdwkt + 1] = str
elseif d then maintdwkt[#maintdwkt + 1] = d end
maintdwkt [#maintdwkt + 1] = text or nil
maintd:wikitext( table.concat( maintdwkt) )
if colspan ~= 1 then
maintd:attr('colspan', tostring(colspan))
end
tr:node(maintd)
if complex then
tr:tag 'td'
:attr( 'colspan', '2' )
:css ( "border-top" , bt )
:css ( "border-bottom", bb )
:css ( "border-right" , br )
:css ( "background" , bg )
end
if item.ap then
tr:node(td_ap)
end
elseif group == "track" then
tr:node(td_pre or nil)
-- unless above is platform pointing down, add bar
local prevItem = numData[i - 1] or {}
local nextItem = numData[i + 1] or {}
local pg = prevItem.group or prevItem.g or prevItem['組'] or prevItem['组']
local pp = prevItem['p'] or prevItem['類'] or prevItem['类']
local ng = nextItem.group or nextItem.g or nextItem['組'] or nextItem['组']
local common = g and mw.ustring.match( g, "[c共]" )
local nextcom = ng and mw.ustring.match( ng , "[c共]" )
-- if pg and pp and mw.ustring.match(pg, '[p台]') and ((
-- (string.find(pp, 'dn') or string.find(pp, '下') )
-- and ( string.find(pp, 'side') or mw.ustring.match(pp, '[側侧]')
-- or string.find(pp, 'split') or mw.ustring.match(pp, '[離离]')
-- or string.find(pp, 'narrow') or mw.ustring.match(pp, '[窄]') )
-- ) or (
-- string.find(pp, 'island') or mw.ustring.match(pp, '[島岛]')
-- or string.find(pp, 'bay') or mw.ustring.match(pp, '[灣湾]')
-- or string.find(pp, 'wide') or mw.ustring.match(pp, '[闊阔]')
-- ))
-- or common then -- do nothing
-- else style = (style or '') .. 'border-top:' .. border.trk end
-- get line info
local l = item.line or item.l or item['綫'] or item['線'] or item['线']
or args.line or args.l or args['綫'] or args['線'] or args['线']
-- @nullable
local line = system and system:getLine(l)
-- platform number
local p = item.platform or item['p'] or item['台']
-- 如果没有指定p,那么line_color、rgb和text_color的值都不会计算。
local line_color = p and ('#' .. (line and line:getColor() or '000000')) or nil
-- 以后有必要引入颜色变量解析
local rgb = p and { tonumber(string.sub(line_color, 2, 3), 16), tonumber(string.sub(line_color, 4, 5), 16), tonumber(string.sub(line_color, 6, 7), 16) } or nil
local text_color = p and (args.textcolor or args.tc or (rgb[1]*0.299 + rgb[2]*0.587 + rgb[3]*0.114) > 186 and "black" or "white") or nil
-- out = out .. '<!-- ' .. rgb[1] .. ',' .. rgb[2] .. ',' .. rgb[3] .. ' -->'
local pl = item.pl
local pr = item.pr
local pu = item['pu'] or item['上台'] or item['上号']
local pd = item['pd'] or item['下台'] or item['下号']
-- arrow direction
local al = (item['al'] or item['左箭'])
if al and mw.ustring.match(al,'[l左]') then al = "L"
elseif al and mw.ustring.match(al,'[r右]') then al = "R"
elseif mw.ustring.match(g, '[l左]') and not al then al = "L"
elseif al == '/' then al = nil end
local arrow_l = al and frame:expandTemplate{ title = 'Arrow', args = {al or "L"} } or ''
local ar = (item['ar'] or item['右箭'])
if ar and mw.ustring.match(ar,'[l左]') then ar = "L"
elseif ar and mw.ustring.match(ar,'[r右]') then ar = "R"
elseif mw.ustring.match(g, '[r右]') and not ar then ar = "R"
elseif ar == '/' then ar = nil end
local arrow_r = ar and frame:expandTemplate{ title = 'Arrow', args = {ar or "R"} } or ''
-- al和ar将决定箭头是否显示
local merge_down = string.find(g, 'md') -- unused
local merge_up = string.find(g, 'mu') -- unused
-- if (args[ 'p' .. i ] or args[ 'pl' .. i ] or args[ 'pr' .. i ])
-- and not (args[ 'pu' .. i ] or args[ 'pd' .. i ]) then
-- local np = args[ 'p' .. i+1 ]
-- if pp == 'side-dn' or pp == 'split-dn' or pp == 'narrow-dn'
-- or pp == 'island' or pp == 'bay' or pp == 'wide' then pu = (al ~= "" and "l" or "r")
-- elseif string.find(np, 'side') or string.find(np, 'split') or string.find(np, 'narrow')
-- or np == 'island' or np == 'bay' or np == 'wide' then pd = (al ~= "" and "l" or "r") end
-- end
-- track text
local ttxt = mw.html.create()
local pre = (future and '未來' or '') .. (l and (line and line:getRichLink() or l) or '')
local d1 = item['d|d1' ] or item['d' ] or item['往' ]
local d1x = item['d|d1x'] or item['d|x' ] or item['往|x' ]
local d2 = item['d|d2' ] or item['及往' ]
local d2x = item['d|d2x'] or item['及往|x' ]
local df = item['d|f' ] or item['未來往' ] or item['未来往' ]
local df2 = item['d|f2' ] or item['未來及往' ] or item['未来及往' ]
local n1 = item['n|d1' ] or item['n' ] or item['下站' ]
local n1x = item['n|d1x'] or item['n|x' ] or item['下站|x' ]
local n2 = item['n|d2' ] or item['另下站' ]
local n2x = item['n|d2x'] or item['另下站|x' ]
local nf = item['n|f' ] or item['未來下站' ] or item['未来下站' ]
local nf2 = item['n|f2' ] or item['未來另下站'] or item['未来另下站' ]
local direction
if al and not ar then
direction = 'left'
elseif ar and not al then
direction = 'right' end
local station_id = item.station or item.s or args.station or args.s
local station = line and station_id and station_id ~= '' and line:getStation(station_id) or nil
local dest1 = stationLink(d1, system)
local condition = nil -- 明确 condition 为 nil
if not d1 and not dest1 and line and direction and (line.data.terminus or line.data.stations) then
-- 如有可能,可以自动决定终点站
local terminalStation = line:getTerminalStation(direction, nil, condition)
if terminalStation then
dest1 = terminalStation:getLink()
end
end
local dest2 = stationLink(d2, system)
local next1 = stationLink(n1, system)
if not n1 and not next1 and line and station_id and direction and line.data.stations then
-- 如有可能,可以自动决定临近车站
local adjacent = line:getAdjacentStations(station_id, condition)
if adjacent and adjacent[direction] then
next1 = adjacent[direction]:getLink()
end
end
local next2 = stationLink(n2, system)
local destf = stationLink(df, system)
local nextf = stationLink(nf, system)
local destf2 = stationLink(df2, system)
local nextf2 = stationLink(nf2, system)
ttxt:wikitext(pre)
if dest1 or d1x then
ttxt:wikitext(dest1 and '往' or nil):wikitext(dest1):wikitext(d1x)
if destf then
ttxt:tag 'span':css('opacity', '50%')
:wikitext ' · 未来:'
:wikitext(destf)
end
if dest2 or d2x then
ttxt:wikitext(dest2 and '或' or nil):wikitext(dest2):wikitext(d2x)
if destf2 then
ttxt:tag 'span':css('opacity', '50%')
:wikitext ' · '
:wikitext(nextf2)
end
end
end
if next1 or n1x then
local small = ttxt:tag 'small'
small
:wikitext '('
:wikitext((next1 or next2) and '下一站:' or nil)
:wikitext(next1):wikitext(n1x)
if nextf then
small:tag 'span':css('opacity', '50%')
:wikitext ' · 未来:'
:wikitext(nextf)
end
if next2 or n2x then
small
:wikitext '或'
:wikitext(next2):wikitext(n2x)
if nextf2 then
small:tag 'span':css('opacity', '50%')
:wikitext ' · 未来:'
:wikitext(nextf2)
end
end
small:wikitext ')'
end
ttxt:wikitext(text)
-- set left cell
local td1 = tr:tag 'td'
:addClass 'platform-arrow platform-arrow-left'
if al and al ~= '/' then
td1
:cssText( common and "padding-top: 0;" or
nextcom and "padding-bottom: 0;" or nil)
:wikitext( arrow_l )
else
td1:css('visibility', 'hidden'):css('width', '0')
end
local td2 = tr:tag 'td'
:addClass 'platform-number platform-number-left'
if not (pl or p or complex and (pu or pd)) then
td2 :css( 'visibility', 'hidden'):css('width', '0')
else
td2 :wikitext( round(pl or p, line_color, text_color) )
:cssText( common and "padding-top: 0;" or
nextcom and "padding-bottom: 0;" or nil)
end
-- set center cell
local td3 = tr:tag 'td'
:addClass 'platform-description'
:cssText( common and "padding-top: 0;" or
nextcom and "padding-bottom: 0;" or nil)
:node( ttxt )
-- set right cell
local td4 = tr:tag 'td'
:addClass 'platform-number platform-number-right'
if not (pr or p or complex and (pu or pd)) then
td4 :css( 'visibility', 'hidden'):css('width', '0')
else
td4 :wikitext( round(pr or p, line_color, text_color) )
:cssText( common and "padding-top: 0;" or
nextcom and "padding-bottom: 0;" or nil)
end
local td5 = tr:tag 'td'
:addClass 'platform-arrow platform-arrow-right'
if ar and ar ~= '/' then
td5
:cssText( common and "padding-top: 0;" or
nextcom and "padding-bottom: 0;" or nil)
:wikitext( arrow_r )
else
td5:css('visibility', 'hidden'):css('width', '0')
end
tr:node(td_ap or nil)
elseif group == "text" then
local should_span_to_pre = (prepend and not item.pre) and 1 or 0
local should_span_to_ap = (append and not item.ap) and 1 or 0
local colspan = 5 + should_span_to_pre + should_span_to_ap
local cstyle = item.cstyle
if item.pre then
tr:node(td_pre)
end
tr:tag 'td':attr('colspan', tostring(colspan))
:cssText( cstyle )
:wikitext( text )
if item.ap then
tr:node(td_ap)
end
end
end
out:wikitext(frame:extensionTag {name = 'templatestyles', args = {src = 'Template:月台配置/style.css'}})
return out
end
return p