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

мНет описания правки
Нет описания правки
 
(не показано 108 промежуточных версий этого же участника)
Строка 1: Строка 1:
---                                        ---
local p = {}
---    LOCAL ENVIRONMENT                  ---
local getArgs = require('Module:Arguments').getArgs
---    ________________________________    ---
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 ucfirst(s)
-- Helper function for `string.gsub()` (for managing zero-padded numbers)
    if not s or s == "" then return s end
local function zero_padded (str)
    return mw.ustring.upper(mw.ustring.sub(s, 1, 1)) .. mw.ustring.sub(s, 2)
return ('%03d%s'):format(#str, str)
end
end


 
local function each_csv_value(str, fn)
-- Helper function for `table.sort()` (for natural sorting)
    if not str or str == "" then
local function natural_sort (var1, var2)
        return
return tostring(var1):gsub('%d+', zero_padded) <
    end
tostring(var2):gsub('%d+', zero_padded)
    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)
-- Return a copy or a reference to a table
    local moduleName = JsonPaths.get(page)
local function copy_or_ref_table (src, refonly)
    local ok, data = pcall(mw.loadData, moduleName)
if refonly then return src end
    if not ok then
newtab = {}
        return nil
for key, val in pairs(src) do newtab[key] = val end
    end
return newtab
    return data
end
end


 
local function load_template_content(path)
-- Remove numerical elements from a table, shifting everything to the left
    local title = mw.title.new("Template:" .. path)
local function remove_numerical_keys (tbl, idx, len)
    if not title then
local cache = {}
        return nil
local tmp = idx + len - 1
    end
for key, val in pairs(tbl) do
    local ok, content = pcall(title.getContent, title)
if type(key) == 'number' and key >= idx then
    if not ok then
if key > tmp then cache[key - len] = val end
        return nil
tbl[key] = nil
    end
end
    return content
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"
local 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)
--[[
    if name == "" then
local function copy_table_expanded (tbl, idx, len)
        return nil
local ret = {}
    end
local tmp = idx + len - 1
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
local 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,
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


-- Functions listed here declare that they don't need the `frame.args`
    return params
-- metatable to be copied into a regular table; if they are modifiers they also
end
-- guarantee that they will make available their own (modified) copy
local refpipe = {
count = true,
value_of = true,
list = true,
list_values = true,
for_each = true,
call_for_each_group = true
}


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


-- Functions listed here declare that they don't need the
local function sort_entries_by_priority(entries)
-- `frame:getParent().args` metatable to be copied into a regular table; if
    table.sort(entries, function(a, b)
-- they are modifiers they also guarantee that they will make available their
        if a.priority == b.priority then return a.idx < b.idx end
-- own (modified) copy
        return a.priority > b.priority
local refparams = {
    end)
--inserting = true,
end
grouping_by_calling = true,
count = true,
concat_and_call = true,
concat_and_invoke = true,
concat_and_magic = true,
value_of = true,
call_for_each_group = true
}


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


-- Maximum number of numerical parameters that can be filled, if missing (we
local function register_switch_mode(name, cfg)
-- chose an arbitrary number for this constant; you can discuss about its
    switchModeRegistry[name] = cfg or {}
-- optimal value at Module talk:Params)
    switchModeOrder[#switchModeOrder + 1] = name
local maxfill = 1024
end


 
local function new_switch_state()
-- The private table of functions
    local state = { keyOrder = {}, keyToTemplates = {}, keySources = {} }
local library = {}
    for _, sw in ipairs(switchModeOrder) do
 
        state.keyOrder[sw] = {}
 
        state.keyToTemplates[sw] = {}
-- Functions that can only be invoked in first position
        state.keySources[sw] = {}
local static_iface = {}
    end
 
    return state
 
-- Create a new context
local function context_new ()
local ctx = {}
ctx.luaname = 'Module:Params' --[[ or `frame:getTitle()` ]]--
ctx.iterfunc = pairs
ctx.sorttype = 0
ctx.firstposonly = static_iface
ctx.n_available = maxfill
return ctx
end
end


 
local function ensure_switch_key(state, sw, key)
-- Move to the next action within the user-given list
    local byKey = state.keyToTemplates[sw]
local function context_iterate (ctx, n_forward)
    if not byKey[key] then
local nextfn
        byKey[key] = {}
if ctx.pipe[n_forward] ~= nil then
        state.keyOrder[sw][#state.keyOrder[sw] + 1] = key
nextfn = ctx.pipe[n_forward]:match'^%s*(.*%S)'
    end
end
    return byKey[key]
if nextfn == nil then error(ctx.luaname ..
': You must specify a function to call', 0) end
if library[nextfn] == nil then
if ctx.firstposonly[nextfn] == nil then error(ctx.luaname ..
': The function ‘' .. nextfn .. '’ does not exist', 0)
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 add_switch_entry(state, sw, key, entry)
-- Main loop
    local bucket = ensure_switch_key(state, sw, key)
local function main_loop (ctx, start_with)
    entry.idx = #bucket + 1
local fn = start_with
    bucket[#bucket + 1] = entry
repeat fn = fn(ctx) until not fn
end
end


 
local function collect_tpl_calls(entries)
-- Parse user arguments of type `...|[let]|[...][number of additional
    local tplCalls = {}
-- parameters]|[parameter 1]|[parameter 2]|[...]`
    local sources = {}
local function parse_child_args (src, start_from, append_after)
    if #entries > 0 then
local names
        sort_entries_by_priority(entries)
local tmp
        for _, e in ipairs(entries) do
local dest = {}
            tplCalls[#tplCalls + 1] = e.tpl
local pin = start_from
            sources[#sources + 1] = e.source
if src[pin] ~= nil and src[pin]:match'^%s*let%s*$' then
        end
names = {}
    end
repeat
    return tplCalls, sources
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
local shf = append_after - pin
for idx = pin + 1, pin + tmp do dest[idx + shf] = src[idx] end
pin = pin + tmp + 1
end
if names ~= nil then
for key, val in pairs(names) do dest[key] = val end
end
return dest, pin
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 "")


-- Parse the arguments of some of the `mapping_*` and `renaming_*` class of
    local tplLabel = "Template:" .. s.tplPath
-- modifiers
    return "[[" .. tplLabel .. "|" .. className .. "]]"
local function parse_callback_args (src, n_skip, default_style)
local style
local shf
local tmp = src[n_skip + 1]
if tmp ~= nil then style = mapping_styles[tmp:match'^%s*(.-)%s*$'] end
if style == nil then
style = default_style
shf = n_skip - 1
else shf = n_skip end
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
local dest, nargs = parse_child_args(src, style[2] + shf, n_exist)
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 dest, nargs, tmp, karg, varg
end
end


 
local function renderTitleBlock(key, tplCalls, sources, includeHeader, frame, showSource)
-- Parse the arguments of some of the `mapping_*` and `renaming_*` class of
    local parts = {}
-- modifiers
    if tplCalls and #tplCalls > 0 then
local function parse_replace_args (opts, fname)
        for i, tpl in ipairs(tplCalls) do
if opts[1] == nil then error(ctx.luaname ..
            local add = true
', ‘' .. fname .. '’: No pattern string was given', 0) end
            if frame then
if opts[2] == nil then error(ctx.luaname ..
                local expanded = frame:preprocess(tpl)
', ‘' .. fname .. '’: No replacement string was given', 0) end
                add = expanded and trim(expanded) ~= ""
local ptn = opts[1]
            end
local repl = opts[2]
            if add then
local argc = 3
                local src = sources and sources[i]
local nmax = tonumber(opts[3])
                local line = '<div>' .. tpl .. '</div>'
if nmax ~= nil or (opts[3] or ''):match'^%s*$' ~= nil then argc = 4 end
                if showSource and src then
local flg = opts[argc]
                    line = line .. '<div class="ts-Сущность-field">' .. makeSourceLink(src) .. '</div>'
if flg ~= nil then flg = mkeywords[flg:match'^%s*(.-)%s*$'] end
                end
if flg == 0 then flg = nil elseif flg ~= nil then argc = argc + 1 end
                parts[#parts + 1] = '<div class="ts-Сущность">' .. line .. '</div>'
return ptn, repl, nmax, flg == 3, argc, (nmax ~= nil and nmax < 1) or
            end
(flg == 3 and ptn == repl)
        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
end


 
local function split_title_key(key)
-- Parse the arguments of the `with_*_matching` class of modifiers
    local main, sub = (key or ""):match("^([^_]+)_(.+)$")
local function parse_pattern_args (ctx, fname)
    if main and sub then
local state = 0
        return main, sub
local cnt = 1
    end
local keyw
    return key, nil
local nptns = 0
local ptns = {}
for _, val in ipairs(ctx.pipe) do
if state == 0 then
nptns = nptns + 1
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 ptns, cnt
end
end


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


-- Map parameters' values using a custom callback and a referenced table
    for _, key in ipairs(keyOrder or {}) do
local value_maps = {
        local mainTitle, subTitle = split_title_key(key)
[0] = function (tbl, margs, karg, varg, fn)
        if mainTitle and mainTitle ~= "" then
for key in pairs(tbl) do tbl[key] = fn() end
            local group = groups[mainTitle]
end,
            if not group then
[1] = function (tbl, margs, karg, varg, fn)
                group = { blocks = {} }
for key, val in pairs(tbl) do
                groups[mainTitle] = group
margs[varg] = val
                groupOrder[#groupOrder + 1] = mainTitle
tbl[key] = fn()
            end
end
end,
[2] = function (tbl, margs, karg, varg, fn)
for key in pairs(tbl) do
margs[karg] = key
tbl[key] = fn()
end
end,
[3] = function (tbl, margs, karg, varg, fn)
for key, val in pairs(tbl) do
margs[karg] = key
margs[varg] = val
tbl[key] = fn()
end
end
}


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


-- Private table for `map_names()`
    local out = {}
local name_thieves_maps = {
    for _, mainTitle in ipairs(groupOrder) do
[0] = function (cache, tbl, rargs, karg, varg, fn)
        local group = groups[mainTitle]
for key, val in pairs(tbl) do
        local parts = {}
steal_if_renamed(val, tbl, key, cache, fn())
end
end,
[1] = function (cache, tbl, rargs, karg, varg, fn)
for key, val in pairs(tbl) do
rargs[varg] = val
steal_if_renamed(val, tbl, key, cache, fn())
end
end,
[2] = function (cache, tbl, rargs, karg, varg, fn)
for key, val in pairs(tbl) do
rargs[karg] = key
steal_if_renamed(val, tbl, key, cache, fn())
end
end,
[3] = function (cache, tbl, rargs, karg, varg, fn)
for key, val in pairs(tbl) do
rargs[karg] = key
rargs[varg] = val
steal_if_renamed(val, tbl, key, cache, fn())
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
local 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)
name_thieves_maps[looptype](cache, tbl, rargs, karg, varg, fn)
            if blockText ~= "" then
for key, val in pairs(cache) do tbl[key] = val end
                if block.subTitle and not noHeaders then
end
                    parts[#parts + 1] = "<h3>" .. mw.text.encode(block.subTitle) .. "</h3>"
                end
                parts[#parts + 1] = blockText
            end
        end


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


-- Return a new table that contains `src` regrouped according to the numerical
    return table.concat(out, "\n")
-- suffixes in its keys
local function make_groups (src)
-- NOTE: `src` might be the original metatable!
local tmp
local prefix
local gid
local groups = {}
for key, val in pairs(src) do
-- `key` must only be a string or a number...
gid = tonumber(key)
if gid == nil then
prefix, gid = key:match'^%s*(.-)%s*(%-?%d*)%s*$'
gid = tonumber(gid) or ''
else prefix = '' end
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
return groups
end
end


 
local function normalizeFilterKey(s)
-- Concatenate the numerical keys from the table of parameters to the numerical
    s = trim(s or "")
-- keys from the table of options; non-numerical keys from the table of options
    s = s:gsub("%s*_%s*", "_")
-- will prevail over colliding non-numerical keys from the table of parameters
    return s
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 matches_card_list(list, callKey, compositeKey)
-- Flush the parameters by calling a custom function for each value (after this
    if not list then
-- function has been invoked `ctx.params` will be no longer usable)
        return false
local function flush_params (ctx, fn)
    end
local tbl = ctx.params
    callKey = normalizeFilterKey(callKey)
if ctx.subset == 1 then
    compositeKey = normalizeFilterKey(compositeKey)
for key, val in ipairs(tbl) do fn(key, val) end
    return list[callKey] or list[compositeKey] or false
return
end
if ctx.subset == -1 then
for key, val in ipairs(tbl) do tbl[key] = nil end
end
if ctx.sorttype > 0 then
local nums = {}
local words = {}
local nn = 0
local nw = 0
for key, val in pairs(tbl) do
if type(key) == 'number' then
nn = nn + 1
nums[nn] = key
else
nw = nw + 1
words[nw] = key
end
end
table.sort(nums)
table.sort(words, natural_sort)
if ctx.sorttype == 2 then
for idx = 1, nw do fn(words[idx], tbl[words[idx]]) end
for idx = 1, nn do fn(nums[idx], tbl[nums[idx]]) end
return
end
for idx = 1, nn do fn(nums[idx], tbl[nums[idx]]) end
for idx = 1, nw 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.sorttype > 0 then error(ctx.luaname ..
    end
': The ‘all_sorted’ and ‘reassorted’ directives are 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|all_sorted|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
if ctx.sorttype == 2 then error(ctx.luaname ..
                if isWhitelisted and content ~= "" then
': The two directives ‘reassorted’ and ‘sequential’ are in contradiction with each other', 0) end
                    rawContentParts[#rawContentParts + 1] = content
ctx.sorttype = 1
                end
return context_iterate(ctx, 1)
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|reassorted|pipe to
                if allowCardEntry and (displayLabel ~= "" or content ~= "") then
library.reassorted = function (ctx)
                    if not merged.sectionsMap[section] then
if ctx.subset == 1 then error(ctx.luaname ..
                        merged.sectionsMap[section] = true
': The ‘reassorted’ directive is redundant after ‘sequential’', 0) end
                        merged.sections[#merged.sections + 1] = section
if ctx.sorttype == 1 then error(ctx.luaname ..
                    end
': The two directives ‘sequential’ and ‘reassorted’ are in contradiction with each other', 0) end
                    if displayLabel ~= "" and (not merged.labelOverrides[compositeKey] or merged.labelOverrides[compositeKey] == "") then
ctx.sorttype = 2
                        merged.labelOverrides[compositeKey] = displayLabel
return context_iterate(ctx, 1)
                    end
end
                    if content ~= "" then
                        local prev = merged.contentByKey[compositeKey]
                        if prev and prev ~= "" then
                            merged.contentByKey[compositeKey] = prev .. "\n" .. content
                        else
                            merged.contentByKey[compositeKey] = content
                        end
                    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|setting|directives|...|pipe to
                if allowCardEntry and tagText ~= "" then
library.setting = function (ctx)
                    if not merged.tagSet[tagText] then
local opts = ctx.pipe
                        merged.tagSet[tagText] = true
local cmd = opts[1]
                        merged.tags[#merged.tags + 1] = tagText
if cmd ~= nil then
                    end
cmd = cmd:gsub('%s+', ''):gsub('/+', '/'):match'^/*(.*[^/])'
                end
end
            end
if cmd == nil then error(ctx.luaname ..
        end
', ‘setting’: No directive was given', 0) end
    end
local sep = string.byte('/')
local argc = 2
local dest = {}
local vname
local chr
for idx = 1, #cmd do
chr = cmd:byte(idx)
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


    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|squeezing|pipe to
    local out = {}
library.squeezing = function (ctx)
    if #rawContentParts > 0 then
local tbl = ctx.params
        out[#out + 1] = table.concat(rawContentParts, "\n")
local store = {}
    end
local indices = {}
local newlen = 0
for key, val in pairs(tbl) do
if type(key) == 'number' then
newlen = newlen + 1
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


    local cardCall = buildCardCall(merged, entityId)


-- Syntax:  #invoke:params|filling_the_gaps|pipe to
    if noHeaders then
library.filling_the_gaps = function (ctx)
        local hasLabel = false
local tbl = ctx.params
        for _, v in pairs(merged.labelOverrides or {}) do
local nmin = 1
            if v and v ~= "" then
local nmax = nil
                hasLabel = true
local nnums = -1
                break
local tmp = {}
            end
for key, val in pairs(tbl) do
        end
if type(key) == 'number' then
        if not hasLabel then
if nmax == nil then
            for _, lst in pairs(merged.labelLists or {}) do
if key < nmin then nmin = key end
                if #lst > 0 then
nmax = key
                    hasLabel = true
elseif key > nmax then nmax = key
                    break
elseif key < nmin then nmin = key end
                end
nnums = nnums + 1
            end
tmp[key] = val
        end
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 hasContent = false
        for _, v in pairs(merged.contentByKey or {}) do
            if v and v ~= "" then
                hasContent = true
                break
            end
        end


-- Syntax:  #invoke:params|clearing|pipe to
        if not hasLabel and not hasContent then
library.clearing = function (ctx)
            return table.concat(out, "\n")
local tbl = ctx.params
        end
local numericals = {}
    end
for key, val in pairs(tbl) do
if type(key) == 'number' then
numericals[key] = val
tbl[key] = nil
end
end
for key, val in ipairs(numericals) do tbl[key] = val end
return context_iterate(ctx, 1)
end


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


-- Syntax:  #invoke:params|cutting|left cut|right cut|pipe to
    return table.concat(out, "\n")
library.cutting = function (ctx)
local lcut = tonumber(ctx.pipe[1])
if lcut == nil then error(ctx.luaname ..
', ‘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
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|cropping|left crop|right crop|pipe to
register_switch_mode("title", {
library.cropping = function (ctx)
    full = true,
local lcut = tonumber(ctx.pipe[1])
    build_entry = function(ctx, key)
if lcut == nil then error(ctx.luaname ..
        return {
', ‘cropping’: Left crop must be a number', 0) end
            tpl = makeTplCall(ctx.tplPath, "title", key, ctx.id, ctx.extra),
local rcut = tonumber(ctx.pipe[2])
            source = ctx.source,
if rcut == nil then error(ctx.luaname ..
            priority = ctx.priority
', ‘cropping’: Right crop must be a number', 0) end
        }
local tbl = ctx.params
    end,
local nmin
    build_preview_entry = function(ctx, key)
local nmax
        return {
for key in pairs(tbl) do
            tpl = makeTplCall(ctx.tplPath, "title", key, ""),
if type(key) == 'number' then
            source = ctx.source,
if nmin == nil then
            priority = ctx.priority
nmin = key
        }
nmax = key
    end,
elseif key > nmax then nmax = key
    render_full = function(frame, keyOrder, keyToTemplates, keySources, entityId, noHeaders, showSource)
elseif key < nmin then nmin = key end
        return renderGroupedTitleBlocks(frame, keyOrder, keyToTemplates, noHeaders, showSource)
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


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


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


 
local function parseListArg(str)
-- Syntax:  #invoke:params|rotating|pipe to
    local res = {}
library.rotating = function (ctx)
    if not str or str == "" then return res end
local tbl = ctx.params
    for item in string.gmatch(str, "[^,]+") do
local numericals = {}
        local s = normalizeFilterKey(item)
local nmax = 0
        if s ~= "" then
for key, val in pairs(tbl) do
            local a, b = s:match("^([^_]+)_(.+)$")
if type(key) == 'number' then
            if a and b then
numericals[key] = val
                res[a] = res[a] or {}
tbl[key] = nil
                res[a][b] = true
if key > nmax then nmax = key end
            end
end
        end
end
    end
for key, val in pairs(numericals) do tbl[nmax - key + 1] = val end
    return res
return context_iterate(ctx, 1)
end
end


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


 
local function build_render_options(filter)
-- Syntax:  #invoke:params|mirroring|pipe to
    return {
--[[
        noHeaders = false,
library.mirroring = function (ctx)
        cardFilter = {
local tbl = ctx.params
            hasWhitelist = filter.whitelist.cardContent and next(filter.whitelist.cardContent) ~= nil or false,
local numericals = {}
            cardWhitelist = filter.whitelist.card or {},
local nmax
            blacklist = filter.blacklist.cardContent or {},
local nmin
            whitelist = filter.whitelist.cardContent or {}
for key, val in pairs(tbl) do
        }
if type(key) == 'number' then
    }
numericals[key] = val
tbl[key] = nil
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 should_include_key(filter, sw, key)
-- Syntax:  #invoke:params|swapping|pipe to
    if filter.hasWhitelist then
--[[
        if filter.whitelist[sw] and filter.whitelist[sw][key] then
library.swapping = function (ctx)
            return true
local tbl = ctx.params
        end
local cache = {}
        if sw == "card" and filter.whitelist.cardContent and filter.whitelist.cardContent[key] then
local nsize = 0
            return true
local tmp
        end
for key in pairs(tbl) do
        return false
if type(key) == 'number' then
    end
nsize = nsize + 1
    return not (filter.blacklist[sw] and filter.blacklist[sw][key])
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
end
]]--


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


local function apply_entity_set_filters(foundSet, whitelistSet, blacklistSet)
    local hasWhitelist = next(whitelistSet or {}) ~= nil


-- Syntax:  #invoke:params|inserting|position|how many|...|pipe to
    if hasWhitelist then
--[[
        for name in pairs(foundSet) do
library.inserting = function (ctx)
            if not whitelistSet[name] then
-- NOTE: `ctx.params` might be the original metatable! As a modifier,
                foundSet[name] = nil
-- this function MUST create a copy of it before returning
            end
local idx = tonumber(ctx.pipe[1])
        end
if idx == nil then error(ctx.luaname ..
    end
', ‘inserting’: Position must be a number', 0) 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
]]--
 


-- Syntax:  #invoke:params|imposing|name|value|pipe to
    for name in pairs(blacklistSet or {}) do
library.imposing = function (ctx)
        foundSet[name] = nil
if ctx.pipe[1] == nil then error(ctx.luaname ..
    end
', ‘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
end


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


-- Syntax:  #invoke:params|providing|name|value|pipe to
    local compList = load_entity_components(id)
library.providing = function (ctx)
    if type(compList) == "table" then
if ctx.pipe[1] == nil then error(ctx.luaname ..
        for _, v in ipairs(compList) do
', ‘providing’: Missing parameter name to provide', 0) end
            if type(v) == "string" and v ~= "" then
local key = ctx.pipe[1]:match'^%s*(.-)%s*$'
                foundComponents[v] = true
key = tonumber(key) or key
            end
if ctx.params[key] == nil then ctx.params[key] = ctx.pipe[2] end
        end
return context_iterate(ctx, 3)
    end
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|discarding|name|[how many]|pipe to
    apply_entity_set_filters(foundComponents, parse_csv_set(componentWhitelist), parse_csv_set(componentBlacklist))
library.discarding = function (ctx)
    apply_entity_set_filters(foundPrototypes, parse_csv_set(prototypeWhitelist), parse_csv_set(prototypeBlacklist))
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


 
    return foundComponents, foundPrototypes
-- Syntax:  #invoke:params|with_name_matching|target 1|[plain flag 1]|[or]
--            |[target 2]|[plain flag 2]|[or]|[...]|[target N]|[plain flag
--            N]|pipe to
library.with_name_matching = function (ctx)
local tbl = ctx.params
local targets, argc = parse_pattern_args(ctx, targets,
'with_name_matching')
local nomatch
for key in pairs(tbl) do
nomatch = true
for _, ptn in ipairs(targets) do
if not ptn[3] then
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 resolve_priority(parsed)
-- Syntax:  #invoke:params|with_name_not_matching|target 1|[plain flag 1]
    local basePriority = 1
--            |[and]|[target 2]|[plain flag 2]|[and]|[...]|[target N]|[plain
    if type(parsed) == "table" and parsed.priority ~= nil then
--            flag N]|pipe to
        if type(parsed.priority) == "number" then
library.with_name_not_matching = function (ctx)
            basePriority = parsed.priority
local tbl = ctx.params
        else
local targets, argc = parse_pattern_args(ctx, targets,
            local pnum = tonumber(parsed.priority)
'with_name_not_matching')
            if pnum then
local yesmatch
                basePriority = pnum
for key in pairs(tbl) do
            end
yesmatch = true
        end
for _, ptn in ipairs(targets) do
    end
if ptn[3] then
    return basePriority
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 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_value_matching|target 1|[plain flag 1]|[or]
    return dp.flattenFieldSelectiveDirect(id, dataPage, paramNames) or ""
--            |[target 2]|[plain flag 2]|[or]|[...]|[target N]|[plain flag
--            N]|pipe to
library.with_value_matching = function (ctx)
local tbl = ctx.params
local targets, argc = parse_pattern_args(ctx, targets,
'with_value_matching')
local nomatch
for key, val in pairs(tbl) do
nomatch = true
for _, ptn in ipairs(targets) 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 add_card_tag_value(tags, seen, value)
-- Syntax:  #invoke:params|with_value_not_matching|target 1|[plain flag 1]
    value = trim(value or "")
--            |[and]|[target 2]|[plain flag 2]|[and]|[...]|[target N]|[plain
    if value == "" or seen[value] then
--            flag N]|pipe to
        return
library.with_value_not_matching = function (ctx)
    end
local tbl = ctx.params
    seen[value] = true
local targets, argc = parse_pattern_args(ctx, targets,
    tags[#tags + 1] = value
'with_value_not_matching')
local yesmatch
for key, val in pairs(tbl) do
yesmatch = true
for _, ptn in ipairs(targets) 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
end


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


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


 
    return table.concat(tags, ", ")
-- Syntax:  #invoke:params|mapping_by_calling|template name|[call
--            style]|[let]|[...][number of additional parameters]|[parameter
--            1]|[parameter 2]|[...]|[parameter N]|pipe to
library.mapping_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 ..
', ‘mapping_by_calling’: No template name was provided', 0) end
local margs, argc, looptype, karg, varg = parse_callback_args(opts, 1,
mapping_styles.values_only)
local model = { title = tname, args = margs }
value_maps[looptype](ctx.params, margs, karg, varg, function ()
return ctx.frame:expandTemplate(model)
end)
return context_iterate(ctx, argc)
end
 
 
-- Syntax:  #invoke:params|mapping_by_invoking|module name|function
--            name|[call style]|[let]|[...]|[number of additional
--            arguments]|[argument 1]|[argument 2]|[...]|[argument N]|pipe to
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, argc, looptype, karg, varg = parse_callback_args(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
value_maps[looptype](ctx.params, margs, karg, varg, function ()
return mfunc(ctx.frame:newChild(model))
end)
return context_iterate(ctx, argc)
end
 
 
-- Syntax:  #invoke:params|mapping_by_magic|parser function|[call
--            style]|[let]|[...][number of additional arguments]|[argument
--            1]|[argument 2]|[...]|[argument N]|pipe to
library.mapping_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 ..
', ‘mapping_by_magic’: No parser function was provided', 0) end
local margs, argc, looptype, karg, varg = parse_callback_args(opts, 1,
mapping_styles.values_only)
value_maps[looptype](ctx.params, margs, karg, varg, function ()
return ctx.frame:callParserFunction(magic, margs)
end)
return context_iterate(ctx, argc)
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_replacing|target|replace|[count]|[plain
        if type(keys) == "table" then
--            flag]|pipe to
            for _, key in ipairs(keys) do
library.mapping_by_replacing = function (ctx)
                if (not filter) or should_include_key(filter, sw, key) then
local ptn, repl, nmax, is_strict, argc, die =
                    local buildFn = isPreview and (mode.build_preview_entry or mode.build_entry) or mode.build_entry
parse_replace_args(ctx.pipe, 'mapping_by_replacing')
                    if type(buildFn) == "function" then
if die then return context_iterate(ctx, argc) end
                        local entry = buildFn(ctx, key)
local tbl = ctx.params
                        if entry then
if is_strict then
                            add_switch_entry(state, sw, key, entry)
for key, val in pairs(tbl) do
                        end
if val == ptn then tbl[key] = repl end
                    end
end
                end
else
            end
if flg == 2 then
        end
-- Copied from Module:String's `str._escapePattern()`
ptn = ptn:gsub('[%(%)%.%%%+%-%*%?%[%^%$%]]', '%%%0')
end
for key, val in pairs(tbl) do
tbl[key] = val:gsub(ptn, repl, nmax)
end
end
return context_iterate(ctx, argc)
end


 
        if (not isPreview) and type(mode.add_entity_extras) == "function" then
-- Syntax:  #invoke:params|renaming_by_calling|template name|[call
            mode.add_entity_extras(state, parsed, ctx)
--            style]|[let]|[...][number of additional parameters]|[parameter
        end
--            1]|[parameter 2]|[...]|[parameter N]|pipe to
    end
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, argc, looptype, karg, varg = parse_callback_args(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 extract_whitelist_search_strings(keyFilter)
    if not keyFilter or not keyFilter.hasWhitelist then
        return nil
    end


-- Syntax:  #invoke:params|renaming_by_invoking|module name|function
    local strings = {}
--            name|[call style]|[let]|[...]|[number of additional
    for sw, keys in pairs(keyFilter.whitelist) do
--            arguments]|[argument 1]|[argument 2]|[...]|[argument N]|pipe to
        if type(keys) == "table" then
library.renaming_by_invoking = function (ctx)
            for key in pairs(keys) do
local opts = ctx.pipe
                strings[#strings + 1] = key
local mname
            end
local fname
        end
if opts[1] ~= nil then mname = opts[1]:match'^%s*(.*%S)' end
    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, argc, looptype, karg, varg = parse_callback_args(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


    if #strings == 0 then
        return nil
    end


-- Syntax:  #invoke:params|renaming_by_magic|parser function|[call
    return strings
--            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, argc, looptype, karg, varg = parse_callback_args(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 content_matches_whitelist(content, searchStrings)
    if not searchStrings then
        return true
    end
    if not content then
        return false
    end


-- Syntax:  #invoke:params|renaming_by_replacing|target|replace|[count]|[plain
    for _, s in ipairs(searchStrings) do
--            flag]|pipe to
        if string.find(content, s, 1, true) then
library.renaming_by_replacing = function (ctx)
            return true
local ptn, repl, nmax, is_strict, argc, die =
        end
parse_replace_args(ctx.pipe, 'renaming_by_replacing')
    end
if die then return context_iterate(ctx, argc) end
local tbl = ctx.params
if is_strict then
local key = tonumber(ptn) or ptn:match'^%s*(.-)%s*$'
local val = tbl[key]
tbl[key] = nil
tbl[tonumber(repl) or repl:match'^%s*(.-)%s*$'] = val
else
if flg == 2 then
-- Copied from Module:String's `str._escapePattern()`
ptn = ptn:gsub('[%(%)%.%%%+%-%*%?%[%^%$%]]', '%%%0')
end
local cache = {}
for key, val in pairs(tbl) do
steal_if_renamed(val, tbl, key, cache,
tostring(key):gsub(ptn, repl, nmax))
end
for key, val in pairs(cache) do tbl[key] = val end
end
return context_iterate(ctx, argc)
end
 


-- Syntax:  #invoke:params|grouping_by_calling|template
    return false
--            name|[let]|[...]|[number of additional arguments]|[argument
--            1]|[argument 2]|[...]|[argument N]|pipe to
library.grouping_by_calling = function (ctx)
-- NOTE: `ctx.params` might be the original metatable! As a modifier,
-- this function MUST create a copy of it before returning
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 ..
', ‘grouping_by_calling’: No template name was provided', 0) end
local model = { title = tmp }
local tmp, argc = parse_child_args(opts, 2, 0)
local gargs = {}
for key, val in pairs(tmp) do
if type(key) == 'number' and key < 1 then gargs[key - 1] = val
else gargs[key] = val end
end
local groups = make_groups(ctx.params)
for gid, group in pairs(groups) do
for key, val in pairs(gargs) do group[key] = val end
group[0] = gid
model.args = group
groups[gid] = ctx.frame:expandTemplate(model)
end
ctx.params = groups
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)


    local compWhitelistSet = parse_csv_set(componentWhitelist)
    local compBlacklistSet = parse_csv_set(componentBlacklist)
    local protoWhitelistSet = parse_csv_set(prototypeWhitelist)
    local protoBlacklistSet = parse_csv_set(prototypeBlacklist)


-- Syntax:  #invoke:params|count
    local compHasWhitelist = next(compWhitelistSet) ~= nil
library.count = function (ctx)
    local protoHasWhitelist = next(protoWhitelistSet) ~= nil
-- NOTE: `ctx.pipe` and `ctx.params` might be the original metatables!
    local anyEntityWhitelist = compHasWhitelist or protoHasWhitelist
local retval = 0
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 whitelistSearchStrings = extract_whitelist_search_strings(keyFilter)


-- Syntax:  #invoke:args|concat_and_call|template name|[prepend 1]|[prepend 2]
    local function processEntity(kind, name, isStore)
--            |[...]|[item n]|[named item 1=value 1]|[...]|[named item n=value
        local pathName = ucfirst(name)
--            n]|[...]
        local tplPath = kind .. "/" .. pathName
library.concat_and_call = function (ctx)
        if isStore then
-- NOTE: `ctx.params` might be the original metatable!
            tplPath = tplPath .. "/store"
local opts = ctx.pipe
        end
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 content = load_template_content(tplPath)
        if not content then
            if onMissing then
                onMissing(kind, name, isStore, tplPath)
            end
            return
        end


-- Syntax:  #invoke:args|concat_and_invoke|module name|function name|[prepend
        if not content_matches_whitelist(content, whitelistSearchStrings) then
--            1]|[prepend 2]|[...]|[item n]|[named item 1=value 1]|[...]|[named
            return
--            item n=value n]|[...]
        end
library.concat_and_invoke = function (ctx)
-- NOTE: `ctx.params` might be the original metatable!
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 ..
', ‘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


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


-- Syntax:  #invoke:args|concat_and_magic|parser function|[prepend 1]|[prepend
        local extra = ""
--            2]|[...]|[item n]|[named item 1=value 1]|[...]|[named item n=
        local paramNames = get_template_params(tplPath, content)
--            value n]|[...]
        if dp then
library.concat_and_magic = function (ctx)
            local dataPage = tplPath .. ".json"
-- NOTE: `ctx.params` might be the original metatable!
            extra = get_selective_extra(id, dataPage, paramNames)
local opts = ctx.pipe
        end
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


        onEntity(parsed, {
            tplPath = tplPath,
            id = id,
            extra = extra,
            source = make_source(kind, name, pathName, tplPath),
            priority = resolve_priority(parsed)
        })
    end


-- Syntax:  #invoke:params|value_of|parameter name
    for compName in pairs(foundComponents) do
library.value_of = function (ctx)
        if not anyEntityWhitelist or compHasWhitelist then
-- NOTE: `ctx.pipe` and `ctx.params` might be the original metatables!
            processEntity("component", compName, false)
local opts = ctx.pipe
        end
local kstr
    end
if opts[1] ~= nil then kstr = opts[1]:match'^%s*(.*%S)' end
    for protoName in pairs(foundPrototypes) do
if kstr == nil then error(ctx.luaname ..
        if not anyEntityWhitelist or protoHasWhitelist then
', ‘value_of’: No parameter name was provided', 0) end
            processEntity("prototype", protoName, false)
local knum = tonumber(kstr)
        end
local len = #ctx.params
    end
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


    local componentStoreDefs = load_module_data("component_store.json")
    if type(componentStoreDefs) == "table" and (not anyEntityWhitelist or compHasWhitelist) then
        local compStore = componentStoreDefs[id]
        if type(compStore) == "table" then
            for compName in pairs(compStore) do
                local allowed = true
                if compBlacklistSet[compName] then
                    allowed = false
                elseif compHasWhitelist and not compWhitelistSet[compName] then
                    allowed = false
                end
                if allowed then
                    processEntity("component", compName, true)
                end
            end
        end
    end


-- Syntax:  #invoke:params|list
    if type(prototypeStoreDefs) == "table" and (not anyEntityWhitelist or protoHasWhitelist) then
library.list = function (ctx)
        local protoStore = prototypeStoreDefs[id]
-- NOTE: `ctx.pipe` might be the original metatable!
        if type(protoStore) == "table" then
local kvs = ctx.pairsep or ''
            for protoName in pairs(protoStore) do
local pps = ctx.itersep or ''
                local allowed = true
local ret = {}
                if protoBlacklistSet[protoName] then
local nss = 0
                    allowed = false
flush_params(
                elseif protoHasWhitelist and not protoWhitelistSet[protoName] then
ctx,
                    allowed = false
function (key, val)
                end
ret[nss + 1] = pps
                if allowed then
ret[nss + 2] = key
                    processEntity("prototype", protoName, true)
ret[nss + 3] = kvs
                end
ret[nss + 4] = val
            end
nss = nss + 4
        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


 
    return true
-- Syntax:  #invoke:params|list_values
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


        if parsed.cardTag and parsed.cardTag ~= "" then
            entries[#entries + 1] = {
                tplTag = makeTplCall(ctx.tplPath, "cardTag", "cardTag", ctx.id, ctx.extra),
                priority = ctx.priority,
                idx = #entries + 1
            }
        end
    end)


-- Syntax:  #invoke:params|invoke_for_each|module name|module function|[append
    if not ok then
--            1]|[append 2]|[...]|[append n]|[named param 1=value 1]|[...]
        return trim(args.tag or "")
--            |[named param n=value n]|[...]
    end
library.invoke_for_each = 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 ..
', ‘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


    sort_entries_by_priority(entries)


-- Syntax:  #invoke:params|magic_for_each|parser function|[append 1]|[append 2]
    local tags = {}
--            |[...]|[append n]|[named param 1=value 1]|[...]|[named param
    local seen = {}
--            n=value n]|[...]
    for _, entry in ipairs(entries) do
library.magic_for_each = function (ctx)
        local tagText = trim(frame:preprocess(entry.tplTag or "") or "")
local opts = ctx.pipe
        add_card_tag_value(tags, seen, tagText)
local magic
    end
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


 
    return merge_card_tag_text(table.concat(tags, ", "), args.tag)
-- Syntax:  #invoke:params|call_for_each_value|template name|[append 1]|[append
--            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 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
ctx.params = make_groups(ctx.params)
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()
---     PUBLIC ENVIRONMENT                ---
    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 = {}


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


--[[ First-position-only modifiers ]]--
    renderOptions.noHeaders = filter.hasWhitelist
---------------------------------------


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


-- Syntax:  #invoke:params|new|pipe to
    return frame:preprocess(table.concat(out, "\n"))
--[[
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


-- Syntax:  #invoke:params|self
    local state = new_switch_state()
static_iface.self = function (frame)
    add_entries_from_meta(state, parsed, {
return frame:getParent():getTitle()
        tplPath = tplPath,
end
        id = "",
        extra = "",
        source = make_source("", tplPath, tplPath, tplPath),
        priority = 1
    }, nil, true)


    local hasWhitelist = previewFilter.hasWhitelist
    renderOptions.noHeaders = hasWhitelist


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


--[[ Public metatable of functions ]]--
    return frame:preprocess(table.concat(out, "\n"))
---------------------------------------
end


 
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
local func = library[fname]
return function (frame)
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
})