mirror of
https://github.com/OTCv8/otclientv8.git
synced 2025-10-19 06:03:27 +02:00
Version 2.6.1
This commit is contained in:
@@ -1,278 +1,278 @@
|
||||
HTTP = {
|
||||
timeout=5,
|
||||
websocketTimeout=15,
|
||||
agent="Mozilla/5.0",
|
||||
imageId=1000,
|
||||
images={},
|
||||
operations={},
|
||||
}
|
||||
|
||||
function HTTP.get(url, callback)
|
||||
if not g_http or not g_http.get then
|
||||
return error("HTTP.get is not supported")
|
||||
end
|
||||
local operation = g_http.get(url, HTTP.timeout)
|
||||
HTTP.operations[operation] = {type="get", url=url, callback=callback}
|
||||
return operation
|
||||
end
|
||||
|
||||
function HTTP.getJSON(url, callback)
|
||||
if not g_http or not g_http.get then
|
||||
return error("HTTP.getJSON is not supported")
|
||||
end
|
||||
local operation = g_http.get(url, HTTP.timeout)
|
||||
HTTP.operations[operation] = {type="get", json=true, url=url, callback=callback}
|
||||
return operation
|
||||
end
|
||||
|
||||
function HTTP.post(url, data, callback)
|
||||
if not g_http or not g_http.post then
|
||||
return error("HTTP.post is not supported")
|
||||
end
|
||||
if type(data) == "table" then
|
||||
data = json.encode(data)
|
||||
end
|
||||
local operation = g_http.post(url, data, HTTP.timeout)
|
||||
HTTP.operations[operation] = {type="post", url=url, callback=callback}
|
||||
return operation
|
||||
end
|
||||
|
||||
function HTTP.postJSON(url, data, callback)
|
||||
if not g_http or not g_http.post then
|
||||
return error("HTTP.postJSON is not supported")
|
||||
end
|
||||
if type(data) == "table" then
|
||||
data = json.encode(data)
|
||||
end
|
||||
local operation = g_http.post(url, data, HTTP.timeout)
|
||||
HTTP.operations[operation] = {type="post", json=true, url=url, callback=callback}
|
||||
return operation
|
||||
end
|
||||
|
||||
function HTTP.download(url, file, callback, progressCallback)
|
||||
if not g_http or not g_http.download then
|
||||
return error("HTTP.download is not supported")
|
||||
end
|
||||
local operation = g_http.download(url, file, HTTP.timeout)
|
||||
HTTP.operations[operation] = {type="download", url=url, file=file, callback=callback, progressCallback=progressCallback}
|
||||
return operation
|
||||
end
|
||||
|
||||
function HTTP.downloadImage(url, callback)
|
||||
if not g_http or not g_http.download then
|
||||
return error("HTTP.downloadImage is not supported")
|
||||
end
|
||||
if HTTP.images[url] ~= nil then
|
||||
if callback then
|
||||
callback('/downloads/' .. HTTP.images[url], nil)
|
||||
end
|
||||
return
|
||||
end
|
||||
local file = "autoimage_" .. HTTP.imageId .. ".png"
|
||||
HTTP.imageId = HTTP.imageId + 1
|
||||
local operation = g_http.download(url, file, HTTP.timeout)
|
||||
HTTP.operations[operation] = {type="image", url=url, file=file, callback=callback}
|
||||
return operation
|
||||
end
|
||||
|
||||
function HTTP.webSocket(url, callbacks, timeout, jsonWebsocket)
|
||||
if not g_http or not g_http.ws then
|
||||
return error("WebSocket is not supported")
|
||||
end
|
||||
if not timeout or timeout < 1 then
|
||||
timeout = HTTP.websocketTimeout
|
||||
end
|
||||
local operation = g_http.ws(url, timeout)
|
||||
HTTP.operations[operation] = {type="ws", json=jsonWebsocket, url=url, callbacks=callbacks}
|
||||
return {
|
||||
id = operation,
|
||||
url = url,
|
||||
close = function()
|
||||
g_http.wsClose(operation)
|
||||
end,
|
||||
send = function(message)
|
||||
if type(message) == "table" then
|
||||
message = json.encode(message)
|
||||
end
|
||||
g_http.wsSend(operation, message)
|
||||
end
|
||||
}
|
||||
end
|
||||
HTTP.WebSocket = HTTP.webSocket
|
||||
|
||||
function HTTP.webSocketJSON(url, callbacks, timeout)
|
||||
return HTTP.webSocket(url, callbacks, timeout, true)
|
||||
end
|
||||
HTTP.WebSocketJSON = HTTP.webSocketJSON
|
||||
|
||||
function HTTP.cancel(operationId)
|
||||
if not g_http or not g_http.cancel then
|
||||
return
|
||||
end
|
||||
HTTP.operations[operationId] = nil
|
||||
return g_http.cancel(operationId)
|
||||
end
|
||||
|
||||
function HTTP.onGet(operationId, url, err, data)
|
||||
local operation = HTTP.operations[operationId]
|
||||
if operation == nil then
|
||||
return
|
||||
end
|
||||
if err and err:len() == 0 then
|
||||
err = nil
|
||||
end
|
||||
if not err and operation.json then
|
||||
local status, result = pcall(function() return json.decode(data) end)
|
||||
if not status then
|
||||
err = "JSON ERROR: " .. result
|
||||
if data and data:len() > 0 then
|
||||
err = err .. " (" .. data:sub(1, 100) .. ")"
|
||||
end
|
||||
end
|
||||
data = result
|
||||
end
|
||||
if operation.callback then
|
||||
operation.callback(data, err)
|
||||
end
|
||||
end
|
||||
|
||||
function HTTP.onGetProgress(operationId, url, progress)
|
||||
local operation = HTTP.operations[operationId]
|
||||
if operation == nil then
|
||||
return
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function HTTP.onPost(operationId, url, err, data)
|
||||
local operation = HTTP.operations[operationId]
|
||||
if operation == nil then
|
||||
return
|
||||
end
|
||||
if err and err:len() == 0 then
|
||||
err = nil
|
||||
end
|
||||
if not err and operation.json then
|
||||
local status, result = pcall(function() return json.decode(data) end)
|
||||
if not status then
|
||||
err = "JSON ERROR: " .. result
|
||||
if data and data:len() > 0 then
|
||||
err = err .. " (" .. data:sub(1, 100) .. ")"
|
||||
end
|
||||
end
|
||||
data = result
|
||||
end
|
||||
if operation.callback then
|
||||
operation.callback(data, err)
|
||||
end
|
||||
end
|
||||
|
||||
function HTTP.onPostProgress(operationId, url, progress)
|
||||
local operation = HTTP.operations[operationId]
|
||||
if operation == nil then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
function HTTP.onDownload(operationId, url, err, path, checksum)
|
||||
local operation = HTTP.operations[operationId]
|
||||
if operation == nil then
|
||||
return
|
||||
end
|
||||
if err and err:len() == 0 then
|
||||
err = nil
|
||||
end
|
||||
if operation.callback then
|
||||
if operation["type"] == "image" then
|
||||
if not err then
|
||||
HTTP.images[url] = path
|
||||
end
|
||||
operation.callback('/downloads/' .. path, err)
|
||||
else
|
||||
operation.callback(path, checksum, err)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function HTTP.onDownloadProgress(operationId, url, progress, speed)
|
||||
local operation = HTTP.operations[operationId]
|
||||
if operation == nil then
|
||||
return
|
||||
end
|
||||
if operation.progressCallback then
|
||||
operation.progressCallback(progress, speed)
|
||||
end
|
||||
end
|
||||
|
||||
function HTTP.onWsOpen(operationId, message)
|
||||
local operation = HTTP.operations[operationId]
|
||||
if operation == nil then
|
||||
return
|
||||
end
|
||||
if operation.callbacks.onOpen then
|
||||
operation.callbacks.onOpen(message, operationId)
|
||||
end
|
||||
end
|
||||
|
||||
function HTTP.onWsMessage(operationId, message)
|
||||
local operation = HTTP.operations[operationId]
|
||||
if operation == nil then
|
||||
return
|
||||
end
|
||||
if operation.callbacks.onMessage then
|
||||
if operation.json then
|
||||
local status, result = pcall(function() return json.decode(message) end)
|
||||
local err = nil
|
||||
if not status then
|
||||
err = "JSON ERROR: " .. result
|
||||
if message and message:len() > 0 then
|
||||
err = err .. " (" .. message:sub(1, 100) .. ")"
|
||||
end
|
||||
end
|
||||
if err then
|
||||
if operation.callbacks.onError then
|
||||
operation.callbacks.onError(err, operationId)
|
||||
end
|
||||
else
|
||||
operation.callbacks.onMessage(result, operationId)
|
||||
end
|
||||
else
|
||||
operation.callbacks.onMessage(message, operationId)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function HTTP.onWsClose(operationId, message)
|
||||
local operation = HTTP.operations[operationId]
|
||||
if operation == nil then
|
||||
return
|
||||
end
|
||||
if operation.callbacks.onClose then
|
||||
operation.callbacks.onClose(message, operationId)
|
||||
end
|
||||
end
|
||||
|
||||
function HTTP.onWsError(operationId, message)
|
||||
local operation = HTTP.operations[operationId]
|
||||
if operation == nil then
|
||||
return
|
||||
end
|
||||
if operation.callbacks.onError then
|
||||
operation.callbacks.onError(message, operationId)
|
||||
end
|
||||
end
|
||||
|
||||
connect(g_http,
|
||||
{
|
||||
onGet = HTTP.onGet,
|
||||
onGetProgress = HTTP.onGetProgress,
|
||||
onPost = HTTP.onPost,
|
||||
onPostProgress = HTTP.onPostProgress,
|
||||
onDownload = HTTP.onDownload,
|
||||
onDownloadProgress = HTTP.onDownloadProgress,
|
||||
onWsOpen = HTTP.onWsOpen,
|
||||
onWsMessage = HTTP.onWsMessage,
|
||||
onWsClose = HTTP.onWsClose,
|
||||
onWsError = HTTP.onWsError,
|
||||
})
|
||||
g_http.setUserAgent(HTTP.agent)
|
||||
HTTP = {
|
||||
timeout=5,
|
||||
websocketTimeout=15,
|
||||
agent="Mozilla/5.0",
|
||||
imageId=1000,
|
||||
images={},
|
||||
operations={},
|
||||
}
|
||||
|
||||
function HTTP.get(url, callback)
|
||||
if not g_http or not g_http.get then
|
||||
return error("HTTP.get is not supported")
|
||||
end
|
||||
local operation = g_http.get(url, HTTP.timeout)
|
||||
HTTP.operations[operation] = {type="get", url=url, callback=callback}
|
||||
return operation
|
||||
end
|
||||
|
||||
function HTTP.getJSON(url, callback)
|
||||
if not g_http or not g_http.get then
|
||||
return error("HTTP.getJSON is not supported")
|
||||
end
|
||||
local operation = g_http.get(url, HTTP.timeout)
|
||||
HTTP.operations[operation] = {type="get", json=true, url=url, callback=callback}
|
||||
return operation
|
||||
end
|
||||
|
||||
function HTTP.post(url, data, callback)
|
||||
if not g_http or not g_http.post then
|
||||
return error("HTTP.post is not supported")
|
||||
end
|
||||
if type(data) == "table" then
|
||||
data = json.encode(data)
|
||||
end
|
||||
local operation = g_http.post(url, data, HTTP.timeout)
|
||||
HTTP.operations[operation] = {type="post", url=url, callback=callback}
|
||||
return operation
|
||||
end
|
||||
|
||||
function HTTP.postJSON(url, data, callback)
|
||||
if not g_http or not g_http.post then
|
||||
return error("HTTP.postJSON is not supported")
|
||||
end
|
||||
if type(data) == "table" then
|
||||
data = json.encode(data)
|
||||
end
|
||||
local operation = g_http.post(url, data, HTTP.timeout)
|
||||
HTTP.operations[operation] = {type="post", json=true, url=url, callback=callback}
|
||||
return operation
|
||||
end
|
||||
|
||||
function HTTP.download(url, file, callback, progressCallback)
|
||||
if not g_http or not g_http.download then
|
||||
return error("HTTP.download is not supported")
|
||||
end
|
||||
local operation = g_http.download(url, file, HTTP.timeout)
|
||||
HTTP.operations[operation] = {type="download", url=url, file=file, callback=callback, progressCallback=progressCallback}
|
||||
return operation
|
||||
end
|
||||
|
||||
function HTTP.downloadImage(url, callback)
|
||||
if not g_http or not g_http.download then
|
||||
return error("HTTP.downloadImage is not supported")
|
||||
end
|
||||
if HTTP.images[url] ~= nil then
|
||||
if callback then
|
||||
callback('/downloads/' .. HTTP.images[url], nil)
|
||||
end
|
||||
return
|
||||
end
|
||||
local file = "autoimage_" .. HTTP.imageId .. ".png"
|
||||
HTTP.imageId = HTTP.imageId + 1
|
||||
local operation = g_http.download(url, file, HTTP.timeout)
|
||||
HTTP.operations[operation] = {type="image", url=url, file=file, callback=callback}
|
||||
return operation
|
||||
end
|
||||
|
||||
function HTTP.webSocket(url, callbacks, timeout, jsonWebsocket)
|
||||
if not g_http or not g_http.ws then
|
||||
return error("WebSocket is not supported")
|
||||
end
|
||||
if not timeout or timeout < 1 then
|
||||
timeout = HTTP.websocketTimeout
|
||||
end
|
||||
local operation = g_http.ws(url, timeout)
|
||||
HTTP.operations[operation] = {type="ws", json=jsonWebsocket, url=url, callbacks=callbacks}
|
||||
return {
|
||||
id = operation,
|
||||
url = url,
|
||||
close = function()
|
||||
g_http.wsClose(operation)
|
||||
end,
|
||||
send = function(message)
|
||||
if type(message) == "table" then
|
||||
message = json.encode(message)
|
||||
end
|
||||
g_http.wsSend(operation, message)
|
||||
end
|
||||
}
|
||||
end
|
||||
HTTP.WebSocket = HTTP.webSocket
|
||||
|
||||
function HTTP.webSocketJSON(url, callbacks, timeout)
|
||||
return HTTP.webSocket(url, callbacks, timeout, true)
|
||||
end
|
||||
HTTP.WebSocketJSON = HTTP.webSocketJSON
|
||||
|
||||
function HTTP.cancel(operationId)
|
||||
if not g_http or not g_http.cancel then
|
||||
return
|
||||
end
|
||||
HTTP.operations[operationId] = nil
|
||||
return g_http.cancel(operationId)
|
||||
end
|
||||
|
||||
function HTTP.onGet(operationId, url, err, data)
|
||||
local operation = HTTP.operations[operationId]
|
||||
if operation == nil then
|
||||
return
|
||||
end
|
||||
if err and err:len() == 0 then
|
||||
err = nil
|
||||
end
|
||||
if not err and operation.json then
|
||||
local status, result = pcall(function() return json.decode(data) end)
|
||||
if not status then
|
||||
err = "JSON ERROR: " .. result
|
||||
if data and data:len() > 0 then
|
||||
err = err .. " (" .. data:sub(1, 100) .. ")"
|
||||
end
|
||||
end
|
||||
data = result
|
||||
end
|
||||
if operation.callback then
|
||||
operation.callback(data, err)
|
||||
end
|
||||
end
|
||||
|
||||
function HTTP.onGetProgress(operationId, url, progress)
|
||||
local operation = HTTP.operations[operationId]
|
||||
if operation == nil then
|
||||
return
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function HTTP.onPost(operationId, url, err, data)
|
||||
local operation = HTTP.operations[operationId]
|
||||
if operation == nil then
|
||||
return
|
||||
end
|
||||
if err and err:len() == 0 then
|
||||
err = nil
|
||||
end
|
||||
if not err and operation.json then
|
||||
local status, result = pcall(function() return json.decode(data) end)
|
||||
if not status then
|
||||
err = "JSON ERROR: " .. result
|
||||
if data and data:len() > 0 then
|
||||
err = err .. " (" .. data:sub(1, 100) .. ")"
|
||||
end
|
||||
end
|
||||
data = result
|
||||
end
|
||||
if operation.callback then
|
||||
operation.callback(data, err)
|
||||
end
|
||||
end
|
||||
|
||||
function HTTP.onPostProgress(operationId, url, progress)
|
||||
local operation = HTTP.operations[operationId]
|
||||
if operation == nil then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
function HTTP.onDownload(operationId, url, err, path, checksum)
|
||||
local operation = HTTP.operations[operationId]
|
||||
if operation == nil then
|
||||
return
|
||||
end
|
||||
if err and err:len() == 0 then
|
||||
err = nil
|
||||
end
|
||||
if operation.callback then
|
||||
if operation["type"] == "image" then
|
||||
if not err then
|
||||
HTTP.images[url] = path
|
||||
end
|
||||
operation.callback('/downloads/' .. path, err)
|
||||
else
|
||||
operation.callback(path, checksum, err)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function HTTP.onDownloadProgress(operationId, url, progress, speed)
|
||||
local operation = HTTP.operations[operationId]
|
||||
if operation == nil then
|
||||
return
|
||||
end
|
||||
if operation.progressCallback then
|
||||
operation.progressCallback(progress, speed)
|
||||
end
|
||||
end
|
||||
|
||||
function HTTP.onWsOpen(operationId, message)
|
||||
local operation = HTTP.operations[operationId]
|
||||
if operation == nil then
|
||||
return
|
||||
end
|
||||
if operation.callbacks.onOpen then
|
||||
operation.callbacks.onOpen(message, operationId)
|
||||
end
|
||||
end
|
||||
|
||||
function HTTP.onWsMessage(operationId, message)
|
||||
local operation = HTTP.operations[operationId]
|
||||
if operation == nil then
|
||||
return
|
||||
end
|
||||
if operation.callbacks.onMessage then
|
||||
if operation.json then
|
||||
local status, result = pcall(function() return json.decode(message) end)
|
||||
local err = nil
|
||||
if not status then
|
||||
err = "JSON ERROR: " .. result
|
||||
if message and message:len() > 0 then
|
||||
err = err .. " (" .. message:sub(1, 100) .. ")"
|
||||
end
|
||||
end
|
||||
if err then
|
||||
if operation.callbacks.onError then
|
||||
operation.callbacks.onError(err, operationId)
|
||||
end
|
||||
else
|
||||
operation.callbacks.onMessage(result, operationId)
|
||||
end
|
||||
else
|
||||
operation.callbacks.onMessage(message, operationId)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function HTTP.onWsClose(operationId, message)
|
||||
local operation = HTTP.operations[operationId]
|
||||
if operation == nil then
|
||||
return
|
||||
end
|
||||
if operation.callbacks.onClose then
|
||||
operation.callbacks.onClose(message, operationId)
|
||||
end
|
||||
end
|
||||
|
||||
function HTTP.onWsError(operationId, message)
|
||||
local operation = HTTP.operations[operationId]
|
||||
if operation == nil then
|
||||
return
|
||||
end
|
||||
if operation.callbacks.onError then
|
||||
operation.callbacks.onError(message, operationId)
|
||||
end
|
||||
end
|
||||
|
||||
connect(g_http,
|
||||
{
|
||||
onGet = HTTP.onGet,
|
||||
onGetProgress = HTTP.onGetProgress,
|
||||
onPost = HTTP.onPost,
|
||||
onPostProgress = HTTP.onPostProgress,
|
||||
onDownload = HTTP.onDownload,
|
||||
onDownloadProgress = HTTP.onDownloadProgress,
|
||||
onWsOpen = HTTP.onWsOpen,
|
||||
onWsMessage = HTTP.onWsMessage,
|
||||
onWsClose = HTTP.onWsClose,
|
||||
onWsError = HTTP.onWsError,
|
||||
})
|
||||
g_http.setUserAgent(HTTP.agent)
|
||||
|
@@ -1,419 +1,419 @@
|
||||
--
|
||||
-- json.lua
|
||||
--
|
||||
-- Copyright (c) 2019 rxi
|
||||
--
|
||||
-- Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
-- this software and associated documentation files (the "Software"), to deal in
|
||||
-- the Software without restriction, including without limitation the rights to
|
||||
-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
-- of the Software, and to permit persons to whom the Software is furnished to do
|
||||
-- so, subject to the following conditions:
|
||||
--
|
||||
-- The above copyright notice and this permission notice shall be included in all
|
||||
-- copies or substantial portions of the Software.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
-- SOFTWARE.
|
||||
--
|
||||
|
||||
json = { _version = "0.1.1" }
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Encode
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local encode
|
||||
|
||||
local escape_char_map = {
|
||||
[ "\\" ] = "\\\\",
|
||||
[ "\"" ] = "\\\"",
|
||||
[ "\b" ] = "\\b",
|
||||
[ "\f" ] = "\\f",
|
||||
[ "\n" ] = "\\n",
|
||||
[ "\r" ] = "\\r",
|
||||
[ "\t" ] = "\\t",
|
||||
}
|
||||
|
||||
local escape_char_map_inv = { [ "\\/" ] = "/" }
|
||||
for k, v in pairs(escape_char_map) do
|
||||
escape_char_map_inv[v] = k
|
||||
end
|
||||
|
||||
|
||||
local function make_indent(state)
|
||||
return string.rep(" ", state.currentIndentLevel * state.indent)
|
||||
end
|
||||
|
||||
|
||||
local function escape_char(c)
|
||||
return escape_char_map[c] or string.format("\\u%04x", c:byte())
|
||||
end
|
||||
|
||||
|
||||
local function encode_nil()
|
||||
return "null"
|
||||
end
|
||||
|
||||
|
||||
local function encode_table(val, state)
|
||||
local res = {}
|
||||
local stack = state.stack
|
||||
local pretty = state.indent > 0
|
||||
|
||||
local close_indent = make_indent(state)
|
||||
local comma = pretty and ",\n" or ","
|
||||
local colon = pretty and ": " or ":"
|
||||
local open_brace = pretty and "{\n" or "{"
|
||||
local close_brace = pretty and ("\n" .. close_indent .. "}") or "}"
|
||||
local open_bracket = pretty and "[\n" or "["
|
||||
local close_bracket = pretty and ("\n" .. close_indent .. "]") or "]"
|
||||
|
||||
-- Circular reference?
|
||||
if stack[val] then error("circular reference") end
|
||||
|
||||
stack[val] = true
|
||||
|
||||
if rawget(val, 1) ~= nil or next(val) == nil then
|
||||
-- Treat as array -- check keys are valid and it is not sparse
|
||||
local n = 0
|
||||
for k in pairs(val) do
|
||||
if type(k) ~= "number" then
|
||||
error("invalid table: mixed or invalid key types")
|
||||
end
|
||||
n = n + 1
|
||||
end
|
||||
if n ~= #val then
|
||||
error("invalid table: sparse array")
|
||||
end
|
||||
-- Encode
|
||||
for _, v in ipairs(val) do
|
||||
state.currentIndentLevel = state.currentIndentLevel + 1
|
||||
table.insert(res, make_indent(state) .. encode(v, state))
|
||||
state.currentIndentLevel = state.currentIndentLevel - 1
|
||||
end
|
||||
stack[val] = nil
|
||||
return open_bracket .. table.concat(res, comma) .. close_bracket
|
||||
|
||||
else
|
||||
-- Treat as an object
|
||||
for k, v in pairs(val) do
|
||||
if type(k) ~= "string" then
|
||||
error("invalid table: mixed or invalid key types")
|
||||
end
|
||||
state.currentIndentLevel = state.currentIndentLevel + 1
|
||||
table.insert(res, make_indent(state) .. encode(k, state) .. colon .. encode(v, state))
|
||||
state.currentIndentLevel = state.currentIndentLevel - 1
|
||||
end
|
||||
stack[val] = nil
|
||||
return open_brace .. table.concat(res, comma) .. close_brace
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function encode_string(val)
|
||||
return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
|
||||
end
|
||||
|
||||
|
||||
local function encode_number(val)
|
||||
-- Check for NaN, -inf and inf
|
||||
if val ~= val or val <= -math.huge or val >= math.huge then
|
||||
error("unexpected number value '" .. tostring(val) .. "'")
|
||||
end
|
||||
return string.format("%.14g", val)
|
||||
end
|
||||
|
||||
|
||||
local type_func_map = {
|
||||
[ "nil" ] = encode_nil,
|
||||
[ "table" ] = encode_table,
|
||||
[ "string" ] = encode_string,
|
||||
[ "number" ] = encode_number,
|
||||
[ "boolean" ] = tostring,
|
||||
}
|
||||
|
||||
|
||||
encode = function(val, state)
|
||||
local t = type(val)
|
||||
local f = type_func_map[t]
|
||||
if f then
|
||||
return f(val, state)
|
||||
end
|
||||
error("unexpected type '" .. t .. "'")
|
||||
end
|
||||
|
||||
function json.encode(val, indent)
|
||||
local state = {
|
||||
indent = indent or 0,
|
||||
currentIndentLevel = 0,
|
||||
stack = {}
|
||||
}
|
||||
return encode(val, state)
|
||||
end
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Decode
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local parse
|
||||
|
||||
local function create_set(...)
|
||||
local res = {}
|
||||
for i = 1, select("#", ...) do
|
||||
res[ select(i, ...) ] = true
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
local space_chars = create_set(" ", "\t", "\r", "\n")
|
||||
local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
|
||||
local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
|
||||
local literals = create_set("true", "false", "null")
|
||||
|
||||
local literal_map = {
|
||||
[ "true" ] = true,
|
||||
[ "false" ] = false,
|
||||
[ "null" ] = nil,
|
||||
}
|
||||
|
||||
|
||||
local function next_char(str, idx, set, negate)
|
||||
for i = idx, #str do
|
||||
if set[str:sub(i, i)] ~= negate then
|
||||
return i
|
||||
end
|
||||
end
|
||||
return #str + 1
|
||||
end
|
||||
|
||||
|
||||
local function decode_error(str, idx, msg)
|
||||
local line_count = 1
|
||||
local col_count = 1
|
||||
for i = 1, idx - 1 do
|
||||
col_count = col_count + 1
|
||||
if str:sub(i, i) == "\n" then
|
||||
line_count = line_count + 1
|
||||
col_count = 1
|
||||
end
|
||||
end
|
||||
error( string.format("%s at line %d col %d", msg, line_count, col_count) )
|
||||
end
|
||||
|
||||
|
||||
local function codepoint_to_utf8(n)
|
||||
-- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
|
||||
local f = math.floor
|
||||
if n <= 0x7f then
|
||||
return string.char(n)
|
||||
elseif n <= 0x7ff then
|
||||
return string.char(f(n / 64) + 192, n % 64 + 128)
|
||||
elseif n <= 0xffff then
|
||||
return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||
elseif n <= 0x10ffff then
|
||||
return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
|
||||
f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||
end
|
||||
error( string.format("invalid unicode codepoint '%x'", n) )
|
||||
end
|
||||
|
||||
|
||||
local function parse_unicode_escape(s)
|
||||
local n1 = tonumber( s:sub(3, 6), 16 )
|
||||
local n2 = tonumber( s:sub(9, 12), 16 )
|
||||
-- Surrogate pair?
|
||||
if n2 then
|
||||
return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
|
||||
else
|
||||
return codepoint_to_utf8(n1)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function parse_string(str, i)
|
||||
local has_unicode_escape = false
|
||||
local has_surrogate_escape = false
|
||||
local has_escape = false
|
||||
local last
|
||||
for j = i + 1, #str do
|
||||
local x = str:byte(j)
|
||||
|
||||
if x < 32 then
|
||||
decode_error(str, j, "control character in string")
|
||||
end
|
||||
|
||||
if last == 92 then -- "\\" (escape char)
|
||||
if x == 117 then -- "u" (unicode escape sequence)
|
||||
local hex = str:sub(j + 1, j + 5)
|
||||
if not hex:find("%x%x%x%x") then
|
||||
decode_error(str, j, "invalid unicode escape in string")
|
||||
end
|
||||
if hex:find("^[dD][89aAbB]") then
|
||||
has_surrogate_escape = true
|
||||
else
|
||||
has_unicode_escape = true
|
||||
end
|
||||
else
|
||||
local c = string.char(x)
|
||||
if not escape_chars[c] then
|
||||
decode_error(str, j, "invalid escape char '" .. c .. "' in string")
|
||||
end
|
||||
has_escape = true
|
||||
end
|
||||
last = nil
|
||||
|
||||
elseif x == 34 then -- '"' (end of string)
|
||||
local s = str:sub(i + 1, j - 1)
|
||||
if has_surrogate_escape then
|
||||
s = s:gsub("\\u[dD][89aAbB]..\\u....", parse_unicode_escape)
|
||||
end
|
||||
if has_unicode_escape then
|
||||
s = s:gsub("\\u....", parse_unicode_escape)
|
||||
end
|
||||
if has_escape then
|
||||
s = s:gsub("\\.", escape_char_map_inv)
|
||||
end
|
||||
return s, j + 1
|
||||
|
||||
else
|
||||
last = x
|
||||
end
|
||||
end
|
||||
decode_error(str, i, "expected closing quote for string")
|
||||
end
|
||||
|
||||
|
||||
local function parse_number(str, i)
|
||||
local x = next_char(str, i, delim_chars)
|
||||
local s = str:sub(i, x - 1)
|
||||
local n = tonumber(s)
|
||||
if not n then
|
||||
decode_error(str, i, "invalid number '" .. s .. "'")
|
||||
end
|
||||
return n, x
|
||||
end
|
||||
|
||||
|
||||
local function parse_literal(str, i)
|
||||
local x = next_char(str, i, delim_chars)
|
||||
local word = str:sub(i, x - 1)
|
||||
if not literals[word] then
|
||||
decode_error(str, i, "invalid literal '" .. word .. "'")
|
||||
end
|
||||
return literal_map[word], x
|
||||
end
|
||||
|
||||
|
||||
local function parse_array(str, i)
|
||||
local res = {}
|
||||
local n = 1
|
||||
i = i + 1
|
||||
while 1 do
|
||||
local x
|
||||
i = next_char(str, i, space_chars, true)
|
||||
-- Empty / end of array?
|
||||
if str:sub(i, i) == "]" then
|
||||
i = i + 1
|
||||
break
|
||||
end
|
||||
-- Read token
|
||||
x, i = parse(str, i)
|
||||
res[n] = x
|
||||
n = n + 1
|
||||
-- Next token
|
||||
i = next_char(str, i, space_chars, true)
|
||||
local chr = str:sub(i, i)
|
||||
i = i + 1
|
||||
if chr == "]" then break end
|
||||
if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
|
||||
end
|
||||
return res, i
|
||||
end
|
||||
|
||||
|
||||
local function parse_object(str, i)
|
||||
local res = {}
|
||||
i = i + 1
|
||||
while 1 do
|
||||
local key, val
|
||||
i = next_char(str, i, space_chars, true)
|
||||
-- Empty / end of object?
|
||||
if str:sub(i, i) == "}" then
|
||||
i = i + 1
|
||||
break
|
||||
end
|
||||
-- Read key
|
||||
if str:sub(i, i) ~= '"' then
|
||||
decode_error(str, i, "expected string for key")
|
||||
end
|
||||
key, i = parse(str, i)
|
||||
-- Read ':' delimiter
|
||||
i = next_char(str, i, space_chars, true)
|
||||
if str:sub(i, i) ~= ":" then
|
||||
decode_error(str, i, "expected ':' after key")
|
||||
end
|
||||
i = next_char(str, i + 1, space_chars, true)
|
||||
-- Read value
|
||||
val, i = parse(str, i)
|
||||
-- Set
|
||||
res[key] = val
|
||||
-- Next token
|
||||
i = next_char(str, i, space_chars, true)
|
||||
local chr = str:sub(i, i)
|
||||
i = i + 1
|
||||
if chr == "}" then break end
|
||||
if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
|
||||
end
|
||||
return res, i
|
||||
end
|
||||
|
||||
|
||||
local char_func_map = {
|
||||
[ '"' ] = parse_string,
|
||||
[ "0" ] = parse_number,
|
||||
[ "1" ] = parse_number,
|
||||
[ "2" ] = parse_number,
|
||||
[ "3" ] = parse_number,
|
||||
[ "4" ] = parse_number,
|
||||
[ "5" ] = parse_number,
|
||||
[ "6" ] = parse_number,
|
||||
[ "7" ] = parse_number,
|
||||
[ "8" ] = parse_number,
|
||||
[ "9" ] = parse_number,
|
||||
[ "-" ] = parse_number,
|
||||
[ "t" ] = parse_literal,
|
||||
[ "f" ] = parse_literal,
|
||||
[ "n" ] = parse_literal,
|
||||
[ "[" ] = parse_array,
|
||||
[ "{" ] = parse_object,
|
||||
}
|
||||
|
||||
|
||||
parse = function(str, idx)
|
||||
local chr = str:sub(idx, idx)
|
||||
local f = char_func_map[chr]
|
||||
if f then
|
||||
return f(str, idx)
|
||||
end
|
||||
decode_error(str, idx, "unexpected character '" .. chr .. "'")
|
||||
end
|
||||
|
||||
|
||||
function json.decode(str)
|
||||
if type(str) ~= "string" then
|
||||
error("expected argument of type string, got " .. type(str))
|
||||
end
|
||||
local res, idx = parse(str, next_char(str, 1, space_chars, true))
|
||||
idx = next_char(str, idx, space_chars, true)
|
||||
if idx <= #str then
|
||||
decode_error(str, idx, "trailing garbage")
|
||||
end
|
||||
return res
|
||||
end
|
||||
--
|
||||
-- json.lua
|
||||
--
|
||||
-- Copyright (c) 2019 rxi
|
||||
--
|
||||
-- Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
-- this software and associated documentation files (the "Software"), to deal in
|
||||
-- the Software without restriction, including without limitation the rights to
|
||||
-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
-- of the Software, and to permit persons to whom the Software is furnished to do
|
||||
-- so, subject to the following conditions:
|
||||
--
|
||||
-- The above copyright notice and this permission notice shall be included in all
|
||||
-- copies or substantial portions of the Software.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
-- SOFTWARE.
|
||||
--
|
||||
|
||||
json = { _version = "0.1.1" }
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Encode
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local encode
|
||||
|
||||
local escape_char_map = {
|
||||
[ "\\" ] = "\\\\",
|
||||
[ "\"" ] = "\\\"",
|
||||
[ "\b" ] = "\\b",
|
||||
[ "\f" ] = "\\f",
|
||||
[ "\n" ] = "\\n",
|
||||
[ "\r" ] = "\\r",
|
||||
[ "\t" ] = "\\t",
|
||||
}
|
||||
|
||||
local escape_char_map_inv = { [ "\\/" ] = "/" }
|
||||
for k, v in pairs(escape_char_map) do
|
||||
escape_char_map_inv[v] = k
|
||||
end
|
||||
|
||||
|
||||
local function make_indent(state)
|
||||
return string.rep(" ", state.currentIndentLevel * state.indent)
|
||||
end
|
||||
|
||||
|
||||
local function escape_char(c)
|
||||
return escape_char_map[c] or string.format("\\u%04x", c:byte())
|
||||
end
|
||||
|
||||
|
||||
local function encode_nil()
|
||||
return "null"
|
||||
end
|
||||
|
||||
|
||||
local function encode_table(val, state)
|
||||
local res = {}
|
||||
local stack = state.stack
|
||||
local pretty = state.indent > 0
|
||||
|
||||
local close_indent = make_indent(state)
|
||||
local comma = pretty and ",\n" or ","
|
||||
local colon = pretty and ": " or ":"
|
||||
local open_brace = pretty and "{\n" or "{"
|
||||
local close_brace = pretty and ("\n" .. close_indent .. "}") or "}"
|
||||
local open_bracket = pretty and "[\n" or "["
|
||||
local close_bracket = pretty and ("\n" .. close_indent .. "]") or "]"
|
||||
|
||||
-- Circular reference?
|
||||
if stack[val] then error("circular reference") end
|
||||
|
||||
stack[val] = true
|
||||
|
||||
if rawget(val, 1) ~= nil or next(val) == nil then
|
||||
-- Treat as array -- check keys are valid and it is not sparse
|
||||
local n = 0
|
||||
for k in pairs(val) do
|
||||
if type(k) ~= "number" then
|
||||
error("invalid table: mixed or invalid key types")
|
||||
end
|
||||
n = n + 1
|
||||
end
|
||||
if n ~= #val then
|
||||
error("invalid table: sparse array")
|
||||
end
|
||||
-- Encode
|
||||
for _, v in ipairs(val) do
|
||||
state.currentIndentLevel = state.currentIndentLevel + 1
|
||||
table.insert(res, make_indent(state) .. encode(v, state))
|
||||
state.currentIndentLevel = state.currentIndentLevel - 1
|
||||
end
|
||||
stack[val] = nil
|
||||
return open_bracket .. table.concat(res, comma) .. close_bracket
|
||||
|
||||
else
|
||||
-- Treat as an object
|
||||
for k, v in pairs(val) do
|
||||
if type(k) ~= "string" then
|
||||
error("invalid table: mixed or invalid key types")
|
||||
end
|
||||
state.currentIndentLevel = state.currentIndentLevel + 1
|
||||
table.insert(res, make_indent(state) .. encode(k, state) .. colon .. encode(v, state))
|
||||
state.currentIndentLevel = state.currentIndentLevel - 1
|
||||
end
|
||||
stack[val] = nil
|
||||
return open_brace .. table.concat(res, comma) .. close_brace
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function encode_string(val)
|
||||
return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
|
||||
end
|
||||
|
||||
|
||||
local function encode_number(val)
|
||||
-- Check for NaN, -inf and inf
|
||||
if val ~= val or val <= -math.huge or val >= math.huge then
|
||||
error("unexpected number value '" .. tostring(val) .. "'")
|
||||
end
|
||||
return string.format("%.14g", val)
|
||||
end
|
||||
|
||||
|
||||
local type_func_map = {
|
||||
[ "nil" ] = encode_nil,
|
||||
[ "table" ] = encode_table,
|
||||
[ "string" ] = encode_string,
|
||||
[ "number" ] = encode_number,
|
||||
[ "boolean" ] = tostring,
|
||||
}
|
||||
|
||||
|
||||
encode = function(val, state)
|
||||
local t = type(val)
|
||||
local f = type_func_map[t]
|
||||
if f then
|
||||
return f(val, state)
|
||||
end
|
||||
error("unexpected type '" .. t .. "'")
|
||||
end
|
||||
|
||||
function json.encode(val, indent)
|
||||
local state = {
|
||||
indent = indent or 0,
|
||||
currentIndentLevel = 0,
|
||||
stack = {}
|
||||
}
|
||||
return encode(val, state)
|
||||
end
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Decode
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local parse
|
||||
|
||||
local function create_set(...)
|
||||
local res = {}
|
||||
for i = 1, select("#", ...) do
|
||||
res[ select(i, ...) ] = true
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
local space_chars = create_set(" ", "\t", "\r", "\n")
|
||||
local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
|
||||
local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
|
||||
local literals = create_set("true", "false", "null")
|
||||
|
||||
local literal_map = {
|
||||
[ "true" ] = true,
|
||||
[ "false" ] = false,
|
||||
[ "null" ] = nil,
|
||||
}
|
||||
|
||||
|
||||
local function next_char(str, idx, set, negate)
|
||||
for i = idx, #str do
|
||||
if set[str:sub(i, i)] ~= negate then
|
||||
return i
|
||||
end
|
||||
end
|
||||
return #str + 1
|
||||
end
|
||||
|
||||
|
||||
local function decode_error(str, idx, msg)
|
||||
local line_count = 1
|
||||
local col_count = 1
|
||||
for i = 1, idx - 1 do
|
||||
col_count = col_count + 1
|
||||
if str:sub(i, i) == "\n" then
|
||||
line_count = line_count + 1
|
||||
col_count = 1
|
||||
end
|
||||
end
|
||||
error( string.format("%s at line %d col %d", msg, line_count, col_count) )
|
||||
end
|
||||
|
||||
|
||||
local function codepoint_to_utf8(n)
|
||||
-- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
|
||||
local f = math.floor
|
||||
if n <= 0x7f then
|
||||
return string.char(n)
|
||||
elseif n <= 0x7ff then
|
||||
return string.char(f(n / 64) + 192, n % 64 + 128)
|
||||
elseif n <= 0xffff then
|
||||
return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||
elseif n <= 0x10ffff then
|
||||
return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
|
||||
f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||
end
|
||||
error( string.format("invalid unicode codepoint '%x'", n) )
|
||||
end
|
||||
|
||||
|
||||
local function parse_unicode_escape(s)
|
||||
local n1 = tonumber( s:sub(3, 6), 16 )
|
||||
local n2 = tonumber( s:sub(9, 12), 16 )
|
||||
-- Surrogate pair?
|
||||
if n2 then
|
||||
return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
|
||||
else
|
||||
return codepoint_to_utf8(n1)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function parse_string(str, i)
|
||||
local has_unicode_escape = false
|
||||
local has_surrogate_escape = false
|
||||
local has_escape = false
|
||||
local last
|
||||
for j = i + 1, #str do
|
||||
local x = str:byte(j)
|
||||
|
||||
if x < 32 then
|
||||
decode_error(str, j, "control character in string")
|
||||
end
|
||||
|
||||
if last == 92 then -- "\\" (escape char)
|
||||
if x == 117 then -- "u" (unicode escape sequence)
|
||||
local hex = str:sub(j + 1, j + 5)
|
||||
if not hex:find("%x%x%x%x") then
|
||||
decode_error(str, j, "invalid unicode escape in string")
|
||||
end
|
||||
if hex:find("^[dD][89aAbB]") then
|
||||
has_surrogate_escape = true
|
||||
else
|
||||
has_unicode_escape = true
|
||||
end
|
||||
else
|
||||
local c = string.char(x)
|
||||
if not escape_chars[c] then
|
||||
decode_error(str, j, "invalid escape char '" .. c .. "' in string")
|
||||
end
|
||||
has_escape = true
|
||||
end
|
||||
last = nil
|
||||
|
||||
elseif x == 34 then -- '"' (end of string)
|
||||
local s = str:sub(i + 1, j - 1)
|
||||
if has_surrogate_escape then
|
||||
s = s:gsub("\\u[dD][89aAbB]..\\u....", parse_unicode_escape)
|
||||
end
|
||||
if has_unicode_escape then
|
||||
s = s:gsub("\\u....", parse_unicode_escape)
|
||||
end
|
||||
if has_escape then
|
||||
s = s:gsub("\\.", escape_char_map_inv)
|
||||
end
|
||||
return s, j + 1
|
||||
|
||||
else
|
||||
last = x
|
||||
end
|
||||
end
|
||||
decode_error(str, i, "expected closing quote for string")
|
||||
end
|
||||
|
||||
|
||||
local function parse_number(str, i)
|
||||
local x = next_char(str, i, delim_chars)
|
||||
local s = str:sub(i, x - 1)
|
||||
local n = tonumber(s)
|
||||
if not n then
|
||||
decode_error(str, i, "invalid number '" .. s .. "'")
|
||||
end
|
||||
return n, x
|
||||
end
|
||||
|
||||
|
||||
local function parse_literal(str, i)
|
||||
local x = next_char(str, i, delim_chars)
|
||||
local word = str:sub(i, x - 1)
|
||||
if not literals[word] then
|
||||
decode_error(str, i, "invalid literal '" .. word .. "'")
|
||||
end
|
||||
return literal_map[word], x
|
||||
end
|
||||
|
||||
|
||||
local function parse_array(str, i)
|
||||
local res = {}
|
||||
local n = 1
|
||||
i = i + 1
|
||||
while 1 do
|
||||
local x
|
||||
i = next_char(str, i, space_chars, true)
|
||||
-- Empty / end of array?
|
||||
if str:sub(i, i) == "]" then
|
||||
i = i + 1
|
||||
break
|
||||
end
|
||||
-- Read token
|
||||
x, i = parse(str, i)
|
||||
res[n] = x
|
||||
n = n + 1
|
||||
-- Next token
|
||||
i = next_char(str, i, space_chars, true)
|
||||
local chr = str:sub(i, i)
|
||||
i = i + 1
|
||||
if chr == "]" then break end
|
||||
if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
|
||||
end
|
||||
return res, i
|
||||
end
|
||||
|
||||
|
||||
local function parse_object(str, i)
|
||||
local res = {}
|
||||
i = i + 1
|
||||
while 1 do
|
||||
local key, val
|
||||
i = next_char(str, i, space_chars, true)
|
||||
-- Empty / end of object?
|
||||
if str:sub(i, i) == "}" then
|
||||
i = i + 1
|
||||
break
|
||||
end
|
||||
-- Read key
|
||||
if str:sub(i, i) ~= '"' then
|
||||
decode_error(str, i, "expected string for key")
|
||||
end
|
||||
key, i = parse(str, i)
|
||||
-- Read ':' delimiter
|
||||
i = next_char(str, i, space_chars, true)
|
||||
if str:sub(i, i) ~= ":" then
|
||||
decode_error(str, i, "expected ':' after key")
|
||||
end
|
||||
i = next_char(str, i + 1, space_chars, true)
|
||||
-- Read value
|
||||
val, i = parse(str, i)
|
||||
-- Set
|
||||
res[key] = val
|
||||
-- Next token
|
||||
i = next_char(str, i, space_chars, true)
|
||||
local chr = str:sub(i, i)
|
||||
i = i + 1
|
||||
if chr == "}" then break end
|
||||
if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
|
||||
end
|
||||
return res, i
|
||||
end
|
||||
|
||||
|
||||
local char_func_map = {
|
||||
[ '"' ] = parse_string,
|
||||
[ "0" ] = parse_number,
|
||||
[ "1" ] = parse_number,
|
||||
[ "2" ] = parse_number,
|
||||
[ "3" ] = parse_number,
|
||||
[ "4" ] = parse_number,
|
||||
[ "5" ] = parse_number,
|
||||
[ "6" ] = parse_number,
|
||||
[ "7" ] = parse_number,
|
||||
[ "8" ] = parse_number,
|
||||
[ "9" ] = parse_number,
|
||||
[ "-" ] = parse_number,
|
||||
[ "t" ] = parse_literal,
|
||||
[ "f" ] = parse_literal,
|
||||
[ "n" ] = parse_literal,
|
||||
[ "[" ] = parse_array,
|
||||
[ "{" ] = parse_object,
|
||||
}
|
||||
|
||||
|
||||
parse = function(str, idx)
|
||||
local chr = str:sub(idx, idx)
|
||||
local f = char_func_map[chr]
|
||||
if f then
|
||||
return f(str, idx)
|
||||
end
|
||||
decode_error(str, idx, "unexpected character '" .. chr .. "'")
|
||||
end
|
||||
|
||||
|
||||
function json.decode(str)
|
||||
if type(str) ~= "string" then
|
||||
error("expected argument of type string, got " .. type(str))
|
||||
end
|
||||
local res, idx = parse(str, next_char(str, 1, space_chars, true))
|
||||
idx = next_char(str, idx, space_chars, true)
|
||||
if idx <= #str then
|
||||
decode_error(str, idx, "trailing garbage")
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
@@ -1,43 +1,43 @@
|
||||
function __genOrderedIndex( t )
|
||||
local orderedIndex = {}
|
||||
for key in pairs(t) do
|
||||
table.insert( orderedIndex, key )
|
||||
end
|
||||
table.sort( orderedIndex )
|
||||
return orderedIndex
|
||||
end
|
||||
|
||||
function orderedNext(t, state)
|
||||
-- Equivalent of the next function, but returns the keys in the alphabetic
|
||||
-- order. We use a temporary ordered key table that is stored in the
|
||||
-- table being iterated.
|
||||
|
||||
local key = nil
|
||||
--print("orderedNext: state = "..tostring(state) )
|
||||
if state == nil then
|
||||
-- the first time, generate the index
|
||||
t.__orderedIndex = __genOrderedIndex( t )
|
||||
key = t.__orderedIndex[1]
|
||||
else
|
||||
-- fetch the next value
|
||||
for i = 1,table.getn(t.__orderedIndex) do
|
||||
if t.__orderedIndex[i] == state then
|
||||
key = t.__orderedIndex[i+1]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if key then
|
||||
return key, t[key]
|
||||
end
|
||||
|
||||
-- no more value to return, cleanup
|
||||
t.__orderedIndex = nil
|
||||
return
|
||||
end
|
||||
|
||||
function orderedPairs(t)
|
||||
-- Equivalent of the pairs() function on tables. Allows to iterate
|
||||
-- in order
|
||||
return orderedNext, t, nil
|
||||
function __genOrderedIndex( t )
|
||||
local orderedIndex = {}
|
||||
for key in pairs(t) do
|
||||
table.insert( orderedIndex, key )
|
||||
end
|
||||
table.sort( orderedIndex )
|
||||
return orderedIndex
|
||||
end
|
||||
|
||||
function orderedNext(t, state)
|
||||
-- Equivalent of the next function, but returns the keys in the alphabetic
|
||||
-- order. We use a temporary ordered key table that is stored in the
|
||||
-- table being iterated.
|
||||
|
||||
local key = nil
|
||||
--print("orderedNext: state = "..tostring(state) )
|
||||
if state == nil then
|
||||
-- the first time, generate the index
|
||||
t.__orderedIndex = __genOrderedIndex( t )
|
||||
key = t.__orderedIndex[1]
|
||||
else
|
||||
-- fetch the next value
|
||||
for i = 1,table.getn(t.__orderedIndex) do
|
||||
if t.__orderedIndex[i] == state then
|
||||
key = t.__orderedIndex[i+1]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if key then
|
||||
return key, t[key]
|
||||
end
|
||||
|
||||
-- no more value to return, cleanup
|
||||
t.__orderedIndex = nil
|
||||
return
|
||||
end
|
||||
|
||||
function orderedPairs(t)
|
||||
-- Equivalent of the pairs() function on tables. Allows to iterate
|
||||
-- in order
|
||||
return orderedNext, t, nil
|
||||
end
|
@@ -139,7 +139,12 @@ function UIMiniWindow:setup()
|
||||
|
||||
local oldParent = self:getParent()
|
||||
|
||||
local settings = g_settings.getNode('MiniWindows')
|
||||
|
||||
local settings = {}
|
||||
if g_settings.getNodeSize('MiniWindows') < 100 then
|
||||
settings = g_settings.getNode('MiniWindows')
|
||||
end
|
||||
|
||||
if settings then
|
||||
local selfSettings = settings[self:getId()]
|
||||
if selfSettings then
|
||||
@@ -184,7 +189,7 @@ function UIMiniWindow:setup()
|
||||
self.miniLoaded = true
|
||||
|
||||
if self.save then
|
||||
if oldParent and oldParent:getClassName() == 'UIMiniWindowContainer' then
|
||||
if oldParent and oldParent:getClassName() == 'UIMiniWindowContainer' and not self.containerWindow then
|
||||
addEvent(function() oldParent:order() end)
|
||||
end
|
||||
if newParent and newParent:getClassName() == 'UIMiniWindowContainer' and newParent ~= oldParent then
|
||||
|
Reference in New Issue
Block a user