Модуль:Песочница/Pok: различия между версиями

Нет описания правки
Нет описания правки
 
(не показано 107 промежуточных версий этого же участника)
Строка 1: Строка 1:
-- source: https://en.wikipedia.org/wiki/Module:Params
local p = {}
---                                        ---
local getArgs = require('Module:Arguments').getArgs
---    LOCAL ENVIRONMENT                  ---
local JsonPaths = require('Module:JsonPaths')
---    ________________________________    ---
---                                        ---


local dpOk, dpModule = pcall(require, "Module:GetField")
local dp = dpOk and dpModule or nil


local switchModeRegistry = {}
local switchModeOrder = {}


--[[ Abstract utilities ]]--
local function trim(s)
----------------------------
    if not s then return s end
    return (s:gsub("^%s*(.-)%s*$", "%1"))
end


 
local function each_csv_value(str, fn)
-- Helper function for `string.gsub()` (for managing zero-padded numbers)
    if not str or str == "" then
function zero_padded(str)
        return
return ("%03d%s"):format(#str, str)
    end
    for item in string.gmatch(str, "[^,]+") do
        local value = trim(item)
        if value ~= "" then
            fn(value)
        end
    end
end
end


 
local function load_module_data(page)
-- Helper function for `table.sort()` (for natural sorting)
    local moduleName = JsonPaths.get(page)
function natural_sort(var1, var2)
    local ok, data = pcall(mw.loadData, moduleName)
return tostring(var1):gsub("%d+", zero_padded) <
    if not ok then
tostring(var2):gsub("%d+", zero_padded)
        return nil
    end
    return data
end
end


 
local function load_template_content(path)
-- Return a copy or a reference to a table
    local title = mw.title.new("Template:" .. path)
local function copy_or_ref_table(src, refonly)
    if not title then
if refonly then return src end
        return nil
newtab = {}
    end
for key, val in pairs(src) do newtab[key] = val end
    local ok, content = pcall(title.getContent, title)
return newtab
    if not ok then
        return nil
    end
    return content
end
end


 
local function lcfirst(s)
-- Remove numerical elements from a table, shifting everything to the left
    if not s or s == "" then return s end
function remove_numerical_keys(tbl, idx, len)
    return string.lower(s:sub(1, 1)) .. (s:sub(2) or "")
local cache = {}
local tmp = idx + len - 1
for key, val in pairs(tbl) do
if type(key) == 'number' and key >= idx then
if key > tmp then cache[key - len] = val end
tbl[key] = nil
end
end
for key, val in pairs(cache) do tbl[key] = val end
end
end


local function load_entity_data(entityId)
    if not entityId or entityId == "" then
        return nil
    end


-- Make a reduced copy of a table (shifting in both directions if necessary)
    local page = "prototype/Entity/" .. entityId .. ".json"
function copy_table_reduced(tbl, idx, len)
    local moduleName = JsonPaths.get(page)
local ret = {}
    local ok, data = pcall(mw.loadData, moduleName)
local tmp = idx + len - 1
    if not ok or type(data) ~= "table" then
if idx > 0 then
        return nil
for key, val in pairs(tbl) do
    end
if type(key) ~= 'number' or key < idx then
    return data
ret[key] = val
elseif key > tmp then ret[key - len] = val end
end
elseif tmp > 0 then
local nshift = 1 - idx
for key, val in pairs(tbl) do
if type(key) ~= 'number' then ret[key] = val
elseif key > tmp then ret[key - tmp] = val
elseif key < idx then ret[key + nshift] = val end
end
else
for key, val in pairs(tbl) do
if type(key) ~= 'number' or key > tmp then
ret[key] = val
elseif key < idx then ret[key + len] = val end
end
end
return ret
end
end


local function normalize_component_name(name)
    if type(name) ~= "string" then
        return nil
    end


-- Make an expanded copy of a table (shifting in both directions if necessary)
    name = trim(name)
function copy_table_expanded(tbl, idx, len)
    if name == "" then
local ret = {}
        return nil
local tmp = idx + len - 1
    end
if idx > 0 then
for key, val in pairs(tbl) do
if type(key) ~= 'number' or key < idx then
ret[key] = val
else ret[key + len] = val end
end
elseif tmp > 0 then
local nshift = idx - 1
for key, val in pairs(tbl) do
if type(key) ~= 'number' then ret[key] = val
elseif key > 0 then ret[key + tmp] = val
elseif key < 1 then ret[key + nshift] = val end
end
else
for key, val in pairs(tbl) do
if type(key) ~= 'number' or key > tmp then
ret[key] = val
else ret[key - len] = val end
end
end
return ret
end


    if name:sub(1, 5) == "type:" then
        return name:sub(6)
    end
    if name:sub(1, 6) == "!type:" then
        return name:sub(7)
    end


-- Move a key from a table to another, but only if under a different name and
    return name
-- always parsing numerical strings as numbers
function steal_if_renamed(val, src, skey, dest, dkey)
local realkey = tonumber(dkey) or dkey:match'^%s*(.-)%s*$'
if skey ~= realkey then
dest[realkey] = val
src[skey] = nil
end
end
end


local function collect_entity_components(entity)
    local out = {}
    local seen = {}


    if type(entity) ~= "table" then
        return out
    end


--[[ Public strings ]]--
    local comps = entity.components
------------------------
    if type(comps) ~= "table" then
        return out
    end


    if #comps > 0 then
        for _, v in ipairs(comps) do
            local name = normalize_component_name(v)
            if name and not seen[name] then
                seen[name] = true
                out[#out + 1] = name
            end
        end
    else
        for k in pairs(comps) do
            local name = normalize_component_name(k)
            if name and not seen[name] then
                seen[name] = true
                out[#out + 1] = name
            end
        end
    end


-- Special match keywords (functions and modifiers MUST avoid these names)
    table.sort(out)
local mkeywords = {
    return out
['or'] = 0,
end
--pattern = 1, -- Simply uncommenting enables the option
plain = 2,
strict = 3
}


local function load_entity_components_from_dp(entityId)
    if not dp then
        return nil
    end


-- Sort functions (functions and modifiers MUST avoid these names)
    local getter = dp.getEntityComponents or dp.collectEntityComponents or dp.getComp
local sortfunctions = {
    if type(getter) ~= "function" then
--alphabetically = false, -- Simply uncommenting enables the option
        return nil
naturally = natural_sort
    end
}


    local ok, result = pcall(getter, { args = { entityId } })
    if not ok or result == nil or result == "" then
        return nil
    end


-- Callback styles for the `mapping_*` and `renaming_*` class of modifiers
    if type(result) == "table" then
-- (functions and modifiers MUST avoid these names)
        return result
--[[
    end


Meanings of the columns:
    if type(result) == "string" then
        local okJson, decoded = pcall(mw.text.jsonDecode, result)
        if okJson and type(decoded) == "table" then
            return decoded
        end
    end


  col[1] = Loop type (0-3)
    return nil
  col[2] = Number of module arguments that the style requires (1-3)
end
  col[3] = Minimum number of sequential parameters passed to the callback
  col[4] = Name of the callback parameter where to place each parameter name
  col[5] = Name of the callback parameter where to place each parameter value
  col[6] = Argument in the modifier's invocation that will override `col[4]`
  col[7] = Argument in the modifier's invocation that will override `col[5]`


A value of `-1` indicates that no meaningful value is stored (i.e. `nil`)
local function load_entity_components(entityId)
    local viaDp = load_entity_components_from_dp(entityId)
    if type(viaDp) == "table" and next(viaDp) ~= nil then
        local out = {}
        local seen = {}
        if #viaDp > 0 then
            for _, v in ipairs(viaDp) do
                local name = normalize_component_name(v)
                if name and not seen[name] then
                    seen[name] = true
                    out[#out + 1] = name
                end
            end
        else
            for k in pairs(viaDp) do
                local name = normalize_component_name(k)
                if name and not seen[name] then
                    seen[name] = true
                    out[#out + 1] = name
                end
            end
        end
        table.sort(out)
        return out
    end


]]--
    local entity = load_entity_data(entityId)
local mapping_styles = {
    return collect_entity_components(entity)
names_and_values = { 3, 2, 2, 1, 2, -1, -1 },
end
values_and_names = { 3, 2, 2, 2, 1, -1, -1 },
values_only = { 1, 2, 1, -1, 1, -1, -1 },
names_only = { 2, 2, 1, 1, -1, -1, -1 },
names_and_values_as = { 3, 4, 0, -1, -1, 2, 3 },
names_only_as = { 2, 3, 0, -1, -1, 2, -1 },
values_only_as = { 1, 3, 0, -1, -1, -1, 2 },
blindly = { 0, 2, 0, -1, -1, -1, -1 }
}


p.loadEntityData = load_entity_data
p.collectEntityComponents = collect_entity_components
p.loadEntityComponents = load_entity_components
p.entityHasComponent = function(entityOrId, compName)
    if not compName or compName == "" then
        return false
    end


-- Memory slots (functions and modifiers MUST avoid these names)
    if type(entityOrId) == "string" then
local memoryslots = {
        local entity = load_entity_data(entityOrId)
i = 'itersep',
        if not entity then
l = 'lastsep',
            return false
p = 'pairsep',
        end
h = 'header',
        local comps = collect_entity_components(entity)
f = 'footer',
        for _, v in ipairs(comps) do
n = 'ifngiven'
            if v == compName then
}
                return true
            end
        end
        return false
    end


    local comps = collect_entity_components(entityOrId)
    for _, v in ipairs(comps) do
        if v == compName then
            return true
        end
    end
    return false
end


-- Functions and modifiers MUST avoid these names too: `let`
local function makeTplCall(tplPath, sw, key, id, extra)
    local tplStr = "{{" .. tplPath .. "|" .. sw .. "|" .. key
    tplStr = tplStr .. "|id=" .. tostring(id)
    if extra and extra ~= "" then tplStr = tplStr .. "|" .. extra end
    tplStr = tplStr .. "}}"
    return tplStr
end


local function add_template_param(params, seen, raw)
    local param = trim(raw or "")
    if param == "" or param == "id" or param:match("^%d+$") then
        return
    end
    if not seen[param] then
        seen[param] = true
        params[#params + 1] = param
    end
end


local function collect_template_params(content)
    local params = {}
    local seen = {}


--[[ Module's private environment ]]--
    if not content or content == "" then
--------------------------------------
        return params
    end


    for param in content:gmatch("{{{%s*([^|}]+)%s*|") do
        add_template_param(params, seen, param)
    end
    for param in content:gmatch("{{{%s*([^|}]+)%s*}}") do
        add_template_param(params, seen, param)
    end


-- Maximum number of numerical parameters that can be filled, if missing (we
    return params
-- chose an arbitrary number for this constant; you can discuss about its
end
-- optimal value at Module talk:Params)
local maxfill = 1024


local function get_template_params(tplPath, content)
    return collect_template_params(content)
end


-- The private table of functions
local function sort_entries_by_priority(entries)
local library = {}
    table.sort(entries, function(a, b)
        if a.priority == b.priority then return a.idx < b.idx end
        return a.priority > b.priority
    end)
end


local function make_source(kind, name, pathName, tplPath)
    return { kind = kind, name = name, pathName = pathName, tplPath = tplPath }
end


-- Functions that can only be invoked in first position
local function register_switch_mode(name, cfg)
local static_iface = {}
    switchModeRegistry[name] = cfg or {}
    switchModeOrder[#switchModeOrder + 1] = name
end


local function new_switch_state()
    local state = { keyOrder = {}, keyToTemplates = {}, keySources = {} }
    for _, sw in ipairs(switchModeOrder) do
        state.keyOrder[sw] = {}
        state.keyToTemplates[sw] = {}
        state.keySources[sw] = {}
    end
    return state
end


-- Create a new context
local function ensure_switch_key(state, sw, key)
local function context_new()
    local byKey = state.keyToTemplates[sw]
local ctx = {}
    if not byKey[key] then
ctx.luaname = 'Module:Params' --[[ or `frame:getTitle()` ]]--
        byKey[key] = {}
ctx.iterfunc = pairs
        state.keyOrder[sw][#state.keyOrder[sw] + 1] = key
ctx.firstposonly = static_iface
    end
ctx.n_available = maxfill
    return byKey[key]
return ctx
end
end


local function add_switch_entry(state, sw, key, entry)
    local bucket = ensure_switch_key(state, sw, key)
    entry.idx = #bucket + 1
    bucket[#bucket + 1] = entry
end


-- Move to the next action within the user-given list
local function collect_tpl_calls(entries)
local function context_iterate(ctx, n_forward)
    local tplCalls = {}
local nextfn
    local sources = {}
if ctx.pipe[n_forward] ~= nil then
    if #entries > 0 then
nextfn = ctx.pipe[n_forward]:match'^%s*(.*%S)'
        sort_entries_by_priority(entries)
end
        for _, e in ipairs(entries) do
if nextfn == nil then error(ctx.luaname ..
            tplCalls[#tplCalls + 1] = e.tpl
': You must specify a function to call', 0) end
            sources[#sources + 1] = e.source
if library[nextfn] == nil then
        end
if ctx.firstposonly[nextfn] == nil then error(ctx.luaname ..
    end
': The function ‘' .. nextfn .. '’ does not exist', 0)
    return tplCalls, sources
else error(ctx.luaname .. ': The ‘' .. nextfn ..
'’ directive can only appear in first position', 0)
end
end
remove_numerical_keys(ctx.pipe, 1, n_forward)
return library[nextfn]
end
end


local function makeSourceLink(s)
    local className =
        (s.name:sub(1, 1):upper() .. s.name:sub(2)) ..
        (s.kind and (s.kind:sub(1, 1):upper() .. s.kind:sub(2)) or "")


-- Main loop
    local tplLabel = "Template:" .. s.tplPath
local function main_loop(ctx, start_with)
    return "[[" .. tplLabel .. "|" .. className .. "]]"
local fn = start_with
repeat fn = fn(ctx) until not fn
end
end


local function renderTitleBlock(key, tplCalls, sources, includeHeader, frame, showSource)
    local parts = {}
    if tplCalls and #tplCalls > 0 then
        for i, tpl in ipairs(tplCalls) do
            local add = true
            if frame then
                local expanded = frame:preprocess(tpl)
                add = expanded and trim(expanded) ~= ""
            end
            if add then
                local src = sources and sources[i]
                local line = '<div>' .. tpl .. '</div>'
                if showSource and src then
                    line = line .. '<div class="ts-Сущность-field">' .. makeSourceLink(src) .. '</div>'
                end
                parts[#parts + 1] = '<div class="ts-Сущность">' .. line .. '</div>'
            end
        end
        if #parts == 0 then
            return ""
        end
        if includeHeader then
            table.insert(parts, 1, "<h2>" .. mw.text.encode(key) .. "</h2>")
        end
    end
    return table.concat(parts, "\n")
end


-- Parse the arguments of the `mapping_*` and `renaming_*` class of modifiers
local function split_title_key(key)
function parse_child_args(dest, src, n_skip, default_style)
    local main, sub = (key or ""):match("^([^_]+)_(.+)$")
local style
    if main and sub then
local shf
        return main, sub
local tmp = src[n_skip + 1]
    end
if tmp ~= nil then style = mapping_styles[tmp:match'^%s*(.-)%s*$'] end
    return key, nil
if style == nil then
style = default_style
shf = n_skip - 1
else shf = n_skip end
local names
local nargs
local pin = style[2] + shf
local n_exist = style[3]
local karg = style[4]
local varg = style[5]
tmp = style[6]
if tmp > -1 then
tmp = src[tmp + shf]
karg = tonumber(tmp)
if karg == nil then karg = tmp:match'^%s*(.-)%s*$'
else n_exist = math.max(n_exist, karg) end
end
tmp = style[7]
if tmp > -1 then
tmp = src[tmp + shf]
varg = tonumber(tmp)
if varg == nil then varg = tmp:match'^%s*(.-)%s*$'
else n_exist = math.max(n_exist, varg) end
end
if src[pin] ~= nil and src[pin]:match'^%s*let%s*$' then
names = {}
repeat
tmp = src[pin + 1] or ''
names[tonumber(tmp) or tmp:match'^%s*(.-)%s*$' or ''] =
src[pin + 2]
pin = pin + 3
until src[pin] == nil or not src[pin]:match'^%s*let%s*$'
end
tmp = tonumber(src[pin])
if tmp ~= nil then
if tmp < 0 then tmp = -1 end
shf = n_exist - pin
for idx = pin + 1, pin + tmp do dest[idx + shf] = src[idx] end
nargs = pin + tmp + 1
else nargs = pin end
if names ~= nil then
for key, val in pairs(names) do dest[key] = val end
end
tmp = style[1]
if (tmp == 3 or tmp == 2) and dest[karg] ~= nil then
tmp = tmp - 2 end
if (tmp == 3 or tmp == 1) and dest[varg] ~= nil then
tmp = tmp - 1 end
return nargs, tmp, karg, varg
end
end


local function renderGroupedTitleBlocks(frame, keyOrder, keyToTemplates, noHeaders, showSource)
    local groups = {}
    local groupOrder = {}


-- Parse the arguments of the `with_*_matching` class of modifiers
    for _, key in ipairs(keyOrder or {}) do
local function parse_pattern_args(ctx, ptns, fname)
        local mainTitle, subTitle = split_title_key(key)
local state = 0
        if mainTitle and mainTitle ~= "" then
local cnt = 1
            local group = groups[mainTitle]
local keyw
            if not group then
local nptns = 0
                group = { blocks = {} }
for _, val in ipairs(ctx.pipe) do
                groups[mainTitle] = group
if state == 0 then
                groupOrder[#groupOrder + 1] = mainTitle
nptns = nptns + 1
            end
ptns[nptns] = { val, false, false }
state = -1
else
keyw = val:match'^%s*(.*%S)'
if keyw == nil or mkeywords[keyw] == nil or (
state > 0 and mkeywords[keyw] > 0
) then break
else
state = mkeywords[keyw]
if state > 1 then ptns[nptns][2] = true end
if state == 3 then ptns[nptns][3] = true end
end
end
cnt = cnt + 1
end
if state == 0 then error(ctx.luaname .. ', ‘' .. fname ..
'’: No pattern was given', 0) end
return cnt
end


            group.blocks[#group.blocks + 1] = {
                subTitle = subTitle,
                entries = keyToTemplates[key] or {}
            }
        end
    end


-- Map parameters' values using a custom callback and a referenced table
    local out = {}
function map_values(tbl, margs, karg, varg, looptype, fn)
    for _, mainTitle in ipairs(groupOrder) do
if looptype == 1 then
        local group = groups[mainTitle]
for key, val in pairs(tbl) do
        local parts = {}
margs[varg] = val
tbl[key] = fn()
end
elseif looptype == 3 then
for key, val in pairs(tbl) do
margs[karg] = key
margs[varg] = val
tbl[key] = fn()
end
elseif looptype == 2 then
for key in pairs(tbl) do
margs[karg] = key
tbl[key] = fn()
end
elseif looptype == 0 then
for key in pairs(tbl) do
tbl[key] = fn()
end
end
end


        if not noHeaders then
            parts[#parts + 1] = "<h2>" .. mw.text.encode(mainTitle) .. "</h2>"
        end


-- Map parameters' names using a custom callback and a referenced table
        for _, block in ipairs(group.blocks or {}) do
function map_names(tbl, rargs, karg, varg, looptype, fn)
            local tplCalls, sources = collect_tpl_calls(block.entries or {})
local cache = {}
            local blockText = renderTitleBlock(block.subTitle or mainTitle, tplCalls, sources, false, frame, showSource)
if looptype == 2 then
            if blockText ~= "" then
for key, val in pairs(tbl) do
                if block.subTitle and not noHeaders then
rargs[karg] = key
                    parts[#parts + 1] = "<h3>" .. mw.text.encode(block.subTitle) .. "</h3>"
steal_if_renamed(val, tbl, key, cache, fn())
                end
end
                parts[#parts + 1] = blockText
elseif looptype == 3 then
            end
for key, val in pairs(tbl) do
        end
rargs[karg] = key
rargs[varg] = val
steal_if_renamed(val, tbl, key, cache, fn())
end
elseif looptype == 1 then
for key, val in pairs(tbl) do
rargs[varg] = val
steal_if_renamed(val, tbl, key, cache, fn())
end
elseif looptype == 0 then
for key, val in pairs(tbl) do
steal_if_renamed(val, tbl, key, cache, fn())
end
end
for key, val in pairs(cache) do tbl[key] = val end
end


        if #parts > 0 then
            out[#out + 1] = table.concat(parts, "\n")
        end
    end


-- Concatenate the numerical keys from the table of parameters to the numerical
    return table.concat(out, "\n")
-- keys from the table of options; non-numerical keys from the table of options
-- will prevail over colliding non-numerical keys from the table of parameters
local function concat_params(ctx)
local tbl = ctx.params
local size = table.maxn(ctx.pipe)
local retval = {}
if ctx.subset == 1 then
-- We need only the sequence
for key, val in ipairs(tbl) do retval[key + size] = val end
else
if ctx.subset == -1 then
for key, val in ipairs(tbl) do tbl[key] = nil end
end
for key, val in pairs(tbl) do
if type(key) == 'number' then retval[key + size] = val
else retval[key] = val end
end
end
for key, val in pairs(ctx.pipe) do retval[key] = val end
return retval
end
end


local function normalizeFilterKey(s)
    s = trim(s or "")
    s = s:gsub("%s*_%s*", "_")
    return s
end


-- Flush the parameters by calling a custom function for each value (after this
local function matches_card_list(list, callKey, compositeKey)
-- function has been invoked `ctx.params` will be no longer usable)
    if not list then
local function flush_params(ctx, fn)
        return false
local tbl = ctx.params
    end
if ctx.subset == 1 then
    callKey = normalizeFilterKey(callKey)
for key, val in ipairs(tbl) do fn(key, val) end
    compositeKey = normalizeFilterKey(compositeKey)
return
    return list[callKey] or list[compositeKey] or false
end
if ctx.subset == -1 then
for key, val in ipairs(tbl) do tbl[key] = nil end
end
if ctx.dosort then
local nums = {}
local words = {}
local nlen = 0
local wlen = 0
for key, val in pairs(tbl) do
if type(key) == 'number' then
nlen = nlen + 1
nums[nlen] = key
else
wlen = wlen + 1
words[wlen] = key
end
end
table.sort(nums)
table.sort(words, natural_sort)
for idx = 1, nlen do fn(nums[idx], tbl[nums[idx]]) end
for idx = 1, wlen do fn(words[idx], tbl[words[idx]]) end
return
end
if ctx.subset ~= -1 then
for key, val in ipairs(tbl) do
fn(key, val)
tbl[key] = nil
end
end
for key, val in pairs(tbl) do fn(key, val) end
end
end


local function buildCardCall(merged, entityId)
    local parts = {}


    if entityId and entityId ~= "" then
        parts[#parts + 1] = "id=" .. mw.text.encode(entityId)
    end


--[[ Modifiers ]]--
    if merged.tags and #merged.tags > 0 then
-----------------------------
        table.sort(merged.tags)
        parts[#parts + 1] = "тип=" .. table.concat(merged.tags, ", ")
    end


    if merged.sections and #merged.sections > 0 then
        table.sort(merged.sections, function(a, b)
            if a == "Сущность" then return true end
            if b == "Сущность" then return false end
            return a < b
        end)
        parts[#parts + 1] = "sections=" .. table.concat(merged.sections, ", ")
        for _, section in ipairs(merged.sections) do
            local labels = merged.labelLists[section]
            if labels and #labels > 0 then
                local enc = {}
                for i = 1, #labels do
                    enc[i] = mw.text.encode(labels[i])
                end
                parts[#parts + 1] = mw.text.encode(section) .. "=" .. table.concat(enc, ", ")
            end
        end
    end


-- Syntax:  #invoke:params|sequential|pipe to
    for compositeKey, displayLabel in pairs(merged.labelOverrides or {}) do
library.sequential = function(ctx)
        if displayLabel and displayLabel ~= "" then
if ctx.subset == -1 then error(ctx.luaname ..
            parts[#parts + 1] = mw.text.encode(compositeKey .. "_label") .. "=" .. displayLabel
': The two directives ‘non-sequential’ and ‘sequential’ are in contradiction with each other', 0) end
        end
if ctx.dosort then error(ctx.luaname ..
    end
': The ‘all_sorted’ directive is redundant when followed by ‘sequential’', 0) end
    for compositeKey, content in pairs(merged.contentByKey or {}) do
ctx.iterfunc = ipairs
        if content and content ~= "" then
ctx.subset = 1
            parts[#parts + 1] = mw.text.encode(compositeKey) .. "=" .. content
return context_iterate(ctx, 1)
        end
end
    end


    if #parts == 0 then
        return ""
    end


-- Syntax:  #invoke:params|non-sequential|pipe to
    return "{{карточка/сущность|" .. table.concat(parts, "|") .. "}}"
library['non-sequential'] = function(ctx)
if ctx.subset == 1 then error(ctx.luaname ..
': The two directives ‘sequential’ and ‘non-sequential’ are in contradiction with each other', 0) end
ctx.iterfunc = pairs
ctx.subset = -1
return context_iterate(ctx, 1)
end
end


local function cardWrapper(frame, keyOrder, keyToTemplates, keySources, entityId, noHeaders, cardFilter)
    local merged = {
        sections = {},
        sectionsMap = {},
        labelLists = {},
        labelSets = {},
        labelOverrides = {},
        contentByKey = {},
        tags = {},
        tagSet = {}
    }
    local rawContentParts = {}
    for _, callKey in ipairs(keyOrder or {}) do
        local entries = keyToTemplates[callKey] or {}
        if #entries > 0 then
            sort_entries_by_priority(entries)
            for _, e in ipairs(entries) do
                local displayLabel = trim(frame:preprocess(e.tplLabel or "") or "")
                local content = trim(frame:preprocess(e.tplContent or "") or "")
                local tagText = ""
                if e.tplTag then
                    tagText = trim(frame:preprocess(e.tplTag or "") or "")
                end
                local compositeKey = (callKey:find("_", 1, true)) and callKey or ("Сущность_" .. callKey)
                local section = (callKey:find("_", 1, true)) and callKey:match("^([^_]+)") or "Сущность"


-- Syntax:  #invoke:params|sort|pipe to
                local isWhitelisted = cardFilter and matches_card_list(cardFilter.whitelist, callKey, compositeKey) or
library.all_sorted = function(ctx)
                    false
if ctx.subset == 1 then error(ctx.luaname ..
                local isBlacklisted = cardFilter and matches_card_list(cardFilter.blacklist, callKey, compositeKey) or
': The ‘all_sorted’ directive is redundant after ‘sequential’', 0) end
                    false
ctx.dosort = true
                if isWhitelisted and content ~= "" then
return context_iterate(ctx, 1)
                    rawContentParts[#rawContentParts + 1] = content
end
                end


                local allowCardEntry = not isWhitelisted and not isBlacklisted and
                    ((not cardFilter) or (not cardFilter.hasWhitelist) or
                        matches_card_list(cardFilter.cardWhitelist, callKey, compositeKey))


-- Syntax:  #invoke:params|setting|directives|...|pipe to
                if allowCardEntry and (displayLabel ~= "" or content ~= "") then
library.setting = function(ctx)
                    if not merged.sectionsMap[section] then
local opts = ctx.pipe
                        merged.sectionsMap[section] = true
local cmd = opts[1]
                        merged.sections[#merged.sections + 1] = section
if cmd ~= nil then
                    end
cmd = cmd:gsub('%s+', ''):gsub('/+', '/'):match'^/*(.*[^/])'
                    if displayLabel ~= "" and (not merged.labelOverrides[compositeKey] or merged.labelOverrides[compositeKey] == "") then
end
                        merged.labelOverrides[compositeKey] = displayLabel
if cmd == nil then error(ctx.luaname ..
                    end
', ‘setting’: No directive was given', 0) end
                    if content ~= "" then
local sep = string.byte('/')
                        local prev = merged.contentByKey[compositeKey]
local argc = 2
                        if prev and prev ~= "" then
local dest = {}
                            merged.contentByKey[compositeKey] = prev .. "\n" .. content
local vname
                        else
local chr
                            merged.contentByKey[compositeKey] = content
for idx = 1, #cmd do
                        end
chr = cmd:byte(idx)
                    end
if chr == sep then
for key, val in ipairs(dest) do
ctx[val] = opts[argc]
dest[key] = nil
end
argc = argc + 1
else
vname = memoryslots[string.char(chr)]
if vname == nil then error(ctx.luaname ..
', ‘setting’: Unknown slot "' ..
string.char(chr) .. '"', 0) end
table.insert(dest, vname)
end
end
for key, val in ipairs(dest) do ctx[val] = opts[argc] end
return context_iterate(ctx, argc + 1)
end


                    merged.labelSets[section] = merged.labelSets[section] or {}
                    if not merged.labelSets[section][compositeKey] then
                        merged.labelSets[section][compositeKey] = true
                        local cur = merged.labelLists[section] or {}
                        cur[#cur + 1] = compositeKey
                        merged.labelLists[section] = cur
                    end
                end


-- Syntax:  #invoke:params|squeezing|pipe to
                if allowCardEntry and tagText ~= "" then
library.squeezing = function(ctx)
                    if not merged.tagSet[tagText] then
local tbl = ctx.params
                        merged.tagSet[tagText] = true
local store = {}
                        merged.tags[#merged.tags + 1] = tagText
local indices = {}
                    end
local newlen = 0
                end
for key, val in pairs(tbl) do
            end
if type(key) == 'number' then
        end
newlen = newlen + 1
    end
indices[newlen] = key
store[key] = val
tbl[key] = nil
end
end
table.sort(indices)
for idx = 1, newlen do tbl[idx] = store[indices[idx]] end
return context_iterate(ctx, 1)
end


    each_csv_value(frame.args.cardTag or "", function(extraTag)
        if not merged.tagSet[extraTag] then
            merged.tagSet[extraTag] = true
            merged.tags[#merged.tags + 1] = extraTag
        end
    end)


-- Syntax:  #invoke:params|filling_the_gaps|pipe to
    local out = {}
library.filling_the_gaps = function(ctx)
    if #rawContentParts > 0 then
local tbl = ctx.params
        out[#out + 1] = table.concat(rawContentParts, "\n")
local nmin = 1
    end
local nmax = nil
local nnums = -1
local tmp = {}
for key, val in pairs(tbl) do
if type(key) == 'number' then
if nmax == nil then
if key < nmin then nmin = key end
nmax = key
elseif key > nmax then nmax = key
elseif key < nmin then nmin = key end
nnums = nnums + 1
tmp[key] = val
end
end
if nmax ~= nil and nmax - nmin > nnums then
ctx.n_available = ctx.n_available + nmin + nnums - nmax
if ctx.n_available < 0 then error(ctx.luaname ..
', ‘filling_the_gaps’: It is possible to fill at most ' ..
tostring(maxfill) .. ' parameters', 0) end
for idx = nmin, nmax, 1 do tbl[idx] = '' end
for key, val in pairs(tmp) do tbl[key] = val end
end
return context_iterate(ctx, 1)
end


    local cardCall = buildCardCall(merged, entityId)


-- Syntax:  #invoke:params|clearing|pipe to
    if noHeaders then
library.clearing = function(ctx)
        local hasLabel = false
local tbl = ctx.params
        for _, v in pairs(merged.labelOverrides or {}) do
local numericals = {}
            if v and v ~= "" then
for key, val in pairs(tbl) do
                hasLabel = true
if type(key) == 'number' then
                break
numericals[key] = val
            end
tbl[key] = nil
        end
end
        if not hasLabel then
end
            for _, lst in pairs(merged.labelLists or {}) do
for key, val in ipairs(numericals) do tbl[key] = val end
                if #lst > 0 then
return context_iterate(ctx, 1)
                    hasLabel = true
end
                    break
                end
            end
        end


        local hasContent = false
        for _, v in pairs(merged.contentByKey or {}) do
            if v and v ~= "" then
                hasContent = true
                break
            end
        end


-- Syntax:  #invoke:params|cutting|left cut|right cut|pipe to
        if not hasLabel and not hasContent then
library.cutting = function(ctx)
            return table.concat(out, "\n")
local lcut = tonumber(ctx.pipe[1])
        end
if lcut == nil then error(ctx.luaname ..
    end
', ‘cutting’: Left cut must be a number', 0) end
local rcut = tonumber(ctx.pipe[2])
if rcut == nil then error(ctx.luaname ..
', ‘cutting’: Right cut must be a number', 0) end
local tbl = ctx.params
local len = #tbl
if lcut < 0 then lcut = len + lcut end
if rcut < 0 then rcut = len + rcut end
local tot = lcut + rcut
if tot > 0 then
local cache = {}
if tot >= len then
for key in ipairs(tbl) do tbl[key] = nil end
tot = len
else
for idx = len - rcut + 1, len, 1 do tbl[idx] = nil end
for idx = 1, lcut, 1 do tbl[idx] = nil end
end
for key, val in pairs(tbl) do
if type(key) == 'number' and key > 0 then
if key > len then cache[key - tot] = val
else cache[key - lcut] = val end
tbl[key] = nil
end
end
for key, val in pairs(cache) do tbl[key] = val end
end
return context_iterate(ctx, 3)
end


    if cardCall ~= "" then
        out[#out + 1] = cardCall
    end


-- Syntax:  #invoke:params|cropping|left crop|right crop|pipe to
    return table.concat(out, "\n")
library.cropping = function(ctx)
local lcut = tonumber(ctx.pipe[1])
if lcut == nil then error(ctx.luaname ..
', ‘cropping’: Left crop must be a number', 0) end
local rcut = tonumber(ctx.pipe[2])
if rcut == nil then error(ctx.luaname ..
', ‘cropping’: Right crop must be a number', 0) end
local tbl = ctx.params
local nmin
local nmax
for key in pairs(tbl) do
if type(key) == 'number' then
if nmin == nil then
nmin = key
nmax = key
elseif key > nmax then nmax = key
elseif key < nmin then nmin = key end
end
end
if nmin ~= nil then
local len = nmax - nmin + 1
if lcut < 0 then lcut = len + lcut end
if rcut < 0 then rcut = len + rcut end
if lcut + rcut - len > -1 then
for key in pairs(tbl) do
if type(key) == 'number' then tbl[key] = nil end
end
elseif lcut + rcut > 0 then
for idx = nmax - rcut + 1, nmax do tbl[idx] = nil end
for idx = nmin, nmin + lcut - 1 do tbl[idx] = nil end
local lshift = nmin + lcut - 1
if lshift > 0 then
for idx = lshift + 1, nmax, 1 do
tbl[idx - lshift] = tbl[idx]
tbl[idx] = nil
end
end
end
end
return context_iterate(ctx, 3)
end
end


register_switch_mode("card", {
    full = true,
    build_entry = function(ctx, key)
        return {
            tplLabel = makeTplCall(ctx.tplPath, "cardLabel", key, ctx.id, ctx.extra),
            tplContent = makeTplCall(ctx.tplPath, "cardContent", key, ctx.id, ctx.extra),
            tplTag = makeTplCall(ctx.tplPath, "cardTag", key, ctx.id, ctx.extra),
            source = ctx.source,
            priority = ctx.priority
        }
    end,
    build_preview_entry = function(ctx, key)
        return {
            tplLabel = makeTplCall(ctx.tplPath, "cardLabel", key, ""),
            tplContent = makeTplCall(ctx.tplPath, "cardContent", key, ""),
            source = ctx.source,
            priority = ctx.priority
        }
    end,
    add_entity_extras = function(state, parsed, ctx)
        if type(parsed) == "table" and parsed.cardTag and parsed.cardTag ~= "" then
            add_switch_entry(state, "card", "cardTag", {
                tplLabel = "",
                tplContent = "",
                tplTag = makeTplCall(ctx.tplPath, "cardTag", "cardTag", ctx.id, ctx.extra),
                source = ctx.source,
                priority = ctx.priority
            })
        end
    end,
    render_full = function(frame, keyOrder, keyToTemplates, keySources, entityId, noHeaders, showSource, cardFilter)
        return cardWrapper(frame, keyOrder, keyToTemplates, keySources, entityId, noHeaders, cardFilter)
    end
})


-- Syntax:  #invoke:params|purging|start offset|length|pipe to
register_switch_mode("title", {
library.purging = function(ctx)
    full = true,
local idx = tonumber(ctx.pipe[1])
    build_entry = function(ctx, key)
if idx == nil then error(ctx.luaname ..
        return {
', ‘purging’: Start offset must be a number', 0) end
            tpl = makeTplCall(ctx.tplPath, "title", key, ctx.id, ctx.extra),
local len = tonumber(ctx.pipe[2])
            source = ctx.source,
if len == nil then error(ctx.luaname ..
            priority = ctx.priority
', ‘purging’: Length must be a number', 0) end
        }
local tbl = ctx.params
    end,
if len < 1 then
    build_preview_entry = function(ctx, key)
len = len + table.maxn(tbl)
        return {
if idx > len then return context_iterate(ctx, 3) end
            tpl = makeTplCall(ctx.tplPath, "title", key, ""),
len = len - idx + 1
            source = ctx.source,
end
            priority = ctx.priority
ctx.params = copy_table_reduced(tbl, idx, len)
        }
return context_iterate(ctx, 3)
    end,
end
    render_full = function(frame, keyOrder, keyToTemplates, keySources, entityId, noHeaders, showSource)
        return renderGroupedTitleBlocks(frame, keyOrder, keyToTemplates, noHeaders, showSource)
    end
})


local function getTemplateMeta(frame, tplPath)
    local expanded = frame:expandTemplate {
        title = tplPath,
        args = { "json" }
    }


-- Syntax:  #invoke:params|backpurging|start offset|length|pipe to
    local ok, data = pcall(mw.text.jsonDecode, expanded)
library.backpurging = function(ctx)
    if not ok or type(data) ~= "table" then
local last = tonumber(ctx.pipe[1])
        return ""
if last == nil then error(ctx.luaname ..
    end
', ‘backpurging’: Start offset must be a number', 0) end
local len = tonumber(ctx.pipe[2])
if len == nil then error(ctx.luaname ..
', ‘backpurging’: Length must be a number', 0) end
local idx
local tbl = ctx.params
if len > 0 then
idx = last - len + 1
else
for key in pairs(tbl) do
if type(key) == 'number' and (idx == nil or
key < idx) then idx = key end
end
if idx == nil then return context_iterate(ctx, 3) end
idx = idx - len
if last < idx then return context_iterate(ctx, 3) end
len = last - idx + 1
end
ctx.params = copy_table_reduced(ctx.params, idx, len)
return context_iterate(ctx, 3)
end


    if data.card == nil then
        local cardKeys = {}
        local seen = {}
        for base, labels in pairs(data) do
            if type(base) == "string" and base ~= "card" and base:sub(1, 4) == "card" and type(labels) == "table" then
                for _, lab in ipairs(labels) do
                    if not seen[lab] then
                        seen[lab] = true
                        cardKeys[#cardKeys + 1] = lab
                    end
                end
            end
        end
        data.card = cardKeys
    end


-- Syntax:  #invoke:params|rotating|pipe to
    return data
library.rotating = function(ctx)
local tbl = ctx.params
local numericals = {}
local nmax = 0
for key, val in pairs(tbl) do
if type(key) == 'number' then
numericals[key] = val
tbl[key] = nil
if key > nmax then nmax = key end
end
end
for key, val in pairs(numericals) do tbl[nmax - key + 1] = val end
return context_iterate(ctx, 1)
end
end


local function parseListArg(str)
    local res = {}
    if not str or str == "" then return res end
    for item in string.gmatch(str, "[^,]+") do
        local s = normalizeFilterKey(item)
        if s ~= "" then
            local a, b = s:match("^([^_]+)_(.+)$")
            if a and b then
                res[a] = res[a] or {}
                res[a][b] = true
            end
        end
    end
    return res
end


-- Syntax:  #invoke:params|pivoting|pipe to
local function build_key_filter(args)
--[[
    local filter = {}
library.pivoting = function(ctx)
    filter.blacklist = parseListArg(args.blacklist or "")
local tbl = ctx.params
    filter.whitelist = parseListArg(args.whitelist or "")
local shift = #tbl + 1
    filter.hasWhitelist = false
if shift < 2 then return library.rotating(ctx) end
    for _, sw in ipairs(switchModeOrder) do
local numericals = {}
        if filter.whitelist[sw] and next(filter.whitelist[sw]) ~= nil then
for key, val in pairs(tbl) do
            filter.hasWhitelist = true
if type(key) == 'number' then
            break
numericals[key] = val
        end
tbl[key] = nil
    end
end
    if not filter.hasWhitelist and filter.whitelist.cardContent and next(filter.whitelist.cardContent) ~= nil then
end
        filter.hasWhitelist = true
for key, val in pairs(numericals) do tbl[shift - key] = val end
    end
return context_iterate(ctx, 1)
    return filter
end
end
]]--


local function build_render_options(filter)
    return {
        noHeaders = false,
        cardFilter = {
            hasWhitelist = filter.whitelist.cardContent and next(filter.whitelist.cardContent) ~= nil or false,
            cardWhitelist = filter.whitelist.card or {},
            blacklist = filter.blacklist.cardContent or {},
            whitelist = filter.whitelist.cardContent or {}
        }
    }
end


-- Syntax:  #invoke:params|mirroring|pipe to
local function should_include_key(filter, sw, key)
--[[
    if filter.hasWhitelist then
library.mirroring = function(ctx)
        if filter.whitelist[sw] and filter.whitelist[sw][key] then
local tbl = ctx.params
            return true
local numericals = {}
        end
local nmax
        if sw == "card" and filter.whitelist.cardContent and filter.whitelist.cardContent[key] then
local nmin
            return true
for key, val in pairs(tbl) do
        end
if type(key) == 'number' then
        return false
numericals[key] = val
    end
tbl[key] = nil
    return not (filter.blacklist[sw] and filter.blacklist[sw][key])
if nmax == nil then
nmax = key
nmin = key
elseif key > nmax then nmax = key
elseif key < nmin then nmin = key end
end
end
for key, val in pairs(numericals) do tbl[nmax + nmin - key] = val end
return context_iterate(ctx, 1)
end
end
]]--


local function parse_csv_set(str)
    local res = {}
    each_csv_value(str, function(name)
        res[name] = true
    end)
    return res
end


-- Syntax:  #invoke:params|swapping|pipe to
local function apply_entity_set_filters(foundSet, whitelistSet, blacklistSet)
--[[
    local hasWhitelist = next(whitelistSet or {}) ~= nil
library.swapping = function(ctx)
local tbl = ctx.params
local cache = {}
local nsize = 0
local tmp
for key in pairs(tbl) do
if type(key) == 'number' then
nsize = nsize + 1
cache[nsize] = key
end
end
table.sort(cache)
for idx = math.floor(nsize / 2), 1, -1 do
tmp = tbl[cache[idx] ]
tbl[cache[idx] ] = tbl[cache[nsize - idx + 1] ]
tbl[cache[nsize - idx + 1] ] = tmp
end
return context_iterate(ctx, 1)
end
]]--


    if hasWhitelist then
        for name in pairs(foundSet) do
            if not whitelistSet[name] then
                foundSet[name] = nil
            end
        end
    end


-- Syntax:  #invoke:params|sorting_sequential_values|[criterion]|pipe to
    for name in pairs(blacklistSet or {}) do
library.sorting_sequential_values = function(ctx)
        foundSet[name] = nil
local sortfn
    end
if ctx.pipe[1] ~= nil then sortfn = sortfunctions[ctx.pipe[1]] end
if sortfn then table.sort(ctx.params, sortfn)
else table.sort(ctx.params) end -- i.e. either `false` or `nil`
if sortfn == nil then return context_iterate(ctx, 1) end
return context_iterate(ctx, 2)
end
end


local function collect_entity_sets(id, prototypeStoreDefs, componentWhitelist, componentBlacklist, prototypeWhitelist, prototypeBlacklist)
    local foundComponents, foundPrototypes = {}, {}


-- Syntax:  #invoke:params|inserting|position|how many|...|pipe to
    local compList = load_entity_components(id)
--[[
    if type(compList) == "table" then
library.inserting = function(ctx)
        for _, v in ipairs(compList) do
-- NOTE: `ctx.params` might be the original metatable! As a modifier,
            if type(v) == "string" and v ~= "" then
-- this function MUST create a copy of it before returning
                foundComponents[v] = true
local idx = tonumber(ctx.pipe[1])
            end
if idx == nil then error(ctx.luaname ..
        end
', ‘inserting’: Position must be a number', 0) end
    end
local len = tonumber(ctx.pipe[2])
if len == nil or len < 1 then error(ctx.luaname ..
', ‘inserting’: The amount must be a number greater than zero', 0) end
local opts = ctx.pipe
local tbl = copy_table_expanded(ctx.params, idx, len)
for key = idx, idx + len - 1 do tbl[key] = opts[key - idx + 3] end
ctx.params = tbl
return context_iterate(ctx, len + 3)
end
]]--


    local protoStore = prototypeStoreDefs and prototypeStoreDefs[id]
    if type(protoStore) == "table" then
        for protoName in pairs(protoStore) do
            if type(protoName) == "string" and protoName ~= "" then
                foundPrototypes[protoName] = true
            end
        end
    end


-- Syntax:  #invoke:params|imposing|name|value|pipe to
    apply_entity_set_filters(foundComponents, parse_csv_set(componentWhitelist), parse_csv_set(componentBlacklist))
library.imposing = function(ctx)
    apply_entity_set_filters(foundPrototypes, parse_csv_set(prototypeWhitelist), parse_csv_set(prototypeBlacklist))
if ctx.pipe[1] == nil then error(ctx.luaname ..
', ‘imposing’: Missing parameter name to impose', 0) end
local key = ctx.pipe[1]:match'^%s*(.-)%s*$'
ctx.params[tonumber(key) or key] = ctx.pipe[2]
return context_iterate(ctx, 3)
end


 
    return foundComponents, foundPrototypes
-- Syntax:  #invoke:params|discarding|name|[how many]|pipe to
library.discarding = function(ctx)
if ctx.pipe[1] == nil then error(ctx.luaname ..
', ‘discarding’: Missing parameter name to discard', 0) end
local key = ctx.pipe[1]
local len = tonumber(ctx.pipe[2])
if len == nil then
ctx.params[tonumber(key) or key:match'^%s*(.-)%s*$'] = nil
return context_iterate(ctx, 2)
end
key = tonumber(key)
if key == nil then error(ctx.luaname ..
', ‘discarding’: A range was provided, but the initial parameter name is not numerical', 0) end
if len < 1 then error(ctx.luaname ..
', ‘discarding’: A range can only be a number greater than zero', 0) end
for idx = key, key + len - 1 do ctx.params[idx] = nil end
return context_iterate(ctx, 3)
end
end


 
local function resolve_priority(parsed)
-- Syntax:  #invoke:params|with_name_matching|pattern 1|[plain flag 1]|[or]
    local basePriority = 1
--            |[pattern 2]|[plain flag 2]|[or]|[...]|[pattern N]|[plain flag
    if type(parsed) == "table" and parsed.priority ~= nil then
--            N]|pipe to
        if type(parsed.priority) == "number" then
library.with_name_matching = function(ctx)
            basePriority = parsed.priority
local tbl = ctx.params
        else
local patterns = {}
            local pnum = tonumber(parsed.priority)
local argc = parse_pattern_args(ctx, patterns, 'with_name_matching')
            if pnum then
local nomatch
                basePriority = pnum
for key in pairs(tbl) do
            end
nomatch = true
        end
for _, ptn in ipairs(patterns) do
    end
if not ptn[3] then
    return basePriority
if string.find(key, ptn[1], 1, ptn[2]) then
nomatch = false
break
end
elseif key == ptn[1] then
nomatch = false
break
end
end
if nomatch then tbl[key] = nil end
end
return context_iterate(ctx, argc)
end
end


local function get_selective_extra(id, dataPage, paramNames)
    if not dp or type(dp.flattenFieldSelectiveDirect) ~= "function" then
        return ""
    end
    if type(paramNames) ~= "table" or #paramNames == 0 then
        return ""
    end


-- Syntax:  #invoke:params|with_name_not_matching|pattern 1|[plain flag 1]
    return dp.flattenFieldSelectiveDirect(id, dataPage, paramNames) or ""
--            |[and]|[pattern 2]|[plain flag 2]|[and]|[...]|[pattern N]|[plain
--            flag N]|pipe to
library.with_name_not_matching = function(ctx)
local tbl = ctx.params
local patterns = {}
local argc = parse_pattern_args(ctx, patterns,
'with_name_not_matching')
local yesmatch
for key in pairs(tbl) do
yesmatch = true
for _, ptn in ipairs(patterns) do
if ptn[3] then
if key ~= ptn[1] then
yesmatch = false
break
end
elseif not string.find(key, ptn[1], 1, ptn[2]) then
yesmatch = false
break
end
end
if yesmatch then tbl[key] = nil end
end
return context_iterate(ctx, argc)
end
end


 
local function add_card_tag_value(tags, seen, value)
-- Syntax:  #invoke:params|with_value_matching|pattern 1|[plain flag 1]|[or]
    value = trim(value or "")
--            |[pattern 2]|[plain flag 2]|[or]|[...]|[pattern N]|[plain flag
    if value == "" or seen[value] then
--            N]|pipe to
        return
library.with_value_matching = function(ctx)
    end
local tbl = ctx.params
    seen[value] = true
local patterns = {}
    tags[#tags + 1] = value
local argc = parse_pattern_args(ctx, patterns, 'with_value_matching')
local nomatch
for key, val in pairs(tbl) do
nomatch = true
for _, ptn in ipairs(patterns) do
if ptn[3] then
if val == ptn[1] then
nomatch = false
break
end
elseif string.find(val, ptn[1], 1, ptn[2]) then
nomatch = false
break
end
end
if nomatch then tbl[key] = nil end
end
return context_iterate(ctx, argc)
end
end


local function merge_card_tag_text(...)
    local tags = {}
    local seen = {}


-- Syntax:  #invoke:params|with_value_not_matching|pattern 1|[plain flag 1]
    for i = 1, select("#", ...) do
--            |[and]|[pattern 2]|[plain flag 2]|[and]|[...]|[pattern N]|[plain
        each_csv_value(select(i, ...), function(value)
--            flag N]|pipe to
            add_card_tag_value(tags, seen, value)
library.with_value_not_matching = function(ctx)
        end)
local tbl = ctx.params
    end
local patterns = {}
local argc = parse_pattern_args(ctx, patterns,
'with_value_not_matching')
local yesmatch
for key, val in pairs(tbl) do
yesmatch = true
for _, ptn in ipairs(patterns) do
if ptn[3] then
if val ~= ptn[1] then
yesmatch = false
break
end
elseif not string.find(val, ptn[1], 1, ptn[2]) then
yesmatch = false
break
end
end
if yesmatch then tbl[key] = nil end
end
return context_iterate(ctx, argc)
end
 


-- Syntax:  #invoke:params|trimming_values|pipe to
    return table.concat(tags, ", ")
library.trimming_values = function(ctx)
local tbl = ctx.params
for key, val in pairs(tbl) do tbl[key] = val:match'^%s*(.-)%s*$' end
return context_iterate(ctx, 1)
end
end


local function add_entries_from_meta(state, parsed, ctx, filter, isPreview)
    for _, sw in ipairs(switchModeOrder) do
        local mode = switchModeRegistry[sw] or {}
        local keys
        if type(mode.get_keys) == "function" then
            keys = mode.get_keys(parsed)
        else
            keys = (type(parsed) == "table" and parsed[sw]) or {}
        end


-- Syntax:  #invoke:params|mapping_by_calling|template name|[call
        if type(keys) == "table" then
--            style]|[let]|[...][number of additional parameters]|[parameter
            for _, key in ipairs(keys) do
--            1]|[parameter 2]|[...]|[parameter N]|pipe to
                if (not filter) or should_include_key(filter, sw, key) then
library.mapping_by_calling = function(ctx)
                    local buildFn = isPreview and (mode.build_preview_entry or mode.build_entry) or mode.build_entry
local opts = ctx.pipe
                    if type(buildFn) == "function" then
local tname
                        local entry = buildFn(ctx, key)
if opts[1] ~= nil then tname = opts[1]:match'^%s*(.*%S)' end
                        if entry then
if tname == nil then error(ctx.luaname ..
                            add_switch_entry(state, sw, key, entry)
', ‘mapping_by_calling’: No template name was provided', 0) end
                        end
local margs = {}
                    end
local argc, looptype, karg, varg = parse_child_args(margs, opts, 1,
                end
mapping_styles.values_only)
            end
local model = { title = tname, args = margs }
        end
map_values(ctx.params, margs, karg, varg, looptype, function()
return ctx.frame:expandTemplate(model)
end)
return context_iterate(ctx, argc)
end


 
        if (not isPreview) and type(mode.add_entity_extras) == "function" then
-- Syntax:  #invoke:params|mapping_by_invoking|module name|function
            mode.add_entity_extras(state, parsed, ctx)
--            name|[call style]|[let]|[...]|[number of additional
        end
--            arguments]|[argument 1]|[argument 2]|[...]|[argument N]|pipe to
    end
library.mapping_by_invoking = function(ctx)
local opts = ctx.pipe
local mname
local fname
if opts[1] ~= nil then mname = opts[1]:match'^%s*(.*%S)' end
if mname == nil then error(ctx.luaname ..
', ‘mapping_by_invoking’: No module name was provided', 0) end
if opts[2] ~= nil then fname = opts[2]:match'^%s*(.*%S)' end
if fname == nil then error(ctx.luaname ..
', ‘mapping_by_invoking’: No function name was provided', 0) end
local margs = {}
local argc, looptype, karg, varg = parse_child_args(margs, opts, 2,
mapping_styles.values_only)
local model = { title = 'Module:' .. mname, args = margs }
local mfunc = require(model.title)[fname]
if mfunc == nil then error(ctx.luaname ..
', ‘mapping_by_invoking’: The function ‘' .. fname ..
'’ does not exist', 0) end
map_values(ctx.params, margs, karg, varg, looptype, function()
return mfunc(ctx.frame:newChild(model))
end)
return context_iterate(ctx, argc)
end
end


local function extract_whitelist_search_strings(keyFilter)
    if not keyFilter or not keyFilter.hasWhitelist then
        return nil
    end


-- Syntax:  #invoke:params|mapping_by_magic|parser function|[call
    local strings = {}
--            style]|[let]|[...][number of additional arguments]|[argument
    for sw, keys in pairs(keyFilter.whitelist) do
--            1]|[argument 2]|[...]|[argument N]|pipe to
        if type(keys) == "table" then
library.mapping_by_magic = function(ctx)
            for key in pairs(keys) do
local opts = ctx.pipe
                strings[#strings + 1] = key
local magic
            end
if opts[1] ~= nil then magic = opts[1]:match'^%s*(.*%S)' end
        end
if magic == nil then error(ctx.luaname ..
    end
', ‘mapping_by_magic’: No parser function was provided', 0) end
local margs = {}
local argc, looptype, karg, varg = parse_child_args(margs, opts, 1,
mapping_styles.values_only)
map_values(ctx.params, margs, karg, varg, looptype, function()
return ctx.frame:callParserFunction(magic, margs)
end)
return context_iterate(ctx, argc)
end


    if #strings == 0 then
        return nil
    end


-- Syntax:  #invoke:params|renaming_by_calling|template name|[call
    return strings
--            style]|[let]|[...][number of additional parameters]|[parameter
--            1]|[parameter 2]|[...]|[parameter N]|pipe to
library.renaming_by_calling = function(ctx)
local opts = ctx.pipe
local tname
if opts[1] ~= nil then tname = opts[1]:match'^%s*(.*%S)' end
if tname == nil then error(ctx.luaname ..
', ‘renaming_by_calling’: No template name was provided', 0) end
local rargs = {}
local argc, looptype, karg, varg = parse_child_args(rargs, opts, 1,
mapping_styles.names_only)
local model = { title = tname, args = rargs }
map_names(ctx.params, rargs, karg, varg, looptype, function()
return ctx.frame:expandTemplate(model)
end)
return context_iterate(ctx, argc)
end
end


local function content_matches_whitelist(content, searchStrings)
    if not searchStrings then
        return true
    end
    if not content then
        return false
    end


-- Syntax:  #invoke:params|renaming_by_invoking|module name|function
    for _, s in ipairs(searchStrings) do
--            name|[call style]|[let]|[...]|[number of additional
        if string.find(content, s, 1, true) then
--            arguments]|[argument 1]|[argument 2]|[...]|[argument N]|pipe to
            return true
library.renaming_by_invoking = function(ctx)
        end
local opts = ctx.pipe
    end
local mname
local fname
if opts[1] ~= nil then mname = opts[1]:match'^%s*(.*%S)' end
if mname == nil then error(ctx.luaname ..
', ‘renaming_by_invoking’: No module name was provided', 0) end
if opts[2] ~= nil then fname = opts[2]:match'^%s*(.*%S)' end
if fname == nil then error(ctx.luaname ..
', ‘renaming_by_invoking’: No function name was provided', 0) end
local rargs = {}
local argc, looptype, karg, varg = parse_child_args(rargs, opts, 2,
mapping_styles.names_only)
local model = { title = 'Module:' .. mname, args = rargs }
local mfunc = require(model.title)[fname]
if mfunc == nil then error(ctx.luaname ..
', ‘renaming_by_invoking’: The function ‘' .. fname ..
'’ does not exist', 0) end
map_names(ctx.params, rargs, karg, varg, looptype, function()
return mfunc(ctx.frame:newChild(model))
end)
return context_iterate(ctx, argc)
end


 
    return false
-- Syntax:  #invoke:params|renaming_by_magic|parser function|[call
--            style]|[let]|[...][number of additional arguments]|[argument
--            1]|[argument 2]|[...]|[argument N]|pipe to
library.renaming_by_magic = function(ctx)
local opts = ctx.pipe
local magic
if opts[1] ~= nil then magic = opts[1]:match'^%s*(.*%S)' end
if magic == nil then error(ctx.luaname ..
', ‘renaming_by_magic’: No parser function was provided', 0) end
local rargs = {}
local argc, looptype, karg, varg = parse_child_args(rargs, opts, 1,
mapping_styles.names_only)
map_names(ctx.params, rargs, karg, varg, looptype, function()
return ctx.frame:callParserFunction(magic, rargs)
end)
return context_iterate(ctx, argc)
end
end


local function each_entity_data(frame, id, onEntity, onMissing, keyFilter)
    local componentWhitelist = frame.args.componentWhitelist or frame.args.componentwhitelist or ""
    local componentBlacklist = frame.args.componentBlacklist or frame.args.componentblacklist or ""
    local prototypeWhitelist = frame.args.prototypeWhitelist or frame.args.prototypewhitelist or ""
    local prototypeBlacklist = frame.args.prototypeBlacklist or frame.args.prototypeblacklist or ""


    local prototypeStoreDefs = load_module_data("prototype_store.json")
    if not prototypeStoreDefs then
        return false
    end


--[[ Functions ]]--
    local foundComponents, foundPrototypes = collect_entity_sets(id, prototypeStoreDefs,
-----------------------------
        componentWhitelist, componentBlacklist, prototypeWhitelist, prototypeBlacklist)
 


-- Syntax:  #invoke:params|count
    local compWhitelistSet = parse_csv_set(componentWhitelist)
library.count = function(ctx)
    local compBlacklistSet = parse_csv_set(componentBlacklist)
-- NOTE: `ctx.pipe` and `ctx.params` might be the original metatables!
    local protoWhitelistSet = parse_csv_set(prototypeWhitelist)
local retval = 0
    local protoBlacklistSet = parse_csv_set(prototypeBlacklist)
for _ in ctx.iterfunc(ctx.params) do retval = retval + 1 end
if ctx.subset == -1 then retval = retval - #ctx.params end
ctx.text = retval
return false
end


    local compHasWhitelist = next(compWhitelistSet) ~= nil
    local protoHasWhitelist = next(protoWhitelistSet) ~= nil
    local anyEntityWhitelist = compHasWhitelist or protoHasWhitelist


-- Syntax:  #invoke:args|concat_and_call|template name|[prepend 1]|[prepend 2]
    local whitelistSearchStrings = extract_whitelist_search_strings(keyFilter)
--            |[...]|[item n]|[named item 1=value 1]|[...]|[named item n=value
--            n]|[...]
library.concat_and_call = function(ctx)
-- NOTE: `ctx.params` might be the original metatable!
local opts = ctx.pipe
local tname
if opts[1] ~= nil then tname = opts[1]:match'^%s*(.*%S)' end
if tname == nil then error(ctx.luaname ..
', ‘concat_and_call’: No template name was provided', 0) end
remove_numerical_keys(opts, 1, 1)
ctx.text = ctx.frame:expandTemplate{
title = tname,
args = concat_params(ctx)
}
return false
end


    local function processEntity(kind, name, isStore)
        local pathName = lcfirst(name)
        local tplPath = kind .. "/" .. pathName
        if isStore then
            tplPath = tplPath .. "/store"
        end


-- Syntax:  #invoke:args|concat_and_invoke|module name|function name|[prepend
        local content = load_template_content(tplPath)
--            1]|[prepend 2]|[...]|[item n]|[named item 1=value 1]|[...]|[named
        if not content then
--            item n=value n]|[...]
            if onMissing then
library.concat_and_invoke = function(ctx)
                onMissing(kind, name, isStore, tplPath)
-- NOTE: `ctx.params` might be the original metatable!
            end
local opts = ctx.pipe
            return
local mname
        end
local fname
if opts[1] ~= nil then mname = opts[1]:match'^%s*(.*%S)' end
if mname == nil then error(ctx.luaname ..
', ‘concat_and_invoke’: No module name was provided', 0) end
if opts[2] ~= nil then fname = opts[2]:match'^%s*(.*%S)' end
if fname == nil then error(ctx.luaname ..
', ‘concat_and_invoke’: No function name was provided', 0) end
remove_numerical_keys(opts, 1, 2)
local mfunc = require('Module:' .. mname)[fname]
if mfunc == nil then error(ctx.luaname ..
', ‘concat_and_invoke’: The function ‘' .. fname ..
'’ does not exist', 0) end
ctx.text = mfunc(ctx.frame:newChild{
title = 'Module:' .. fname,
args = concat_params(ctx)
})
return false
end


        if not content_matches_whitelist(content, whitelistSearchStrings) then
            return
        end


-- Syntax:  #invoke:args|concat_and_magic|parser function|[prepend 1]|[prepend
        local parsed = getTemplateMeta(frame, tplPath)
--            2]|[...]|[item n]|[named item 1=value 1]|[...]|[named item n=
        if type(parsed) ~= "table" then
--            value n]|[...]
            parsed = {}
library.concat_and_magic = function(ctx)
        end
-- NOTE: `ctx.params` might be the original metatable!
local opts = ctx.pipe
local magic
if opts[1] ~= nil then magic = opts[1]:match'^%s*(.*%S)' end
if magic == nil then error(ctx.luaname ..
', ‘concat_and_magic’: No parser function was provided', 0) end
remove_numerical_keys(opts, 1, 1)
ctx.text = ctx.frame:callParserFunction(magic, concat_params(ctx))
return false
end


        local extra = ""
        local paramNames = get_template_params(tplPath, content)
        if dp then
            local dataPage = tplPath .. ".json"
            extra = get_selective_extra(id, dataPage, paramNames)
        end


-- Syntax:  #invoke:params|value_of|parameter name
        onEntity(parsed, {
library.value_of = function(ctx)
            tplPath = tplPath,
-- NOTE: `ctx.pipe` and `ctx.params` might be the original metatables!
            id = id,
local opts = ctx.pipe
            extra = extra,
local kstr
            source = make_source(kind, name, pathName, tplPath),
if opts[1] ~= nil then kstr = opts[1]:match'^%s*(.*%S)' end
            priority = resolve_priority(parsed)
if kstr == nil then error(ctx.luaname ..
        })
', ‘value_of’: No parameter name was provided', 0) end
    end
local knum = tonumber(kstr)
local len = #ctx.params
local val = ctx.params[knum or kstr]
if val ~= nil and (
ctx.subset ~= -1 or knum == nil or knum > len or knum < 1
) and (
ctx.subset ~= 1 or (knum ~= nil and knum <= len and knum > 0)
) then
ctx.text = (ctx.header or '') .. val .. (ctx.footer or '')
return false
end
ctx.text = ctx.ifngiven or ''
return false
end


    for compName in pairs(foundComponents) do
        if not anyEntityWhitelist or compHasWhitelist then
            processEntity("component", compName, false)
        end
    end
    for protoName in pairs(foundPrototypes) do
        if not anyEntityWhitelist or protoHasWhitelist then
            processEntity("prototype", protoName, false)
        end
    end


-- Syntax:  #invoke:params|list
    local componentStoreDefs = load_module_data("component_store.json")
library.list = function(ctx)
    if type(componentStoreDefs) == "table" and (not anyEntityWhitelist or compHasWhitelist) then
-- NOTE: `ctx.pipe` might be the original metatable!
        local compStore = componentStoreDefs[id]
local kvs = ctx.pairsep or ''
        if type(compStore) == "table" then
local pps = ctx.itersep or ''
            for compName in pairs(compStore) do
local ret = {}
                local allowed = true
local nss = 0
                if compBlacklistSet[compName] then
flush_params(
                    allowed = false
ctx,
                elseif compHasWhitelist and not compWhitelistSet[compName] then
function(key, val)
                    allowed = false
ret[nss + 1] = pps
                end
ret[nss + 2] = key
                if allowed then
ret[nss + 3] = kvs
                    processEntity("component", compName, true)
ret[nss + 4] = val
                end
nss = nss + 4
            end
end
        end
)
    end
if nss > 0 then
if nss > 4 and ctx.lastsep ~= nil then
ret[nss - 3] = ctx.lastsep
end
ret[1] = ctx.header or ''
if ctx.footer ~= nil then ret[nss + 1] = ctx.footer end
ctx.text = table.concat(ret)
return false
end
ctx.text = ctx.ifngiven or ''
return false
end


    if type(prototypeStoreDefs) == "table" and (not anyEntityWhitelist or protoHasWhitelist) then
        local protoStore = prototypeStoreDefs[id]
        if type(protoStore) == "table" then
            for protoName in pairs(protoStore) do
                local allowed = true
                if protoBlacklistSet[protoName] then
                    allowed = false
                elseif protoHasWhitelist and not protoWhitelistSet[protoName] then
                    allowed = false
                end
                if allowed then
                    processEntity("prototype", protoName, true)
                end
            end
        end
    end


-- Syntax:  #invoke:params|list_values
    return true
library.list_values = function(ctx)
-- NOTE: `ctx.pipe` might be the original metatable!
local pps = ctx.itersep or ''
local ret = {}
local nss = 0
flush_params(
ctx,
function(key, val)
ret[nss + 1] = pps
ret[nss + 2] = val
nss = nss + 2
end
)
if nss > 0 then
if nss > 2 and ctx.lastsep ~= nil then
ret[nss - 1] = ctx.lastsep
end
ret[1] = ctx.header or ''
if ctx.footer ~= nil then ret[nss + 1] = ctx.footer end
ctx.text = table.concat(ret)
return false
end
ctx.text = ctx.ifngiven or ''
return false
end
end


local function collect_card_tag_text(frame, args, id)
    local filter = build_key_filter(args)
    local cardFilter = build_render_options(filter).cardFilter
    local entries = {}


-- Syntax:  #invoke:params|for_each|wikitext
    local ok = each_entity_data(frame, id, function(parsed, ctx)
library.for_each = function(ctx)
        local keys = parsed.card or {}
-- NOTE: `ctx.pipe` might be the original metatable!
local txt = ctx.pipe[1] or ''
local pps = ctx.itersep or ''
local ret = {}
local nss = 0
flush_params(
ctx,
function(key, val)
ret[nss + 1] = pps
ret[nss + 2] = txt:gsub('%$#', key):gsub('%$@', val)
nss = nss + 2
end
)
if nss > 0 then
if nss > 2 and ctx.lastsep ~= nil then
ret[nss - 1] = ctx.lastsep
end
ret[1] = ctx.header or ''
if ctx.footer ~= nil then ret[nss + 1] = ctx.footer end
ctx.text = table.concat(ret)
return false
end
ctx.text = ctx.ifngiven or ''
return false
end


        if type(keys) == "table" then
            for _, key in ipairs(keys) do
                local compositeKey = (key:find("_", 1, true)) and key or ("Сущность_" .. key)
                local isWhitelisted = matches_card_list(cardFilter.whitelist, key, compositeKey)
                local isBlacklisted = matches_card_list(cardFilter.blacklist, key, compositeKey)
                local allowCardEntry = not isWhitelisted and not isBlacklisted and
                    ((not cardFilter.hasWhitelist) or matches_card_list(cardFilter.cardWhitelist, key, compositeKey))


-- Syntax:  #invoke:params|call_for_each|template name|[append 1]|[append 2]
                if allowCardEntry then
--            |[...]|[append n]|[named param 1=value 1]|[...]|[named param
                    entries[#entries + 1] = {
--            n=value n]|[...]
                        tplTag = makeTplCall(ctx.tplPath, "cardTag", key, ctx.id, ctx.extra),
library.call_for_each = function(ctx)
                        priority = ctx.priority,
local opts = ctx.pipe
                        idx = #entries + 1
local tname
                    }
if opts[1] ~= nil then tname = opts[1]:match'^%s*(.*%S)' end
                end
if tname == nil then error(ctx.luaname ..
            end
', ‘call_for_each’: No template name was provided', 0) end
        end
local model = { title = tname, args = opts }
local ccs = ctx.itersep or ''
local ret = {}
local nss = 0
table.insert(opts, 1, true)
flush_params(
ctx,
function(key, val)
opts[1] = key
opts[2] = val
ret[nss + 1] = ccs
ret[nss + 2] = ctx.frame:expandTemplate(model)
nss = nss + 2
end
)
if nss > 0 then
if nss > 2 and ctx.lastsep ~= nil then
ret[nss - 1] = ctx.lastsep
end
ret[1] = ctx.header or ''
if ctx.footer ~= nil then ret[nss + 1] = ctx.footer end
ctx.text = table.concat(ret)
return false
end
ctx.text = ctx.ifngiven or ''
return false
end
 


-- Syntax:  #invoke:params|invoke_for_each|module name|module function|[append
        if parsed.cardTag and parsed.cardTag ~= "" then
--            1]|[append 2]|[...]|[append n]|[named param 1=value 1]|[...]
            entries[#entries + 1] = {
--            |[named param n=value n]|[...]
                tplTag = makeTplCall(ctx.tplPath, "cardTag", "cardTag", ctx.id, ctx.extra),
library.invoke_for_each = function(ctx)
                priority = ctx.priority,
local opts = ctx.pipe
                idx = #entries + 1
local mname
            }
local fname
        end
if opts[1] ~= nil then mname = opts[1]:match'^%s*(.*%S)' end
    end)
if mname == nil then error(ctx.luaname ..
', ‘invoke_for_each’: No module name was provided', 0) end
if opts[2] ~= nil then fname = opts[2]:match'^%s*(.*%S)' end
if fname == nil then error(ctx.luaname ..
', ‘invoke_for_each’: No function name was provided', 0) end
local model = { title = 'Module:' .. mname, args = opts }
local mfunc = require(model.title)[fname]
local ccs = ctx.itersep or ''
local ret = {}
local nss = 0
flush_params(
ctx,
function(key, val)
opts[1] = key
opts[2] = val
ret[nss + 1] = ccs
ret[nss + 2] = mfunc(ctx.frame:newChild(model))
nss = nss + 2
end
)
if nss > 0 then
if nss > 2 and ctx.lastsep ~= nil then
ret[nss - 1] = ctx.lastsep
end
ret[1] = ctx.header or ''
if ctx.footer ~= nil then ret[nss + 1] = ctx.footer end
ctx.text = table.concat(ret)
return false
end
ctx.text = ctx.ifngiven or ''
return false
end


    if not ok then
        return trim(args.tag or "")
    end


-- Syntax:  #invoke:params|magic_for_each|parser function|[append 1]|[append 2]
    sort_entries_by_priority(entries)
--            |[...]|[append n]|[named param 1=value 1]|[...]|[named param
--            n=value n]|[...]
library.magic_for_each = function(ctx)
local opts = ctx.pipe
local magic
if opts[1] ~= nil then magic = opts[1]:match'^%s*(.*%S)' end
if magic == nil then error(ctx.luaname ..
', ‘magic_for_each’: No parser function was provided', 0) end
local ccs = ctx.itersep or ''
local ret = {}
local nss = 0
table.insert(opts, 1, true)
flush_params(
ctx,
function(key, val)
opts[1] = key
opts[2] = val
ret[nss + 1] = ccs
ret[nss + 2] = ctx.frame:callParserFunction(magic,
opts)
nss = nss + 2
end
)
if nss > 0 then
if nss > 2 and ctx.lastsep ~= nil then
ret[nss - 1] = ctx.lastsep
end
ret[1] = ctx.header or ''
if ctx.footer ~= nil then ret[nss + 1] = ctx.footer end
ctx.text = table.concat(ret)
return false
end
ctx.text = ctx.ifngiven or ''
return false
end


    local tags = {}
    local seen = {}
    for _, entry in ipairs(entries) do
        local tagText = trim(frame:preprocess(entry.tplTag or "") or "")
        add_card_tag_value(tags, seen, tagText)
    end


-- Syntax:  #invoke:params|call_for_each_value|template name|[append 1]|[append
    return merge_card_tag_text(table.concat(tags, ", "), args.tag)
--            2]|[...]|[append n]|[named param 1=value 1]|[...]|[named param
--            n=value n]|[...]
library.call_for_each_value = function(ctx)
local opts = ctx.pipe
local tname
if opts[1] ~= nil then tname = opts[1]:match'^%s*(.*%S)' end
if tname == nil then error(ctx.luaname ..
', ‘call_for_each_value’: No template name was provided', 0) end
local model = { title = tname, args = opts }
local ccs = ctx.itersep or ''
local ret = {}
local nss = 0
flush_params(
ctx,
function(key, val)
opts[1] = val
ret[nss + 1] = ccs
ret[nss + 2] = ctx.frame:expandTemplate(model)
nss = nss + 2
end
)
if nss > 0 then
if nss > 2 and ctx.lastsep ~= nil then
ret[nss - 1] = ctx.lastsep
end
ret[1] = ctx.header or ''
if ctx.footer ~= nil then ret[nss + 1] = ctx.footer end
ctx.text = table.concat(ret)
return false
end
ctx.text = ctx.ifngiven or ''
return false
end
end


p.mergeCardTagText = merge_card_tag_text
p.collectCardTagText = collect_card_tag_text


-- Syntax:  #invoke:params|invoke_for_each_value|module name|[append 1]|[append
local function build_missing_template_error(kind, name, isStore, tplPath)
--            2]|[...]|[append n]|[named param 1=value 1]|[...]|[named param
    local baseType = (kind and (kind:sub(1, 1):upper() .. kind:sub(2)) or "")
--            n=value n]|[...]
    local classType = baseType
library.invoke_for_each_value = function(ctx)
    if isStore then
local opts = ctx.pipe
        classType = classType .. "Store"
local mname
    end
local fname
    local className = name .. baseType
if opts[1] ~= nil then mname = opts[1]:match'^%s*(.*%S)' end
    local tplLabel = "Template:" .. tplPath
if mname == nil then error(ctx.luaname ..
    return "{{сущность/infobox/base|тип=" .. classType .. "|название=" .. className .. "|ссылка=" .. tplLabel .. "}}"
', ‘invoke_for_each_value’: No module name was provided', 0) end
if opts[2] ~= nil then fname = opts[2]:match'^%s*(.*%S)' end
if fname == nil then error(ctx.luaname ..
', ‘invoke_for_each_value’: No function name was provided', 0) end
local model = { title = 'Module:' .. mname, args = opts }
local mfunc = require(model.title)[fname]
local ccs = ctx.itersep or ''
local ret = {}
local nss = 0
remove_numerical_keys(opts, 1, 1)
flush_params(
ctx,
function(key, val)
opts[1] = val
ret[nss + 1] = ccs
ret[nss + 2] = mfunc(ctx.frame:newChild(model))
nss = nss + 2
end
)
if nss > 0 then
if nss > 2 and ctx.lastsep ~= nil then
ret[nss - 1] = ctx.lastsep
end
ret[1] = ctx.header or ''
if ctx.footer ~= nil then ret[nss + 1] = ctx.footer end
ctx.text = table.concat(ret)
return false
end
ctx.text = ctx.ifngiven or ''
return false
end
end


 
local function renderBlocks(frame, state, renderOptions, entityId, showSource)
-- Syntax:  #invoke:params|magic_for_each_value|parser function|[append 1]
    local outLocal = {}
--            |[append 2]|[...]|[append n]|[named param 1=value 1]|[...]|[named
    local noHeaders = renderOptions and renderOptions.noHeaders
--            param n=value n]|[...]
    local cardFilter = renderOptions and renderOptions.cardFilter
library.magic_for_each_value = function(ctx)
    for _, sw in ipairs(switchModeOrder) do
local opts = ctx.pipe
        local mode = switchModeRegistry[sw] or {}
local magic
        if mode.full then
if opts[1] ~= nil then magic = opts[1]:match'^%s*(.*%S)' end
            local outStr = ""
if magic == nil then error(ctx.luaname ..
            if type(mode.render_full) == "function" then
', ‘magic_for_each_value’: No parser function was provided', 0) end
                outStr = mode.render_full(frame, state.keyOrder[sw], state.keyToTemplates[sw], state.keySources[sw],
local ccs = ctx.itersep or ''
                    entityId, noHeaders, showSource, cardFilter)
local ret = {}
            end
local nss = 0
            if outStr and outStr ~= "" then outLocal[#outLocal + 1] = outStr end
flush_params(
        else
ctx,
            for _, key in ipairs(state.keyOrder[sw] or {}) do
function(key, val)
                local entries = state.keyToTemplates[sw][key] or {}
opts[1] = val
                if type(mode.render_key) == "function" then
ret[nss + 1] = ccs
                    local outStr = mode.render_key(frame, key, entries, noHeaders, showSource)
ret[nss + 2] = ctx.frame:callParserFunction(magic,
                    if outStr and outStr ~= "" then outLocal[#outLocal + 1] = outStr end
opts)
                end
nss = nss + 2
            end
end
        end
)
    end
if nss > 0 then
    return outLocal
if nss > 2 and ctx.lastsep ~= nil then
ret[nss - 1] = ctx.lastsep
end
ret[1] = ctx.header or ''
if ctx.footer ~= nil then ret[nss + 1] = ctx.footer end
ctx.text = table.concat(ret)
return false
end
ctx.text = ctx.ifngiven or ''
return false
end
end


function p.get(frame)
    local args = getArgs(frame, { removeBlanks = false })
    local id = args[1] or ""
    if id == "" then return "" end


-- Syntax:  #invoke:params|call_for_each_group|template name|[append 1]|[append
    local showSource = trim(args.showSource or "") == ""
--            2]|[...]|[append n]|[named param 1=value 1]|[...]|[named param
--            n=value n]|[...]
library.call_for_each_group = function(ctx)
-- NOTE: `ctx.pipe` and `ctx.params` might be the original metatables!
local opts = ctx.pipe
local tmp
if opts[1] ~= nil then tmp = opts[1]:match'^%s*(.*%S)' end
if tmp == nil then error(ctx.luaname ..
', ‘call_for_each_group’: No template name was provided', 0) end
local model = { title = tmp }
local ccs = ctx.itersep or ''
local nss = 0
local prefix
local gid
local groups = {}
local ret = {}
opts = {}
for key, val in pairs(ctx.pipe) do
if type(key) == 'number' then opts[key - 1] = val
else opts[key] = val end
end
ctx.pipe = opts
for key, val in pairs(ctx.params) do
prefix, gid = tostring(key):match'^%s*(.-)%s*(%-?%d*)%s*$'
gid = tonumber(gid) or ''
if groups[gid] == nil then groups[gid] = {} end
tmp = tonumber(prefix)
if tmp ~= nil then
if tmp < 1 then prefix = tmp - 1 else prefix = tmp end
end
groups[gid][prefix] = val
end
ctx.params = groups
flush_params(
ctx,
function(gid, group)
for key, val in pairs(opts) do group[key] = val end
group[0] = gid
model.args = group
ret[nss + 1] = ccs
ret[nss + 2] = ctx.frame:expandTemplate(model)
nss = nss + 2
end
)
if nss > 0 then
if nss > 2 and ctx.lastsep ~= nil then
ret[nss - 1] = ctx.lastsep
end
ret[1] = ctx.header or ''
if ctx.footer ~= nil then ret[nss + 1] = ctx.footer end
ctx.text = table.concat(ret)
return false
end
ctx.text = ctx.ifngiven or ''
return false
end


    local filter = build_key_filter(args)
    local renderOptions = build_render_options(filter)


    local state = new_switch_state()
    local errors = {}
    local ok = each_entity_data(frame, id, function(parsed, ctx)
        add_entries_from_meta(state, parsed, ctx, filter, false)
    end, function(kind, name, isStore, tplPath)
        if not filter.hasWhitelist then
            errors[#errors + 1] = build_missing_template_error(kind, name, isStore, tplPath)
        end
    end, filter)
    if not ok then return "" end


---                                        ---
     local out = {}
---     PUBLIC ENVIRONMENT                ---
---    ________________________________    ---
---                                        ---


    if #errors > 0 then
        out[#out + 1] = '{{сущность/infobox|' .. table.concat(errors, "\n") .. '}}'
    end


    renderOptions.noHeaders = filter.hasWhitelist


--[[ First-position-only modifiers ]]--
    local blocks = renderBlocks(frame, state, renderOptions, id, showSource)
---------------------------------------
    for _, b in ipairs(blocks) do
        out[#out + 1] = b
    end


 
    return frame:preprocess(table.concat(out, "\n"))
-- Syntax:  #invoke:params|new|pipe to
--[[
static_iface.new = function(frame)
local ctx = context_new()
ctx.frame = frame:getParent()
ctx.pipe = copy_or_ref_table(frame.args, false)
ctx.params = {}
main_loop(ctx, context_iterate(ctx, 1))
return ctx.text
end
end
]]--


function p.preview(frame)
    local args = getArgs(frame, { removeBlanks = false })
    local tplPath = args[1] or ""
    if tplPath == "" then return "" end


    local showSource = trim(args.nosource or "") == ""
    local previewFilter = build_key_filter(args)
    local renderOptions = build_render_options(previewFilter)


--[[ First-position-only functions ]]--
    local content = load_template_content(tplPath)
---------------------------------------
    if not content then
        return ""
    end


    local parsed = getTemplateMeta(frame, tplPath) or {}
    if type(parsed) ~= "table" then
        parsed = {}
    end


    local state = new_switch_state()
    add_entries_from_meta(state, parsed, {
        tplPath = tplPath,
        id = "",
        extra = "",
        source = make_source("", tplPath, tplPath, tplPath),
        priority = 1
    }, nil, true)


-- Syntax:  #invoke:params|self
    local hasWhitelist = previewFilter.hasWhitelist
static_iface.self = function(frame)
    renderOptions.noHeaders = hasWhitelist
return frame:getParent():getTitle()
end


    local out = {}
    local blocks = renderBlocks(frame, state, renderOptions, "", showSource)
    for _, b in ipairs(blocks) do
        out[#out + 1] = b
    end


    return frame:preprocess(table.concat(out, "\n"))
end


--[[ Public metatable of functions ]]--
return p
---------------------------------------
 
 
 
return setmetatable(static_iface, {
__index = function(iface, _fname_)
local ctx = context_new()
local fname = _fname_:match'^%s*(.*%S)'
if fname == nil then error(ctx.luaname ..
': You must specify a function to call', 0) end
if library[fname] == nil then error(ctx.luaname ..
': The function ‘' .. fname .. '’ does not exist', 0) end
return function(frame)
local func = library[fname]
local refpipe = {
count = true,
value_of = true,
list = true,
list_values = true,
for_each = true,
call_for_each_group = true
}
local refparams = {
--inserting = true,
count = true,
concat_and_call = true,
concat_and_invoke = true,
concat_and_magic = true,
value_of = true,
call_for_each_group = true
}
ctx.frame = frame:getParent()
ctx.pipe = copy_or_ref_table(frame.args, refpipe[fname])
ctx.params = copy_or_ref_table(ctx.frame.args, refparams[fname])
main_loop(ctx, func)
return ctx.text
end
end
})