Модуль:GetField: различия между версиями
Pok (обсуждение | вклад) Нет описания правки |
Pok (обсуждение | вклад) Нет описания правки |
||
| (не показана 1 промежуточная версия этого же участника) | |||
| Строка 4: | Строка 4: | ||
local getArgs = require('Module:Arguments').getArgs | local getArgs = require('Module:Arguments').getArgs | ||
local function load_cached_data | local function load_cached_data(pagePath) | ||
local titleName = JsonPaths.get(pagePath) | |||
local titleName = JsonPaths. | |||
if not titleName or titleName == "" then | if not titleName or titleName == "" then | ||
return nil | return nil | ||
end | end | ||
local ok, data = pcall(mw. | local ok, data = pcall(mw.loadData, titleName) | ||
if not ok or type(data) ~= "table" then | if not ok or type(data) ~= "table" then | ||
return nil | return nil | ||
| Строка 108: | Строка 100: | ||
local t = type(v) | local t = type(v) | ||
if t == "string" | if t == "string" then | ||
return v | |||
elseif t == "number" or t == "boolean" then | |||
return tostring(v) | return tostring(v) | ||
elseif t == "table" then | elseif t == "table" then | ||
| Строка 122: | Строка 116: | ||
local function is_array(tbl) | local function is_array(tbl) | ||
local | local n = #tbl | ||
if n == 0 then | |||
return false | |||
end | |||
if tbl[n] == nil then | |||
return false | |||
end | |||
local count = 0 | local count = 0 | ||
for k in pairs(tbl) do | for k in pairs(tbl) do | ||
| Строка 128: | Строка 128: | ||
return false | return false | ||
end | end | ||
count = count + 1 | count = count + 1 | ||
end | end | ||
return count | return count == n | ||
end | |||
local function is_array_of_primitives(tbl) | |||
if type(tbl) ~= "table" then return false end | |||
local n = #tbl | |||
if n == 0 or tbl[n] == nil then return false end | |||
local count = 0 | |||
for k, v in pairs(tbl) do | |||
if type(k) ~= "number" then return false end | |||
if type(v) == "table" then return false end | |||
count = count + 1 | |||
end | |||
return count == n | |||
end | end | ||
| Строка 148: | Строка 158: | ||
end | end | ||
local function | local function merge_copy(base, specific) | ||
for k, v in pairs( | local result = {} | ||
if type(v) == "table" and type( | for k, v in pairs(base) do | ||
local specificV = specific[k] | |||
if specificV ~= nil then | |||
if type(v) == "table" and type(specificV) == "table" then | |||
result[k] = merge_copy(v, specificV) | |||
elseif type(specificV) == "table" then | |||
result[k] = deep_copy(specificV) | |||
else | |||
result[k] = specificV | |||
end | |||
elseif type(v) == "table" then | elseif type(v) == "table" then | ||
result[k] = deep_copy(v) | |||
else | else | ||
result[k] = v | |||
end | |||
end | |||
for k, v in pairs(specific) do | |||
if result[k] == nil then | |||
if type(v) == "table" then | |||
result[k] = deep_copy(v) | |||
else | |||
result[k] = v | |||
end | |||
end | end | ||
end | end | ||
return result | |||
end | end | ||
| Строка 177: | Строка 205: | ||
local base = data["default"] | local base = data["default"] | ||
if type(base) == "table" then | if type(base) == "table" then | ||
return merge_copy(base, specific) | |||
end | end | ||
return specific | return specific | ||
| Строка 195: | Строка 221: | ||
local function load_entry(id, pagePath, data) | local function load_entry(id, pagePath, data) | ||
data = data or load_cached_data | data = data or load_cached_data(pagePath) | ||
return resolve_entry_from_data(data, id) | return resolve_entry_from_data(data, id) | ||
end | end | ||
| Строка 228: | Строка 254: | ||
end | end | ||
if type(v) == "table" then | if type(v) == "table" then | ||
for _, item in pairs(v) do | |||
for _, item in pairs(v) do | |||
if tostring(item) == target then | if tostring(item) == target then | ||
return true | return true | ||
| Строка 315: | Строка 332: | ||
local function walk(tbl, prefix) | local function walk(tbl, prefix) | ||
local | local entries = {} | ||
for k in pairs(tbl) do | for k in pairs(tbl) do | ||
entries[#entries + 1] = { k, tostring(k) } | |||
end | end | ||
table.sort( | table.sort(entries, function(a, b) return a[2] < b[2] end) | ||
for _, | for _, e in ipairs(entries) do | ||
local k = e[1] | |||
local kStr = e[2] | |||
local v = tbl[k] | local v = tbl[k] | ||
local key = (prefix == "" and kStr or prefix .. "." .. kStr) | local key = (prefix == "" and kStr or prefix .. "." .. kStr) | ||
| Строка 447: | Строка 463: | ||
local templatePath = resolve_template_path(tplPath) | local templatePath = resolve_template_path(tplPath) | ||
local | local tplParts = { "{{Шаблон:" .. templatePath } | ||
if extraTplArgs ~= "" then | if extraTplArgs ~= "" then | ||
tplParts[#tplParts + 1] = extraTplArgs | |||
end | end | ||
tplParts[#tplParts + 1] = "id=" .. tostring(id) | |||
if extra ~= "" then | if extra ~= "" then | ||
tplParts[#tplParts + 1] = extra | |||
end | end | ||
tplParts[#tplParts + 1] = "}}" | |||
return table.concat(tplParts, "|") | |||
return | |||
end | end | ||
| Строка 474: | Строка 489: | ||
local storeName = (kind == "prototype") and "prototype_store.json" or "component_store.json" | local storeName = (kind == "prototype") and "prototype_store.json" or "component_store.json" | ||
local data = | local data = load_cached_data(storeName) | ||
if not data then | if not data then | ||
return "" | return "" | ||
| Строка 662: | Строка 677: | ||
local function add_scalar_values(value, set) | local function add_scalar_values(value, set) | ||
if type(value) == "table" then | if type(value) == "table" then | ||
for _, item in pairs(value) do | |||
add_scalar_values(item, set) | |||
end | end | ||
elseif value ~= nil then | elseif value ~= nil then | ||
| Строка 711: | Строка 720: | ||
end | end | ||
local data = | local data = load_cached_data(pagePath) | ||
if not data then | if not data then | ||
return "[]" | return "[]" | ||
| Строка 748: | Строка 757: | ||
end | end | ||
function | local function entry_has_all_targets_component(entry, wanted) | ||
if type(entry) ~= "table" then | |||
return false | |||
end | |||
local set = {} | |||
for _, v in pairs(entry) do | |||
set[tostring(v)] = true | |||
end | end | ||
for i = 1, #wanted do | |||
if not set[wanted[i]] then | |||
return false | |||
end | |||
end | end | ||
local | return true | ||
if | end | ||
local function entry_has_all_targets_tag(entry, wanted) | |||
if type(entry) ~= "table" then | |||
return false | |||
end | end | ||
local | local tags = entry.tags or entry.tag | ||
if | if type(tags) ~= "table" then | ||
return | return false | ||
end | end | ||
local | local set = {} | ||
for _, v in pairs(tags) do | |||
set[tostring(v)] = true | |||
end | end | ||
for i = 1, #wanted do | |||
if not set[wanted[i]] then | |||
if | |||
return false | return false | ||
end | end | ||
end | |||
return true | |||
end | |||
function p.search(frame) | |||
local args = getArgs(frame, { removeBlanks = false }) | |||
local mode = (args[1] or ""):lower() | |||
local rawList = args[2] or "" | |||
return | if mode ~= "component" and mode ~= "tag" then | ||
return "[]" | |||
end | end | ||
local | local targets = split_csv_list(rawList) | ||
if #targets == 0 then | |||
return "[]" | |||
end | |||
local pagePath | |||
if mode == "component" then | |||
pagePath = "component.json" | |||
end | else | ||
pagePath = "component/tag.json" | |||
end | |||
local data = load_cached_data(pagePath) | |||
if not data or type(data) ~= "table" then | |||
return "[]" | |||
end | |||
local ids = collect_id_keys(data) | |||
if #ids == 0 then | |||
return "[]" | |||
end | |||
local sortedIds = {} | |||
for i = 1, #ids do | |||
sortedIds[i] = { ids[i], tostring(ids[i]) } | |||
end | end | ||
table.sort(sortedIds, function(a, b) return a[2] < b[2] end) | |||
local matches = {} | local matches = {} | ||
for _, | for _, e in ipairs(sortedIds) do | ||
local idKey = e[1] | |||
local entry = resolve_entry_from_data(data, idKey) | local entry = resolve_entry_from_data(data, idKey) | ||
if mode == "component" then | if mode == "component" then | ||
| Строка 885: | Строка 891: | ||
end | end | ||
local data = | local data = load_cached_data(pagePath) | ||
if not data then | if not data then | ||
return "" | return "" | ||
| Строка 984: | Строка 990: | ||
end | end | ||
local data = | local data = load_cached_data(pagePath) | ||
if not data then | if not data then | ||
return "" | return "" | ||
| Строка 1044: | Строка 1050: | ||
end | end | ||
local | local data = load_cached_data("component.json") | ||
if not data then | if not data then | ||
return "false" | return "false" | ||
| Строка 1076: | Строка 1081: | ||
end | end | ||
local | local data = load_cached_data("component.json") | ||
if not data or type(data) ~= "table" then | if not data or type(data) ~= "table" then | ||
return "[]" | return "[]" | ||
| Строка 1112: | Строка 1116: | ||
end | end | ||
local data = | local data = load_cached_data(pagePath) | ||
if not data then | if not data then | ||
return "" | return "" | ||
| Строка 1162: | Строка 1166: | ||
end | end | ||
local data = | local data = load_cached_data(pagePath) | ||
if not data then | if not data then | ||
return "" | return "" | ||
| Строка 1196: | Строка 1200: | ||
end | end | ||
return nil | return nil | ||
end | |||
local function collect_sorted_entries(tbl, stringOnly) | |||
if stringOnly then | |||
local keys = {} | |||
for k in pairs(tbl) do | |||
if type(k) == "string" then | |||
keys[#keys + 1] = k | |||
end | |||
end | |||
table.sort(keys) | |||
local entries = {} | |||
for i = 1, #keys do | |||
entries[i] = { keys[i], keys[i] } | |||
end | |||
return entries | |||
end | |||
local entries = {} | |||
for k in pairs(tbl) do | |||
entries[#entries + 1] = { k, tostring(k) } | |||
end | |||
table.sort(entries, function(a, b) return a[2] < b[2] end) | |||
return entries | |||
end | end | ||
local function collect_sorted_keys(tbl, stringOnly) | local function collect_sorted_keys(tbl, stringOnly) | ||
local entries = collect_sorted_entries(tbl, stringOnly) | |||
local keys = {} | local keys = {} | ||
for | for i = 1, #entries do | ||
keys[i] = entries[i][1] | |||
end | end | ||
return keys | return keys | ||
end | end | ||
local function choose_id_key(obj) | local function choose_id_key(obj) | ||
local | local entries = {} | ||
for k in pairs(obj) do | for k in pairs(obj) do | ||
if type(k) == "string" then | if type(k) == "string" then | ||
entries[#entries + 1] = { k, type(obj[k]) ~= "table" } | |||
end | end | ||
end | end | ||
if # | if #entries == 0 then | ||
return nil | return nil | ||
end | end | ||
table.sort( | table.sort(entries, function(a, b) | ||
if a[2] ~= b[2] then | |||
return not a[2] | |||
return not | |||
end | end | ||
return a[1] < b[1] | |||
return | |||
end) | end) | ||
return | return entries[1][1] | ||
end | end | ||
local function is_wrapper_block_key(key) | local function is_wrapper_block_key(key) | ||
return type(key) == "string" and not key:match("^[%a_][%w_]*$") | return type(key) == "string" and not key:match("^[%a_][%w_]*$") | ||
end | end | ||
| Строка 1276: | Строка 1276: | ||
end | end | ||
local | local entries = collect_sorted_entries(value, false) | ||
for _, | for _, e in ipairs(entries) do | ||
local k = e[1] | |||
local kStr = e[2] | |||
if not (options.nestedKeyMode == "raw" and type(k) == "number") then | if not (options.nestedKeyMode == "raw" and type(k) == "number") then | ||
local v = value[k] | local v = value[k] | ||
local key | local key | ||
if prefix then | if prefix then | ||
key = prefix .. "." .. | key = prefix .. "." .. kStr | ||
else | else | ||
key = | key = kStr | ||
end | end | ||
| Строка 1295: | Строка 1297: | ||
end | end | ||
elseif options.nestedKeyMode == "raw" then | elseif options.nestedKeyMode == "raw" then | ||
local json = encode_nowiki_json(v) | local json = encode_nowiki_json(v) | ||
if json then | if json then | ||
parts[#parts + 1] = key .. "=" .. json | parts[#parts + 1] = key .. "=" .. json | ||
end | end | ||
end | end | ||
if next(v) ~= nil and not is_array_of_primitives(v) then | if next(v) ~= nil and not is_array_of_primitives(v) then | ||
local childPrefix | local childPrefix | ||
if options.nestedKeyMode == "prefixed" then | if options.nestedKeyMode == "prefixed" then | ||
childPrefix = key | childPrefix = key | ||
elseif type(k) == "string" then | elseif type(k) == "string" then | ||
childPrefix = key | childPrefix = key | ||
else | else | ||
childPrefix = nil | childPrefix = nil | ||
end | end | ||
append_table_fields(parts, v, options, childPrefix) | append_table_fields(parts, v, options, childPrefix) | ||
end | end | ||
else | else | ||
parts[#parts + 1] = key .. "=" .. tostring(v) | parts[#parts + 1] = key .. "=" .. tostring(v) | ||
end | end | ||
end | end | ||
end | |||
end | |||
local function is_object_map(tbl) | |||
local count = 0 | |||
for k, v in pairs(tbl) do | |||
if type(k) ~= "string" or type(v) ~= "table" then | |||
return false | |||
end | |||
count = count + 1 | |||
end | end | ||
return count > 1 | |||
end | end | ||
| Строка 1346: | Строка 1359: | ||
skipPrimitiveRoot = true, | skipPrimitiveRoot = true, | ||
} | } | ||
local firstParamOverride = mw.text.unstripNoWiki(args.first or args.firstParam or args.idKey or "") | local firstParamOverride = mw.text.unstripNoWiki(args.first or args.firstParam or args.idKey or "") | ||
| Строка 1379: | Строка 1381: | ||
end | end | ||
parts[#parts + 1] = | parts[#parts + 1] = idKey | ||
local | local entries = collect_sorted_entries(obj, true) | ||
for _, | for _, e in ipairs(entries) do | ||
local k = e[1] | |||
local v = obj[k] | local v = obj[k] | ||
| Строка 1436: | Строка 1439: | ||
end | end | ||
elseif is_object_map(data) then | elseif is_object_map(data) then | ||
local | local dataEntries = collect_sorted_entries(data, true) | ||
for _, | for _, e in ipairs(dataEntries) do | ||
makeCall({ [ | makeCall({ [e[1]] = data[e[1]] }) | ||
end | end | ||
else | else | ||
| Строка 1533: | Строка 1536: | ||
end | end | ||
out[#out + 1] = line | |||
end | end | ||
end | end | ||
else | else | ||
local | local dataEntries = {} | ||
for k in pairs(data) do | for k in pairs(data) do | ||
dataEntries[#dataEntries + 1] = { k, tostring(k) } | |||
end | end | ||
table.sort( | table.sort(dataEntries, function(a, b) return a[2] < b[2] end) | ||
local processed = 0 | local processed = 0 | ||
for _, | for _, de in ipairs(dataEntries) do | ||
processed = processed + 1 | processed = processed + 1 | ||
if maxItems ~= nil and processed > maxItems then | if maxItems ~= nil and processed > maxItems then | ||
| Строка 1553: | Строка 1554: | ||
end | end | ||
local k = de[1] | |||
local kStr = de[2] | |||
local v = data[k] | local v = data[k] | ||
local vStr | local vStr | ||
| Строка 1567: | Строка 1570: | ||
end | end | ||
local baseKey = apply_pattern( | local baseKey = apply_pattern(kStr, keyPattern, "\\1") | ||
local MARK_KEY = "\31KEY\31" | local MARK_KEY = "\31KEY\31" | ||
| Строка 1576: | Строка 1579: | ||
local MARK_VAL = "\31VAL\31" | local MARK_VAL = "\31VAL\31" | ||
local kRepl = (keyReplace or "\\1"):gsub("\\2", MARK_VAL) | local kRepl = (keyReplace or "\\1"):gsub("\\2", MARK_VAL) | ||
local keyStr0 = apply_pattern( | local keyStr0 = apply_pattern(kStr, keyPattern, kRepl) | ||
local keyStr = tostring(keyStr0):gsub(MARK_VAL, vStr0) | local keyStr = tostring(keyStr0):gsub(MARK_VAL, vStr0) | ||
| Строка 1595: | Строка 1598: | ||
end | end | ||
out[#out + 1] = line | |||
end | end | ||
end | end | ||
Текущая версия от 03:45, 21 июня 2026
Модуль предназначен для получения данных из кэшированных JSON-страниц и их использования в шаблонах. С его помощью можно получить поле по пути, найти id по значению или сразу собрать вызов шаблона по найденным данным.
Поля берутся из json страниц
Участник:IanComradeBot/component/название(пример: Участник:IanComradeBot/component/access.json)Участник:IanComradeBot/prototype/название(пример: Участник:IanComradeBot/prototype/tag.json)
Для подпроектов
Участник:IanComradeBot/component/Проект/название(пример: Участник:IanComradeBot/Goob/component/access.json)Участник:IanComradeBot/prototype/Проект/название(пример: Участник:IanComradeBot/Goob/prototype/tag.json)
Основные функции
get
Возвращает запись целиком или отдельное поле по её id.
Использование:
- {{#invoke:GetField
|get |id |pagePath |keyPath}}
Простые значения возвращаются как текст, а таблицы в JSON-виде.
Например, json сущности MopItem из Участник:IanComradeBot/component/meleeWeapon.json выглядит так:
"MopItem": {
"damage": {
"types": {
"Blunt": 10
}
},
...
},
то мы можем получить значение как как:
- {{#invoke:GetField
|get |MopItem |component/meleeWeapon.json |damage}} -> "types": {"Blunt": 10}} - {{#invoke:GetField
|get |MopItem |component/meleeWeapon.json |damage.types}} -> {"Blunt": 10} - {{#invoke:GetField
|get |MopItem |component/meleeWeapon.json |damage.types.Blunt}} -> 10
| Параметр | Описание | Обязателен? |
|---|---|---|
|1 = | Id записи. | Да |
|2 = | Путь до JSON-страницы, например component/meleeWeapon.json. | Да |
|3 = | Путь до поля внутри записи. Поддерживает вложенность через точку и индексы вида field.1 | Да |
getTpl
Строит вызов шаблона для одного id передавая в него развёрнутые поля записи.
Использование:
- {{#invoke:GetField
|getTpl |id |pagePath |template}}
Шаблон вызывается в виде {{Имя шаблона|id=...|...поля записи...}}. Вложенные таблицы передаются как плоские параметры, а также в JSON-виде там, где это нужно для сохранения структуры.
Пример:
- {{#invoke:GetField
|getTpl |MopItem |сomponent/spillable.json |сomponent/spillable}}
| Параметр | Описание | Обязателен? |
|---|---|---|
|1 = | Id записи. | Да |
|2 = | Путь до JSON-страницы. | Да |
|3 = | Имя шаблона, который будет вызван как {{Имя шаблона|id=...|...}}. | Да |
searchId / searchIdTpl
Обе функции ищут id по значению в указанном поле. Разница в результате:
searchIdвозвращает найденные id в виде JSON-массива.searchIdTplпо найденным id сразу вызывает шаблон.
Использование:
- {{#invoke:GetField
|searchId |searchValue |pagePath |keyPath}} - {{#invoke:GetField
|searchIdTpl |searchValue |pagePath |keyPath |template}} - {{#invoke:GetField
|searchIdTpl |pagePath |keyPath |template |searchType=path}}
Режимы поиска:
searchType=value— ищет id, у которых значение поля равно указанному значению. Это режим по умолчанию.searchType=key— ищет id, у которых в поле-таблице существует ключ с указанным именем.searchType=path— только дляsearchIdTpl; выводит все id, у которых поле по указанному пути существует и не пустое. В этом режиме первым параметром передаётсяpagePath, а не значение для поиска.
searchIdTpl вызывает шаблон в виде {{Имя шаблона|id=...|...поля записи...}}. Если найдено несколько id, вызовы собираются подряд через пробел.
Примеры:
- {{#invoke:GetField
|searchId |Elements |prototype/reaction.json |group}} -> ["LemonLime","IrishCoffee","DemonsBlood","SapBoiling","DevilsKiss","MoscowMule","OrangeLimeSoda","RedMead","Vodka","Tortuga","Daiquiri","Andalusia","DoctorsDelight","LongIslandIcedTea","BloodyMary","Lemonade","SpaceGlue","AmmoniaFromBlood"]
| Параметр | Описание | Обязателен? |
|---|---|---|
|1 = | Значение для поиска. | Да |
|2 = | Путь до JSON-страницы. | Да |
|3 = | Путь до поля внутри записи. Поддерживает вложенность через точку и индексы вида field.1 | Да |
|4 = | Имя шаблона, который будет вызван для каждого найденного id. | Только для searchIdTpl |
|searchType = | Режим поиска: value, key или path. | Нет; value |
search
Ищет сущности, у которых есть указанные компоненты или теги. Поддерживается поиск по нескольким значениям через запятую: сущность попадёт в результат только если содержит все перечисленные элементы.
Использование:
Режимы:
component— ищет сущности по списку компонентов.tag— ищет сущности по списку тегов.
Результат:
- возвращается JSON-массив id найденных сущностей;
- в режиме
componentрезультат также JSON-массив, без дополнительного форматирования.
Примеры:
- {{#invoke:GetField
|search |component |Emag}} -> ["AccessBreaker","AccessBreakerUnlimited","Emag","EmagUnlimited","MobRevenant"] - {{#invoke:GetField
|search |tag |Crayon}} -> ["CrayonBlack","CrayonBlue","CrayonBorg","CrayonGreen","CrayonInfinite","CrayonMime","CrayonOrange","CrayonPurple","CrayonRainbow","CrayonRed","CrayonWhite","CrayonYellow"]
| Параметр | Описание | Обязателен? |
|---|---|---|
|1 = | Режим поиска: component или tag. | Да |
|2 = | Список компонентов или тегов через запятую. Все указанные значения должны присутствовать у сущности. | Да |
searchStore / searchStoreTpl
Эти функции находят прототипы или компоненты содержащие указанный id, используя Участник:IanComradeBot/prototype_store.json или Участник:IanComradeBot/component_store.json.
Использование:
- {{#invoke:GetField
|searchStore |searchId |prototype |Название}} - {{#invoke:GetField
|searchStore |searchId |component |Название}} - {{#invoke:GetField
|searchStoreTpl |searchId |prototype |Название |Шаблон}}
searchStore возвращает JSON-массив id, найденных в компонентах/прототипах. searchStoreTpl по тем же id сразу вызывает шаблон, используя страницу вида prototype/Название.json или component/Название.json.
Примеры:
- {{#invoke:GetField
|searchStore |MopItem |component |itemBorgModule}} -> ["BorgModuleCleaning"] - {{#invoke:GetField
|searchStore |MopItem |prototype |latheRecipe}} -> ["MopItem"]
| Параметр | Описание | Обязателен? |
|---|---|---|
|1 = | Id, который ищется в хранилище. | Да |
|2 = | Тип: prototype или component. | Да |
|3 = | Имя хранилищя без .json. | Да |
|4 = | Имя шаблона, который будет вызван для каждого найденного id. | Только для searchStoreTpl |
getAll / getAllTpl
Эти функции получают все id прототипов или компонентов.
Использование:
getAll по умолчанию возвращает JSON-массив id.
getAllTpl вызывает шаблон для каждого id в виде {{Имя шаблона|id=...|...поля записи...}}.
Примеры:
- {{#invoke:GetField
|getAll |component/staticPrice.json}} -> выводит все id сущностей с этим компонентом в формате JSON - {{#invoke:GetField
|getAllTpl |component/staticPrice.json |component/staticPrice/wrapper}} -> выводит все id сущностей с этим компонентом обёрнутым в шаблон {{component/staticPrice/wrapper}}
| Параметр | Описание | Обязателен? |
|---|---|---|
|1 = | Путь до JSON-страницы. | Да |
|2 = | Имя шаблона для getAllTpl. | Только для getAllTpl |
|replace = | Строка замены для getAll. Если задана, результат выводится построчно вместо JSON-массива. | Нет |
hasComp
Проверяет, есть ли у сущности указанный компонент.
Использование:
- {{#invoke:GetField
|hasComp |entityId |componentName}}
Возвращает строку true или false.
Пример:
- {{#invoke:GetField
|hasComp |MopItem |Item}}
| Параметр | Описание | Обязателен? |
|---|---|---|
|1 = | Id сущности. | Да |
|2 = | Имя компонента для проверки. | Да |
getComp
Возвращает список всех компонентов сущности в виде JSON-массива.
Использование:
- {{#invoke:GetField
|getComp |entityId}}
Пример:
- {{#invoke:GetField
|getComp |MopItem}} -> ["Item","Sprite","MeleeWeapon","SolutionContainerManager"...]
| Параметр | Описание | Обязателен? |
|---|---|---|
|1 = | Id сущности. | Да |
jsonList
Преобразует JSON в список, перечисление или простой текст. Подходит для быстрого вывода данных без отдельного шаблона.
Использование:
- {{#invoke:GetField
|jsonList |["a","b","c"]}} - {{#invoke:GetField
|jsonList |{"MopItem":"Швабра"}}} - {{#invoke:GetField
|jsonList |{"MopItem":"Швабра"} |type=enum}} - {{#invoke:GetField
|jsonList |["a","b","c"] |max=1}}
Основные параметры:
type— формат вывода:list,enumилиnone.prefix— префикс строки для режима списка. По умолчанию*.sep— разделитель между ключом и значением. По умолчанию:.replace— дополнительная обработка регулярным выражением уже собранной строки.key_replace,value_replace— обработка ключей и значений регулярным выражением по отдельности.max— максимальное количество элементов, которые нужно обработать. Если не задан, обрабатывается весь список.
Примеры:
- {{#invoke:GetField
|jsonList |["a","b","c"]}} - {{#invoke:GetField
|jsonList |{"MopItem":"Швабра"} |type=list}} - {{#invoke:GetField
|jsonList |{"MopItem":"Швабра"} |type=none |value_replace=<nowiki>[[\1]]</nowiki>}} - {{#invoke:GetField
|jsonList |["a","b","c"] |max=1}}
| Параметр | Описание | Обязателен? |
|---|---|---|
|1 = | JSON-строка. Можно также передать именованным параметром json. | Да |
|type = | Формат вывода: list, enum или none. | Нет; list |
|prefix = | Префикс строки для режима list. | Нет; * |
|sep = | Разделитель между ключом и значением. | Нет; : |
|key_replace = | Строка замены для ключей. | Нет; \1 |
|value_replace = | Строка замены для значений. | Нет; \1 |
|replace = | Строка замены для всего вывода. | Нет; \1 |
|max = | Максимальное количество элементов для обработки. | Нет; весь список |
json
Преобразует JSON-объект или JSON-массив объектов в набор вызовов шаблона и сразу обрабатывает их.
Использование:
- {{#invoke:GetField
|json |{"MopItem":{"name":"Швабра"}} |Предмет}}
Если значение по id является объектом, его поля разворачиваются в параметры шаблона. Если значение простое, оно передаётся как value=....
Пример вызова, который будет собран функцией:
{{Предмет|id=MopItem|name=Швабра}}
| Параметр | Описание | Обязателен? |
|---|---|---|
|1 = | JSON-строка. | Да |
|2 = | Имя шаблона. | Да |
См. также
Примечания
- Если запись, поле или JSON-страница не найдены, функции обычно возвращают пустую строку.
- Функция
getвозвращает таблицы в JSON-виде. - В
searchIdиsearchIdTplзначения сравниваются как строки. - Параметр
keyPathподдерживает доступ к вложенным полям и индексам. getTplиsearchIdTplудобны, когда нужно не получить сырые данные, а сразу отрендерить карточку или другой шаблон.- Функции с
searchStoreработают только с генераторными страницами и хранилищами, где структура данных уже подготовлена под поиск по id. jsonиjsonListожидают корректный JSON; если строка не разбирается, результат будет пустым.
local p = {}
local JsonPaths = require('Module:JsonPaths')
local getArgs = require('Module:Arguments').getArgs
local function load_cached_data(pagePath)
local titleName = JsonPaths.get(pagePath)
if not titleName or titleName == "" then
return nil
end
local ok, data = pcall(mw.loadData, titleName)
if not ok or type(data) ~= "table" then
return nil
end
return data
end
local function parse_path(path)
if not path or path == "" then
return nil
end
local parsed = {}
for part in string.gmatch(path, "([^%.]+)") do
local key, idxStr = string.match(part, "^(.-)%[(%d+)%]$")
if key then
parsed[#parsed + 1] = { key, tonumber(idxStr) }
else
local num = tonumber(part)
if num then
parsed[#parsed + 1] = { nil, num }
else
parsed[#parsed + 1] = { part, nil }
end
end
end
return parsed
end
local function navigate_path(tbl, path)
if not tbl then return nil end
if not path or path == "" then return tbl end
local cur = tbl
local start = 1
local len = #path
while start <= len do
local dotPos = string.find(path, ".", start, true)
local part
if dotPos then
part = string.sub(path, start, dotPos - 1)
start = dotPos + 1
else
part = string.sub(path, start)
start = len + 1
end
if type(cur) ~= "table" then return nil end
local bracketPos = string.find(part, "[", 1, true)
if bracketPos then
local key = string.sub(part, 1, bracketPos - 1)
local idx = tonumber(string.sub(part, bracketPos + 1, #part - 1))
if key ~= "" then
local val = cur[key]
if val == nil then val = cur["!type:" .. key] end
cur = val
end
if idx then
if type(cur) ~= "table" then return nil end
cur = cur[idx]
end
else
local num = tonumber(part)
if num then
cur = cur[num]
else
local val = cur[part]
if val == nil then val = cur["!type:" .. part] end
cur = val
end
end
if cur == nil then return nil end
end
return cur
end
local function format_value(v)
if v == nil then
return ""
end
local t = type(v)
if t == "string" then
return v
elseif t == "number" or t == "boolean" then
return tostring(v)
elseif t == "table" then
local ok, json = pcall(mw.text.jsonEncode, v)
if ok and json then
return json
end
return ""
else
return tostring(v)
end
end
local function is_array(tbl)
local n = #tbl
if n == 0 then
return false
end
if tbl[n] == nil then
return false
end
local count = 0
for k in pairs(tbl) do
if type(k) ~= "number" then
return false
end
count = count + 1
end
return count == n
end
local function is_array_of_primitives(tbl)
if type(tbl) ~= "table" then return false end
local n = #tbl
if n == 0 or tbl[n] == nil then return false end
local count = 0
for k, v in pairs(tbl) do
if type(k) ~= "number" then return false end
if type(v) == "table" then return false end
count = count + 1
end
return count == n
end
local function deep_copy(src)
local dst = {}
for k, v in pairs(src) do
if type(v) == "table" then
dst[k] = deep_copy(v)
else
dst[k] = v
end
end
return dst
end
local function merge_copy(base, specific)
local result = {}
for k, v in pairs(base) do
local specificV = specific[k]
if specificV ~= nil then
if type(v) == "table" and type(specificV) == "table" then
result[k] = merge_copy(v, specificV)
elseif type(specificV) == "table" then
result[k] = deep_copy(specificV)
else
result[k] = specificV
end
elseif type(v) == "table" then
result[k] = deep_copy(v)
else
result[k] = v
end
end
for k, v in pairs(specific) do
if result[k] == nil then
if type(v) == "table" then
result[k] = deep_copy(v)
else
result[k] = v
end
end
end
return result
end
local function resolve_entry_from_data(data, id)
if type(data) ~= "table" then
return nil
end
if id and id ~= "" then
local direct = data[id]
if direct ~= nil then
return direct
end
local idsTable = data.id
if type(idsTable) == "table" then
local specific = idsTable[id]
if type(specific) == "table" then
local base = data["default"]
if type(base) == "table" then
return merge_copy(base, specific)
end
return specific
end
end
end
local base = data["default"]
if type(base) == "table" then
return base
end
return nil
end
local function load_entry(id, pagePath, data)
data = data or load_cached_data(pagePath)
return resolve_entry_from_data(data, id)
end
local function collect_id_keys(data)
if type(data) ~= "table" then
return {}
end
local idsTable = data.id
local ids = {}
if type(idsTable) == "table" then
for k in pairs(idsTable) do
ids[#ids + 1] = k
end
return ids
end
for k in pairs(data) do
if k ~= "default" and k ~= "id" then
ids[#ids + 1] = k
end
end
return ids
end
local function contains_target(v, target)
if type(v) == "string" then
return v == target
end
if type(v) == "table" then
for _, item in pairs(v) do
if tostring(item) == target then
return true
end
end
return false
end
return tostring(v) == target
end
local function is_nonempty_value(v)
if v == nil then
return false
end
if type(v) == "table" then
return next(v) ~= nil
end
return true
end
local function preprocess_or_return(frame, text)
if type(frame) == "table" and type(frame.preprocess) == "function" then
return frame:preprocess(text)
end
return text
end
local function get_field_loose(entry, fieldId)
local value = entry[fieldId]
if value ~= nil then
return value
end
if fieldId == "" then
return nil
end
local first = string.sub(fieldId, 1, 1)
local tail = string.sub(fieldId, 2)
value = entry[string.lower(first) .. tail]
if value ~= nil then
return value
end
return entry[string.upper(first) .. tail]
end
local function apply_pattern(s, pattern, repl)
if not pattern or pattern == "" or not s then
return s
end
local text = tostring(s)
local replacement
if repl and repl ~= "" then
replacement = tostring(repl)
replacement = replacement:gsub("\\(%d)", "%%%1")
else
replacement = "%1"
end
local patt = pattern
if not patt:find("%^") and not patt:find("%$") then
patt = "^" .. patt .. "$"
end
return (text:gsub(patt, replacement))
end
local function flatten_parts(entry)
if type(entry) ~= "table" then
return {}
end
local parts = {}
local function walk(tbl, prefix)
local entries = {}
for k in pairs(tbl) do
entries[#entries + 1] = { k, tostring(k) }
end
table.sort(entries, function(a, b) return a[2] < b[2] end)
for _, e in ipairs(entries) do
local k = e[1]
local kStr = e[2]
local v = tbl[k]
local key = (prefix == "" and kStr or prefix .. "." .. kStr)
if type(v) == "table" then
if next(v) ~= nil then
local ok, json = pcall(mw.text.jsonEncode, v)
if ok and json then
parts[#parts + 1] = key .. "=<nowiki>" .. json .. "</nowiki>"
end
if is_array(v) then
local first = v[1]
if type(first) == "table" then
walk(first, key)
end
else
walk(v, key)
end
end
else
parts[#parts + 1] = key .. "=" .. tostring(v)
end
end
end
walk(entry, "")
return parts
end
local function flatten_entry(entry)
local parts = flatten_parts(entry)
if #parts == 0 then
return ""
end
return table.concat(parts, "|")
end
local function append_flattened_part(parts, key, value)
if value == nil then
return
end
if type(value) == "table" then
if next(value) == nil then
return
end
local ok, json = pcall(mw.text.jsonEncode, value)
if ok and json then
parts[#parts + 1] = key .. "=<nowiki>" .. json .. "</nowiki>"
end
return
end
parts[#parts + 1] = key .. "=" .. tostring(value)
end
local function flatten_selected_parts(entry, keys)
if type(entry) ~= "table" or type(keys) ~= "table" then
return {}
end
local parts = {}
local seen = {}
for i = 1, #keys do
local key = keys[i]
if type(key) == "string" and key ~= "" and not seen[key] then
seen[key] = true
append_flattened_part(parts, key, navigate_path(entry, key))
end
end
return parts
end
local function resolve_template_path(tplPath)
local templatePath = tplPath
local project = JsonPaths.project()
if project ~= nil and project ~= "" then
templatePath = tplPath .. "/" .. project
templatePath = "{{#ifexist:Шаблон:" .. templatePath .. "|" .. templatePath .. "|" .. tplPath .. "}}"
end
return templatePath
end
local function split_template_spec(tplPath, tplArgs)
tplPath = mw.text.unstripNoWiki(tplPath or "")
tplArgs = mw.text.unstripNoWiki(tplArgs or "")
if tplArgs ~= "" and string.sub(tplArgs, 1, 1) == "|" then
tplArgs = string.sub(tplArgs, 2)
end
if tplArgs == "" then
local pipePos = string.find(tplPath, "|", 1, true)
if pipePos then
tplArgs = mw.text.unstripNoWiki(string.sub(tplPath, pipePos + 1))
if tplArgs ~= "" and string.sub(tplArgs, 1, 1) == "|" then
tplArgs = string.sub(tplArgs, 2)
end
tplPath = string.sub(tplPath, 1, pipePos - 1)
end
end
return tplPath, tplArgs
end
local function build_tpl(id, pagePath, tplPath, data, tplArgs, preEntry)
if id == "" or pagePath == "" or tplPath == "" then
return ""
end
local entry = preEntry or load_entry(id, pagePath, data)
if entry == nil then
return ""
end
local extra = flatten_entry(entry)
local extraTplArgs = tplArgs or ""
local templatePath = resolve_template_path(tplPath)
local tplParts = { "{{Шаблон:" .. templatePath }
if extraTplArgs ~= "" then
tplParts[#tplParts + 1] = extraTplArgs
end
tplParts[#tplParts + 1] = "id=" .. tostring(id)
if extra ~= "" then
tplParts[#tplParts + 1] = extra
end
tplParts[#tplParts + 1] = "}}"
return table.concat(tplParts, "|")
end
function p.findInGenerator(frame)
local args = frame.args or {}
local searchId = args[1] or ""
local kind = (args[2] or ""):lower()
local fieldId = args[3] or ""
if searchId == "" or fieldId == "" then
return ""
end
if kind ~= "prototype" and kind ~= "component" then
return ""
end
local storeName = (kind == "prototype") and "prototype_store.json" or "component_store.json"
local data = load_cached_data(storeName)
if not data then
return ""
end
local entry = data[searchId]
if type(entry) ~= "table" then
return ""
end
local value = get_field_loose(entry, fieldId)
if value == nil then
return ""
end
local out = {}
local t = type(value)
if t == "table" then
for _, v in ipairs(value) do
out[#out + 1] = v
end
else
out[1] = value
end
return mw.text.jsonEncode(out)
end
function p.flattenField(frame)
local args = frame.args or {}
local id = args[1] or ""
local pagePath = args[2] or ""
if id == "" or pagePath == "" then
return ""
end
local entry = load_entry(id, pagePath) or {}
return flatten_entry(entry)
end
function p.flattenFieldSelective(frame)
local args = frame.args or {}
local id = args[1] or ""
local pagePath = args[2] or ""
local keysJson = mw.text.unstripNoWiki(args[3] or args.keys or "")
if id == "" or pagePath == "" or keysJson == "" then
return ""
end
local okKeys, keys = pcall(mw.text.jsonDecode, keysJson)
if not okKeys or type(keys) ~= "table" or #keys == 0 then
return ""
end
local entry = load_entry(id, pagePath) or {}
local parts = flatten_selected_parts(entry, keys)
if #parts == 0 then
return ""
end
return table.concat(parts, "|")
end
function p.get(frame)
local args = getArgs(frame, { removeBlanks = false })
local id = args[1] or ""
local pagePath = args[2] or ""
local keyPath = args[3] or ""
if pagePath == "" then
return ""
end
local entry = load_entry(id, pagePath)
if entry == nil then
return ""
end
if keyPath == "" then
return format_value(entry)
end
return format_value(navigate_path(entry, keyPath))
end
local function collect_by_parsed_path(tbl, parsedPath, pos, out)
if pos > #parsedPath then
out[#out + 1] = tbl
return
end
if type(tbl) ~= "table" then
return
end
local token = parsedPath[pos]
local key = token[1]
local idx = token[2]
if key == "*" then
for _, child in pairs(tbl) do
local nextCur = child
if idx then
if type(nextCur) ~= "table" then
nextCur = nil
else
nextCur = nextCur[idx]
end
end
collect_by_parsed_path(nextCur, parsedPath, pos + 1, out)
end
return
end
local nextCur
if key and key ~= "" then
nextCur = tbl[key]
if nextCur == nil then nextCur = tbl["!type:" .. key] end
else
nextCur = tbl
end
if idx then
if type(nextCur) ~= "table" then
return
end
nextCur = nextCur[idx]
end
collect_by_parsed_path(nextCur, parsedPath, pos + 1, out)
end
local function get_by_parsed_path_multi(tbl, parsedPath)
local out = {}
collect_by_parsed_path(tbl, parsedPath, 1, out)
return out
end
local function entry_matches_path(entry, parsedPath, searchValue, searchType)
local values = get_by_parsed_path_multi(entry, parsedPath)
local target = tostring(searchValue)
for _, v in ipairs(values) do
if searchType == "key" then
if type(v) == "table" and v[target] ~= nil then
return true
end
else
if contains_target(v, target) then
return true
end
end
end
return false
end
local function entry_has_any_nonempty_path(entry, parsedPath)
local values = get_by_parsed_path_multi(entry, parsedPath)
for _, v in ipairs(values) do
if is_nonempty_value(v) then
return true
end
end
return false
end
local function split_csv_list(text)
text = mw.text.unstripNoWiki(text or "")
local out = {}
for part in string.gmatch(text, "([^,]+)") do
part = mw.text.trim(part)
if part ~= "" then
out[#out + 1] = part
end
end
return out
end
local function add_scalar_values(value, set)
if type(value) == "table" then
for _, item in pairs(value) do
add_scalar_values(item, set)
end
elseif value ~= nil then
set[tostring(value)] = true
end
end
local function table_has_all_values(value, targets)
if type(targets) ~= "table" or #targets == 0 then
return false
end
if type(value) ~= "table" then
return #targets == 1 and tostring(value) == targets[1]
end
local set = {}
add_scalar_values(value, set)
for i = 1, #targets do
if not set[targets[i]] then
return false
end
end
return true
end
function p.searchId(frame)
local args = getArgs(frame, { removeBlanks = false })
local searchValue = args[1] or ""
local pagePath = args[2] or ""
local keyPath = args[3] or ""
local searchType = (args.searchType or ""):lower()
if searchValue == "" or pagePath == "" or keyPath == "" then
return ""
end
if searchType == "" then
searchType = "value"
end
local data = load_cached_data(pagePath)
if not data then
return "[]"
end
local parsedPath = parse_path(keyPath)
if not parsedPath then
return ""
end
local ids = collect_id_keys(data)
if #ids == 0 then
return ""
end
local matches = {}
local target = tostring(searchValue)
for _, idKey in ipairs(ids) do
local entry = resolve_entry_from_data(data, idKey)
if type(entry) == "table" and entry_matches_path(entry, parsedPath, target, searchType) then
matches[#matches + 1] = idKey
end
end
if #matches == 0 then
return ""
end
local ok, json = pcall(mw.text.jsonEncode, matches)
if ok and json then
return json
end
return ""
end
local function entry_has_all_targets_component(entry, wanted)
if type(entry) ~= "table" then
return false
end
local set = {}
for _, v in pairs(entry) do
set[tostring(v)] = true
end
for i = 1, #wanted do
if not set[wanted[i]] then
return false
end
end
return true
end
local function entry_has_all_targets_tag(entry, wanted)
if type(entry) ~= "table" then
return false
end
local tags = entry.tags or entry.tag
if type(tags) ~= "table" then
return false
end
local set = {}
for _, v in pairs(tags) do
set[tostring(v)] = true
end
for i = 1, #wanted do
if not set[wanted[i]] then
return false
end
end
return true
end
function p.search(frame)
local args = getArgs(frame, { removeBlanks = false })
local mode = (args[1] or ""):lower()
local rawList = args[2] or ""
if mode ~= "component" and mode ~= "tag" then
return "[]"
end
local targets = split_csv_list(rawList)
if #targets == 0 then
return "[]"
end
local pagePath
if mode == "component" then
pagePath = "component.json"
else
pagePath = "component/tag.json"
end
local data = load_cached_data(pagePath)
if not data or type(data) ~= "table" then
return "[]"
end
local ids = collect_id_keys(data)
if #ids == 0 then
return "[]"
end
local sortedIds = {}
for i = 1, #ids do
sortedIds[i] = { ids[i], tostring(ids[i]) }
end
table.sort(sortedIds, function(a, b) return a[2] < b[2] end)
local matches = {}
for _, e in ipairs(sortedIds) do
local idKey = e[1]
local entry = resolve_entry_from_data(data, idKey)
if mode == "component" then
if entry_has_all_targets_component(entry, targets) then
matches[#matches + 1] = idKey
end
else
if entry_has_all_targets_tag(entry, targets) then
matches[#matches + 1] = idKey
end
end
end
local ok, json = pcall(mw.text.jsonEncode, matches)
if ok and json then
return json
end
return "[]"
end
function p.searchIdTpl(frame)
local args = getArgs(frame, { removeBlanks = false })
local searchValue = args[1] or ""
local pagePath = args[2] or ""
local keyPath = args[3] or ""
local tplPath = mw.text.unstripNoWiki(args[4] or "")
local tplArgs = args.tplArgs or args.templateArgs or ""
local searchType = (args.searchType or ""):lower()
if searchType == "" then
searchType = "value"
end
if searchType == "path" then
searchValue = ""
pagePath = args[1] or ""
keyPath = args[2] or ""
tplPath = mw.text.unstripNoWiki(args[3] or "")
tplArgs = args[4] or args.tplArgs or args.templateArgs or ""
end
tplPath, tplArgs = split_template_spec(tplPath, tplArgs)
if pagePath == "" or keyPath == "" or tplPath == "" then
return ""
end
if searchType ~= "path" and searchValue == "" then
return ""
end
local data = load_cached_data(pagePath)
if not data then
return ""
end
local parsedPath = parse_path(keyPath)
if not parsedPath then
return ""
end
local ids = collect_id_keys(data)
if #ids == 0 then
return ""
end
local matches = {}
local entryCache = {}
if searchType == "path" then
for _, idKey in ipairs(ids) do
local entry = resolve_entry_from_data(data, idKey)
entryCache[idKey] = entry
if type(entry) == "table" and entry_has_any_nonempty_path(entry, parsedPath) then
matches[#matches + 1] = idKey
end
end
else
local target = tostring(searchValue)
for _, idKey in ipairs(ids) do
local entry = resolve_entry_from_data(data, idKey)
entryCache[idKey] = entry
if type(entry) == "table" and entry_matches_path(entry, parsedPath, target, searchType) then
matches[#matches + 1] = idKey
end
end
end
if #matches == 0 then
return ""
end
local out = {}
for _, idKey in ipairs(matches) do
local tpl = build_tpl(idKey, pagePath, tplPath, data, tplArgs, entryCache[idKey])
if tpl ~= "" then
out[#out + 1] = tpl
end
end
if #out == 0 then
return ""
end
local result = table.concat(out, " ")
return preprocess_or_return(frame, result)
end
function p.getTpl(frame)
local args = getArgs(frame, { removeBlanks = false })
local id = args[1] or ""
local pagePath = args[2] or ""
local tplPath = mw.text.unstripNoWiki(args[3] or "")
local tplArgs = args[4] or args.tplArgs or args.templateArgs or ""
tplPath, tplArgs = split_template_spec(tplPath, tplArgs)
if id == "" or pagePath == "" or tplPath == "" then
return ""
end
local data = frame.data
local tplStr = build_tpl(id, pagePath, tplPath, data, tplArgs)
return preprocess_or_return(frame, tplStr)
end
function p.searchStoreTpl(frame)
local args = getArgs(frame, { removeBlanks = false })
local searchId = args[1] or ""
local kind = (args[2] or ""):lower()
local generatorId = args[3] or ""
local tplPath = mw.text.unstripNoWiki(args[4] or "")
local tplArgs = args[5] or args.tplArgs or args.templateArgs or ""
tplPath, tplArgs = split_template_spec(tplPath, tplArgs)
if searchId == "" or generatorId == "" or tplPath == "" then
return ""
end
if kind ~= "prototype" and kind ~= "component" then
return ""
end
local dir = (kind == "prototype") and "prototype/" or "component/"
local pagePath = dir .. generatorId .. ".json"
local idsJson = p.findInGenerator({ args = { searchId, kind, generatorId } })
local ok, ids = pcall(mw.text.jsonDecode, idsJson or "")
if not ok or type(ids) ~= "table" or #ids == 0 then
return ""
end
local data = load_cached_data(pagePath)
if not data then
return ""
end
local out = {}
for _, id in ipairs(ids) do
local entry = resolve_entry_from_data(data, id)
if entry ~= nil then
local tpl = build_tpl(id, pagePath, tplPath, data, tplArgs, entry)
if tpl ~= "" then
out[#out + 1] = tpl
end
end
end
local result = table.concat(out, " ")
return preprocess_or_return(frame, result)
end
function p.flattenParams(entry)
return flatten_parts(entry)
end
function p.searchStore(frame)
local args = getArgs(frame, { removeBlanks = false })
local searchId = args[1] or ""
local kind = (args[2] or ""):lower()
local generatorId = args[3] or ""
if searchId == "" or generatorId == "" then
return ""
end
if kind ~= "prototype" and kind ~= "component" then
return ""
end
local idsJson = p.findInGenerator({ args = { searchId, kind, generatorId } })
local ok, ids = pcall(mw.text.jsonDecode, idsJson or "")
if not ok or type(ids) ~= "table" or #ids == 0 then
return ""
end
local okOut, outJson = pcall(mw.text.jsonEncode, ids)
if okOut and outJson then
return outJson
end
return ""
end
function p.hasComp(frame)
local args = getArgs(frame, { removeBlanks = false })
local entityId = args[1] or ""
local compName = args[2] or ""
if entityId == "" or compName == "" then
return "false"
end
local data = load_cached_data("component.json")
if not data then
return "false"
end
if type(data) ~= "table" then
return "false"
end
local entry = data[entityId]
if type(entry) ~= "table" then
return "false"
end
for _, v in ipairs(entry) do
if tostring(v) == compName then
return "true"
end
end
return "false"
end
function p.getComp(frame)
local args = getArgs(frame, { removeBlanks = false })
local entityId = args[1] or ""
if entityId == "" then
return "[]"
end
local data = load_cached_data("component.json")
if not data or type(data) ~= "table" then
return "[]"
end
local entry = data[entityId]
if type(entry) ~= "table" then
return "[]"
end
local out = {}
for _, v in ipairs(entry) do
if v ~= nil and v ~= "" then
out[#out + 1] = v
end
end
local ok, json = pcall(mw.text.jsonEncode, out)
if ok and json then
return json
end
return "[]"
end
function p.getAll(frame)
local args = getArgs(frame, { removeBlanks = false })
local pagePath = args[1] or ""
local replace = mw.text.unstripNoWiki(args.replace or "")
local pattern = mw.text.unstripNoWiki(args.pattern or "(.*)")
if pagePath == "" then
return ""
end
local data = load_cached_data(pagePath)
if not data then
return ""
end
local idsTable = data.id or data
if type(idsTable) ~= "table" then
return ""
end
local ids = {}
for k in pairs(idsTable) do
ids[#ids + 1] = k
end
table.sort(ids)
if replace ~= "" then
local out = {}
for _, id in ipairs(ids) do
local text = apply_pattern(id, pattern, replace)
if text ~= "" then
out[#out + 1] = text
end
end
if #out == 0 then
return ""
end
return preprocess_or_return(frame, table.concat(out, "\n"))
end
local ok, json = pcall(mw.text.jsonEncode, ids)
if ok and json then
return json
end
return ""
end
function p.getAllTpl(frame)
local args = getArgs(frame, { removeBlanks = false })
local pagePath = args[1] or ""
local tplPath = args[2] or ""
local tplArgs = args[3] or args.tplArgs or args.templateArgs or ""
tplPath, tplArgs = split_template_spec(tplPath, tplArgs)
if pagePath == "" or tplPath == "" then
return ""
end
local data = load_cached_data(pagePath)
if not data then
return ""
end
local idsTable = data.id or data
if type(idsTable) ~= "table" then
return ""
end
local out = {}
for idKey in pairs(idsTable) do
local entry = resolve_entry_from_data(data, idKey)
if entry ~= nil then
local tpl = build_tpl(idKey, pagePath, tplPath, data, tplArgs, entry)
if tpl ~= "" then
out[#out + 1] = tpl
end
end
end
table.sort(out)
local result = table.concat(out, " ")
return preprocess_or_return(frame, result)
end
local function encode_nowiki_json(value)
local ok, json = pcall(mw.text.jsonEncode, value)
if ok and json then
return "<nowiki>" .. json .. "</nowiki>"
end
return nil
end
local function collect_sorted_entries(tbl, stringOnly)
if stringOnly then
local keys = {}
for k in pairs(tbl) do
if type(k) == "string" then
keys[#keys + 1] = k
end
end
table.sort(keys)
local entries = {}
for i = 1, #keys do
entries[i] = { keys[i], keys[i] }
end
return entries
end
local entries = {}
for k in pairs(tbl) do
entries[#entries + 1] = { k, tostring(k) }
end
table.sort(entries, function(a, b) return a[2] < b[2] end)
return entries
end
local function collect_sorted_keys(tbl, stringOnly)
local entries = collect_sorted_entries(tbl, stringOnly)
local keys = {}
for i = 1, #entries do
keys[i] = entries[i][1]
end
return keys
end
local function choose_id_key(obj)
local entries = {}
for k in pairs(obj) do
if type(k) == "string" then
entries[#entries + 1] = { k, type(obj[k]) ~= "table" }
end
end
if #entries == 0 then
return nil
end
table.sort(entries, function(a, b)
if a[2] ~= b[2] then
return not a[2]
end
return a[1] < b[1]
end)
return entries[1][1]
end
local function is_wrapper_block_key(key)
return type(key) == "string" and not key:match("^[%a_][%w_]*$")
end
local function append_table_fields(parts, value, options, prefix)
if type(value) ~= "table" or next(value) == nil then
return
end
if options.skipPrimitiveRoot and is_array_of_primitives(value) then
return
end
if prefix and options.includeJsonAtPrefix then
local json = encode_nowiki_json(value)
if json then
parts[#parts + 1] = prefix .. "=" .. json
end
end
local entries = collect_sorted_entries(value, false)
for _, e in ipairs(entries) do
local k = e[1]
local kStr = e[2]
if not (options.nestedKeyMode == "raw" and type(k) == "number") then
local v = value[k]
local key
if prefix then
key = prefix .. "." .. kStr
else
key = kStr
end
if type(v) == "table" then
if is_array_of_primitives(v) then
local json = encode_nowiki_json(v)
if json then
parts[#parts + 1] = key .. "=" .. json
end
elseif options.nestedKeyMode == "raw" then
local json = encode_nowiki_json(v)
if json then
parts[#parts + 1] = key .. "=" .. json
end
end
if next(v) ~= nil and not is_array_of_primitives(v) then
local childPrefix
if options.nestedKeyMode == "prefixed" then
childPrefix = key
elseif type(k) == "string" then
childPrefix = key
else
childPrefix = nil
end
append_table_fields(parts, v, options, childPrefix)
end
else
parts[#parts + 1] = key .. "=" .. tostring(v)
end
end
end
end
local function is_object_map(tbl)
local count = 0
for k, v in pairs(tbl) do
if type(k) ~= "string" or type(v) ~= "table" then
return false
end
count = count + 1
end
return count > 1
end
function p.json(frame)
local args = getArgs(frame, { removeBlanks = false })
local jsonStr = mw.text.unstripNoWiki(args[1] or args.json or "")
local tplPath = mw.text.unstripNoWiki(args[2] or args.template or "")
local tplArgs = args[3] or args.tplArgs or args.templateArgs or ""
tplPath, tplArgs = split_template_spec(tplPath, tplArgs)
if jsonStr == "" or tplPath == "" then
return ""
end
local ok, data = pcall(mw.text.jsonDecode, jsonStr)
if not ok or type(data) ~= "table" then
return ""
end
local calls = {}
local nestedOptions = {
includeJsonAtPrefix = true,
nestedKeyMode = "prefixed",
skipPrimitiveRoot = false,
}
local rawTypeOptions = {
includeJsonAtPrefix = false,
nestedKeyMode = "raw",
skipPrimitiveRoot = true,
}
local firstParamOverride = mw.text.unstripNoWiki(args.first or args.firstParam or args.idKey or "")
local function makeCall(obj)
if type(obj) ~= "table" then
return
end
local idKey = firstParamOverride
if idKey == "" then
idKey = choose_id_key(obj)
end
if not idKey then
return
end
local parts = { "{{Шаблон:" .. resolve_template_path(tplPath) }
if tplArgs ~= "" then
parts[#parts + 1] = tplArgs
end
parts[#parts + 1] = idKey
local entries = collect_sorted_entries(obj, true)
for _, e in ipairs(entries) do
local k = e[1]
local v = obj[k]
if k == idKey then
if is_wrapper_block_key(k) then
if type(v) == "table" then
local json = encode_nowiki_json(v)
if json then
parts[#parts + 1] = "value=" .. json
end
append_table_fields(parts, v, rawTypeOptions, nil)
elseif v ~= nil then
parts[#parts + 1] = "value=" .. tostring(v)
end
elseif type(v) == "table" then
if is_array_of_primitives(v) then
local json = encode_nowiki_json(v)
if json then
parts[#parts + 1] = "value=" .. json
end
end
if next(v) ~= nil then
append_table_fields(parts, v, nestedOptions, k)
append_table_fields(parts, v, rawTypeOptions, nil)
end
elseif v ~= nil then
parts[#parts + 1] = "value=" .. tostring(v)
parts[#parts + 1] = k .. "=" .. tostring(v)
end
else
if type(v) == "table" then
if next(v) ~= nil then
append_table_fields(parts, v, nestedOptions, k)
end
elseif v ~= nil then
parts[#parts + 1] = k .. "=" .. tostring(v)
end
end
end
parts[#parts + 1] = "}}"
calls[#calls + 1] = table.concat(parts, "|")
end
if is_array(data) then
for _, item in ipairs(data) do
if type(item) == "table" then
makeCall(item)
elseif item ~= nil and item ~= "" then
makeCall({ [item] = {} })
end
end
elseif is_object_map(data) then
local dataEntries = collect_sorted_entries(data, true)
for _, e in ipairs(dataEntries) do
makeCall({ [e[1]] = data[e[1]] })
end
else
makeCall(data)
end
if #calls == 0 then
return ""
end
return frame:preprocess(table.concat(calls, " "))
end
function p.jsonList(frame)
local args = getArgs(frame, { removeBlanks = false })
local jsonStr = mw.text.unstripNoWiki(args[1] or args.json or "")
if jsonStr == "" then
return ""
end
local ok, data = pcall(mw.text.jsonDecode, jsonStr)
if not ok or type(data) ~= "table" then
return ""
end
local outputType = (args.type or "list")
local bullet = mw.text.unstripNoWiki(args.prefix or "* ")
local sep = mw.text.unstripNoWiki(args.sep or ": ")
if outputType == "none" then
bullet = ""
sep = ""
elseif outputType == "revertList" then
sep = mw.text.unstripNoWiki(args.sep or " ")
end
local keyPattern = mw.text.unstripNoWiki(args.key_pattern or "(.*)")
local keyReplace = mw.text.unstripNoWiki(args.key_replace or "\\1")
local valuePattern = mw.text.unstripNoWiki(args.value_pattern or "(.*)")
local valueReplace = mw.text.unstripNoWiki(args.value_replace or "\\1")
local pairPattern = mw.text.unstripNoWiki(args.pattern or "(.*)")
local pairReplace = mw.text.unstripNoWiki(args.replace or "\\1")
local maxItems = tonumber(args.max or args.limit or args.max_count or args.maxCount or "")
if maxItems ~= nil then
maxItems = math.floor(maxItems)
if maxItems < 0 then
maxItems = nil
end
end
local out = {}
if is_array(data) then
local processed = 0
for _, v in ipairs(data) do
processed = processed + 1
if maxItems ~= nil and processed > maxItems then
break
end
local text = ""
if type(v) == "table" then
if is_array(v) then
text = table.concat(v, ", ")
else
local okJson, jsonVal = pcall(mw.text.jsonEncode, v)
if okJson and jsonVal then
text = jsonVal
end
end
else
text = tostring(v)
end
if text ~= "" then
local patt = valuePattern ~= "" and valuePattern or keyPattern
local repl = valueReplace ~= "" and valueReplace or keyReplace
text = apply_pattern(text, patt, repl)
local line
if outputType == "enum" then
line = text
else
line = bullet .. text
end
if pairPattern ~= "" then
line = apply_pattern(line, pairPattern, pairReplace)
end
out[#out + 1] = line
end
end
else
local dataEntries = {}
for k in pairs(data) do
dataEntries[#dataEntries + 1] = { k, tostring(k) }
end
table.sort(dataEntries, function(a, b) return a[2] < b[2] end)
local processed = 0
for _, de in ipairs(dataEntries) do
processed = processed + 1
if maxItems ~= nil and processed > maxItems then
break
end
local k = de[1]
local kStr = de[2]
local v = data[k]
local vStr
if type(v) == "table" then
local okJson, jsonVal = pcall(mw.text.jsonEncode, v)
if okJson and jsonVal then
vStr = jsonVal
else
vStr = ""
end
else
vStr = tostring(v)
end
local baseKey = apply_pattern(kStr, keyPattern, "\\1")
local MARK_KEY = "\31KEY\31"
local vRepl = (valueReplace or "\\1"):gsub("\\2", MARK_KEY)
local vStr0 = apply_pattern(vStr, valuePattern, vRepl)
vStr0 = tostring(vStr0):gsub(MARK_KEY, baseKey)
local MARK_VAL = "\31VAL\31"
local kRepl = (keyReplace or "\\1"):gsub("\\2", MARK_VAL)
local keyStr0 = apply_pattern(kStr, keyPattern, kRepl)
local keyStr = tostring(keyStr0):gsub(MARK_VAL, vStr0)
vStr = vStr0
if vStr ~= "" then
local line
if outputType == "enum" then
line = vStr .. " " .. keyStr
elseif outputType == "revertList" then
line = bullet .. vStr .. sep .. keyStr
else
line = bullet .. keyStr .. sep .. vStr
end
if pairPattern ~= "" then
line = apply_pattern(line, pairPattern, pairReplace)
end
out[#out + 1] = line
end
end
end
if outputType == "enum" then
return frame:preprocess(table.concat(out, ", "))
elseif outputType == "list" or outputType == "revertList" then
return frame:preprocess(table.concat(out, "\n"))
else
return frame:preprocess(table.concat(out, " "))
end
end
function p.flattenFieldSelectiveDirect(id, dataPage, paramNames)
if id == "" or dataPage == "" or type(paramNames) ~= "table" or #paramNames == 0 then
return ""
end
local entry = load_entry(id, dataPage) or {}
local parts = flatten_selected_parts(entry, paramNames)
if #parts == 0 then
return ""
end
return table.concat(parts, "|")
end
return p