|
|
| (не показано 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
| |
| })
| |