Модуль:Routemap

Материал из Metrostroi
Перейти к: навигация, поиск
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

Для документации этого модуля может быть создана страница Модуль:Routemap/doc

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 = '[[Категория:Википедия:Статьи с неверно заполненными маршрутными картами]]'
    },
    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 !important; background-color:%s"| ||style="width:8px"|',
        ["cell-filler-empty-fmt"] = '\n|style="width:%s;min-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

        -- в «margin:auto !important» у таблицы «!important» — для мобильного вида
        ["row-general-fmt"] = '\
|-%s\
|colspan="%s" style="text-align:right;%s"|%s\
|style="text-align:left;padding:0 %s;%s"|<span style="font-size:90%%;">%s</span>\
|style="padding:0;background-color:%s"|\
{|cellspacing="0" cellpadding="0" style="margin:auto !important; text-align:center; line-height: 0px !important; padding:0 !important;"\
|-%s\
|}\
|style="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:none !important;white-space:nowrap"',-- parameters: bg, "collapsible "/"mw-collapsible mw-", collapse-state, "float:right;" / ""

        ["row-collapsible-end-fmt"] = '\n|}',

        ["row-collapsible-left-button-width"] = '50px',-- 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"] = '72px',-- 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:nowrap;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',
        maroon   = '800000', ex_maroon   = 'B16464',
        ochre    = 'CC6600', ex_ochre    = 'DEA164',
        orange   = 'FF6600', ex_orange   = 'FF9955',
        pink     = 'F0668D', ex_pink     = 'F4A1B8',
        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', '20px')
    end
end
local function fillercell(code)
    if code == '' then
    	return mw.ustring.format(i18n.html['cell-filler-empty-fmt'], '20px', '20px')
    elseif code == 'd' then
    	return mw.ustring.format(i18n.html['cell-filler-empty-fmt'], '10px', '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 = '2', '2', '', ''
    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])
    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 '')
    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'], q.linfo1_pad, '', result['linfo1'], result['rowProp']['bg'],
    		result['cells'], q.rinfo1_pad, '', 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]=]}}))

]]