模块:Routemap

维基百科,自由的百科全书
跳转至: 导航搜索
Documentation icon 模块文档[查看] [编辑] [页面历史] [清除]

本模塊由俄語維基百科用戶KPu3uC B PoccuuYLSS創建維護。

基本用法[编辑]

圖標代碼\圖標代碼\圖標代碼~~時間距離~~主文字~~備註~~右備註

圖標代碼\圖標代碼\圖標代碼~~主文字
{{BS-map
|map-title=例1
|map=
KBHFa~~起點
WASSERq\hWSTR\WASSERq~~ ~~ ~~ ~~天橋
LDER\INT\~~1公里~~中途站~~轉乘高鐵
\KBHFe\BUS~~2公里~~終點~~巴士總站
}}
例1
起點
天橋
1公里 中途站 轉乘高鐵
2公里 終點 巴士總站
  • 圖標分隔的斜間(\)與XML元素的終結斜間(/)正好相反。
  • 如果圖標欄後只有一個文字欄分隔「波浪-波浪」(~~),後面的文字會顯示在主文字欄而非時間距離欄。
  • 文字欄之間留空的話必須輸入空格,否則維基軟件會誤認為用戶簽名

圖標重疊與圖標鏈接[编辑]

{{BS-map
|map-title=例2
|map=
uSTRq!~ÜWol!~BHF!~HUB01~~圖標重疊
uBHF!@超級站~~圖標鏈接
}}
例2
圖標重疊
圖標鏈接
  • 重疊分隔「感嘆號-波浪」(!~)必須跟隨在下一層被重疊的圖標代碼之後。
  • 圖標重疊數基本是無限。
  • 使用圖標重疊的話,圖標連接分隔(!@)必須跟隨在最後(最頂)的圖標代碼之後。

摺疊[编辑]

{{BS-map
|map-title=例3.1:基本摺疊
|text-width=100
|map=
-startCollapsible-collapsed
\KBHFa\~~起點
BRÜCKEa
WASSERq\hWSTR\WASSERq~~ ~~ ~~ ~~天橋
BRÜCKEe
-endCollapsible-
LDER\INT\~~ ~~中途站~~轉乘高鐵
\KBHFe\BUS~~ ~~終點~~巴士總站
|map2-title=例3.2:單雙圖標行混用的摺疊
|text-width2=100
|map2=
-startCollapsible-collapsed
d\KBHFa\d~~起點
hWSTR~~ ~~ ~~ ~~天橋
-endCollapsible-
BS2+l\BS2+r~~分叉
}}
例3.1:基本摺疊
起點
天橋
中途站 轉乘高鐵
終點 巴士總站
例3.2:單雙圖標行混用的摺疊
起點
天橋
分叉
  • 調整{{{text-width}}}參數直到路線圖的圖標之間沒有斷裂。
    • 默認單位為px,可以使用其他單位如em。
    • 注意要為不同瀏覽器調整最合適的最大寬度。
  • 摺疊行(摺疊區域的首行)的圖標欄數目必須等於整個路線圖的最大圖標欄數目。
    • 單雙數量的圖標行混用的場合下如例3.2,可以用半寬的空白圖標未定義路線圖圖標屬性"d"d)作為填塞。
  • 將「collapsed」改為「nil」可以令摺疊部分預設為展開。

摺疊取代[编辑]

{{BS-map
|map-title=例4
|text-width=110
|map=
KBHFa~~起點
-startCollapsible-collapsed-replace
\LUECKE\~~可有可無的部分
\BRÜCKEa\
WASSERq\hWSTR\WASSERq~~ ~~ ~~ ~~天橋
BRÜCKEe
-endCollapsible-
LDER\INT\~~ ~~中途站~~轉乘高鐵
\KBHFe\BUS~~ ~~終點~~巴士總站
|map2-title=例4.2:空白填塞
|text-width2=100
|map2=
-startCollapsible-collapsed-replace
exCONTg~~建築中
leer
exKBHFa~~未來起點
exBHF~~未來站
-endCollapsible
KBHFxa~~起點
KBHFe~~終點
}}
例4.1
起點
可有可無的部分
天橋
中途站 轉乘高鐵
終點 巴士總站
例4.2:空白填塞
建築中
未來起點
未來站
起點
終點
  • 取代與被取代的兩行的圖標欄數目都必須等於整個路線圖的最大圖標欄數目。
  • 如欲隱藏取代行展開後的圖標,可以如例4.2以空白圖標leer)作為填塞。

雙文字欄[编辑]

左左備註~~左備註~~左主文字~~左時間距離! !圖標代碼~~右時間距離~~右主文字~~右備註~~右右備註

左主文字! !圖標代碼~~右主文字
{{BS-map
|map-title=例5
|map=
通勤起點! !uKBHFa\\KBHFa~~區域起點
鹹魚河~~ ~~ ~~! !uhWSTR\WASSERq\hWSTR~~ ~~ ~~ ~~天橋
轉乘高鐵~~中途站~~1公里! !uINT\LDER\LUECKE
通勤終點~~2公里! !uKBHFe\\KBHFe~~2公里~~區域終點
}}
例5
通勤起點
區域起點
鹹魚河
天橋
轉乘高鐵 中途站 1公里
通勤終點 2公里
2公里 區域終點
  • 左文字欄以「感嘆號-空格-感嘆號」(! !)分隔圖標欄。
  • 「感嘆號-空格-感嘆號」的左邊沒有任何「波浪-波浪」(~~)文字分隔的話,其左邊第一組及唯一一組文字會顯示在左主文字欄。

雙文字欄摺疊[编辑]

{{BS-map
|map-title=例6.1:使用了全部文字欄
|text-width=40,60,35,35,60,
|map=
-startCollapsible
通勤起點! !uKBHFa\leer\KBHFa~~區域起點
鹹魚河~~ ~~ ~~! !uhWSTR\WASSERq\hWSTR~~ ~~ ~~ ~~天橋
-endCollapsible
轉乘高鐵~~中途站~~1公里! !uINT\LDER\LUECKE
通勤終點~~2公里! !uKBHFe\\KBHFe~~2公里~~區域終點
|map2-title=例6.2:只用了主文字欄
|text-width2=,60,,,60,
|map2=
-startCollapsible
通勤起點! !uKBHFa\\KBHFa~~區域起點
鹹魚河! !uhWSTR\WASSERq\hWSTR~~天橋
-endCollapsible
中途站! !uINT\LDER\LUECKE
通勤終點! !uKBHFe\\KBHFe~~區域終點
}}
例6.1:使用了全部文字欄
通勤起點
區域起點
鹹魚河
天橋
轉乘高鐵 中途站 1公里
通勤終點 2公里
2公里 區域終點
例6.2:只用了主文字欄
通勤起點
區域起點
鹹魚河
天橋
中途站
通勤終點
區域終點
  • 需要使用{{{text-width}}}參數的進階定義避免圖標行之間斷裂:
    • 只有一個數值時僅代表右主文字+右備註的寬度
    • 有3個數值時,以半型逗號分隔代表文字欄的寬度為:右時間距離,右主文字+右備註,右右備註
    • 6個數值:左左備註,左備註+左主文字,左時間距離,右時間距離,右主文字+右備註,右右備註
    • 數值不是1、3或6組會令參數失效。
  • 右右備註的文字長度少於一定量可以省略其寬度定義,如例6.1。
  • 如果整個路線圖都沒有使用某一組文字欄,該欄的寬度定義可以省略留空,如例6.2。
  • 摺疊行的空白圖標欄如果被擠壓,可以使用空白圖標leer)固定寬度。
local i18n = {
    errors = {
        ["parameter-missing"] = "参数缺失!",
        ["collapsible-block-not-closed"] = "折叠单元未闭合!",
        ["collapsible-block-not-open"] = "找不到折叠单元开始标记!",
        ["collapsible-block-empty"] = "折叠单元不能为空!",
        ["collapsible-block-no-first-row"] = "折叠单元没有第一行!",
        ["collapsible-block-no-replacement"] = "折单元没有替代!",
        ["colspan-less-rows-than-set"] = "行数少于列跨距!",
    },
    ["error-categories"] = {
        default = '[[Category:包含错误线路图的条目]]'
    },
    html = {
        ["cell-icon-fmt"] = '\
|[[File:BSicon_%s.svg|x20px|link=%s|alt=]]',
        ["cell-overlapicon-fmt"] = '<div style="position:absolute;left:0px;top:0px;padding:0">[[File:BSicon_%s.svg|x20px|link=%s|alt=]]</div>',
        ["cell-icon-fmt-with-overlap"] = '\
|<div style="position:relative">%s</div>[[File:BSicon_%s.svg|x20px|link=|alt=]]',
        ["cell-filler-fmt"] = '\n|style="width:8px"| ||style="width:4px; background-color:%s"| ||style="width:8px"|',
        ["cell-filler-empty-fmt"] = '\n|style="width:%s"|',
 
        ["row-linfo4-fmt"] = '\
|style="padding-right:3px;text-align:left;%s"|<span style="font-size:90%%;">%s</span>',-- parameters:linfo4-width, linfo4
        ["row-linfo3-fmt"] = '<span style="font-size:90%%;">%s</span> ',
        ["row-rinfo3-fmt"] = ' <span style="font-size:90%%;">%s</span>',
        ["row-rinfo4-fmt"] = '\
|style="padding-left:3px;text-align:right;%s"|<span style="font-size:90%%;">%s</span>',-- parameters:rinfo4-width, rinfo4
 
        ["row-general-fmt"] = '\
|-%s\
|colspan="%s" style="text-align:right;%s"|%s\
|style="font-family:Liberation Mono,Courier New,Courier,Microsoft JhengHei,Microsoft YaHei,monospace;text-align:left;padding:0 %s;%s"|<span style="font-size:90%%;">%s</span>\
|style="padding:0;background-color:%s"|<center>\
{|cellspacing="0" cellpadding="0" style="line-height: 0px !important;padding:0 !important"\
|-%s\
|}</center>\
|style="font-family:Liberation Mono,Courier New,Courier,Microsoft JhengHei,Microsoft YaHei,monospace;text-align:right;padding:0 %s;%s"|<span style="font-size:90%%;">%s</span>\
|colspan="%s" style="text-align:left;%s"|%s%s',-- parameters: linfo4-fmt, colspan-left, linfo3+2-width, linfo3+2, linfo1-pad, linfo1-width, linfo1, bg, cells, rinfo1-pad, rinfo1-width, rinfo1, colspan-right, rinfo2+3-width, rinfo2+3, rinfo4-fmt
 
        ["row-collapsible-begin-fmt"] = '\
|-\
|colspan="7" style="padding:0 !important;background-color:%s"|\
{|class="%s%s" cellpadding="0" cellspacing="0" style="%s padding:0 !important;vertical-align:middle;margin:0 !important;white-space:normal;"',-- parameters: bg, "collapsible "/"mw-collapsible mw-", collapse-state, "float:right;" / ""
 
        ["row-collapsible-end-fmt"] = '\n|}',
 
        ["row-collapsible-left-button-width"] = '45px',-- 50px is the minimal width for [показать] / [скрыть] button. Use 40px for [show] / [hide]
        ["row-collapsible-left-button-fmt"] = '\n! style="padding-right:3px;min-width:%s;%s" |',--parameters: left-button-width, linfo4-width
        ["row-collapsible-left-linfo4+3+2-fmt"] = '\
{|cellspacing="0" cellpadding="0" width="100%%"\
|style="padding:0 3px 0 1px;text-align:left;"| <span style="font-size:90%%;">%s</span>\
|style="text-align:right"| %s\
|}',-- parameters: linfo4, linfo3+2
        ["row-collapsible-right-button-width"] = '45px',-- 72px is the minimal width for [развернуть] / [свернуть] button at 90%. Use 58px for [expand] / [collapse]
        ["row-collapsible-right-rinfo2+3+4-fmt"] = '\
{|cellspacing="0" cellpadding="0" width="100%%"\
|style="text-align:left"| %s\
|style="padding:0 1px 0 3px;text-align:right;"| <span style="font-size:90%%;">%s</span>\
|}',-- parameters: rinfo2+3, linfo4
        ["row-collapsible-right-button-fmt"] = '\n| style="padding-left:3px;font-size:90%%;min-width:%s;%s" |',--parameters: right-button-width, rinfo4-width
 
        ["row-collapsible-replace-begin-fmt"] = '\
|-\
|colspan="7" style="padding:0 %s"|<div style="position:relative">\
{| cellspacing="0" cellpadding="0" style="position:absolute;bottom:0px;%s vertical-align:middle;white-space:normal;background-color:%s"',-- parameters: "right-button-width 0 0" / "0 0 left-button-width", "right:0px" / "", bg
        ["row-collapsible-replace-end-fmt"] = '\n|}</div>',
 
        ["colspan-fmt"] = '%s\n|-\n| colspan="7" style="background-color:%s;text-align:%s;%s"|\n%s',
        ["empty-row-fmt"] = '\n|-\n| style="padding-right:3px;%s" |\n| style="%s" |\n| style="padding:0 %s;%s" |\n|\n| style="padding:0 %s;%s" |\n| style="%s" |\n| style="padding-left:3px;%s" |'
        }
}
local p,q={},{}
 
local function formaterror(key,param)
    local result = mw.ustring.format(i18n.html['colspan-fmt'], '', '', '', '', '<span class="error">' .. mw.ustring.format(i18n.errors[key] or (tostring(key) .. ' %s'),
        tostring(param or '')) .. '</span>')
    if mw.site.namespaces[mw.title.getCurrentTitle().namespace].isContent then result = result .. (i18n['errors-categories'][key] or i18n['errors-categories'].default or '') end
    return result
end
 
local function RGBbyCode(code)-- RGB codes for BSicon sets at Commons:Category:Icons for railway descriptions/other colors
    local colors = {--       Any changes should be discussed at Commons:Talk:BSicon/Colors
        bahn     = 'BE2D2C', ex          = 'D77F7E',
        u        = '003399', uex         = '6281C0',
        f        = '008000', fex         = '64B164',
        g        = '2CA05A', gex         = '7EC49A',
        azure    = '3399FF', ex_azure    = '99CCFF',
        black    = '000000', ex_black    = '646464',
        blue     = '0078BE', ex_blue     = '64ACD6',
        brown    = '8D5B2D', ex_brown    = 'B89A7F',
        cerulean = '1A8BB9', ex_cerulean = '73B7D3',
        cyan     = '40E0D0', ex_cyan     = '8AEAE1',
        denim    = '00619F', ex_denim    = '649EC3',
        fuchsia  = 'B5198D', ex_fuchsia  = 'D173B8',
        golden   = 'D7C447', ex_golden   = 'E5DA8E',
        green    = '2DBE2C', ex_green    = '7FD67E',
        grey     = '999999', ex_grey     = 'C0C0C0',
        jade     = '53B147', ex_jade     = '95CE8E',
        lavender = '9999FF', ex_lavender = 'C0C0FF',
        lime     = '99CC00', ex_lime     = 'D1E681',
        ochre    = 'CC6600', ex_ochre    = 'DEA164',
        orange   = 'FF6600', ex_orange   = 'FF9955',
        purple   = '8171AC', ex_purple   = 'B1A8CB',
        red      = 'EF161E', ex_red      = 'F37176',
        ruby     = 'CC0066', ex_ruby     = 'DE64A1',
        saffron  = 'FFAB2E', ex_saffron  = 'FFC969',
        sky      = '069DD3', ex_sky      = '67C2E3',
        steel    = 'A1B3D4', ex_steel    = 'C4CFE3',
        teal     = '339999', ex_teal     = '82C0C0',
        violet   = '800080', ex_violet   = 'B164B1',
        yellow   = 'FFD702', ex_yellow   = 'FFEB81',
    }
    return colors[code] or colors.bahn
end
 
local function cell(icon,overlapIcons)
--Icon handling. Each icon is defined as in the following example:
--icon ID!~overlap icon ID!@image link target
--No limit on overlap icons, just separate them by "!~".
    local tmp,link={},''
    if #overlapIcons>0 then
        tmp = mw.text.split(overlapIcons[#overlapIcons], '!@')
        overlapIcons[#overlapIcons] = tmp[1]
        if #tmp > 1 then link = tmp[2] end
        tmp = {}
        for i,v in ipairs(overlapIcons) do
            if i==#overlapIcons then local link=link else local link='' end
            table.insert(tmp,mw.ustring.format(i18n.html['cell-overlapicon-fmt'],mw.text.trim(v),link))end
        return mw.ustring.format(i18n.html['cell-icon-fmt-with-overlap'],mw.text.trim(table.concat(tmp)),icon)
    end
    tmp = mw.text.split(icon, '!@')
    icon = mw.text.trim(tmp[1])
    if #tmp > 1 then link = tmp[2] end
    if icon ~= '' then
    	return mw.ustring.format(i18n.html['cell-icon-fmt'], icon, link)
    else
    	return mw.ustring.format(i18n.html['cell-filler-empty-fmt'], '20px')
    end
end
local function fillercell(code)
    if code == '' then
    	return mw.ustring.format(i18n.html['cell-filler-empty-fmt'], '20px')
    elseif code == 'd' then
        return mw.ustring.format(i18n.html['cell-filler-empty-fmt'], '10px')
    elseif string.sub(code,1,1) == '#' then
    	return mw.ustring.format(i18n.html['cell-filler-fmt'], code)
    else
    	return mw.ustring.format(i18n.html['cell-filler-fmt'],'#' .. RGBbyCode(code))
    end
end
local function properties(str)
--str is a combination of properties with following syntax:
--[property name=value[!@property name1=value1[!@property name1=value1]]] and so on
    local result = {}
    for i, v in ipairs(mw.text.split(str, '!@')) do
        if v ~= '' then
            local t = mw.text.split(v, '=')
            table.insert(result, t[1])
            result[t[1]] = table.concat(t, '=', 2) or ''--fill table with pairs "property"="value"
        end
    end
    return result
end
 
local function row(pattern,noformatting,filler)
--Row handling. Each row looks like the following:
--row properties~~linfo4~~linfo3~~linfo2~~linfo1! !(icon pattern)~~rinfo1~~rinfo2~~rinfo3~~rinfo4~~row properties
    local result = {['linfo4'] = '', ['linfo3+2'] = '', ['linfo1'] = '', ['cells'] = {}, ['rinfo1'] = '', ['rinfo2+3'] = '', ['rinfo4'] = '', ['rowProp'] = {}}
    local lcolspan, rcolspan, linfo4_fmt, rinfo4_fmt, l1pad, r1pad = '2', '2', '', '', q.linfo1_pad, q.rinfo1_pad
    local left, rigth, icons, overlapIcons, tmp = {}, {}, {}, {}, mw.text.split(pattern, '! !')
    if #tmp > 1 then--splitting the pattern by '! !'
        left = tmp[1] ; right = tmp[2]
    else
        left = '' ; right = tmp[1] or ''
    end
 
    tmp = mw.text.split(left, '~~')--analysing the left part
    if #tmp > 1 then--if there are several ~~
        result['linfo1'] = mw.getCurrentFrame():preprocess(mw.text.trim(tmp[#tmp]))
        result['linfo3+2'] = mw.text.trim(tmp[#tmp - 1])
        if #tmp > 2 then
            tmp[#tmp - 2] = mw.text.trim(tmp[#tmp - 2])
            if tmp[#tmp - 2] ~= '' then result['linfo3+2'] = mw.ustring.format(i18n.html['row-linfo3-fmt'], tmp[#tmp - 2]) .. result['linfo3+2'] end
            if #tmp > 3 then
                tmp[#tmp - 3] = mw.text.trim(tmp[#tmp - 3])
                if tmp[#tmp - 3] ~= '' then
                    result['linfo4'] = mw.getCurrentFrame():preprocess(tmp[#tmp - 3])
                    lcolspan = '1'
                    linfo4_fmt = mw.ustring.format(i18n.html['row-linfo4-fmt'], '', result['linfo4'])
                end
                if #tmp > 4 then result['rowProp'] = properties(mw.text.trim(tmp[#tmp - 4])) end
            end
        end
    else--assume only linfo2 was provided.
        result['linfo3+2'] = mw.text.trim(tmp[1])
        l1pad = '0'
    end
    result['linfo3+2'] = mw.getCurrentFrame():preprocess(result['linfo3+2'])--expand possible templates in info.
 
    tmp = mw.text.split(right, '~~')--analysing the right part
    if #tmp > 2 then
        result['rinfo1'] = mw.getCurrentFrame():preprocess(mw.text.trim(tmp[2]))
        result['rinfo2+3'] = mw.text.trim(tmp[3])
        if #tmp > 3 then
            tmp[4] = mw.text.trim(tmp[4])
            if tmp[4] ~= '' then result['rinfo2+3'] = result['rinfo2+3'] .. mw.ustring.format(i18n.html['row-rinfo3-fmt'], tmp[4]) end
            if #tmp > 4 then
                tmp[5] = mw.text.trim(tmp[5])
                if tmp[5] ~= '' then
                    result['rinfo4'] = mw.getCurrentFrame():preprocess(tmp[5])
                    rcolspan = '1'
                    rinfo4_fmt = mw.ustring.format(i18n.html['row-rinfo4-fmt'], '', result['rinfo4'])
                end
                if #tmp > 5 then result['rowProp'] = properties(mw.text.trim(tmp[6])) end
            end
        end
    else--assume only rinfo2 was provided.
        result['rinfo2+3'] = mw.text.trim(tmp[2] or '')
        r1pad = '0'
    end
    result['rinfo2+3'] = mw.getCurrentFrame():preprocess(result['rinfo2+3'])
 
    icons = mw.text.split(tmp[1], '\\')--splitting the string of icons first by "\"
    if type(filler) == 'string' then
    	result['cells'][1] = 'style="height:' .. filler .. '"'--row parameter before any cells
        for i, v in ipairs(icons) do table.insert(result['cells'], fillercell(v)) end--no !@ or !~ for filler row
    else
        for i, v in ipairs(icons) do
            tmp = mw.text.split(v, '!~')
            icons[i] = tmp[1]
            table.remove(tmp, 1)
            table.insert(overlapIcons, tmp)
        end
        for i, v in ipairs(icons) do table.insert(result['cells'], cell(v, overlapIcons[i])) end
    end
    result['cells'] = table.concat(result['cells'])
    if result['rowProp']['bg'] == nil or result['rowProp']['bg'] == '' then result['rowProp']['bg'] = 'transparent' end
 
    if noformatting then
    	return result
    else
    	return  mw.ustring.format(i18n.html['row-general-fmt'], linfo4_fmt, lcolspan, '', result['linfo3+2'], l1pad, '', result['linfo1'], result['rowProp']['bg'],
    		result['cells'], r1pad, '', result['rinfo1'], rcolspan, '', result['rinfo2+3'], rinfo4_fmt)
    end
end
 
q = {collapsibles = -1, text_width = {'', '', '', '', '', ''}, linfo1_pad = '3px', rinfo1_pad = '3px', bg = '#f9f9f9'}
q.isKeyword = function(pattern, i, rows, justTest)
    if string.sub(pattern, 1, 1) ~= '-' then if justTest then return false else return nil end end--not a valid keyword
    local tmp = mw.text.split(string.sub(pattern, 2), '%-')
    if type(q[tmp[1]])=="function" and tmp[1] ~= 'isKeyword' then
        if justTest then return tmp[1] else return q[tmp[1]](tmp, i, rows) end--valid keyword
    else
        if justTest then return false else return nil end
    end
end
q['startCollapsible'] = function(params, i, rows)
    table.remove(rows, i)
    local tmp = q.isKeyword(rows[i], i, rows, true)
    if tmp then
    	if tmp == 'endCollapsible' then return formaterror('collapsible-block-empty')
        else return formaterror('collapsible-block-no-first-row') ..  q.isKeyword(rows[i], i, rows) --no valid keywords that can follow "startCollapsible"
        end
    end
    if q.collapsibles == -1 then q.collapsibles = 1 else q.collapsibles = q.collapsibles + 1 end--q.collapsibles == -1 means there are no collapsibles at all; 0 - all closed; >0 - some not closed
    local collapsed, replace, props = params[2], params[3] or '', properties(table.concat(params, '-', 4))--params[1] is the keyword name so all indices are shifted by one.
    if collapsed == nil or collapsed == '' then collapsed = 'collapsed' end
    if props['bg'] == nil or props['bg'] == '' then props['bg'] = 'transparent' ; props['bg-replace'] = q.bg else props['bg-replace'] = props['bg'] end
    local mode, float, result
    if q.rinfo1_pad == '' then mode = 'collapsible ' ; float = 'float:right;'
    else mode = 'mw-collapsible mw-' ; float = ''
    end
    result = mw.ustring.format(i18n.html["row-collapsible-begin-fmt"], props['bg'], mode, collapsed, float)
    tmp = row(rows[i], true, nil)
    local linfo4_3_2_fmt, rinfo2_3_4_fmt = '', ''
    if q.rinfo1_pad == '' then
        if tmp['linfo4'] ~= '' or tmp['linfo3+2'] ~= '' then linfo4_3_2_fmt = mw.ustring.format(i18n.html['row-collapsible-left-linfo4+3+2-fmt'], tmp['linfo4'], tmp['linfo3+2']) end
        result = result .. mw.ustring.format(i18n.html['row-general-fmt'], mw.ustring.format(i18n.html['row-collapsible-left-button-fmt'], i18n.html['row-collapsible-left-button-width'], q.text_width[1]),
        	'1', q.text_width[2], linfo4_3_2_fmt, q.linfo1_pad, q.text_width[3], tmp['linfo1'], tmp['rowProp']['bg'], tmp['cells'], '', '', '', '1', '', '', mw.ustring.format(i18n.html['row-rinfo4-fmt'], '', ''))
    else
        if tmp['rinfo4'] ~= '' or tmp['rinfo2+3'] ~= '' then rinfo2_3_4_fmt = mw.ustring.format(i18n.html['row-collapsible-right-rinfo2+3+4-fmt'], tmp['rinfo2+3'], tmp['rinfo4']) end
        result = result .. mw.ustring.format(i18n.html['row-general-fmt'], mw.ustring.format(i18n.html['row-linfo4-fmt'], q.text_width[1], tmp['linfo4']),
        	'1', q.text_width[2], tmp['linfo3+2'], q.linfo1_pad, q.text_width[3], tmp['linfo1'], tmp['rowProp']['bg'], tmp['cells'], q.rinfo1_pad, q.text_width[4], tmp['rinfo1'],
        	'1', q.text_width[5], rinfo2_3_4_fmt, mw.ustring.format(i18n.html['row-collapsible-right-button-fmt'], i18n.html['row-collapsible-right-button-width'], q.text_width[6]))
    end
    if replace ~= '' then
        if q.isKeyword(rows[i + 1], i, rows, true) then return result .. formaterror('collapsible-block-no-replacement') end--a plain row needed for replacement
        table.remove(rows, i)
        tmp = row(rows[i], true, nil)
        local padding, right = i18n.html['row-collapsible-right-button-width'] .. ' 0 0', ''
        if q.rinfo1_pad == '' then padding = '0 0 ' .. i18n.html['row-collapsible-left-button-width'] ; right = 'right:0px;' end
        result = result .. mw.ustring.format(i18n.html['row-collapsible-replace-begin-fmt'], padding, right, props['bg-replace'])
        linfo4_3_2_fmt = '' ; rinfo2_3_4_fmt = ''
        if q.rinfo1_pad == '' then
            if tmp['linfo4'] ~= '' or tmp['linfo3+2'] ~= '' then linfo4_3_2_fmt = mw.ustring.format(i18n.html['row-collapsible-left-linfo4+3+2-fmt'], tmp['linfo4'], tmp['linfo3+2']) end
            result = result .. mw.ustring.format(i18n.html['row-general-fmt'], mw.ustring.format(i18n.html['row-linfo4-fmt'], '', ''), '1', q.text_width[2], linfo4_3_2_fmt,
            	q.linfo1_pad, q.text_width[3], tmp['linfo1'], tmp['rowProp']['bg'], tmp['cells'], '', '', '', '1', '', '', mw.ustring.format(i18n.html['row-rinfo4-fmt'], '', ''))
        else
            if tmp['rinfo4'] ~= '' or tmp['rinfo2+3'] ~= '' then rinfo2_3_4_fmt = mw.ustring.format(i18n.html['row-collapsible-right-rinfo2+3+4-fmt'], tmp['rinfo2+3'], tmp['rinfo4']) end
            result = result .. mw.ustring.format(i18n.html['row-general-fmt'], mw.ustring.format(i18n.html['row-linfo4-fmt'], q.text_width[1], tmp['linfo4']), '1', q.text_width[2],
            	tmp['linfo3+2'], q.linfo1_pad, q.text_width[3], tmp['linfo1'], tmp['rowProp']['bg'], tmp['cells'], q.rinfo1_pad, q.text_width[4], tmp['rinfo1'], '1', q.text_width[5],
            	rinfo2_3_4_fmt, mw.ustring.format(i18n.html['row-rinfo4-fmt'], '', ''))
        end
        result = result .. i18n.html['row-collapsible-replace-end-fmt']
    end
    return result
end
q['endCollapsible'] = function(params, i, rows)
    if q.collapsibles > 0 then
        q.collapsibles = q.collapsibles - 1
        return i18n.html['row-collapsible-end-fmt']
    else
        return formaterror('collapsible-block-not-open')
    end
end
q['colspan'] = function(params, i, rows)
    if params[2] == 'end' then return '' end
    local tmp, j, nrows, props = {}, 0, tonumber(params[2]), properties(table.concat(params, '-', 3))
    if nrows ~= 0 then table.remove(rows, i) end
    if nrows == nil then nrows = #rows - i + 1 end
    while j < nrows and i <= #rows do
        j = j + 1
        if rows[i] == '-colspan-end'  then
            j = nrows
        else
            table.insert(tmp, rows[i])
        end
        if nrows ~= j or i == #rows then table.remove(rows, i) end
    end
    if j < nrows then j = formaterror('colspan-less-rows-than-set',j) else j = '' end
    return mw.ustring.format(i18n.html['colspan-fmt'], j, props['bg'] or '', props['align'] or '', props['style'] or '', mw.getCurrentFrame():preprocess(table.concat(tmp, '\n')))
end
q['filler'] = function(params, i, rows)
	local tmp, height = table.concat(params, '-', 3), '5px'
    if #params < 3 or tmp == '' then return formaterror('parameter-missing') end--TODO: указать имя нужного параметра.
	if params[2] ~= '' then height = params[2] end
    return row(tmp, nil, height)
end
 
function p.RGBbyCode(frame)
    return RGBbyCode(mw.text.trim(frame.args[1] or ''))
end
 
function p.route(frame)
    local rows, tmp = mw.text.trim(frame.args['pattern'] or ''), {}
    if rows == '' then return formaterror('parameter-missing') end
    if mw.text.trim(frame.args['bg'] or '') ~= '' then q.bg = frame.args['bg'] end
    tmp = mw.text.split(mw.text.trim(frame.args['text-width'] or ''), ',')
    if #tmp == 6 then
        for i = 1, 6 do if tmp[i] ~= '' then if tonumber(string.sub(tmp[i],-1)) then q.text_width[i] = 'width:' .. tmp[i] .. 'px;' else q.text_width[i] = 'width:' .. tmp[i] .. ';' end end end
        if tmp[4] == '' and tmp[5] == '' and tmp[6] == '' then q.rinfo1_pad = ''--padding for rinfo1 column = 0, not 3px
            elseif tmp[1] == '' and tmp[2] == '' and tmp[3] == '' then q.linfo1_pad = '' end--padding for linfo1 column = 0, not 3px
    elseif #tmp == 3 then
        for i = 1, 3 do if tmp[i] ~= '' then if tonumber(string.sub(tmp[i],-1)) then q.text_width[i + 3] = 'width:' .. tmp[i] .. 'px;' else q.text_width[i + 3] = 'width:' .. tmp[i] .. ';' end end end
        q.linfo1_pad = ''
    elseif #tmp == 1 and tmp[1]~='' then
        if tonumber(string.sub(tmp[1],-1)) then q.text_width[5] = 'width:' .. tmp[1] .. 'px;' else q.text_width[5] = 'width:' .. tmp[1] .. ';' end
        q.linfo1_pad = ''
    end
    tmp = {}
 
    rows = mw.text.split(rows, '\n')
    local i, j = next(rows), next(rows, i)--removing empty lines
    while j ~= nil do
        if mw.text.trim(rows[j]) == '' then table.remove(rows, j) else i = j end
        j = next(rows, i)
    end
 
    for i, v in ipairs(rows) do
        local keyword = q.isKeyword(v, i, rows)
        if type(keyword) ~= "string" then table.insert(tmp, row(v, nil, nil)) else table.insert(tmp, keyword) end
    end
 
    if q.collapsibles > 0 then table.insert(tmp, formaterror('collapsible-block-not-closed') .. q['endCollapsible']()) end
    if q.collapsibles ~= -1 then if q.rinfo1_pad == '' then q.text_width[1] = q.text_width[1] .. 'min-width:' .. i18n.html['row-collapsible-left-button-width'] .. ';'
                                                       else q.text_width[6] = q.text_width[6] .. 'min-width:' .. i18n.html['row-collapsible-right-button-width'] .. ';' end end
    -- ↓ empty row to set column widths; ↑ if q.collapsibles ≠ -1 and there are collapsible sections, leftmost or rightmost column should be wide enough to accomodate the button
    table.insert(tmp, mw.ustring.format(i18n.html['empty-row-fmt'], q.text_width[1], q.text_width[2], q.linfo1_pad, q.text_width[3], q.rinfo1_pad, q.text_width[4], q.text_width[5], q.text_width[6]))
    return table.concat(tmp)
end
 
return p
 
--[[for testing in console:
 
print(p.route({['args']={['text-width']='',['pattern']=[=[
STR
STR]=]}}))
 
]]