模块:RailSystems

本页使用了标题或全文手工转换,现处于中国大陆简体模式
求闻百科,共笔求闻

RailSystems模块可用于处理、访问轨道交通线路系统,并允许进行面向对象使用,支持管理线路连接、标志色、车站的页面名称和显示名称等数据。许多其他模块和模板都使用了本模块。

本模块共提供3个可用于面向对象的类:系统(System)、线路(Line)和车站(Station)。

数据存储

本模块的各个子页面存储各轨道交通系统的数据,详见Special:前缀索引/Module:RailSystems/。例如,Module:RailSystems/SHMetro存储上海轨道交通系统的数据,访问与上海轨道交通有关的数据时就会使用此页面的数据,Module:RailSystems/SuzhouRT则是存储的苏州轨道交通的数据。

子页面的数据都是通过mw.loadData加载的,因此有一些限制,可以指定override参数表示另一个模块的名称,将加载该模块以调整数据的行为。

为确保兼容性,本模块会尝试加载Module:Adjacent stations的子页面,并尽可能地转换为RailSystems的格式。请注意这种转换是有限的。建议有能力的用户直接创建对应的RailSystems子页面的版本,Adjacent stations的子模块将被逐渐弃用。

数据格式

本模块的子页面均为记录各个轨道交通线网的表,遵从#System的数据格式。这些表均通过mw.loadData加载,因此不能存有任何的函数。下列列表列举出了表中可以拥有的各个字段及其说明,加粗内容为字段名称(也就是键)。

System的数据格式

  • name:可选字符串。该线网系统的名称,如“苏州轨道交通”。
  • icon:字符串或表。若为字符串,则为图标文件名称。
  • icon:字符串或表。若为表,表示轨道交通系统的图标。
    • file:轨道交通系统的图标文件名称、
    • size:图标的默认大小。
    • link:图标的默认链接,可以是空字符串。
    • alt:图标的替换文字。
  • line_index:表。该线网系统的线路ID的索引。
    • 准则名称:由线路ID组成的数组。准则名称可以是all、open等,也可以是自定义的准则名称,如tram(表示列举所有的有轨电车线路)。
      之所以使用line_index,是因为线路在lines字段中是以表的形式存在的,且迭代顺序不确定。此外,还存在一个线路数据对应多个键的情况。
  • lines:表。该线网系统的所有线路。
  • stationNames:表。指定一些特殊的车站的显示名称和页面名称。将影响该轨道交通系统中的所有车站。该字段的值的各个字段都有两种写法:
    • 车站ID:字符串。键为车站ID,值的格式为“页面名称|线路名称”。如果其值含有链接的话,那么获取该车站的ID时,直接返回这个整个字符串。
    • 车站ID:表。键为车站ID,值的格式如下:
      • page:字符串。该车站对应的页面名称。可能为nil,表示该车站没有对应页面,生成链接时将产生纯文本。
      • name:字符串。该车站的显示名称。
      • link:可选字符串,一般不建议使用,表示该车站的链接。
  • override:可选字符串。指定一个模块名称,加载该系统时,将导入(require)此模块,并根据该模块的内容覆盖默认的行为,通常不需要使用。
  • stationFormat:可选字符串。表示各站对应页面名称的默认格式。默认为“%s站”。
  • plain_link_template:已经弃用,不再有用

Line的数据格式

  • short_name:字符串。线路的短名称,如“1号线”。
  • full_name:字符串,可选。线路的长名称,如“上海轨道交通1号线”。
  • name:字符串,可选。线路的通用名称,一般和短名称一致。
  • page:字符串,可选。该线路的页面名称。若不存在且不等于false,则取线路的完整名称,若仍不存在,则取线路的通用名称。若为false,则表示指定该线路没有页面名称,不产生链接。
  • link:字符串,可选且不建议。若存在,则获取线路链接(line:getLink())时,强制使用此值,忽略nameType和isStyled参数,因此不建议使用。
  • title:字符串。已弃用。一个形如“页面名称|线路名称”的字符串。
  • color:字符串。线路的十六进制颜色(不带井号)。
  • textColor:字符串。当文字显示在以线路颜色为底色的环境下时,文字应当显示的颜色。默认为白色,但对于部分线路应当设为黑色。
  • icon:字符串或表。与#System的数据格式中的icon相同。
  • stations:数组。该线路的车站列表。可选,但如果不存在,部分功能将受到限制。
  • terminus:数组。格式和用法同stations,用来表示终点站。调用Line:getTerminalStation时,如果该字段存在则根据该字段获取终点站,否则根据stations的值来推断终点站。
  • notOpen:布尔值,表示线路是否未开通。我们约定这里的开通是指的施工状态的开通,通常,受疫情、天气等因素临时影响运营的,不在此字段考虑范围内。若为true,表示线路未开通,此时该线路的所有车站(包括与已开通车站的换乘站)都是未开通的。
  • planning:布尔值,表示线路是否仍在规划中。若为true,则该线路的所有车站都是规划中的。
  • terminals:表。已弃用,表示线路的终点站。
    • left:表。
    • right:表。
  • isLoop:布尔值,表示是否为环线。如果为环线,则首尾两站也可以视为邻站。
  • trainTime:表。已弃用。
    • leftright字符串。已弃用。列车时间,格式为hh:mm:ss的格式。
  • openDates:表。已弃用。

Station的数据格式

在数据中,可以直接使用字符串表示一个简单车站,不为该车站指定特殊属性,其车站ID即为该字符串的值。尝试生成车站链接时,会尝试根据车站的ID从系统数据的stationNames参数中查找命名规则。如果表示一个带有特殊情况的车站(比如一条已开通的线路的多个车站中,有一个没有开通),或者一个抽象车站(将一条子线,或者线路的主线支线部分抽象为一个车站),则使用表,这种情况下表的格式如下:

  • id:字符串。车站的id。通常与车站名称一致。有时候不同的车站会有相同的名称(例如上海轨道交通4号线和6号线的浦电路是不同的车站),此时车站的id应当不同。
  • type:字符串,可选。表示车站的类型。"branches"表示该车站是由线路的主线和支线抽象而成的车站。"adjacent_branches"表示某站在主线和支线上的两个相邻车站构成的一个抽象车站(通常在轨道交通系统数据中不会使用)。"line"表示将一个子线路,将该线路抽象为车站,当有一段连续的车站未开通时,可以将这些车站放在一个子线路中,并将这个子线路的notOpen设为true。
  • notOpen:表示该车站是否暂未开通。若车站属于一个未开通的线路,则认为该线路中的所有车站是未开通的,不再需要为各车站设置notOpen的值。
  • planning:表示该车站是否还在规划中。若车站属于一个规划中的线路,则认为该线路中的所有车站都在规划中,不再需要为各车站设置planning的值。
  • icon:字符串或表。与#System的数据格式中的icon相同。
  • main:表,可选。若type为branches或adjacent_branches,则该值为一条线路数据,参见#Line的数据格式
  • branch:表,可选。若type为branches或adjacent_branches,则该值为一条线路数据,参见#Line的数据格式
  • line:表,可选。若type为line,则该值为一条线路数据,参见#Line的数据格式

System

System类表示一个轨道交通系统。每个轨道交通系统都有其data字段,其值为一个表(可以是mw.loadData返回的只读表)。

下面的所有例子中,system均表示一个System对象(注意大小写)。

System.of

System.of(data, override)

根据轨道交通系统的数据,生成一个System对象。通常只是用于内部使用,一般情况下请使用System.load。

data
该轨道交通系统的数据,是一个表,可以是mw.loadData返回的只读表。
override
值为函数组成的表,用来覆盖其自身及其返回的线路、车站对象的默认行为。一般不需要使用。

System.load

System.load(name)

从本模块的子页面(Module:RailSystems/name)中加载数据,根据加载的数据来返回一个System对象。例如,System.load 'SHMetro'就会加载Module:RailSystems/SHMetro的数据,并根据这些数据,返回一个表示上海轨道交通的线网系统对象。

如果Module:RailSystems/name不存在,则会尝试加载Module:Adjacent stations/name,并将其转换为本模块格式的数据。这种转换是有限的,这样做只是为了与Module:Adjacent stations保持兼容,并不建议这么做。

如果对应的数据不存在,则会抛出错误。如果name为nil,则返回nil,不抛出错误。

system:getIcon

system:getIcon(params)

返回该系统的图标。参数params是一个表,可以为nil。如果params是一个表,则可以拥有如下字段:

  • size:图标的大小,如果未指定,则取线路数据中的icon字段定义的图标大小。
  • link:图标的链接,如果未指定,则取线路数据中的icon字段定义的图标链接。可以是空字符串,表示没有链接。

注意:图标文件是由线路数据中的icon字段指定的,无法通过params参数指定。

system:getLines

system:getLines(criterion, condition)

获取该线网中的所有线路,返回由线路对象组成的数组。

criterion
一个指定线网列表的准则。通常可用的值由数据中的line_index字段决定。以上海轨道交通为例,Module:RailSystems/SHMetro中的line_index有all、tram、metro等字段,其中metro字段的值为{'1', '2', '3', ..., '26', '磁', '浦', '崇'},因此System.load 'SHMetro':getLines 'metro'将会返回由1号线至26号线以及磁浮线、浦江线、崇明线组成的数组。
如果线路数据没有line_index字段,则该函数会产生错误。
该参数的默认值为'all'。
condition
该线路需要符合的条件。默认为nil,即不需要满足任何条件。参见#line:meets

system:getLine

system:getLine(key)

返回该线网中的线路对象。key的值为线网数据中的lines字段的键。如果线路不存在,则返回nil。

调用此函数不需要线路数据中有line_index字段。

system:getLineOrThrow

system:getLineOrThrow(key)

返回该线网中的线路对象。如果线路不存在,则抛出异常。

system:getLineOrAnonymous

system:getLineOrAnonymous(key)

返回该线网中的线路对象。如果线路不存在,则返回仅使用该id的线路。

system:getStation

system:getStation(id)

根据指定的车站id,返回一个Station对象。

由于这种方法并未指定车站所属的线路,因此车站对象不会受到任何线路数据的影响,但是,该Station对象仍有可能受到系统数据中的stationNames字段影响。

该方法并不要求系统中确实存在这个id的车站,如果是一个不存在的车站,仍能正常返回结果。

Line

Line表示一条轨道交通线路。system:getLine(key)可用从线网系统中获得一个线路对象。

下面所有的例子中,line均表示一个Line对象。

Line.of

Line.of(data, system)

根据指定的线路数据和所属的系统,返回一个线路对象。通常用于内部使用。

line:getIcon

line:getIcon(params)

根据线路数据中的icon字段,返回线路的图标。如果该线路的图标不存在(大多数情况都是如此),则直接使用系统的图标。

line:getName

line:getName(nameType, isStyled)

返回线路的名称。nameType参数决定你是要获取该线路的完整名称、长名称、短名称还是简写名称。例如,

local line4 = System.load 'SHMetro':getLine '4'
print (line4:getName 'short')  -- '4号线'
print (line4:getName 'full')  -- '上海轨道交通4号线'
print (line4:getName 'blink')  -- '4'

根据线路数据,线路名称会依次尝试根据data.<nameType>_namedata.namedata.short_namedata.title决定。

isStyled视为布尔值,如果为true,则如果该线路未开通,就会显示为灰色,如果线路仍在规划中,则会显示为斜体(这两种情况可能会叠加)。

line:getStyledName

line:getStyledName(nameType)

相当于line:getName(nameType, true)

line:getColor

line:getColor(prefixed)

返回该线路的标志色。prefixed参数会在线路标志色前面加个井号,这样可以直接用于CSS。

local line = System.load 'SHMetro':getLine '4'
print (line:getColor())  -- '4F1F91'
print (line:getColor(true))  -- '#4F1F91'

如果线路没有指定标志色,则会返回nil。

line:getPage

line:getPage()

返回该线路对应的页面的名称。可能为nil。

会依次尝试根据线路数据的data.pagedata.titledata.full_namedata.name决定。其中,若由data.title决定,则取管道符之前的部分。

line:getLink

line:getLink(nameType, rich, isStyled)

返回一个该线路的维基链接。nameTypeisStyled参数请见#line:getName

local line4 = System.load 'SHMetro':getLine '4'
print (line:getLink())  -- '[[上海轨道交通4号线|4号线]]'
print (line:getLink(nil, true))  -- '[[上海轨道交通4号线|<span style="color:#4F1F91">■</span> 4号线]]''

如果nameType为"blink",那么会根据data.textColor决定文字颜色。

line:getRichLink

line:getRichLink(nameType, isStyled)

相当于line:getLink(nameType, true, isStyled)

line:getAdjacentStations

line:getAdjacentStations(station_id, condition)

返回该线路车站的指定条件的相邻车站。如果该线路数据没有stations字段,该函数会抛出错误。

如果该线路找到了id为station_id的车站,则会返回像{left = 左侧邻站对象, right = 右侧邻站对象}这样的表。如果没有符合指定条件的邻站(比如该车站是线路的终点站),则该侧相邻车站对象为nil。如果线路位于支线交汇处导致某个方向的邻站不止一个(例如龙溪路站),则这种情况下,相邻车站对象可以将多个车站看成一个抽象车站对象。

如果该线路没有找到id为station_id的车站,则返回nil(不是空表)。

condition的值请参考#station:meets

line:getStation

line:getStation(id, condition)

返回该线路中id为指定的id的车站。如果该线路不存在该车站,则返回nil;如果存在车站但不符合指定的条件(condition),也会返回nil。

如果该线路数据的车站列表(stations字段)中存在由线路组成的抽象车站(type为"line"或"branches"),则会尝试在该抽象车站中包含的车站中查找。

如果线路数据没有stations字段,则直接返回nil。

line:getSubline

line:getSubline(line)

该方法主要用于模块内部。line是另一个车站对象。

line:listStations

line:listStations(condition)

返回由该线路中的所有车站组成的数组。condition参数参见#station:meets

如果线路数据中没有stations字段,则无法列举出所有车站,将抛出错误。

line:getTerminalStation

line:getTerminalStation(direction, type, condition)

返回该线路指定方向的终点站的Station对象。direction可以是'left'或'right',如果为'both',则会返回像{left = 左侧终点站对象, right = 右侧终点站对象}这样的表。

type参数主要用于主线与支线。其值可以为'left'或'right',若为'both',则会返回像{main = 主线终点站对象, branch = 支线终点站对象}这样的表。

该方法会根据线路数据中的terminus字段或stations字段推断终点站。如果上述字段均不存在,则该方法返回nil。

line:renderStationList

line:renderStationList {isStyled = isStyled, showInterchange = showInterchange, condition = condition, interchangeCondition = interchangeCondition, stationCondition = stationCondition}

返回该线路所有车站链接字符串组成的列表。所有参数均可选。其中interchangeCondition和stationCondition参数若未指定,则使用condition参数。请参见#line:meets#station:meets

如果线路数据的stations字段不存在,则抛出错误。如果stations字段中有类型为"branches"的抽象车站,则这个抽象车站只会显示为“主线/支线”。

line:meets

line:meets(condition)

返回该线路是否满足指定的条件。condition可以是字符串、表或函数。

  • 若condition为字符串,则其值可以是'not_open'、'open'或'planning'。
  • 若condition为表,则其数据必须与该表匹配。
  • 若condition为函数,则返回condition(line)的值。

Station

Station对象通常是通过对线路对象调用getStationlistStations方法获得的。

下面的例子中,station均表示一个Station对象。

Station.of

Station.of(data, line, system)

根据指定的数据、线路和系统,构造一个Station对象。

参数line表示该车站是位于指定线路的车站。参数system表示该车站是位于指定系统的车站,若line参数存在,则默认为line.system。

line参数意义在于,如果该线路未开通(线路数据指定了未开通,但是车站自身数据未指定是否未开通),则一定认为该车站是未开通的。

system参数意义在于,获取名称和页面时受到该系统数据stationNames影响。

该函数主要由模块内部使用。

station:getIcon

station:getIcon(params)

返回该车站的图标,由该车站数据的icon字段决定。如果不存在,则尝试获取线路或系统的图标,如果扔不存在,则返回nil。

station:meets

station:meets(condition)

返回该线路是否满足指定的条件。condition可以是字符串、表或函数。

  • 若condition为字符串,则其值可以是'not_open'、'open'或'planning'。注意:车站是否未开通或处于规划中,由该车站对象自身的数据和车站对象对应的线路对象(若有)的数据共同决定。例如,对于指定了线路的车站对象,如果该线路未开通,则认为车站一定未开通(包括与已开通线路的换乘站)。
  • 若condition为表,则其数据必须与该表匹配。
  • 若condition为函数,则返回condition(line)的值。

station:getName

station:getName(isStyled, notShort)

返回车站名称。

参数说明:

isStyled
布尔值。若为true,则未开通的车站会显示为灰色,规划中的车站显示为斜体,与已开通线路的换乘站显示为粗体。
notShort
布尔值。若为true,则车站名称结尾加上“站”字。这种情况下会根据页面名称推断站名,一般不建议这样做。若为false,则一般与车站id相同。

station:getStyledName

station:getStyledName(notShort)

等同于station:getName(true, notShort)

station:getPage

station:getPage()

返回该车站的页面名称。页面名称通常由该对象所属的System对象的数据中的stationNames对象指定。

local SHMetro = System.load 'SHMetro'
print(SHMetro:getStation '人民广场':getPage())  -- '人民广场站 (上海市)'
print(SHMetro:getStation '浦电路4':getPage())  -- '浦电路站 (4号线)'

若该车站为抽象车站,则会抛出错误。

station:getLink

station:getLink(isStyled, notShort)

返回该车站的维基链接。参见#station:getName#station:getPage。同样也会受到系统数据(若有)中的stationNames字段影响。

station:getLines

station:getLines(criterion, condition, avoid)

获取该车站在线网中所属的线路组成的数组。例如,在上海轨道交通中,真如站是11号线与14号线的换乘站:

local SHMetro = System.load 'SHMetro'
local Zhenru = SHMetro:getStation '真如'
mw.logObject(
Zhenru:getLines()  -- 其值大致相当于 {SHMetro:getLine '11', SHMetro:getLine '14'}
)

criterioncondition参数与#system:getLines中的参数类似。

avoid参数是字符串,用于在结果中回避该车站对象自身所属的线路。前提是该车站对象必须指定了所属的线路。例如:

local SHMetro = System.load 'SHMetro'
local L11Zhenru SHMetro:getLine '11':getStation '真如'
mw.logObject(
Zhenru:getLines()  -- 其值大致相当于 {SHMetro:getLine '11', SHMetro:getLine '14'}
Zhenru:getLines(nil, nil, true)  -- 其值大致相当于 {SHMetro:getLine '14'}

通过invoke调用

本模块除了可以由Lua使用之外,也可以直接通过维基文本来使用。

stationLink

返回车站链接。其中name参数与station_id参数等价。

  • 模板链接:{{#invoke:RailSystems|stationLink|name=真如|system=SHMetro}}
  • 模板链接:{{#invoke:RailSystems|stationLink|name=盘蠡路|system=SuzhouRT}}
  • 模板链接:{{#invoke:RailSystems|stationLink|name=浦电路4|system=SHMetro}}
  • 模板链接:{{#invoke:RailSystems|stationLink|name=浦电路4|short=false|system=SHMetro}}

参数styled可用于将未开通或者计划中的车站添加特殊的样式。

lineColor

返回线路颜色。

  • 模板链接:{{#invoke:RailSystems|lineColor|line=1|system=SHMetro}}
    • 效果:EA0437
  • 模板链接:{{#invoke:RailSystems|lineColor|line=1|prefixed=1|system=SHMetro}}
    • 效果:
  1. EA0437
  • 模板链接:{{#invoke:RailSystems|lineColor|default=ffffff|line=114514|system=SHMetro}}
    • 效果:ffffff

lineLink

返回线路链接。如果optional为true,那么当线路id不存在时,返回空白字符串,否则抛出错误(下同)。

  • 模板链接:{{#invoke:RailSystems|lineLink|line=1|system=SHMetro}}
  • 模板链接:{{#invoke:RailSystems|lineLink|line=114514|optional=true|system=SHMetro}}
    • 效果:
  • 模板链接:{{#invoke:RailSystems|lineLink|line=1|rich=1|system=SHMetro}}
  • 模板链接:{{#invoke:RailSystems|lineLink|line=1|style=full|system=SHMetro}}

参数styled可用于将未开通或者计划中的线路添加特殊的样式。

lineName

返回线路名称,name_type参数与style参数等价。

linePage

返回线路对应的页面名称。

lineRichLink

与lineLink类似,但rich始终为true。参数styled可用于将未开通或者计划中的线路添加特殊的样式。

lineStationList

显示车站列表,其中name参数与line_id参数等价。

参数styled可用于将未开通或者计划中的车站添加特殊的样式。

lineTerminalLink

返回该车站的终点站链接。

  • 模板链接:{{#invoke:RailSystems|lineTerminalLink|direction=left|line=2|system=SHMetro}}
  • 模板链接:{{#invoke:RailSystems|lineTerminalLink|condition=open|direction=left|line=2|system=SHMetro}}
  • 模板链接:{{#invoke:RailSystems|lineTerminalLink|direction=left|line=11|system=SHMetro}}
  • 模板链接:{{#invoke:RailSystems|lineTerminalLink|direction=left|line=10|system=SHMetro}}

参数styled可用于将未开通或者计划中的车站添加特殊的样式。

lineTerminalName

返回该车站的终点站名称(一般不带链接)。

  • 模板链接:{{#invoke:RailSystems|lineTerminalName|direction=left|line=2|system=SHMetro}}
    • 徐泾东
  • 模板链接:{{#invoke:RailSystems|lineTerminalName|condition=open|direction=left|line=2|system=SHMetro}}
    • 徐泾东
  • 模板链接:{{#invoke:RailSystems|lineTerminalName|direction=left|line=11|system=SHMetro}}
    • 嘉定北/花桥
  • 模板链接:{{#invoke:RailSystems|lineTerminalName|direction=left|line=10|system=SHMetro}}
    • 虹桥火车站/航中路

测试

  1. 测试成功,结果:[[宁波站|宁波站]]
  2. 测试成功,结果:[[宁波站|宁波站]]
  3. 测试成功,结果:[[高桥西站(宁波)|高桥西]]
  4. 测试成功,结果:[[高桥西站(宁波)|高桥西站]]
  5. 测试成功,结果:[[泽民站|泽民]]
  6. 结果[[内嵌模板站|内嵌{{!}}模板]]不符合预期[[内嵌模板站|内嵌|模板]]
  7. 测试成功,结果:3180b7
  8. 测试成功,结果:#3180b7
  9. 测试成功,结果:[[宁波轨道交通1号线|1号线]]
  10. 测试成功,结果:[[西铁线|西铁线]]
  11. 测试成功,结果:[[宁波东站|宁波东站]]
  12. 测试成功,结果:高桥西
  13. 测试成功,结果:宁波站
  14. 测试成功,结果:宁波东站
  15. 测试成功,结果:余姚
  16. 测试成功,结果:马渚
  17. 测试成功,结果:杭州湾
  18. 测试成功,结果:22:06
  19. 测试成功,结果:06:08
  20. 测试成功,结果:往[[高桥西站(宁波)|高桥西]]方向:22:06
  21. 测试成功,结果:往[[霞浦站(宁波)|霞浦]]方向:06:08
  22. 测试成功,结果:06:07-22:08
  23. 测试成功,结果:06:07-22:10
  24. 测试成功,结果:在2016年的3月的19日,月份2016年3月,日期2016年3月19日开通
  25. 结果[[Category:需要去除时间判断模板的页面]]不符合预期2016年
  26. 测试成功,结果:[[Category:需要去除时间判断模板的页面]]
  27. 结果[[Category:需要去除时间判断模板的页面]]不符合预期2016年3月
  28. 测试成功,结果:[[Category:需要去除时间判断模板的页面]]
  29. 测试成功,结果:2016年3月19日
  30. 测试成功,结果:[[Category:需要去除时间判断模板的页面]]
  31. 测试成功,结果:2019年12月28日
  32. 测试成功,结果:二期规划中
  33. 测试成功,结果:规划中
  34. 函数调用出错:attempt to call a nil value,预期为1号线从[[高桥西站(宁波)|高桥西]] |到[[霞浦站(宁波)|霞浦]]
  35. 函数调用出错:attempt to call a nil value,预期为1号线从[[高桥西站(宁波)|高桥西站]] |到[[霞浦站(宁波)|霞浦站]]

测试完成,共成功30次,失败5次。

上述文档内容嵌入自Module:RailSystems/doc编辑 | 历史
编者可以在本模块的沙盒创建 | 镜像和测试样例创建页面进行实验。
请将模块自身所属的分类添加在文档中。本模块的子页面
local System = {}
local Line = {}
local Station = {}
local p = {System=System, Line=Line, Station=Station}
local deprecated = {
	DEFAULT_DATE_ELAPSED_TEXT = '[[Category:需要去除时间判断模板的页面]]',
	DEFAULT_LINE_DATE_ABSENT_TEXT = '规划中',
	DEFAULT_STATION_TRAIN_TIME_TEMPLATE = '{start_time|H:i|次日}-{end_time|H:i|次日}'
}
local allowDeprecated = true
if not allowDeprecated then
	setmetatable(deprecated, {
		__index = function () error "弃用的内容已停用!" end,
		__newindex = function () end
	})
end

require 'Module:No globals'

--[=[
	根据page和name,创建一个[[page|name]]这样的内链。
	且如果page为nil,则直接返回name。
	page必须是nil或字符串,name必须是字符串。
]=]
local function createLink(page, name)
	return page and '[[' .. page .. '|' .. name .. ']]' or name
end


--[[
== 元表定义和构造函数部分 ==
]]


--- 类似于 RailSystems 模块中的 _loadSystemData,不过会将其用元表包装,返回一个System对象。
-- @string name 铁路系统名称,也就是Module:RailSystems/后的子页面的部分。
function System.load(name)
	if not name then return nil end
	local successes, data = pcall(mw.loadData, "Module:RailSystems/" .. name)
	
	if not successes then
		-- 导入失败的情况
		mw.log ("[RailSystems] 无法加载[[Module:RailSystems/" .. name .. "]]的数据,原因:" .. data)
		local successes2, data2 = pcall(p.importAdjacentStations, name)
		if successes2 then
			mw.addWarning (("'''来自[[Module:RailSystems]]的警告:'''[[Module:RailSystems/%s]]的数据不存在(或者加载错误),已导入[[Module:Adjacent stations/%s]]的数据。这可能会降低性能,未来将不再支持。请有能力的用户尽快创建RailSystems中的数据表。"):format(name, name))
			mw.log ("[RailSystems] 加载转换自[[Module:Adjacent stations/" .. name .. "]]的数据。")
			data = data2
		else
			mw.log ("[RailSystems] 无法加载[[Module:Adjacent stations/" .. name .. "]]中的数据,原因:" .. data2)
			error ("无法加载铁路系统数据,原因:" .. data)
		end
	end
	
	local override = nil
	if data.override then
		-- override 字段用于如何修改 System、Line、Station类。
		-- 这样可以使得部分系统能够拥有自己的行为。
		override = require (data.override)
	end
	if data.plain_link_template then
		mw.addWarning('“' .. name .. '”的plain_link_template字段已被弃用,且不再被使用,请将其移除。')
	end
	local system = System.of(data, override)
	system.modulePageName = name
	return system
end

local metatable1 = {
		__index = System,
		__name = 'System',
		__tostring = function (self) return 'System(name=' .. tostring(self.data.name or self.modulePageName) .. ')' end
	}
p.metatable1 = metatable1

--- 这部分代码用于处理铁路系统的override。
local package_processed_override = {}

local function add_super_as_metatable(t, super)
	local metatable = getmetatable(t)
	if not metatable then
		metatable = {}
		setmetatable(t, metatable)
	end
	
	for k, v in pairs(t) do
		if type(v) == 'table' then
			add_super_as_metatable(v, super[k])
		end
	end
	metatable.__index = super
end

local function process_override(override)
	if package_processed_override[override] then
		-- 已经处理过了,直接返回处理过的。
		return override
	end
	-- override.super 可用于在override的部分中调用被override的部分。
	override.super = p
	for k, v in pairs(override) do
		if type(v) == 'table' and type(override.super[k]) == 'table' then
			add_super_as_metatable(v, override.super[k])
		end
	end
	package_processed_override[override] = true
	return override
end

				
--- 根据整个系统的数据(可以是mw.loadData导出的数据)生成一个可以作为面向对象调用的System对象。
-- @table[opt] data 	铁路系统的数据。需要注意,这个data可能是通过mw.loadData导出的只读的表,因此应该是不可变的。
-- @table[opt] override 在线路数据中,指定override字段,其值作为模块名称,加载此模块。该模块应该“继承”此模块的p。
-- @return				System对象,或者nil。
function System.of(data, override)
	if data == nil then return nil
	elseif type(data) == 'string' then
    	error(string.format("System.of中的参数data应当是一个表。你是不是想调用System.load(%q)?", data))
    elseif type(data) ~= 'table' then
		error(string.format("System.of中的参数data应当是一个表,而非%s。", type(data)))
	elseif data == System then
		error "System.of第一个参数错误,你是否误调用了System:of()?"
	end
	if override then
		override = process_override(override)
	end
	return setmetatable({data=data, override=override}, override and override.metatable1 or metatable1)
end

local metatable2 = {
		__index = Line,
		__name = 'Line',
		__eq = function (self,other) return self.data == other.data end,
		__tostring = function (self)
			return 'Line(' .. tostring(self:getName() or 'unnamed') .. ', system=' .. tostring(self.system and (self.system.name or self.system.modulePageName) or nil) .. ')'
		end
	}
p.metatable2 = metatable2

--- 根据一条线路的数据(可以是mw.loadData导出的数据),生成一个新的线路对象。
-- @table[opt]|str data 	线路的数据。需要注意,这个data可能是通过mw.loadData导出的只读的表,因此应该是不可变的。
-- @table[opt] system	该线路所属的轨道系统。
-- @return				Line对象,或者nil。
function Line.of(data, system)
	if data == nil then return nil
	elseif data == Line then
		error "Line.of第一个参数错误,你是否误调用了Line:of()?"
	elseif type(data) == 'string' then
		data = {stations = {data}}
	end
	return setmetatable(
		{data=data, system=system}, 
		(system and system.override) and system.override.metatable2 or metatable2
	)
end

local metatable3 = {
		__index = Station,
		__name = 'Station',
		__tostring = function (self)
			if self.type == 'line' then
				return string.format('AbstractStation:Line(%s)', tostring(self.line and (self.line:getName() or 'unnamed') or nil))
			elseif self.type == 'branches' then
				return string.format('AbstractStation:Branches(main=%s, branch=%s)', tostring(self.main and (self.main:getName() or 'unnamed') or nil), tostring(self.branch and (self.branch:getName() or 'unnamed') or nil))
			elseif self.type == 'adjacent_branches' then
				return string.format('AbstractStation:AdjacentBranches(main=%s, branch=%s)', tostring(self.main and (self.main:getName() or 'unnamed') or nilo), tostring(self.branch and (self.branch:getName() or 'unnamed') or nil))
			end
			local paras = {}
			if self.data and self.data.id then
				table.insert(paras, 'id=' .. tostring(self.data.id))
			end
			if self.line then
				table.insert(paras, 'line=' .. tostring(self.line:getName()))
			end
			if self.line and self.line.system~=self.system then
				table.insert(paras, 'system=' .. tostring(self.system and (self.system.name or self.system.modulePageName) or nil))
			end
			return 'Station(' .. table.concat(paras, ', ') .. ')'
		end
	}
p.metatable3 = metatable3

--- 根据车站数据(可以是表或者字符串),以及可选的线路和系统,生成一个新的车站对象。
-- @table[opt]	data	车站的数据。可能是mw.loadData导出的只读的表,因此不应该更改。
-- @string[opt] data	相当于 {id=data}。
-- @table[opt]	line	该车站所属的线路。应当是一个Line对象。
-- @table[opt=line.system]
--				system	该车站所属的系统。应当是一个System对象。
-- @return		Station对象,或者nil。
function Station.of(data, line, system)
	system = system or (line and line.system)
	if data == nil then
		return nil
	elseif data == Station then
		error "Station.of第一个参数错误,你是否误调用了Station:of()?"
	elseif type(data)=='string' then
		data = {id = data}
	end
	local metatable3 = system and system.override and system.override.metatable3 or metatable3
	-- @string[opt] data.type 可能的取值:branches、adjacent_branches、line或nil。
	if data.type == 'branches' or data.type == 'adjacent_branches' then
		-- branches				其本质为由主线和支线组成的抽象车站。将这两段分支(不包括分支合并的车站)看做是一个车站。
		-- adjacent_branches	其本质为由支线交汇处的两个临站组成的抽象车站。将同一侧不同分支的两个车站看做是一个车站。
		return setmetatable({
			type = data.type,
			main = Line.of(data.main, system),
			branch = Line.of(data.branch, system),
			line = line,
			system = system
		}, metatable3)
	elseif data.type == 'line' then
				-- 其本质为线路的抽象车站。将一段线路看做是一个车站。
		return setmetatable({
			type = data.type,
			line = Line.of(data.line,system),
			system=system,
		}, metatable3)
	else
		return setmetatable({data=data,line=line,system=system}, metatable3)
	end
end


--[[
== 日志管理方法部分 ==
]]

--- 向控制台输出一条警告消息,并在预览时显示该警告消息。消息会显示自身对象名称。
-- @return 始终为nil,因此可用在短路运算中。
function System:warn(msg)
	msg = '[' .. tostring(self) .. '] ' .. msg
	mw.log(msg)
	mw.addWarning(msg)
end
Line.warn = System.warn
Station.warn = System.warn

--- 类似于System:warn,但是会格式化字符串。
function System:warnf(msg, ...)
	return self:warn(string.format(msg, ...))
end
Line.warnf = System.warnf
Station.warnf = System.warnf

--- 抛出错误,错误内容中会显示自身对象名称。
function System:error(msg)
	error('[' .. tostring(self) .. '] ' .. msg)
end
Line.error = System.error
Station.error = System.error

--- 抛出错误,且可以格式化字符串。
function System:errorf(msg, ...)
	return self:error(string.format(msg, ...))
end
Line.errorf = System.errorf
Station.errorf = System.errorf


--[[
== 实例方法部分 ==
]]

--- 获取该轨道交通系统的图标。
function System:getIcon(params)
	params = params or {}
	local size = params.size
	local iconLink = params.link
	local iconFile
	local iconData = self.data.icon
	local alt = params.alt
	if not iconData then
		return nil
	elseif type(iconData) == 'string' then
		iconFile = iconData
		size = size or '20px'
		iconLink = iconLink or self.data.link
	elseif type(iconData) == 'table' then
		iconFile = iconData.file
		size = size or iconData.size or '20px'
		iconLink = iconLink or iconData.link or self.data.link
		alt = alt or iconData.alt
	end
	
	if not iconFile or iconFile == '' then
		return nil
	end
	
	local builder = {'[[', iconFile}
	builder[#builder + 1] = size and ('|' .. tostring(size)) or nil
	builder[#builder + 1] = iconLink and ('|link=' .. tostring(iconLink)) or nil
	builder[#builder + 1] = alt and ('|alt=' .. tostring(alt) or nil)
	builder[#builder + 1] = ']]'
	return table.concat(builder)
end

function Line:getIcon(params)
	return System.getIcon(line, params) or (self.system and self.system:getIcon(params) or nil)
end

function Station:getIcon(params)
	return System.getIcon(station, params) or (self.line and self.line:getIcon(params) or nil)
end

--- 根据指定的准则(criterion),返回该系统内符合该准则的所有线路对象组成的数组。
-- 会迭代line_index[criterion],然后访问lines字段,但不会迭代lines字段。
-- 返回的结果中按照line_index中的顺序排序。
-- @return {Line}
function System:getLines(criterion, condition)
	local lines = {}
	criterion = criterion or 'all'
	local line_index = self.data.line_index
	if line_index and line_index[criterion] then
		for i, index in ipairs(line_index[criterion]) do
			local line = self:getLine(index)
			-- 插入lines的内容,使用原始方式以提升性能。
			lines[#lines + 1] = (line:meets(condition) and line or nil)
		end
	elseif line_index then -- and not line_index[criterion]
		self:errorf('调用System:getLines(criterion, condition)时出现错误,未知的准则名称:%s', tostring(criterion))
	else -- if not line_index
		self:error('无法调用System:getLines,因为该系统数据的line_index字段不存在')
	end
	return lines
end

--- 根据lines字段中的键,获取线路对象。如果不存在该键,则返回nil。
function System:getLine(key)
	local lines = self.data.lines
	return lines and lines[key] and Line.of(lines[key], self) or nil
end

--- 根据lines字段中的键,获取线路对象。如果不存在该键,则抛出错误。
function System:getLineOrThrow(key)
	return self:getLine(key) or self:error("未知的线路id:" .. tostring(key))
end

--- 根据lines字段中的键,获取线路对象。如果不存在该键,则创建虚拟的。
function System:getLineOrAnonymous(key)
	return self:getLine(key) or Line.of({name = key, page = false}, self) or nil
end

--- 根据指定的车站名称,获取该车站对应的车站对象。
-- 需要注意:此情况下获取的车站可能根本不存在,但仍会正常返回,这与Line:getStation中的不同。
-- @return Station对象。
function System:getStation(id)
	return Station.of(id, nil, self)
end

-- @deprecated
-- 指定车站的链接,例如从“朝阳门”转换至“[[朝阳门站(北京)|朝阳门]]”。
function System:getStationLink(id, frame)
	local stationNames = self.data.stationNames
	local station = self:getStation(id)
	return station:getLink()
end

--- 获得指定类型的线路名,不带链接。
-- @string[opt='short'] 
--			nameType	线路名称的类型,可以是 short full blink 等值,默认为short。
-- @bool	isStyled	是否对线路应用加粗、灰色等格式。
-- @see Station:getName中的isStyled参数。
-- @see Line:getStyledName
-- @return 会依次获取data.<nameType_>name、data.short_name、data.name和data.title。
function Line:getName(nameType, isStyled)
	if isStyled then
		return self:getStyledName(nameType)
	end
	return self.data[nameType and nameType .. '_name' or 'name']
		or (self.data.name
		or self.data.short_name)
		-- 读取title字段,这是考虑到向前兼容。在之前的版本中,title的值是像“苏州轨道交通1号线|1号线”这样的。
		or (self.data.title
		-- 通过tabel.remove来返回最后一个值,
		-- 如果仍然不存在,则返回nil。
		and mw.text.split(self.data.title, '|')[2] or self.data.title)
end

--- 获取指定类型的线路名,且进行格式化:若线路未开通,则显示为灰色;若线路还在计划中,则显示为斜体。
function Line:getStyledName(nameType)
	local name = self:getName(nameType, false)
	if self:meets 'not_open' then
		name = '<span class=graylink>' .. name .. '</span>'
	end
	if self:meets 'planning' then
		name = '<i>' .. name .. '</i>'
	end
	return name
end

--- 线路颜色。
-- @bool	prefixed	是否给返回的颜色加上井号前缀。
-- @return	string[opt] 线路颜色。可能是nil。
function Line:getColor(prefixed)
	return self.data.color and ((prefixed and '#' or '') .. self.data.color) or 'transparent'
end

-- @return 该线路对应的页面名称。
function Line:getPage()
	if self.data.page == false then
		return false
	end
	return self.data.page
		-- 读取title字段,这是考虑到向前兼容。在之前的版本中,title的值是像“苏州轨道交通1号线|1号线”这样的。
		or (self.data.title
		and mw.text.split(self.data.title, '|')[1])
		or self.data.full_name
		or self.data.name
		-- 如果仍然不存在,则返回nil。
end

--- 返回线路链接。
---@string	style	可以是short full blink等。@see Line:getName中的style参数。
---@bool	rich	是否展示其标志色。
---@bool	isStyled 是否对线路应用加粗、灰色等格式。
function Line:getLink(nameType, rich, isStyled)
	local page = self:getPage()
	local name
	if rich then
		local color = self:getColor(true) -- 以#开头的颜色
		if nameType == 'blink' then
			name = self:getName(nameType, false)
			local notOpen = isStyled and self:meets 'not_open'
			-- 这里或许最好还是应当可配置比较好。
			name = string.format([[<span class="mw-no-invert RailSystems-blink" style="display: inline-block; height: 1.25em; min-width: 1.25em; line-height: 1.25em; text-align: center; color: %s; border-radius: 2px; background-color: %s; white-space: nowrap">%s</span>]], '#' .. (notOpen and 'ffffff' or self.data.textColor or 'ffffff'), notOpen and '#cccccc' or color, name)
		else
			name = self:getName(nameType, isStyled)
			name = (color and name) and '<span style="background-color:' .. color .. '; display: inline-block; height:1em; width:1em; line-height:1em; margin-inline:0.2em; user-select:none" class="mw-no-invert RailSystems-color-block" aria-hidden="true"><br></span>' .. name or name
		end
	else
		if self.data.link then
			return self.data.link
		end
		name = self:getName(nameType, isStyled)
	end
	return name and createLink(page, name) or nil
end

--- 相当于getLink(style, rich = true)
function Line:getRichLink(style, isStyled)
	return self:getLink(style, true, isStyled)
end

--- 在index处,以step的步长,寻找符合条件condition的邻站。
-- @int/string	index	在line中的所在位置。若为字符串'end',则表示从头或尾开始寻找。
-- @int 		step	步长,表示需要寻找的方向,一般为1或-1。
-- @func|str[opt] 
--				condition 寻找的车站需要符合的条件。@see Station:meets
function Line:seekAdjacentStation(index, step, condition)
	local stations_data = self.data.stations
    -- stations_data的长度。
	local hash = 0
	if not stations_data then
		self:error "无法调用seekAdjacentStations,因为其线路数据中没有stations字段"
	end
	-- 计算stations_data的长度。由于可能是通过mw.loadData导出的数据,因此“#”可能不起作用。
	for k, v in ipairs(stations_data) do
		hash = hash + 1
	end
	if not index then return nil end
	if hash < 1 then
		self:error "无法调用seekAdjacentStations,因为其线路数据的stations字段是空的。"
	elseif not(index=='end' or index <= hash) then
		self:error "调用seekAdjacentStation(index, step, condition)的参数index无效。index不能超过data.stations的长度。"
	end
	if index=='end' then index = step < 0 and hash + 1 or 0 end
	local limit = 0
	local i = index
	repeat
		i = i + step
		if self.data.isLoop then -- 处理环线的情况。
			if i < 1 then i = hash end
			if i > hash then i = 1 end
			if i == index then return nil end
		end
		local station_data = stations_data[i]
		if station_data and station_data.type == 'branches' then
			local ret =  Station.of(
				-- data = 
				{ type = 'adjacent_branches' },
				-- (Line) line =
				self
			)
			ret.main = Line.of(station_data.main, self.system):seekAdjacentStation('end', step, condition)
			ret.branch = Line.of(station_data.branch, self.system):seekAdjacentStation('end', step, condition)
			return ret
		elseif station_data and station_data.type == 'line' then
			local line = Line.of(station_data.line, self.system)
			if line and line:meets(condition) then
				local ret = line:seekAdjacentStation('end', step, condition)
				if ret then return ret end
			end
		else
			local station = Station.of(station_data, self)
			if station and station:meets(condition) then return station end
		end
		limit = limit + 1
	until limit > hash
	return nil
end
			

--- 根据指定车站,获取其临站。注意可能会有支线的情况。
-- @treturn {left = 左侧临站对象, right = 右侧临站对象},但如果是线路位于终点站等原因,则可能为nil。
-- 如果线路根本找不到station_id的车站,则直接返回nil。
-- 如果某站位于支线交汇处,则该站的其中一个临站会是一个抽象的AdjacentBranches对象,大多情况下可以作为Station对象看待。
-- @string station_id
-- @func @str[opt] condition
function Line:getAdjacentStations(station_id, condition)
	local stations = self.data.stations
	if not stations then
		self:error "无法调用getAdjacentStations,因为其数据中没有stations字段。"
	end
	local index
	local left, right
	for k, v in ipairs(stations) do
		local id = type(v)=='string' and v or v.id
		if v.type == 'line' then
			local line = Line.of(v.line, self.system)
			local result = line:getAdjacentStations(station_id, condition)
			if result then
				left = left or result.left
				right = right or result.right
				index = k
				break
			end
		elseif v.type == 'branches' then
			local mainLine = Line.of(v.main, self.system)
			local branchLine = Line.of(v.branch, self.system)
			local result = mainLine:getAdjacentStations(station_id, condition) or branchLine:getAdjacentStations(station_id,condition)
			if result then
				left = left or result.left
				right = right or result.right
				index = k
				break
			end
		elseif v.type == 'adjacent_branches' then -- 一般不会存在这种情况,但是仍然写在代码中。
			self:error "类型为adjacent_branches的抽象车站数据暂无法用于Line:getAdjacentStations方法"
		elseif id==station_id then
			index = k
			break
		end
	end
	-- 如果该线路找得到车站,则index不会为nil,返回表格结果(尽管结果可能为空)。
	-- 如果该线路不存在该车站,则返回nil。
	return index and {
			left = left or self:seekAdjacentStation(index, -1, condition),
			right = right or self:seekAdjacentStation(index, 1, condition)
		} or nil
end

--- 根据id返回该线路的车站对象。如果不存在此车站则会返回nil。
-- @str 	id			车站id。
-- @func @str[opt]
--			condition	返回的车站必须符合此条件。如果发现符合id的车站但不符合条件,则仍会返回nil。
function Line:getStation(id, condition)
	if not self.data.stations then return nil end
	if condition then
		local ret = self:getStation(id, nil) -- 注意避免无限循环调用。
		return ret and ret:meets(condition) and ret or nil
	end -- 以下内容只有在不存在condition时执行。
	for i, station_id in ipairs(self.data.stations) do
		-- 这个station_id可能是车站id,也有可能是带有特殊属性的车站对象,这是需要留意的。
		if type(station_id) == 'string' and station_id == id then
			-- 此时 station_id是字符串,在构造函数中自动转化为 {id = station_id}。
			return Station.of(station_id, self)
		elseif type(station_id) == 'table' then
			if station_id.type == 'line' then
				local ret = Line.of(station_id.line, self.system):getStation(id)
				if ret then return ret end
			elseif station_id.type == 'branches' then
				local ret = Line.of(station_id.main, self.system):getStation(id)
					or Line.of(station_id.branch, self.system):getStation(id)
				if ret then return ret end
			else -- 默认认为 station_id.type == 'station' then
				if station_id.id == id then
					return Station.of(station_id, self)
				end
			end
		end
	end
	return nil
end

--- 若该线路车站存在:
    -- 以line为线路的抽象线路车站,或
    -- 以line为主线或支线的抽象线路车站,
-- 则将其返回,否则返回nil。
-- 例如,苏州地铁4号线:getLine(苏州地铁4号线支线),将会得到苏州地铁4号线支线。
--		 苏州地铁3号线:getLine(苏州地铁4号线支线),将会得到nil。
--		 苏州地铁4号线:getLine(苏州地铁3号线),将会得到nil。
-- @return Line对象或nil。
function Line:getSubline(line)
	if not self.data.stations then return nil end
	if not line then return nil end
	for i, station_id in ipairs(self.data.stations) do
		if type(station_id)~='table' then
			-- 如果station数据不是table,则直接continue。
		elseif station_id.type == 'line' then
			if line.data == station_id.line then
				return  Line.of(line.data, self.system)
			else
				-- 尝试获取子线路的子线路,看看其是否存在数据相符的子线路。
				local ret = Line.of(station_id.line, self.system):getSubline(line)
				if ret then return ret end
			end
		elseif station_id.type == 'branches' then
			if line.data == station_id.main or line.data == station_id.branch then
				return Line.of(line.data, self.system)
			else
				local ret = Line.of(station_id.main, self.system):getSubline(line)
						or  Line.of(station_id.branch, self.system):getSubline(line)
				if ret then return ret end
			end
		end
	end
	return nil
end
				

--- 返回该线路中指定条件的车站的数组。
-- @func @str[opt] condition
-- @return {Line}
function Line:listStations(condition)
	local list = {}
	for i, station_id in ipairs(self.data.stations or self:error '由于线路数据中没有stations字段,无法调用listStations') do
		local station = Station.of(station_id, self)
		-- 使用最原始的方式以加快速度。
		list[#list + 1] = (station:meets(condition) and station or nil)
	end
	return list
end

--- 获取该线路指定方向的终点站。
-- @str[opt='both'] direction	方向。可以是'left'、'right'、'both'。
-- @str[opt='both'] t			终点站类型。
--			可以是'main'(获取主线终点站)、'branch'(获取支线终点站)、'both'(若存在分支,则两个都获取。
-- @return			Station[opt]
-- @treturn 		{main=Station, branch=Station}
function Line:getTerminalStation(direction, t, condition)
	if direction == nil or direction == 'both' then
		return {
			left = self:getTerminalStation('left', t, condition),
			right = self:getTerminalStation('right', t, condition)
		}
	end
	if condition == nil and self:meets 'open' then -- 需要考虑condition为false的情况
		condition = 'open'
	end
	local isLeft = direction == 'left'
		and true or direction ~= 'right'
		and self:errorf("getTerminalStation(direction, t, condition) 的参数direction错误,可以是'left'、'right'、nil或者'both',但得到了%s", tostring(direction))
		or false
	local stations = self.data.stations or self.data.terminus
	local hash = 0 -- 相当于#stations,但是stations是loadData加载出来的只读表,“#”不起作用,故迭代。
	-- 参照旧版模块,按照旧版数据和规则来返回车站。
	-- @deprecated
	if not stations then
		if true and self.data.terminals then
			local function get_condition(cond, data)
				if type(data) == 'table' then
					local c = cond[data['#field']]
					return get_condition(cond, data[c] or data['#default'])
				else
					return data
				end
			end
			local side_data = self.data.terminals[direction]
			-- local conds = { ['type'] = t, ['branch'] = branch }
			local conds = type(t)=='table' and t or {type = t}
			if type(side_data)=='table' then
				self:warnf('检测到仍在使用过时的方式获取线路终点站:%s', debug.traceback())
			end
			return self.system:getStation(get_condition(conds, side_data))
		else
			return nil
		end
	end
	-- 旧版模块语法部分结束。
	for k, v in ipairs(stations) do hash = hash + 1 end
	for i = (isLeft and 1 or hash), (isLeft and hash or 1), (isLeft and 1 or -1) do
		local station = Station.of(stations[i], self)
		if station.type == 'branches' then
			-- 如果该车站为代表分支的抽象车站,则精细划分。
			local mainLine = station.main
			local branchLine = station.branch
			if t == 'main' then
				return mainLine:getTerminalStation(direction, t, condition)
			elseif t == 'branch' then
				return branchLine:getTerminalStation(direction, t, condition)
			elseif t == nil or t == 'both' then
				local ret = Station.of({
					type = 'adjacent_branches'
				}, self)
				ret.main = mainLine:getTerminalStation(direction, t, condition)
				ret.branch = branchLine:getTerminalStation(direction, t, condition)
				return ret
			else
				self:errorf("调用Line:getTerminalStation时参数错误:应为'main'、'branch'、'both'或nil,而不是%s", tostring(t))
			end
		elseif station.type == 'line' then
			local ret = station.line:getTerminalStation(direction, t, condition)
			if ret and ret:meets(condition) then
				return ret
			end
		else -- if station.type == 'station' or nil then
			if station:meets(condition) then
				return station
			end
		end
	end
	return nil -- 获取不到终点站的情况。
end

--- 将线路转化为由链接组成的列表。
-- @treturn {@str}
function Line:renderStationList(params)
	-- @bool params.isStyled:是否对链接进行修饰。该值将会用于Station:getLink或Station:getName方法。
		-- 默认为true,若为false则为false。
	local isStyled = params.isStyled==nil and true or params.isStyled
	-- @bool params.noLink:是否不生成链接,即若为true,则会调用getName而非getLink。
	-- @bool params.showInterchange:是否显示换乘站。
		-- 默认为true,若为false则为false、
	local showInterchange = params.showInterchange==nil and true or params.showInterchange
	-- @func @str[opt] params.condition:显示符合该条件的车站和换乘站。
		-- 必须是同时适用于线路和车站的条件。
		-- 默认为'',若为false则为false。
	local condition = params.condition==nil and '' or params.condition
	-- @func @str[opt] params.interchangeCondition:显示换乘站时,换乘站需要符合的条件。
		-- 默认为condition,若为false则为false。
	local interchangeCondition = params.interchangeCondition==nil and condition or params.interchangeCondition
	-- @func @str[opt] params.stationCondition:显示的车站需要符合的条件。
		-- 默认为condition,若为false则为false。
	local stationCondition = params.stationCondition==nil and condition or params.stationCondition
	local stations = self.data.stations
	if not stations then
		self:error "无法调用renderStationList,因为线路数据的stations字段不存在。"
	end
	local list = {}
	for k,v in ipairs(stations) do
		local station = Station.of(v, self)
		if station.type == 'line' then
			for k1, v1 in ipairs(station.line:renderStationList(params)) do
				list[#list + 1] = v1
			end
		elseif station.type == 'branches' then
			list[#list + 1] = '主线/支线'
		elseif station.type == 'adjacent_branches' then
			local elem = (params.noLink and station.getName or station.getLink)(station, isStyled)
			list[#list + 1] = elem
		elseif station:meets(stationCondition) then -- and station.type == others
			local elem = (params.noLink and station.getName or station.getLink)(station, isStyled)
			if showInterchange then
				-- 加载换乘站列表。
				local interchanges = {} -- 由Line对象的链接组成的数组。
				local array1, array2 = station:getLines('all', interchangeCondition, true)
				for k2,v2 in ipairs(array1) do -- 以后会改成condition
					local v3 = array2[k2] -- Station对象。
					function v2:meets(condition) return v3:meets(condition) end
					if v2~=station.line then
						interchanges[#interchanges + 1] = (v2:getRichLink('blink', isStyled))
					end
				end
				if #interchanges>0 then
					elem = elem .. '(' .. table.concat(interchanges, '') .. ')'
				end
			end
			list[#list + 1] = elem
		end
	end
	return list
end

-- @deprecated
function deprecated._parseTime(time_str)
	local base_parts = mw.text.split(time_str, ':')
	local parts_size = #base_parts
	local hour = 0
	local minute = 0
	local sec = 0
	if parts_size == 1 then
		minute = tonumber(mw.text.trim(base_parts[1]))
	elseif parts_size == 2 then
		hour = tonumber(mw.text.trim(base_parts[1]))
		minute = tonumber(mw.text.trim(base_parts[2]))
	elseif parts_size == 3 then
		hour = tonumber(mw.text.trim(base_parts[1]))
		minute = tonumber(mw.text.trim(base_parts[2]))
		sec = tonumber(mw.text.trim(base_parts[3]))
	else
		error('非法的时间输入')
	end
	return hour, minute, sec
end

-- @deprecated
function deprecated._extractSeconds(totalsec)
	local hour = math.floor(totalsec / 3600)
	local minute = math.floor(totalsec % 3600 / 60)
	local sec = totalsec % 60
	return hour, minute, sec
end

-- @deprecated
function deprecated._formatTemplateTime(frame, sparts, hour, minute, second)
	local tomorrow_prefix = ''
	if #sparts >= 3 then
		if hour >= 24 then 
			tomorrow_prefix = mw.text.trim(sparts[3])
			if tomorrow_prefix == '' then tomorrow_prefix = '次日' end
			hour = hour - 24
		end
	end
	local time_str = string.format('%d:%d:%d', hour, minute, second)
	return tomorrow_prefix .. frame:callParserFunction{ name = '#time', args = { sparts[2], time_str } }
end


--- 获取列车首班车或末班车的运营时间。
-- @deprecated
-- @para dir 方向。
-- @str type 可以是 'first' 或者 'last'。
-- @return 3个数字,分别表示时分秒。
function Line:getTrainTime(dir, type, delta)
	local dir_info = assert(assert(self.data.trainTime, '该线路数据无trainTime字段,无法获取列车时间。')[dir], '没有' .. tostring(dir) .. '方向的线路数据')
	local bhour, bmin, bsec = deprecated._parseTime(dir_info[type])
	local dhour, dmin, dsec = deprecated._parseTime(delta)
	local totalsec = (bhour * 60 + bmin) * 60 + bsec + (dhour * 60 + dmin) * 60 + dsec
	return deprecated._extractSeconds(totalsec)
end
if not allowDeprecated then Line.getTrainTime = nil end

-- @deprecated
function Line:getDateMessage(t, options)
	local system = assert(self.system, "没有指定system的Line对象无法调用getDateMessage")
	local reprs = options
	local lowest_level = 0 -- 0: everything is ok, 1: year, 2: month, 3: day
	local frame = mw.getCurrentFrame()
	local cur_time = frame:callParserFunction{ name = '#time', args = { 'U' } }
	local open_date, year, month, day, new_year, new_month
	local ret_list = {}
	
	for i, repr in ipairs(reprs) do
		repr = mw.text.trim(repr)
		if lowest_level < 1 and (repr == 'year') then
			lowest_level = 1
		elseif lowest_level < 2 and (repr == 'month' or repr == 'ym') then
			lowest_level = 2
		elseif  lowest_level < 3 and (repr == 'day' or repr == 'date' or repr == 'ymd') then
			lowest_level = 3
		end
	end
	
	options = options or {}
	options.auto_hide = options.auto_hide -- 暂时免除convBool
	options.auto_defer = options.auto_defer -- 暂时免除convBool
	if options.cur_time then -- for testing purpose
		cur_time = frame:callParserFunction{ name = '#time', args = { 'U', options.cur_time } }
	end
	
	open_date = self.data.openDates or {}
	
	if type(open_date) == 'table' then
		if open_date[t] then
			open_date = open_date[t]
		elseif open_date['#default'] then
			open_date = open_date['#default']
		elseif lowest_level == 0 then
			for i, repr in ipairs(reprs) do ret_list[#ret_list + 1] = repr end
			return table.concat(ret_list)
		else
			return deprecated.DEFAULT_LINE_DATE_ABSENT_TEXT
		end
	end
	year = tonumber(frame:callParserFunction{ name = '#time', args = { 'Y', open_date } })
	month = tonumber(frame:callParserFunction{ name = '#time', args = { 'm', open_date } })
	day = tonumber(frame:callParserFunction{ name = '#time', args = { 'd', open_date } })
	if options.auto_defer then
		if lowest_level == 1 then
			open_date = string.format('%04d-01-01', year + 1)
		elseif lowest_level == 2 then
			new_year, new_month = year, month
			if month == 12 then
				new_year = new_year + 1
				new_month = 1
			else
				new_month = new_month + 1
			end
			open_date = string.format('%04d-%02d-01', new_year, new_month)
		end
	end
	open_date = frame:callParserFunction{ name = '#time', args = { 'U', open_date } }
	
	if options.auto_hide and cur_time >= open_date then
		return deprecated.DEFAULT_DATE_ELAPSED_TEXT
	end
	for i, repr in ipairs(reprs) do
		if repr == 'year' then
			ret_list[#ret_list + 1] = year .. '年'
		elseif repr == 'month' then
			ret_list[#ret_list + 1] = month .. '月'
		elseif repr == 'day' then
			ret_list[#ret_list + 1] = day .. '日'
		elseif repr == 'ym' then
			ret_list[#ret_list + 1] = string.format('%s年%s月', year, month)
		elseif repr == 'date' or repr == 'ymd' then
			ret_list[#ret_list + 1] = string.format('%s年%s月%s日', year, month, day)
		else
			ret_list[#ret_list + 1] = repr
		end
	end
	return table.concat(ret_list)
end
if not allowDeprecated then Line.getDateMessage = nil end

--- 判断该线路是否符合指定的条件。若condition为nil,则认为肯定符合。
-- @func @str[opt] condition
function Line:meets(condition)
	local add = true
	if condition == '' or condition == 'all' or not condition then
		return true
	elseif type(condition) == 'function' then
		add = add and condition(self)
	elseif type(condition) == 'table' then
		for k,v in pairs(condition) do
			add = add and (self.data[k] or false) == v
		end
	elseif condition == 'not_open' then
		-- 该车站是否暂未开通。如果该车站所在的线路未开通,则该车站一定未开通。
		-- 注意:以实际工程状态为准。
		-- 因2019冠状病毒病疫情、洪涝灾害、气象灾害、节日限流等原因关闭的,
		-- 不在此范围内。
		add = add and self.data.notOpen or (self.line and self.line.data.notOpen)
	elseif condition == 'open' then
		-- 与not_open相反。
		add = add and not self.data.notOpen and not (self.line and self.line.data.notOpen)
	elseif condition == 'planning' then
		add = add and self.data.planning or (self.line and self.line.data.planning)
	end
	return add
end

--- 判断该车站是否符合指定的条件。若condition为nil,则认为肯定符合。
-- @func @str[opt] condition
function Station:meets(condition)
	local add = true
	if condition == '' or condition == 'all' or not condition then
		return true
	elseif type(condition) == 'function' then
		add = add and condition(self)
	elseif type(condition) == 'table' then
		for k,v in pairs(condition) do
			add = add and (self.data[k] or false) == v
		end
	elseif condition == 'not_open' then
		-- 该车站是否暂未开通。如果该车站所在的线路未开通,则该车站一定未开通。
		-- 注意:以实际工程状态为准。
		-- 因2019冠状病毒病疫情、洪涝灾害、气象灾害、节日限流等原因关闭的,
		-- 不在此范围内。
		add = add and self.data.notOpen or (self.line and self.line.data.notOpen)
	elseif condition == 'open' then
		-- 与not_open相反。
		add = add and not self.data.notOpen and not (self.line and self.line.data.notOpen)
	elseif condition == 'planning' then
		add = add and self.data.planning or (self.line and self.line.data.planning)
	end
	return add
end

--- 获取该车站的名称,如:“浦电路”“世纪大道”“虹桥火车站”。
-- @bool isStyled 是否对结果进行加粗、灰色等处理,如果为true,则跳到Station:getStyledName(),该函数会调用Station:getName(style=false)。
-- @bool notShort 是否在结尾加上“站”字。此情况将不使用车站数据中的名称,而是根据页面名称来推断车站名称。
-- @return string
function Station:getName(isStyled, notShort)
	if isStyled then return self:getStyledName() end
	if self.data and self.data.name then return self.data.name end
	if self.type == 'branches' then
		return (self.main and self.main:getName 'short' or 'nil') .. '/' .. (self.branch and self.branch:getName 'short' or 'nil')
	elseif self.type == 'line' then
		error "无法对抽象线路车站对象调用getName"
	elseif self.type == 'adjacent_branches' then
		return (self.main and self.main:getName() or 'nil') .. '/' .. (self.branch and self.branch:getName() or 'nil')
	end
	if self.system == nil then return self.data.id end
	local stationNames = self.system and self.system.data.stationNames
	if notShort then
		-- 直接根据页面名称来推断站名。
		-- 例如,如果页面名称为“长沙火车站(地铁)”,则返回的值为“长沙火车站”。
		-- 如果页面名称为“平阳站(湖南省)”,则返回的值为“平阳站”。
		local page = self:getPage()
		return page and page:match '^(.-)%s*[(%(].-[%))]$' or page
	elseif stationNames and stationNames[self.data.id] then
		local specifiedName = stationNames[self.data.id]
		if type(specifiedName) == 'string' then
			if specifiedName:match '%[%[.-%]%]' then
				return specifiedName
			end
			return mw.text.split(specifiedName, '|')[2] or specifiedName
		elseif type(specifiedName) == 'table' then
			return specifiedName.name or self.data.id
		else
			error(("指定的线路名称的值(%s)的格式不正确,应为string或table,实际上为%s")
				:format(tostring(specifiedName), type(specifiedName)))
		end
	else
		return self.data.id
	end
end

--- 获取车站名称,但是会进行格式化:
-- * 若车站未开通,则显示为灰色。
-- * 若车站还在规划中,则显示为斜体。
-- * 若车站为换乘站,则显示为粗体。
-- @see Station:getName
function Station:getStyledName(notShort)
	local result = self:getName(false, notShort)
	if self.data.notOpen or (self.line and self.line.data.notOpen) then
		result = '<span class="graylink">' .. result .. '</span>'
	end
	if self.data.planning or (self.line and self.line.data.planning) then
		result = '<i>' .. result .. '</i>'
	end
	if #self:getLines('all', 'open', true) > 0 then
		result = '<b>' .. result .. '</b>'
	end
	return result
end

--- 获取该车站对应的页面,如:浦电路站 (4号线)、金湖站 (苏州)。
-- 如果该车站数据有page字段,则直接将其返回。
function Station:getPage()
	if self.data and self.data.page then return self.data.page end
	if self.type == 'branches' or self.type == 'adjacent_branches' or self.type == 'line' then
		self:error "无法获取抽象车站对象对应的页面名称。"
	end
	if self.system == nil then return self.data.id end
	local stationNames = self.system.data.stationNames
	if stationNames and stationNames[self.data.id] then
		local specifiedName = stationNames[self.data.id]
		if type(specifiedName) == 'string' then
			if specifiedName:match '%[%[.-%]%]' then
				return nil
			end
			return mw.text.split(specifiedName, '|')[1] or specifiedName
		elseif type(specifiedName) == 'table' then
			return specifiedName.page or nil -- 注意可能为nil
		else
			self:errorf ("线路指定的名称(stationNames[%s])字段的类型不正确,应为string或table,实际为%s。", tostring(self.data.id), type(specifiedName))
		end
	else
		return self.data.id and (self.system and self.system.data.stationFormat or '%s站'):format(self.data.id) or nil
	end
end

--- 获取该车站对应的链接,如[[浦电路站(4号线)|浦电路]]、[[虹桥火车站站|虹桥火车站]]。
-- 若该车站数据中有link字段,则直接将其返回,该值将覆盖stationNames中的行为。
-- @bool isStyled 是否对样式进行加粗、灰色等操作。
function Station:getLink(isStyled, notShort)
	if self.data and self.data.link then return self.data.link end
	if self.type == 'branches' then
		return (self.main and self.main:getLink 'short' or 'nil') .. '/' .. (self.branch and self.branch:getLink 'short' or 'nil')
	elseif self.type == 'line' then
		self:error "无法将抽象线路车站对象转化为链接"
	elseif self.type == 'adjacent_branches' then
		return (self.main and self.main:getLink(isStyled) or 'nil') .. '/' .. (self.branch and self.branch:getLink(isStyled) or 'nil')
	end
	local stationNames = self.system and self.system.data.stationNames or nil
	local specifiedName = stationNames and stationNames[self.data.id]
	if specifiedName then
		if type(specifiedName) == 'table' then
			return specifiedName.link or
				specifiedName.page and '[[' .. specifiedName.page .. '|' .. (specifiedName.name or self.data.id) .. ']]'
				or (specifiedName.name or self.data.id)
		elseif type(specifiedName) == 'string' and specifiedName:find '%[%[.-%]%]' then
			-- 如果指定的名称中有链接,则无论是否有isStyled和notShort,直接强制返回这个链接文本
			return specifiedName
		end
	end
	return createLink(self:getPage(), self:getName(isStyled, notShort))
end

--- 返回该车站所在系统内,拥有该车站的所有线路。如果该车站对象没有指定所在系统,则会抛出错误。
	-- @return 由线路对象组成的数组。可能为空,但不会是nil。
	-- @return 由这些线路中,该车站对象组成的数组。
	-- @func @str[opt] condition 返回的线路中该车站必须符合此条件。@see Station:meets
		-- 注意是车站要符合的条件,而不是线路要符合的条件。
	-- @table avoid 是否在结果中规避该车站对象所属的线路(self.line)。
	    -- 例如,3号线虹桥路站的getLines(avoid = true)只会返回4号线和10号线。
		-- 若为true,则规避station对象自身所在线路。
		-- 若为线路对象,则规避该线路对象。这是为了避免支线或子线路上的车站没有规避自身所在完整线路,或其他类似情况。
function Station:getLines(criterion, condition, avoid)
	local system = self.system
	if not system then
		-- 如果没有对应的系统对象,不跑错,直接返回空。
		return {}, {}
	end
	local result = {}
	local result2 = {}
	for i, line in ipairs(system:getLines(criterion)) do
		local lineToAvoid = avoid==true and self.line or avoid or self.line
		if line and not (avoid and line==lineToAvoid or line:getSubline(lineToAvoid)) then
			local stationInThisLine = line:getStation(self.data.id, condition)
			if stationInThisLine then
				-- 使用原始的赋值方式而非table.insert,以提高性能。
				result[#result + 1] = line
				result2[#result2 + 1] = stationInThisLine
			end
		end
	end
	return result, result2
end

-- 以下部分为模块调用专用。
function p.stationLink(frame)
	local args = require 'Module:Arguments'.getArgs(frame)
	local yesno = require 'Module:Yesno'
	local station_id = args.name or args.station or args.station_id -- 车站名。注意实质上是车站id,因此并不是station:getName()得到的名字。
	local system_name = args.system
	local isStyled = yesno (args.styled or args.isStyled, false)
	local short = yesno (args.short)
	if short==nil then short=true end
	if not station_id then
		error "请提供车站id(参数station)"
	end
	return System.load(system_name):getStation(station_id):getLink(isStyled, not short)
end

function p.lineColor(frame)
	local args = require 'Module:Arguments'.getArgs(frame)
	local line_id = args.name or args.line or args.line_id -- 线路名。注意是在lines字段中的键,而不是line:getName()得到的线路名。
	if not line_id then
		error "请提供线路id(参数line)"
	end
	local system_name = args.system
	local prefixed = require 'Module:Yesno' (args.prefixed or args.prefix)
	local optional = require 'Module:Yesno' (args.optional)
	local line = System.load(system_name):getLine(line_id)
	return line and line:getColor(prefixed) or args.default
		or (optional and '' or error('找不到id为“' .. tostring(line_id) .. '”的线路'))
end

function p.lineTitle(frame)
	mw.addWarning("已弃用的函数lineTitle,请使用lineLink。")
	return p.lineLink(frame)
end

function p.lineLink(frame)
	local args = require 'Module:Arguments'.getArgs(frame)
	local line_id = args.name or args.line or args.line_id
	if not line_id then
		error "请提供线路id(参数line)"
	end
	local system_name = args.system
	local style = args.nameType or args.name_type or args.style
	local rich = require 'Module:Yesno' (args.rich)
	local optional = require 'Module:Yesno' (args.optional)
	local system = System.load(system_name)
	local line = not optional and system:getLineOrThrow(line_id) or system:getLine(line_id)
	local styled = require 'Module:Yesno' (args.styled)
	return line and line:getLink(style, rich, styled) or ''
end

function p.lineName(frame)
	local args = require 'Module:Arguments'.getArgs(frame)
	local line_id = args.name or args.line or args.line_id
	if not line_id then
		error "请提供线路id(参数line)"
	end
	local system_name = args.system
	local nameType = args.nameType or args.name_type or args.style
	local rich = require 'Module:Yesno' (args.rich)
	local system = System.load(system_name)
	local optional = require 'Module:Yesno' (args.optional)
	local line = not optional and system:getLineOrThrow(line_id) or system:getLine(line_id)
	return line and line:getName(nameType, rich) or ''
end

function p.linePage(frame)
	local args = require 'Module:Arguments'.getArgs(frame)
	local line_id = args.name or args.line or args.line_id
	if not line_id then
		error "请提供线路id(参数line)"
	end
	local system_name = args.system
	local optional = require 'Module:Yesno' (args.optional)
	local system = System.load(system_name)
	local line = not optional and system:getLineOrThrow(line_id) or system:getLine(line_id)
	return line and line:getPage() or ''
end

function p.lineRichTitle(frame)
	mw.addWarning("已弃用的函数lineRichTitle,请使用lineRichLink。")
	return p.lineRichLink(frame)
end

function p.lineRichLink(frame)
	local args = require 'Module:Arguments'.getArgs(frame)
	local line_id = args.name or args.line or args.line_id or error "缺少参数:line"
	if not line_id then
		error "请提供线路id(参数line)"
	end
	local optional = require 'Module:Yesno' (args.optional)
	local system_name = args.system or error "缺少参数:system"
	local style = args.nameType or args.name_type or args.style
	local styled = require 'Module:Yesno' (args.styled or args.isStyled)
	local system = System.load(system_name)
	local line = not optional and system:getLineOrThrow(line_id) or system:getLine(line_id)
	return line and line:getLink(style, true, styled) or ''
end

function p.lineStationList(frame)
	local yesno = require 'Module:Yesno' 
	local args = require 'Module:Arguments'.getArgs(frame)
	local line_id = args.name or args.line or args.line_id or error "缺少参数:line"
	local system_name = args.system or error "缺少参数:system"
	local system = System.load(system_name)
	local separator = args.separator or ' – '
	local params = {}
	params.showInterchange = yesno(args.showInterchange, true)
	params.isStyled = yesno(args.styled or args.isStyled, true)
	params.noLink = yesno(args.noLink) 
	params.condition = args.condition
	params.interchangeCondition = args.interchangeCondition
	params.stationCondition = args.stationCondition
	return table.concat(system:getLine(line_id):renderStationList(params), separator)
end

function p.lineTerminalLink(frame)
	local args = require 'Module:Arguments'.getArgs(frame)
	local line_id = args.name or args.line or args.line_id or error "缺少参数:line"
	local system_name = args.system or error "缺少参数:system"
	local system = System.load(system_name)
	local line = system:getLine(line_id)
	local terminal = line:getTerminalStation(args.side or args.direction, args.branch and {type = args.type, branch = args.branch} or args.type, args.condition)
	if terminal and terminal.getLink then
		return terminal and terminal:getLink() or ''
	else return '' end
end

function p.lineTerminal(frame)
	mw.addWarning '已弃用的函数lineTerminal,请使用lineTerminalLink'
	return p.lineTerminalLink(frame)
end

function p.lineTerminalName(frame)
	local args = require 'Module:Arguments'.getArgs(frame)
	local line_id = args.name or args.line or args.line_id or error "缺少参数:line"
	local system_name = args.system or error "缺少参数:system"
	local system = System.load(system_name)
	local line = system:getLine(line_id)
	local terminal = line:getTerminalStation(args.side or args.direction, args.branch and {type = args.type, branch = args.branch} or args.type, args.condition)
	return terminal and terminal:getName() or ''
end

-- @deprecatd
function p.trainTime(frame)
	mw.addWarning "trainTime函数已经弃用,请考虑不再使用此函数。"
	local args = require 'Module:Arguments'.getArgs(frame)
	local t = args.type
	if t == 'f' or t == 'F' then t = 'first' end
	if t == 'l' or t == 'L' then t = 'last' end
	local system = System.load(args.system or error "缺少参数:system")
	local line = system:getLine(args.name or args.line or args.line_id or error "缺少参数:line")
	local h,m,s = line:getTrainTime(args.dir, t, args.delta)
	return string.format('%02d:%02d', h, m)
end

-- @deprecated
function p.trainDirectionTime(frame)
	(allowDeprecated and mw.addWarning or error) "trainDirectionTime函数已经弃用,请考虑不再使用此函数。"
	local args = require 'Module:Arguments'.getArgs(frame)
	local t = args.type
	if t == 'f' or t == 'F' then t = 'first' end
	if t == 'l' or t == 'L' then t = 'last' end
	local system = System.load(args.system or error "缺少参数:system")
	local line = system:getLine(args.name or args.line or args.line_id or error "缺少参数:line")
	local h,m,s = line:getTrainTime(args.dir, t, args.delta)
	local endService = (line.data.trainTime[args.dir].endService)
	local terminalStation = line:getStation(endService) or system:getStation(endService)
	--[[
	if sparts[1] == 'station_name' then
			str = plain_replace(str, '{' .. c .. '}', terminal)
		elseif sparts[1] == 'station_link' then
			str = plain_replace(str, '{' .. c .. '}', p._internalStationLink(terminal, '', true, a.system, frame))
		elseif sparts[1] == 'train_time' then
			str = plain_replace(str, '{' .. c .. '}', p._formatTemplateTime(frame, sparts, hour, minute, sec))
		end
	]]
	local template = '往{station_link}方向:{train_time|H:i|次日}'
	return template:gsub('%{(.-)%}', function (content)
		local sparts = mw.text.split(content, '|')
		if content == 'station_name' then return terminalStation
		elseif sparts[1] == 'station_link' then
			return terminalStation:getLink()
		elseif sparts[1] == 'train_time' then
			return deprecated._formatTemplateTime(frame, sparts, h, m, s)
		end
	end)
end

-- @deprecated
function p.stationTrainTime(frame)
	(allowDeprecated and mw.addWarning or error) "stationTrainTime函数已经弃用,请考虑不再使用此函数。"
	local args = require 'Module:Arguments'.getArgs(frame)
	local system = System.load(args.system or error "缺少参数:system")
	local dir_reprs = {}
	
	local min_time_data = 999999
	local max_time_data = -999999
	
	local line_names = mw.text.split(args.name, ',')
	
	for k, v in pairs(args) do
		if type(k) == 'number' then
			dir_reprs[k] = v
		end
	end
	
	for k, line_name in ipairs(line_names) do
		for i = 1, #(dir_reprs), 2 do
			local dir = dir_reprs[i]
			local diff = dir_reprs[i + 1]
			local dir_times = system.data.lines[line_name].trainTime[dir]
	
			if dir_times then
				local dh, dm, ds = deprecated._parseTime(diff)
				for _, dir_time in ipairs({dir_times.first, dir_times.last}) do
					if dir_time ~= nil then
						local h, m, s = deprecated._parseTime(dir_time)
						local t = (h * 60 + m) * 60 + s + (dh * 60 + dm) * 60 + ds
						min_time_data = math.min(min_time_data, t)
						max_time_data = math.max(max_time_data, t)
					end
				end
			end
		end
	end
	
	local min_h, min_m, min_s = deprecated._extractSeconds(min_time_data)
	local max_h, max_m, max_s = deprecated._extractSeconds(max_time_data)
	
	local str = frame.template
	str = str or deprecated.DEFAULT_STATION_TRAIN_TIME_TEMPLATE
	local l = 0
	return str:gsub('%{(.-)%}', function (content)
		local sparts = mw.text.split(content, '|')
		if sparts[1] == 'start_time' then
			return deprecated._formatTemplateTime(frame, sparts, min_h, min_m, min_s)
		elseif sparts[1] == 'end_time' then
			return deprecated._formatTemplateTime(frame, sparts, max_h, max_m, max_s)
		end
	end)
end


-- @deprecated
function p.lineDateMessage(frame)
	(allowDeprecated and mw.addWarning or error) "lineDataMessage函数已经弃用,请考虑不再使用此函数。"
	local args = require 'Module:Arguments'.getArgs(frame)
	local system = System.load(args.system or error "缺少参数:system")
	local line = system:getLine(args.name or args.line or args.line_id or error "缺少参数:line")
	local reprs, options = {}, {}
	local preserved = { name = true, ['type'] = true, system = true }
	return line:getDateMessage(args.type, args)
end

local testCases = {
	-- {function, args, expected_result}
	{p.stationLink, {name='宁波站', system='UseCase'}, '[[宁波站|宁波站]]'},
	{p.stationLink, {name='寧波站', system='UseCase'}, '[[宁波站|宁波站]]'},
	{p.stationLink, {name='高桥西', system='UseCase'}, '[[高桥西站(宁波)|高桥西]]'},
	{p.stationLink, {name='高桥西', short='0', system='UseCase'}, '[[高桥西站(宁波)|高桥西站]]'},
	{p.stationLink, {name='泽民', system='UseCase'}, '[[泽民站|泽民]]'},
	{p.stationLink, {name='内嵌模板', system='UseCase'}, '[[内嵌模板站|内嵌|模板]]'},
	{p.lineColor, {name='1', system='UseCase'}, '3180b7'},
	{p.lineColor, {name='1', prefix='1', system='UseCase'}, '#3180b7'},
	-- {p.lineColor, {name='1', system='TestComp'}, 'cc0000'},
	{p.lineLink, {name='1', system='UseCase'}, "[[宁波轨道交通1号线|1号线]]"},
	{p.lineLink, {name='WRL', system='UseCase'}, "[[西鐵綫|西鐵綫]]"},
	-- {p.lineLink, {name='WRL', link='0', system='UseCase'}, "西鐵綫"},
	-- {p.lineTitle, {name='1', system='TestComp'}, '[[哈尔滨地铁1号线|1号线]]'},
	{p.lineTerminal, {name='S1', side='left', type='F', system='UseCase'}, '[[宁波东站|宁波东站]]'},
	-- {p.lineTerminal, {name='1', side='left', type='s1', branch='b1', system='TestComp'}, '[[S1B1]]'},
	
	{p.lineTerminalName, { name='1', side='left', system='UseCase'}, '高桥西'},
	{p.lineTerminalName, { name='S1', side='left', system='UseCase'}, '宁波站'},
	{p.lineTerminalName, { name='S1', side='left', type='F', system='UseCase'}, '宁波东站'},
	{p.lineTerminalName, { name='S1', side='right', system='UseCase'}, '余姚'},
	{p.lineTerminalName, { name='S1', side='right', type='F', system='UseCase'}, '马渚'},
	{p.lineTerminalName, { name='S1', side='right', type='F', branch='HZW', system='UseCase'}, '杭州湾'},
	-- {p.lineTerminalName, { name='1', side='left', ['type']='s1', branch='b1', system='TestComp'}, 'S1B1'},
	-- {p.lineTerminalName, { name='1', side='left', ['type']='s1', system='TestComp'}, 'S1DEFAULT'},
	-- {p.lineTerminalName, { name='1', side='left', ['type']='s2', system='TestComp'}, 'DEFAULT'},

	{p.trainTime, {name='1', dir='霞高', ['type']='L', delta='0:3', system='UseCase'}, '22:06'},
	{p.trainTime, {name='1', dir='高霞', ['type']='F', delta='0:4', system='UseCase'}, '06:08'},
	{p.trainDirectionTime, {name='1', dir='霞高', ['type']='L', delta='0:3', system='UseCase'}, "往[[高桥西站(宁波)|高桥西]]方向:22:06"},
	{p.trainDirectionTime, {name='1', dir='高霞', ['type']='F', delta='0:4', system='UseCase'}, "往[[霞浦站(宁波)|霞浦]]方向:06:08"},
	
	{p.stationTrainTime, {'高霞', '0:3', name='1', system='UseCase'}, "06:07-22:08"},
	{p.stationTrainTime, {'高霞', '0:3', '栎清', '0:1', name='1,2', system='UseCase'}, "06:07-22:10"},
	
	{p.lineDateMessage, {'在', 'year', '的', 'month', '的', 'day', ',月份', 'ym', ',日期', 'date', '开通', name='1', ['type']='2', cur_time = '2016-1-1', auto_hide=true, system='UseCase'}, '在2016年的3月的19日,月份2016年3月,日期2016年3月19日开通'},
	{p.lineDateMessage, {'year', name='1', ['type']='2', cur_time = '2016-5-19', auto_hide=true, system='UseCase'}, '2016年' },
	{p.lineDateMessage, {'year', name='1', ['type']='2', cur_time = '2017-3-19', auto_hide=true, system='UseCase'}, deprecated.DEFAULT_DATE_ELAPSED_TEXT},
	{p.lineDateMessage, {'ym', name='1', ['type']='2', cur_time = '2016-3-30', auto_hide=true, system='UseCase'}, '2016年3月'},
	{p.lineDateMessage, {'ym', name='1', ['type']='2', cur_time = '2016-4-19', auto_hide=true, system='UseCase'}, deprecated.DEFAULT_DATE_ELAPSED_TEXT},
	{p.lineDateMessage, {'ymd', name='1', ['type']='2', cur_time = '2016-3-18', auto_hide=true, system='UseCase'}, '2016年3月19日'},
	{p.lineDateMessage, {'ymd', name='1', ['type']='2', cur_time = '2016-3-19', auto_hide=true, system='UseCase'},deprecated.DEFAULT_DATE_ELAPSED_TEXT},
	{p.lineDateMessage, {'ymd', name='S1', cur_time = '2016-3-18', auto_hide=true, system='UseCase'}, '2019年12月28日'},
	{p.lineDateMessage, {'二期规划中', name='2', auto_hide=true, system='UseCase'},'二期规划中'},
	{p.lineDateMessage, {'ymd', name='2', auto_hide=true, system='UseCase'}, deprecated.DEFAULT_LINE_DATE_ABSENT_TEXT},
	{p.renderStationLinks, {system='UseCase', '1号线从$$高桥西$$\n', '到$$霞浦$$\n'}, '1号线从[[高桥西站(宁波)|高桥西]]\n|到[[霞浦站(宁波)|霞浦]]'},
	{p.renderStationLinks, {system='UseCase', short='0', '1号线从$$高桥西$$\n', '到$$霞浦$$\n'}, '1号线从[[高桥西站(宁波)|高桥西站]]\n|到[[霞浦站(宁波)|霞浦站]]'},
}

function p.testCase(frame)
	frame = frame or mw.getCurrentFrame()
	local fail = 0
	local success = 0
	local root = mw.html.create()
	local ol = root:tag 'ol'
	for k1, v1 in ipairs(testCases) do
		local func, args, expected = (unpack or table.unpack)(v1)
		local state, result = pcall(func, frame:newChild{args=args})
		if not state then
			fail = fail + 1
			ol	:newline()
				:tag 'li'
				:wikitext(string.format('函数调用出错:<tt class="error">%s</tt>,预期为<code>%s</code>', result, mw.text.nowiki(expected)))
		elseif result ~= expected then
			fail = fail + 1
			ol	:newline()
				:tag 'li'
				:wikitext(string.format('结果<code>%s</code>不符合预期<code>%s</code>', mw.text.nowiki(tostring(result)), mw.text.nowiki(expected)))
		else
			success = success + 1
			ol	:newline()
				:tag 'li'
				:css('color','gray')
				:wikitext(string.format('测试成功,结果:<tt>%s</tt>', mw.text.nowiki(tostring(result))))
		end
	end
	return root:newline():wikitext(string.format("测试完成,共成功%s次,失败%s次。",success,fail))
end

--- 从[[Module:Adjacent stations]]的子页面的数据中导入,并返回转换后的数据。
-- 未来将被弃用!
function p.importAdjacentStations(name)
	local data = mw.loadData ('Module:Adjacent stations/' .. name)
	local result = {}
	
	for k, v in pairs(data) do
		if type(v)~='table' and k ~= 'system title' and k ~= 'system icon'
		and k ~= 'station format' and k ~= 'lines' and k ~= 'aliases' then
			result[k] = v
		end
	end
	result.title = data['system title']
	do
		local icon = data['system icon']
		if icon then
			icon = icon:match '%[%[(.-)%]%]' or icon
			result.icon = {}
			for piece in mw.text.gsplit(icon, "|") do
				local matched
				if (function ()
					matched = piece:match '^alt=(.*)' return matched end)() then
					result.icon.alt = matched
				elseif (function ()
					matched = piece:match '^link=(.*)' return matched end)() then
					result.icon.link = matched
				elseif piece:match '^%d+px$' or piece:match '^%d+x%d+px$' then
					result.icon.size = piece
				else
					result.icon.file = result.icon.file or piece
				end
			end
		end
	end
	result.stationNames = {}
	for k, v in pairs(data['station format'] or {}) do
		if type(v) == 'table' then
			v = v[1]
		end
		v = tostring(v):gsub('%%1', k)
		local stripped = v:match '^%[%[([^%[%]]+)%]%]$'
		if stripped then
			v = stripped
		elseif not v:find '%|' then
			v = v .. '|' .. k
		end
		result.stationNames[k] = v
	end
	result.lines = {}
	for k, v in pairs(data.lines or {}) do
		if type(k) == 'string' then
			local line = {
				link = v.title,
				terminus = {
				},
				color = v.color,
				isLoop = v.circular
			}
			for _, key in ipairs{'left terminus', 'right terminus'} do
				local v1 = v[key]
				line.terminus[#line.terminus + 1] = type(v1) == 'table' and {main = v1[1], branch = v1[2]} or tostring(v1)
			end
			if v.title then
				local name = v.title:match '^%[%[(.-)%]%]$'
				if name then
					local split = mw.text.split(name, '%|')
					line.page = split[1]
					line.name = split[2] or split[1]
				else
					line.name = name
				end
			end
			if not (line.name or line.short_name) then
				-- 确保线路拥有名字
				line.name = k
				line.page = line.page or line.name
			end
			for k1, v1 in pairs(v) do
				if type(v) ~= 'table' and k1 ~= 'title' and k1 ~= 'left terminus'
				and k1 ~= 'right terminus' and k1 ~= 'types' and k1 ~= 'circular' and k1 ~= 'color' then
					line[k1] = v1
				end
			end
			if v.types and v.types['在建'] then
				table.insert(line.terminus, 1, {notOpen = true, id = v.types['在建']['left terminus']})
				table.insert(line.terminus, {notOpen = true, id = v.types['在建']['right terminus']})
			end
			result.lines[k] = line
		end
	end
	for k, v in pairs(data.aliases or {}) do
		result.lines[k] = result.lines[v]
	end
	result._imported = true
	return result
end

-- 仅出于兼容性考虑存在
-- 参见 {{RenderStations}}

function p._internalStationLink(name, style, short, system_data, frame)
	local station_parts, station_data
	local station_link, station_name
	local repr = ''
	if short == nil then short = true end
	
	local function marshalStation(station_link, station_name, style)
		if not short then
			local link_pos, _ = string.find(station_link, ' %(')
			if link_pos ~= nil then
				station_name = string.sub(station_link, 1, link_pos - 1)
			else
				station_name = station_link
			end
		end
		if style == nil or mw.text.trim(style) == '' then
			return '[[' .. station_link .. '|' .. station_name .. ']]'
		else
			return '[[' .. station_link .. '|<span style="' .. style .. '">' .. station_name .. '</span>]]'
		end
	end

	if type(system_data) == 'string' then
		system_data = _loadSystemData(system_data, false)
	end
	if system_data == nil then
		return nil
	end

	if system_data.stationNames[name] ~= nil then
		station_data = system_data.stationNames[name]
		if type(station_data) ~= 'table' then
			station_data = {station_data, }
		end
		for i, datum in ipairs(station_data) do
			if string.find(datum, '|', 1, true) == nil or string.find(datum, '[[', 1, true) ~= nil or string.find(datum, '{{', 1, true) ~= nil then
				repr = repr .. datum
			else
				station_parts = mw.text.split(datum, '|')
				station_link = station_parts[1]
				station_name = table.concat(station_parts, '|', 2)
				if frame ~= nil and string.find(station_name, '{', 1, true) ~= nil then
					station_name = frame:preprocess(table.concat(station_parts, '|', 2))
				end
				repr = repr .. marshalStation(station_link, station_name, style)
			end
		end
		return repr
	else
		station_link = name .. '站'
		return marshalStation(station_link, name, style)
	end
end

local function _loadSystemData(system, raises)
	local system_data = nil
	local state
	if raises == nil then
		raises = true
	end
	if system ~= nil then
		state, system_data = pcall(mw.loadData, "Module:RailSystems/" .. system)
		if not state then
			if raises then
				error(string.format('铁道系统“%s”的数据模块不存在', system))
			else
				system_data = nil
			end
		end
	end
	return system_data
end

local function _convBool(val, default)
	if val == nil then
		return default
	elseif type(val) == 'boolean' then
		return val
	elseif mw.text.trim(val) == '' then
		return default
	end
	return (mw.text.trim(val) == '1')
end

function p.renderStationLinks(frame)
	local a = frame:getParent().args
	local short = _convBool(a.short, true)
	local spos, epos, station_name = 1, 1, nil
	local pos = 1
	local system_data = _loadSystemData(a.system)
	local sys_args = { system=true, short=true }
	
	local text = ''
	for k, v in frame:getParent():argumentPairs() do
		if type(k) == 'number' then
			if string.len(text) > 0 then
				text = text .. '|' .. v
			else
				text = v
			end
		else
			if sys_args[k] == nil then
				if string.len(text) > 0 then
					text = text .. '|' .. k .. '=' .. v
				else
					text = k .. '=' .. v
				end
			end
		end
	end 
	text = mw.text.trim(text)
	
	local out_text = ''
	while spos ~= nil do
		spos, epos, station_name = string.find(text, '%$%$([^%$]+)%$%$', pos)
		if spos ~= nil then
			out_text = out_text .. string.sub(text, pos, spos - 1) .. p._internalStationLink(station_name, '', short, system_data, frame)
			pos = epos + 1
		else
			out_text = out_text .. string.sub(text, pos, string.len(text))
		end
	end
	return out_text
end

return p