Модуль:GetField: различия между версиями
Pok (обсуждение | вклад) Нет описания правки Метка: отменено |
Pok (обсуждение | вклад) мНет описания правки |
||
| (не показано 8 промежуточных версий этого же участника) | |||
| Строка 4: | Строка 4: | ||
local getArgs = require('Module:Arguments').getArgs | local getArgs = require('Module:Arguments').getArgs | ||
function p.loadCachedData(pagePath) | |||
local titleName = JsonPaths.get(pagePath) | local titleName = JsonPaths.get(pagePath) | ||
if not titleName or titleName == "" then | if not titleName or titleName == "" then | ||
| Строка 100: | Строка 100: | ||
local t = type(v) | local t = type(v) | ||
if t == "string" | if t == "string" or t == "number" or t == "boolean" then | ||
return tostring(v) | return tostring(v) | ||
elseif t == "table" then | elseif t == "table" then | ||
| Строка 116: | Строка 114: | ||
local function is_array(tbl) | local function is_array(tbl) | ||
local | local max = 0 | ||
local count = 0 | local count = 0 | ||
for k in pairs(tbl) do | for k in pairs(tbl) do | ||
if type(k) ~= "number" then | if type(k) ~= "number" then | ||
return false | return false | ||
end | |||
if k > max then | |||
max = k | |||
end | end | ||
count = count + 1 | count = count + 1 | ||
end | end | ||
return count | return count > 0 and max == count | ||
end | end | ||
| Строка 158: | Строка 140: | ||
end | end | ||
local function | local function deep_merge(dst, src) | ||
for k, v in pairs(src) do | |||
for k, v in pairs( | if type(v) == "table" and type(dst[k]) == "table" then | ||
deep_merge(dst[k], v) | |||
elseif type(v) == "table" then | elseif type(v) == "table" then | ||
dst[k] = deep_copy(v) | |||
else | else | ||
dst[k] = v | |||
end | end | ||
end | end | ||
end | end | ||
function p.ucfirst(s) | |||
if type( | if not s or s == "" then return s end | ||
return string.upper(s:sub(1, 1)) .. (s:sub(2) or "") | |||
end | |||
function p.normalizeComponentName(name) | |||
if type(name) ~= "string" or name == "" then | |||
return nil | return nil | ||
end | end | ||
name = mw.text.trim(name) | |||
if name:sub(1, 5) == "type:" then | |||
name = name:sub(6) | |||
elseif name:sub(1, 6) == "!type:" then | |||
name = name:sub(7) | |||
end | |||
if name == "" then | |||
return nil | |||
end | |||
return name | |||
end | |||
local function split_data_page_path(pagePath) | |||
local kind, name = string.match(pagePath or "", "^(component)/([^/]+)%.json$") | |||
if not kind then | |||
return nil, nil | |||
end | end | ||
return kind, p.ucfirst(name) | |||
end | |||
local function get_component_from_entity(entity, componentName) | |||
if type( | if type(entity) ~= "table" or type(entity.components) ~= "table" then | ||
return | return nil | ||
end | end | ||
return | return entity.components["type:" .. componentName] | ||
or entity.components[componentName] | |||
or entity.components["!type:" .. componentName] | |||
end | end | ||
local function | local function load_specific_entry(pagePath, id, allowDefault) | ||
local kind, name = split_data_page_path(pagePath) | |||
return | if not kind or not id or id == "" then | ||
end | return nil | ||
end | |||
if kind ~= "component" then | |||
return nil | |||
end | |||
local | local base | ||
if type( | local defaultData = p.loadCachedData(kind .. "/" .. name .. "/defaultFields.json") | ||
if type(defaultData) == "table" then | |||
base = deep_copy(defaultData) | |||
end | end | ||
local | local entityPage = "prototype/Entity/" .. id .. ".json" | ||
local | local entity = p.loadCachedData(entityPage) | ||
local specific = get_component_from_entity(entity, name) | |||
if type( | if type(specific) ~= "table" then | ||
if allowDefault == false then | |||
return nil | |||
end | end | ||
return | return base | ||
end | end | ||
if base then | |||
deep_merge(base, specific) | |||
return base | |||
end | end | ||
return | return specific | ||
end | end | ||
local function | local function resolve_entry_from_data(data, id, allowDefault) | ||
if type( | if type(data) ~= "table" then | ||
return | return nil | ||
end | end | ||
if type( | |||
if id and id ~= "" then | |||
local direct = data[id] | |||
return | 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 | |||
local merged = deep_copy(base) | |||
deep_merge(merged, specific) | |||
return merged | |||
end | |||
return specific | |||
end | end | ||
end | end | ||
end | end | ||
if allowDefault == false then | |||
end | return nil | ||
end | |||
local | local base = data["default"] | ||
if | if type(base) == "table" then | ||
return | return base | ||
end | end | ||
return | return nil | ||
end | |||
local function load_entry(id, pagePath, data, allowDefault) | |||
local specificPageEntry = load_specific_entry(pagePath, id, allowDefault) | |||
if specificPageEntry ~= nil then | |||
return specificPageEntry | |||
end | end | ||
data = data or p.loadCachedData(pagePath) | |||
if | if allowDefault == nil then | ||
allowDefault = true | |||
end | end | ||
return | return resolve_entry_from_data(data, id, allowDefault) | ||
end | end | ||
local function | local function collect_id_keys(data) | ||
if type(data) ~= "table" then | |||
if | return {} | ||
return | |||
end | end | ||
local | local idsTable = data.id | ||
local | 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 | end | ||
return | return ids | ||
end | end | ||
local function | local function contains_target(v, target) | ||
if | if type(v) == "string" then | ||
return | return v == target | ||
end | end | ||
if type(v) == "table" then | |||
if is_array(v) then | |||
for _, item in ipairs(v) do | |||
if tostring(item) == target then | |||
return true | |||
end | |||
end | |||
return false | |||
end | |||
for _, item in pairs(v) do | |||
if tostring(item) == target then | |||
return true | |||
end | |||
end | |||
return false | |||
end | end | ||
return tostring(v) == target | |||
end | end | ||
local function | local function is_nonempty_value(v) | ||
if type( | if v == nil then | ||
return | return false | ||
end | |||
if type(v) == "table" then | |||
return next(v) ~= nil | |||
end | 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 | end | ||
local first = string.sub(fieldId, 1, 1) | |||
local tail = string.sub(fieldId, 2) | |||
value = entry[string.lower(first) .. tail] | |||
if value ~= nil then | |||
if | return value | ||
return | |||
end | end | ||
return | |||
return entry[string.upper(first) .. tail] | |||
end | end | ||
local function | local function apply_pattern(s, pattern, repl) | ||
if | if not pattern or pattern == "" or not s then | ||
return | return s | ||
end | end | ||
local text = tostring(s) | |||
local replacement | |||
if repl and repl ~= "" then | |||
end | 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 | end | ||
return (text:gsub(patt, replacement)) | |||
end | end | ||
local function | local function flatten_parts(entry) | ||
if type(entry | if type(entry) ~= "table" then | ||
return {} | return {} | ||
end | end | ||
local parts = {} | local parts = {} | ||
for | local function walk(tbl, prefix) | ||
local keys = {} | |||
for k in pairs(tbl) do | |||
keys[#keys + 1] = k | |||
end | |||
table.sort(keys, function(a, b) | |||
return tostring(a) < tostring(b) | |||
end) | |||
for _, k in ipairs(keys) do | |||
local v = tbl[k] | |||
local kStr = tostring(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 | ||
end | end | ||
walk(entry, "") | |||
return parts | return parts | ||
end | end | ||
local function | local function flatten_entry(entry) | ||
local | local parts = flatten_parts(entry) | ||
if #parts == 0 then | |||
if | return "" | ||
end | end | ||
return table.concat(parts, "|") | |||
return | |||
end | end | ||
local function | local function append_flattened_part(parts, key, value) | ||
if value == nil then | |||
return | |||
if | |||
end | end | ||
if | 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 | end | ||
return | |||
end | end | ||
parts[#parts + 1] = key .. "=" .. tostring(value) | |||
end | end | ||
local function | local function flatten_selected_parts(entry, keys) | ||
if | if type(entry) ~= "table" or type(keys) ~= "table" then | ||
return | return {} | ||
end | end | ||
local | local parts = {} | ||
local seen = {} | |||
local | 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 | local function resolve_template_path(tplPath) | ||
if | local templatePath = tplPath | ||
local project = JsonPaths.project() | |||
if project ~= nil and project ~= "" then | |||
templatePath = tplPath .. "/" .. project | |||
templatePath = "{{#ifexist:Шаблон:" .. templatePath .. "|" .. templatePath .. "|" .. tplPath .. "}}" | |||
end | end | ||
return templatePath | |||
return | |||
end | end | ||
function | 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 | 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 | end | ||
if | |||
return tplPath, tplArgs | |||
end | |||
local function build_tpl(id, pagePath, tplPath, data, tplArgs, preEntry, strictExact) | |||
if id == "" or pagePath == "" or tplPath == "" then | |||
return "" | return "" | ||
end | end | ||
local | local entry | ||
if strictExact then | |||
entry = load_entry(id, pagePath, data, false) | |||
else | |||
entry = preEntry or load_entry(id, pagePath, data) | |||
end | end | ||
if entry == nil then | |||
return "" | return "" | ||
end | end | ||
local | local extra = flatten_entry(entry) | ||
if | local extraTplArgs = tplArgs or "" | ||
local templatePath = resolve_template_path(tplPath) | |||
local tplStr = "{{Шаблон:" .. templatePath | |||
if extraTplArgs ~= "" then | |||
tplStr = tplStr .. "|" .. extraTplArgs | |||
end | end | ||
tplStr = tplStr .. "|id=" .. tostring(id) | |||
if extra ~= "" then | |||
tplStr = tplStr .. "|" .. extra | |||
if | |||
end | end | ||
tplStr = tplStr .. "}}" | |||
return | return tplStr | ||
end | end | ||
function p. | function p.findInGenerator(frame) | ||
local args = frame.args or {} | local args = frame.args or {} | ||
local | local searchId = args[1] or "" | ||
local | local kind = (args[2] or ""):lower() | ||
if | local fieldId = p.ucfirst(args[3] or "") | ||
if searchId == "" or fieldId == "" then | |||
return "" | return "" | ||
end | end | ||
if kind ~= "prototype" and kind ~= "component" then | |||
if | |||
return "" | return "" | ||
end | end | ||
local | local storeName = (kind == "prototype") and "prototype_store.json" or "component_store.json" | ||
if not | local data = p.loadCachedData(storeName) | ||
if not data then | |||
return "" | return "" | ||
end | end | ||
local entry = | local entry = data[searchId] | ||
if type(entry) ~= "table" then | |||
return "" | return "" | ||
end | end | ||
return table. | 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 | end | ||
function p. | function p.flattenField(frame) | ||
local args = | local args = frame.args or {} | ||
local id = args[1] or "" | local id = args[1] or "" | ||
local pagePath = args[2] or "" | local pagePath = args[2] or "" | ||
if id == "" or pagePath == "" then | |||
return "" | return "" | ||
end | end | ||
local entry = load_entry(id, pagePath) | local entry = load_entry(id, pagePath) or {} | ||
if | 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 "" | return "" | ||
end | end | ||
if | local entry = load_entry(id, pagePath) or {} | ||
return | local parts = flatten_selected_parts(entry, keys) | ||
if #parts == 0 then | |||
return "" | |||
end | end | ||
return | return table.concat(parts, "|") | ||
end | end | ||
local function collect_by_parsed_path(tbl, parsedPath, pos, out) | 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 | if pos > #parsedPath then | ||
out[#out + 1] = tbl | out[#out + 1] = tbl | ||
| Строка 675: | Строка 744: | ||
end | end | ||
function p.searchId(frame) | |||
local args = getArgs(frame, { removeBlanks = false }) | |||
function p.searchId(frame) | |||
local args = getArgs(frame, { removeBlanks = false }) | |||
local searchValue = args[1] or "" | local searchValue = args[1] or "" | ||
local pagePath = args[2] or "" | local pagePath = args[2] or "" | ||
| Строка 720: | Строка 758: | ||
end | end | ||
local data = | local data = p.loadCachedData(pagePath) | ||
if not data then | if not data then | ||
return "[]" | return "[]" | ||
| Строка 757: | Строка 795: | ||
end | end | ||
local function | local function collect_ids_from_entry(entry) | ||
local out = {} | |||
local seen = {} | |||
local | local function add(v) | ||
if v == nil then | |||
return | |||
end | |||
local s = tostring(v) | |||
if s ~= "" and not seen[s] then | |||
seen[s] = true | |||
out[#out + 1] = s | |||
end | |||
end | end | ||
for | if type(entry) == "table" then | ||
if | if is_array(entry) then | ||
for _, v in ipairs(entry) do | |||
add(v) | |||
end | |||
else | |||
for _, v in pairs(entry) do | |||
if type(v) == "table" and is_array(v) then | |||
for _, item in ipairs(v) do | |||
add(item) | |||
end | |||
elseif type(v) ~= "table" then | |||
add(v) | |||
end | |||
end | |||
end | end | ||
else | |||
add(entry) | |||
end | end | ||
return | return out | ||
end | 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 "" | |||
return | |||
end | end | ||
local | local targets = split_csv_list(rawList) | ||
if #targets == 0 then | |||
return "" | |||
end | end | ||
local pagePath = (mode == "component") and "component.json" or "tag.json" | |||
local data = p.loadCachedData(pagePath) | |||
if not data then | |||
return "" | |||
end | end | ||
local matches = {} | |||
local seen = {} | |||
local function add_id(id) | |||
local | local s = tostring(id) | ||
if s ~= "" and not seen[s] then | |||
seen[s] = true | |||
matches[#matches + 1] = s | |||
end | |||
end | end | ||
local | for _, key in ipairs(targets) do | ||
local entry = resolve_entry_from_data(data, key) | |||
if entry ~= nil then | |||
local ids = collect_ids_from_entry(entry) | |||
for _, id in ipairs(ids) do | |||
add_id(id) | |||
end | |||
end | |||
end | end | ||
table.sort(matches, function(a, b) | |||
return tostring(a) < tostring(b) | |||
end) | |||
end | |||
local | local ok, json = pcall(mw.text.jsonEncode, matches) | ||
if | if ok and json then | ||
return | return json | ||
end | end | ||
return "" | |||
end | end | ||
| Строка 891: | Строка 916: | ||
end | end | ||
local data = | local data = p.loadCachedData(pagePath) | ||
if not data then | if not data then | ||
return "" | return "" | ||
| Строка 934: | Строка 959: | ||
local out = {} | local out = {} | ||
for _, idKey in ipairs(matches) do | for _, idKey in ipairs(matches) do | ||
local tpl = build_tpl(idKey, pagePath, tplPath, data, tplArgs, entryCache[idKey]) | local tpl = build_tpl(idKey, pagePath, tplPath, data, tplArgs, entryCache[idKey], true) | ||
if tpl ~= "" then | if tpl ~= "" then | ||
out[#out + 1] = tpl | out[#out + 1] = tpl | ||
| Строка 961: | Строка 986: | ||
local data = frame.data | local data = frame.data | ||
local tplStr = build_tpl(id, pagePath, tplPath, data, tplArgs) | local tplStr = build_tpl(id, pagePath, tplPath, data, tplArgs, nil, true) | ||
return preprocess_or_return(frame, tplStr) | return preprocess_or_return(frame, tplStr) | ||
end | end | ||
| Строка 969: | Строка 994: | ||
local searchId = args[1] or "" | local searchId = args[1] or "" | ||
local kind = (args[2] or ""):lower() | local kind = (args[2] or ""):lower() | ||
local generatorId = args[3] or "" | local generatorId = p.ucfirst(args[3] or "") | ||
local tplPath = mw.text.unstripNoWiki(args[4] or "") | local tplPath = mw.text.unstripNoWiki(args[4] or "") | ||
local tplArgs = args[5] or args.tplArgs or args.templateArgs or "" | local tplArgs = args[5] or args.tplArgs or args.templateArgs or "" | ||
| Строка 990: | Строка 1015: | ||
end | end | ||
local data = | local data = p.loadCachedData(pagePath) | ||
if not data then | if not data then | ||
return "" | return "" | ||
| Строка 999: | Строка 1024: | ||
local entry = resolve_entry_from_data(data, id) | local entry = resolve_entry_from_data(data, id) | ||
if entry ~= nil then | if entry ~= nil then | ||
local tpl = build_tpl(id, pagePath, tplPath, data, tplArgs, entry) | local tpl = build_tpl(id, pagePath, tplPath, data, tplArgs, entry, true) | ||
if tpl ~= "" then | if tpl ~= "" then | ||
out[#out + 1] = tpl | out[#out + 1] = tpl | ||
| Строка 1018: | Строка 1043: | ||
local searchId = args[1] or "" | local searchId = args[1] or "" | ||
local kind = (args[2] or ""):lower() | local kind = (args[2] or ""):lower() | ||
local generatorId = args[3] or "" | local generatorId = p.ucfirst(args[3] or "") | ||
if searchId == "" or generatorId == "" then | if searchId == "" or generatorId == "" then | ||
| Строка 1041: | Строка 1066: | ||
end | end | ||
function p. | function p.loadEntityData(entityId) | ||
if entityId == "" then | |||
local entityId | return nil | ||
end | |||
local pagePath = "prototype/Entity/" .. entityId .. ".json" | |||
return p.loadCachedData(pagePath) | |||
end | |||
if | function p.entityHasComponent(entity, compName) | ||
return | if type(entity) ~= "table" or type(entity.components) ~= "table" then | ||
return false | |||
end | end | ||
return entity.components[compName] ~= nil | |||
or entity.components["type:" .. compName] ~= nil | |||
or entity.components["!type:" .. compName] ~= nil | |||
end | |||
if type( | function p.collectEntityComponents(entity) | ||
return | if type(entity) ~= "table" or type(entity.components) ~= "table" then | ||
return {} | |||
end | end | ||
local | local out = {} | ||
local seen = {} | |||
local comps = entity.components | |||
for _, v in ipairs( | if #comps > 0 then | ||
for _, v in ipairs(comps) do | |||
local name = p.normalizeComponentName(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 = p.normalizeComponentName(k) | |||
if name and not seen[name] then | |||
seen[name] = true | |||
out[#out + 1] = name | |||
end | |||
end | end | ||
end | end | ||
return | table.sort(out) | ||
return out | |||
end | end | ||
function p. | function p.hasComp(frame) | ||
local args = getArgs(frame, { removeBlanks = false }) | local args = getArgs(frame, { removeBlanks = false }) | ||
local entityId = args[1] or "" | local entityId = args[1] or "" | ||
local compName = args[2] or "" | |||
if entityId == "" then | if entityId == "" or compName == "" then | ||
return " | return "false" | ||
end | end | ||
local | local entity = p.loadEntityData(entityId) | ||
if not | if not entity then | ||
return " | return "false" | ||
end | end | ||
local | return p.entityHasComponent(entity, compName) and "true" or "false" | ||
if | end | ||
return " | |||
function p.getComp(frame) | |||
local args = getArgs(frame, { removeBlanks = false }) | |||
local entityId = args[1] or "" | |||
if entityId == "" then | |||
return "" | |||
end | end | ||
local | local entity = p.loadEntityData(entityId) | ||
if not entity then | |||
return "" | |||
end | end | ||
local out = p.collectEntityComponents(entity) | |||
local ok, json = pcall(mw.text.jsonEncode, out) | local ok, json = pcall(mw.text.jsonEncode, out) | ||
if ok and json then | if ok and json then | ||
| Строка 1103: | Строка 1152: | ||
end | end | ||
return " | return "" | ||
end | end | ||
| Строка 1116: | Строка 1165: | ||
end | end | ||
local data = | local data = p.loadCachedData(pagePath) | ||
if not data then | if not data then | ||
return "" | return "" | ||
| Строка 1166: | Строка 1215: | ||
end | end | ||
local data = | local data = p.loadCachedData(pagePath) | ||
if not data then | if not data then | ||
return "" | return "" | ||
| Строка 1181: | Строка 1230: | ||
local entry = resolve_entry_from_data(data, idKey) | local entry = resolve_entry_from_data(data, idKey) | ||
if entry ~= nil then | if entry ~= nil then | ||
local tpl = build_tpl(idKey, pagePath, tplPath, data, tplArgs, entry) | local tpl = build_tpl(idKey, pagePath, tplPath, data, tplArgs, entry, true) | ||
if tpl ~= "" then | if tpl ~= "" then | ||
out[#out + 1] = tpl | out[#out + 1] = tpl | ||
| Строка 1202: | Строка 1251: | ||
end | end | ||
local function | local function collect_sorted_keys(tbl, stringOnly) | ||
local keys = {} | |||
for k in pairs(tbl) do | |||
if not stringOnly or type(k) == "string" then | |||
keys[#keys + 1] = k | |||
end | end | ||
end | end | ||
table.sort(keys, function(a, b) | |||
return tostring(a) < tostring(b) | |||
end) | |||
return keys | return keys | ||
end | end | ||
local function choose_id_key(obj) | local function choose_id_key(obj) | ||
local | local keys = {} | ||
for k in pairs(obj) do | for k in pairs(obj) do | ||
if type(k) == "string" then | if type(k) == "string" then | ||
keys[#keys + 1] = k | |||
end | end | ||
end | end | ||
if # | if #keys == 0 then | ||
return nil | return nil | ||
end | end | ||
table.sort( | table.sort(keys, function(a, b) | ||
local av = obj[a] | |||
return not | local bv = obj[b] | ||
local aPrimitive = type(av) ~= "table" | |||
local bPrimitive = type(bv) ~= "table" | |||
if aPrimitive ~= bPrimitive then | |||
return not aPrimitive | |||
end | end | ||
return a | |||
return tostring(a) < tostring(b) | |||
end) | end) | ||
return | return keys[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 | |||
local function is_array_of_primitives(tbl) | |||
if type(tbl) ~= "table" or not is_array(tbl) then | |||
return false | |||
end | |||
for _, v in ipairs(tbl) do | |||
if type(v) == "table" then | |||
return false | |||
end | |||
end | |||
return true | |||
end | end | ||
| Строка 1276: | Строка 1329: | ||
end | end | ||
local | local keys = collect_sorted_keys(value, false) | ||
for _, | for _, k in ipairs(keys) do | ||
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 .. "." .. tostring(k) | ||
else | else | ||
key = | key = tostring(k) | ||
end | end | ||
| Строка 1319: | Строка 1370: | ||
end | end | ||
end | end | ||
end | end | ||
| Строка 1359: | Строка 1399: | ||
skipPrimitiveRoot = true, | skipPrimitiveRoot = true, | ||
} | } | ||
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 | |||
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 "") | ||
| Строка 1381: | Строка 1432: | ||
end | end | ||
parts[#parts + 1] = idKey | parts[#parts + 1] = tostring(idKey) | ||
local | local keys = collect_sorted_keys(obj, true) | ||
for _, | for _, k in ipairs(keys) do | ||
local v = obj[k] | local v = obj[k] | ||
| Строка 1439: | Строка 1489: | ||
end | end | ||
elseif is_object_map(data) then | elseif is_object_map(data) then | ||
local | local keys = collect_sorted_keys(data, true) | ||
for _, | for _, k in ipairs(keys) do | ||
makeCall({ [ | makeCall({ [k] = data[k] }) | ||
end | end | ||
else | else | ||
| Строка 1536: | Строка 1586: | ||
end | end | ||
out | table.insert(out, line) | ||
end | end | ||
end | end | ||
else | else | ||
local | local keys = {} | ||
for k in pairs(data) do | for k in pairs(data) do | ||
keys[#keys + 1] = k | |||
end | end | ||
table.sort( | table.sort(keys, function(a, b) | ||
return tostring(a) < tostring(b) | |||
end) | |||
local processed = 0 | local processed = 0 | ||
for _, | for _, k in ipairs(keys) do | ||
processed = processed + 1 | processed = processed + 1 | ||
if maxItems ~= nil and processed > maxItems then | if maxItems ~= nil and processed > maxItems then | ||
| Строка 1554: | Строка 1606: | ||
end | end | ||
local v = data[k] | local v = data[k] | ||
local vStr | local vStr | ||
| Строка 1570: | Строка 1620: | ||
end | end | ||
local baseKey = apply_pattern( | local baseKey = apply_pattern(tostring(k), keyPattern, "\\1") | ||
local MARK_KEY = "\31KEY\31" | local MARK_KEY = "\31KEY\31" | ||
| Строка 1579: | Строка 1629: | ||
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(tostring(k), keyPattern, kRepl) | ||
local keyStr = tostring(keyStr0):gsub(MARK_VAL, vStr0) | local keyStr = tostring(keyStr0):gsub(MARK_VAL, vStr0) | ||
| Строка 1598: | Строка 1648: | ||
end | end | ||
out | table.insert(out, line) | ||
end | end | ||
end | end | ||