|
Метки: замена ручная отмена |
| (не показаны 4 промежуточные версии этого же участника) |
| Строка 1: |
Строка 1: |
| local p = {} | | local p = {} |
|
| |
| local function skip_ws(text, pos)
| |
| local len = #text
| |
| while pos <= len do
| |
| local ch = string.sub(text, pos, pos)
| |
| if ch ~= " " and ch ~= "\n" and ch ~= "\r" and ch ~= "\t" then
| |
| return pos
| |
| end
| |
| pos = pos + 1
| |
| end
| |
| return pos
| |
| end
| |
|
| |
| local function skip_string(text, pos)
| |
| pos = pos + 1
| |
| local len = #text
| |
| while pos <= len do
| |
| local ch = string.sub(text, pos, pos)
| |
| if ch == "\\" then
| |
| pos = pos + 2
| |
| elseif ch == "\"" then
| |
| return pos
| |
| else
| |
| pos = pos + 1
| |
| end
| |
| end
| |
| return nil
| |
| end
| |
|
| |
| local function find_value_end(text, pos)
| |
| local ch = string.sub(text, pos, pos)
| |
| if ch == "\"" then
| |
| return skip_string(text, pos)
| |
| end
| |
|
| |
| if ch == "{" or ch == "[" then
| |
| local depth = 0
| |
| local len = #text
| |
| while pos <= len do
| |
| ch = string.sub(text, pos, pos)
| |
| if ch == "\"" then
| |
| pos = skip_string(text, pos)
| |
| if not pos then
| |
| return nil
| |
| end
| |
| elseif ch == "{" or ch == "[" then
| |
| depth = depth + 1
| |
| elseif ch == "}" or ch == "]" then
| |
| depth = depth - 1
| |
| if depth == 0 then
| |
| return pos
| |
| end
| |
| end
| |
| pos = pos + 1
| |
| end
| |
| return nil
| |
| end
| |
|
| |
| local len = #text
| |
| while pos <= len do
| |
| ch = string.sub(text, pos, pos)
| |
| if ch == "," or ch == "}" or ch == "]" or ch == "\n" or ch == "\r" then
| |
| return pos - 1
| |
| end
| |
| pos = pos + 1
| |
| end
| |
| return len
| |
| end
| |
|
| |
| local function decode_json(text)
| |
| local ok, data = pcall(mw.text.jsonDecode, text)
| |
| if ok then
| |
| return data
| |
| end
| |
| return nil
| |
| end
| |
|
| |
| local function find_member_range(text, objectStart, keyName)
| |
| local pos = skip_ws(text, objectStart)
| |
| if string.sub(text, pos, pos) ~= "{" then
| |
| return nil, nil
| |
| end
| |
|
| |
| pos = skip_ws(text, pos + 1)
| |
| while pos <= #text and string.sub(text, pos, pos) ~= "}" do
| |
| if string.sub(text, pos, pos) ~= "\"" then
| |
| return nil, nil
| |
| end
| |
|
| |
| local keyStart = pos
| |
| local keyEnd = skip_string(text, pos)
| |
| if not keyEnd then
| |
| return nil, nil
| |
| end
| |
|
| |
| local key = decode_json(string.sub(text, keyStart, keyEnd))
| |
| pos = skip_ws(text, keyEnd + 1)
| |
| if string.sub(text, pos, pos) ~= ":" then
| |
| return nil, nil
| |
| end
| |
|
| |
| local valueStart = skip_ws(text, pos + 1)
| |
| local valueEnd = find_value_end(text, valueStart)
| |
| if not valueEnd then
| |
| return nil, nil
| |
| end
| |
|
| |
| if key == keyName then
| |
| return valueStart, valueEnd
| |
| end
| |
|
| |
| pos = skip_ws(text, valueEnd + 1)
| |
| local sep = string.sub(text, pos, pos)
| |
| if sep == "," then
| |
| pos = skip_ws(text, pos + 1)
| |
| elseif sep == "}" then
| |
| return nil, nil
| |
| else
| |
| return nil, nil
| |
| end
| |
| end
| |
|
| |
| return nil, nil
| |
| 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 deep_merge(dst, src)
| |
| for k, v in pairs(src) do
| |
| if type(v) == "table" and type(dst[k]) == "table" then
| |
| deep_merge(dst[k], v)
| |
| elseif type(v) == "table" then
| |
| dst[k] = deep_copy(v)
| |
| else
| |
| dst[k] = v
| |
| end
| |
| end
| |
| end
| |
|
| |
| local function new_loader(titleName)
| |
| local loader = {}
| |
| local title = mw.title.new(titleName)
| |
| local jsonData
| |
| local fullData
| |
| local defaultData
| |
| local idDataCache = {}
| |
|
| |
| local function get_json()
| |
| if jsonData == nil then
| |
| jsonData = title and title:getContent() or ""
| |
| end
| |
| return jsonData
| |
| end
| |
|
| |
| local function decode_root_member(keyName)
| |
| local text = get_json()
| |
| local rootStart = string.find(text, "{", 1, true)
| |
| if not rootStart then
| |
| return nil
| |
| end
| |
|
| |
| local valueStart, valueEnd = find_member_range(text, rootStart, keyName)
| |
| if not valueStart then
| |
| return nil
| |
| end
| |
|
| |
| return decode_json(string.sub(text, valueStart, valueEnd))
| |
| end
| |
|
| |
| local function decode_id_member(id)
| |
| if idDataCache[id] ~= nil then
| |
| return idDataCache[id]
| |
| end
| |
|
| |
| local text = get_json()
| |
| local rootStart = string.find(text, "{", 1, true)
| |
| if not rootStart then
| |
| idDataCache[id] = false
| |
| return nil
| |
| end
| |
|
| |
| local idsStart = find_member_range(text, rootStart, "id")
| |
| if not idsStart then
| |
| idDataCache[id] = false
| |
| return nil
| |
| end
| |
|
| |
| local valueStart, valueEnd = find_member_range(text, idsStart, id)
| |
| if not valueStart then
| |
| idDataCache[id] = false
| |
| return nil
| |
| end
| |
|
| |
| local data = decode_json(string.sub(text, valueStart, valueEnd))
| |
| idDataCache[id] = data or false
| |
| return data
| |
| end
| |
|
| |
| function loader.all()
| |
| if fullData == nil then
| |
| local data = decode_json(get_json())
| |
| fullData = type(data) == "table" and data or {}
| |
| end
| |
| return fullData
| |
| end
| |
|
| |
| function loader.get(id)
| |
| id = tostring(id or "")
| |
|
| |
| if defaultData == nil then
| |
| local data = decode_root_member("default")
| |
| defaultData = type(data) == "table" and data or false
| |
| end
| |
|
| |
| local base = defaultData ~= false and defaultData or nil
| |
| if id == "" then
| |
| return base
| |
| end
| |
|
| |
| local specific = decode_id_member(id)
| |
| if type(specific) ~= "table" then
| |
| return base
| |
| end
| |
|
| |
| if type(base) == "table" then
| |
| local merged = deep_copy(base)
| |
| deep_merge(merged, specific)
| |
| return merged
| |
| end
| |
|
| |
| return specific
| |
| end
| |
|
| |
| return loader
| |
| end
| |
|
| |
|
| function p.getFromTitle(titleName) | | function p.getFromTitle(titleName) |
| return new_loader(titleName)
| | local title = mw.title.new(titleName) |
| | local jsonText = title and title:getContent() or "" |
| | local ok, data = pcall(mw.text.jsonDecode, jsonText) |
| | if ok and type(data) == "table" then |
| | return data |
| | else |
| | return {} |
| | end |
| end | | end |
|
| |
|
| function p.invoke(frame) | | function p.invoke(frame) |
| local name = frame.args.title
| | local name = frame.args.title |
| local loader = p.getFromTitle(name)
| | return p.getFromTitle(name) |
| return loader.all()
| |
| end | | end |
|
| |
|
| return p | | return p |
Для документации этого модуля может быть создана страница Модуль:JsonLoader/doc
local p = {}
function p.getFromTitle(titleName)
local title = mw.title.new(titleName)
local jsonText = title and title:getContent() or ""
local ok, data = pcall(mw.text.jsonDecode, jsonText)
if ok and type(data) == "table" then
return data
else
return {}
end
end
function p.invoke(frame)
local name = frame.args.title
return p.getFromTitle(name)
end
return p