initial release
This commit is contained in:
1678
cosmic rage/lua/InfoBox.lua
Normal file
1678
cosmic rage/lua/InfoBox.lua
Normal file
File diff suppressed because it is too large
Load Diff
158
cosmic rage/lua/addxml.lua
Normal file
158
cosmic rage/lua/addxml.lua
Normal file
@@ -0,0 +1,158 @@
|
||||
-- addxml.lua
|
||||
|
||||
-- lets you add triggers, aliases, timers, macros using XML code
|
||||
--
|
||||
-- See: http://www.gammon.com.au/forum/?id=7123
|
||||
|
||||
--[[
|
||||
|
||||
This lets you add items by simply setting up a table of their attributes and then adding it.
|
||||
|
||||
Exposed functions are:
|
||||
|
||||
addxml.trigger -- add a trigger
|
||||
addxml.alias -- add an alias
|
||||
addxml.timer -- add a timer
|
||||
addxml.macro -- add a macro
|
||||
addxml.save -- convert one of the above back into a table
|
||||
|
||||
Example of adding a trigger:
|
||||
|
||||
require "addxml"
|
||||
addxml.trigger { match = "swordfish",
|
||||
regexp = true,
|
||||
['repeat'] = true, -- repeat is lua keyword
|
||||
send = "hi there",
|
||||
sequence = 50,
|
||||
enabled = true,
|
||||
name = "boris",
|
||||
}
|
||||
|
||||
Example of converting a trigger back into a table:
|
||||
|
||||
require "tprint"
|
||||
require "addxml"
|
||||
tprint (addxml.save ("trigger", "boris"))
|
||||
|
||||
--]]
|
||||
|
||||
|
||||
module (..., package.seeall)
|
||||
|
||||
local html_replacements = {
|
||||
["<"] = "<",
|
||||
[">"] = ">",
|
||||
["&"] = "&",
|
||||
['"'] = """,
|
||||
}
|
||||
|
||||
-- fix text so that < > & and double-quote are escaped
|
||||
local function fixhtml (s)
|
||||
|
||||
return (string.gsub (tostring (s), '[<>&"]',
|
||||
function (str)
|
||||
return html_replacements [str] or str
|
||||
end ))
|
||||
|
||||
end -- fixhtml
|
||||
|
||||
|
||||
local function GeneralAdd (t, which, plural)
|
||||
|
||||
assert (type (t) == "table", "Table must be supplied to add a " .. which)
|
||||
|
||||
local k, v
|
||||
local xml = {}
|
||||
|
||||
local send = fixhtml (t.send or "") -- send is done differently
|
||||
t.send = nil
|
||||
|
||||
-- turn into XML options
|
||||
for k, v in pairs (t) do
|
||||
|
||||
-- fix true/false to y/n
|
||||
if v == true then
|
||||
v = "y"
|
||||
elseif v == false then
|
||||
v = "n"
|
||||
end -- if true or false
|
||||
|
||||
table.insert (xml, k .. '="' .. fixhtml (v) .. '"')
|
||||
end -- for loop
|
||||
|
||||
assert (ImportXML (string.format (
|
||||
"<%s><%s %s ><send>%s</send></%s></%s>",
|
||||
plural, -- eg. triggers
|
||||
which, -- eg. trigger
|
||||
table.concat (xml, "\n"), -- eg. match="nick"
|
||||
send, -- eg. "go north"
|
||||
which, -- eg. trigger
|
||||
plural) -- eg. triggers
|
||||
) == 1, "Import of " .. which .. " failed")
|
||||
|
||||
end -- GeneralAdd
|
||||
|
||||
function trigger (t)
|
||||
GeneralAdd (t, "trigger", "triggers")
|
||||
-- force script entry-point resolution
|
||||
if t.name and t.script then
|
||||
SetTriggerOption (t.name, "script", t.script)
|
||||
end -- if trigger has a name, and a script name
|
||||
end -- addxml.trigger
|
||||
|
||||
function alias (t)
|
||||
GeneralAdd (t, "alias", "aliases")
|
||||
-- force script entry-point resolution
|
||||
if t.name and t.script then
|
||||
SetAliasOption (t.name, "script", t.script)
|
||||
end -- if alias has a name, and a script name
|
||||
end -- addxml.alias
|
||||
|
||||
function timer (t)
|
||||
GeneralAdd (t, "timer", "timers")
|
||||
-- force script entry-point resolution
|
||||
if t.name and t.script then
|
||||
SetTimerOption (t.name, "script", t.script)
|
||||
end -- if timer has a name, and a script name
|
||||
end -- addxml.timer
|
||||
|
||||
function macro (t)
|
||||
GeneralAdd (t, "macro", "macros")
|
||||
end -- addxml.macro
|
||||
|
||||
function save (which, name)
|
||||
|
||||
local typeconversion =
|
||||
{
|
||||
trigger = 0,
|
||||
alias = 1,
|
||||
timer = 2,
|
||||
macro = 3,
|
||||
-- variable = 4,
|
||||
-- keypad = 5
|
||||
}
|
||||
|
||||
local itemtype = assert (typeconversion [which], "Unknown type: " .. which)
|
||||
|
||||
local xml = ExportXML (itemtype, name)
|
||||
|
||||
-- if not found returns empty string
|
||||
assert (xml ~= "", "Can't find " .. which .. ": " .. name)
|
||||
|
||||
-- parse back into table entries
|
||||
local xmlnodes = assert (utils.xmlread (xml), "Bad XML")
|
||||
|
||||
-- all attributes should be a couple of levels down
|
||||
local result = xmlnodes.nodes [1].nodes [1].attributes
|
||||
|
||||
-- find "send" text
|
||||
|
||||
-- another level?
|
||||
if xmlnodes.nodes [1].nodes [1].nodes then
|
||||
if xmlnodes.nodes [1].nodes [1].nodes [1].name == "send" then
|
||||
result.send = xmlnodes.nodes [1].nodes [1].nodes [1].content
|
||||
end -- have a "send" field
|
||||
end -- have a child node
|
||||
|
||||
return result
|
||||
end -- addxml.save
|
||||
80
cosmic rage/lua/alphanum.lua
Normal file
80
cosmic rage/lua/alphanum.lua
Normal file
@@ -0,0 +1,80 @@
|
||||
-- alphanum.lua
|
||||
--
|
||||
-- Adapted somewhat from: http://www.davekoelle.com/files/alphanum.lua
|
||||
-- Also see: http://www.davekoelle.com/alphanum.html
|
||||
--
|
||||
-- Implements a sort function that does a more "human readable" sort order.
|
||||
-- It breaks the sort strings into "chunks" and then compares each one naturally,
|
||||
-- depending on whether it is a string or a number (eg. z9.doc compares less than z20.doc)
|
||||
-- It also does a case-insensitive compare (so "nick" and "Nick" come out together).
|
||||
|
||||
-- See also: http://www.gammon.com.au/forum/?id=8698
|
||||
|
||||
--[[
|
||||
Example:
|
||||
|
||||
require "alphanum"
|
||||
|
||||
t={
|
||||
"z18.doc","z19.doc","z2.doc","z16.doc","z17.doc",
|
||||
"z1.doc","z101.doc","z102.doc","z11.doc","z12.doc",
|
||||
"z13.doc","z14.doc","z15.doc","z20.doc","z3.doc",
|
||||
"z4.doc","z5.doc","z6.doc","z10.doc","z100.doc",
|
||||
"z7.doc","z8.doc","z9.doc", "Z9A.doc",
|
||||
}
|
||||
|
||||
table.sort(t, alphanum (t))
|
||||
|
||||
for i=1, #t do
|
||||
print(t[i])
|
||||
end
|
||||
|
||||
--]]
|
||||
|
||||
function alphanum (t)
|
||||
assert (type (t) == "table", "Must pass table to be sorted to alphanum")
|
||||
|
||||
local function chunkString(str)
|
||||
local c = {}
|
||||
for a, b in tostring (str):gmatch("(%d*)(%D*)") do
|
||||
if a ~= "" then c[#c+1] = tonumber(a) end
|
||||
if b ~= "" then c[#c+1] = b end
|
||||
end
|
||||
return c
|
||||
end
|
||||
|
||||
local chunks = {}
|
||||
-- build temporary table of the keys
|
||||
for i=1, #t do
|
||||
chunks [t [i]] = chunkString (t [i])
|
||||
end
|
||||
|
||||
return function (a, b) -- return our sort comparison function
|
||||
|
||||
-- lookup chunked information from previously-built table
|
||||
local achunks = chunks [a]
|
||||
local bchunks = chunks [b]
|
||||
|
||||
for i = 1, math.min (#achunks, #bchunks) do
|
||||
local as, bs = achunks [i], bchunks [i]
|
||||
|
||||
-- if one is a string, make them both strings
|
||||
if type (as) == "string" or type (bs) == "string" then
|
||||
as = (tostring (as)):upper ()
|
||||
bs = (tostring (bs)):upper ()
|
||||
end -- at least one is a string
|
||||
|
||||
-- if they are equal, move onto the next chunk
|
||||
if as ~= bs then
|
||||
return as < bs
|
||||
end -- if
|
||||
end -- for each chunk
|
||||
|
||||
-- still equal? the one with fewer chunks compares less
|
||||
return #achunks < #bchunks
|
||||
|
||||
end -- sort function
|
||||
|
||||
end -- alphanum
|
||||
|
||||
return alphanum
|
||||
29
cosmic rage/lua/check.lua
Normal file
29
cosmic rage/lua/check.lua
Normal file
@@ -0,0 +1,29 @@
|
||||
--
|
||||
-- check.lua
|
||||
--
|
||||
-- ----------------------------------------------------------
|
||||
-- return-code checker for MUSHclient functions that return error codes
|
||||
-- ----------------------------------------------------------
|
||||
--
|
||||
--[[
|
||||
|
||||
Call for those MUSHclient functions that return a result code (like eOK).
|
||||
Not all functions return such a code.
|
||||
|
||||
eg.
|
||||
|
||||
require "check
|
||||
check (SetVariable ("abc", "def")) --> works ok
|
||||
check (SetVariable ("abc-", "def")) --> The name of this object is invalid
|
||||
|
||||
--]]
|
||||
|
||||
function check (result)
|
||||
if result ~= error_code.eOK then
|
||||
error (error_desc [result] or
|
||||
string.format ("Unknown error code: %i", result),
|
||||
2) -- error level - whoever called this function
|
||||
end -- if
|
||||
end -- function check
|
||||
|
||||
return check
|
||||
90
cosmic rage/lua/checkplugin.lua
Normal file
90
cosmic rage/lua/checkplugin.lua
Normal file
@@ -0,0 +1,90 @@
|
||||
-- checkplugin.lua
|
||||
|
||||
-- Checks the nominated plugin is installed
|
||||
|
||||
function do_plugin_check_now (id, name)
|
||||
|
||||
local me -- who am I? plugin or main world script?
|
||||
local location -- location to attempt to load plugin from
|
||||
|
||||
-- allow for being called from main world script
|
||||
if GetPluginID () == "" then
|
||||
me = "world script"
|
||||
location = GetInfo (60)
|
||||
else
|
||||
me = GetPluginName () .. " plugin"
|
||||
location = GetPluginInfo(GetPluginID (), 20)
|
||||
end -- if
|
||||
|
||||
-- first check if installed
|
||||
if not IsPluginInstalled (id) then
|
||||
ColourNote ("white", "green", "Plugin '" .. name .. "' not installed. Attempting to install it...")
|
||||
LoadPlugin (location .. name .. ".xml")
|
||||
|
||||
if IsPluginInstalled (id) then
|
||||
ColourNote ("white", "green", "Success!")
|
||||
|
||||
-- here if still not installed
|
||||
else
|
||||
ColourNote ("white", "red", string.rep ("-", 80))
|
||||
ColourNote ("white", "red", "Plugin '" .. name .. "' not installed. Please download and install it.")
|
||||
ColourNote ("white", "red", "It is required for the correct operation of the " .. me)
|
||||
ColourNote ("white", "red", string.rep ("-", 80))
|
||||
return -- skip enabled check
|
||||
end -- if not installed
|
||||
end -- plugin was not installed
|
||||
|
||||
|
||||
-- now make sure enabled (suggested by Fiendish - version 4.74+ )
|
||||
|
||||
if not GetPluginInfo(id, 17) then
|
||||
ColourNote ("white", "green", "Plugin '" .. name .. "' not enabled. Attempting to enable it...")
|
||||
EnablePlugin(id, true)
|
||||
if GetPluginInfo(id, 17) then
|
||||
ColourNote ("white", "green", "Success!")
|
||||
else
|
||||
ColourNote ("white", "red", string.rep ("-", 80))
|
||||
ColourNote ("white", "red", "Plugin '" .. name .. "' not enabled. Please make sure it can be enabled.")
|
||||
ColourNote ("white", "red", "It is required for the correct operation of the " .. me)
|
||||
ColourNote ("white", "red", string.rep ("-", 80))
|
||||
end -- if
|
||||
end -- if not enabled
|
||||
|
||||
end -- do_plugin_check_now
|
||||
|
||||
|
||||
function checkplugin (id, name)
|
||||
|
||||
if GetOption ("enable_timers") ~= 1 then
|
||||
ColourNote ("white", "red", "WARNING! Timers not enabled. Plugin dependency checks will not work properly.")
|
||||
end -- if timers disabled
|
||||
|
||||
-- give them time to load
|
||||
DoAfterSpecial (2,
|
||||
"do_plugin_check_now ('" .. id .. "', '" .. name .. "')",
|
||||
sendto.script)
|
||||
end -- checkplugin
|
||||
|
||||
function load_ppi (id, name)
|
||||
local PPI = require "ppi"
|
||||
|
||||
local ppi = PPI.Load(id)
|
||||
if ppi then
|
||||
return ppi
|
||||
end
|
||||
|
||||
ColourNote ("white", "green", "Plugin '" .. name .. "' not installed. Attempting to install it...")
|
||||
LoadPlugin (GetPluginInfo(GetPluginID (), 20) .. name .. ".xml")
|
||||
|
||||
ppi = PPI.Load(id) -- try again
|
||||
if ppi then
|
||||
ColourNote ("white", "green", "Success!")
|
||||
return ppi
|
||||
end
|
||||
|
||||
ColourNote ("white", "red", string.rep ("-", 80))
|
||||
ColourNote ("white", "red", "Plugin '" .. name .. "' not installed. Please download and install it.")
|
||||
ColourNote ("white", "red", string.rep ("-", 80))
|
||||
|
||||
return nil
|
||||
end -- function load_ppi
|
||||
269
cosmic rage/lua/colors.lua
Normal file
269
cosmic rage/lua/colors.lua
Normal file
@@ -0,0 +1,269 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- Provides support for color manipulation in HSL color space.
|
||||
--
|
||||
-- http://sputnik.freewisdom.org/lib/colors/
|
||||
--
|
||||
-- License: MIT/X
|
||||
--
|
||||
-- (c) 2008 Yuri Takhteyev (yuri@freewisdom.org) *
|
||||
--
|
||||
-- * rgb_to_hsl() implementation was contributed by Markus Fleck-Graffe.
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module(..., package.seeall)
|
||||
|
||||
local Color = {}
|
||||
local Color_mt = {__metatable = {}, __index = Color}
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Instantiates a new "color".
|
||||
--
|
||||
-- @param H hue (0-360) _or_ an RGB string ("#930219")
|
||||
-- @param S saturation (0.0-1.0)
|
||||
-- @param L lightness (0.0-1.0)
|
||||
-- @return an instance of Color
|
||||
-----------------------------------------------------------------------------
|
||||
function new(H, S, L)
|
||||
if type(H) == "string" and H:sub(1,1)=="#" and H:len() == 7 then
|
||||
H, S, L = rgb_string_to_hsl(H)
|
||||
end
|
||||
assert(Color_mt)
|
||||
return setmetatable({H = H, S = S, L = L}, Color_mt)
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Converts an HSL triplet to RGB
|
||||
-- (see http://homepages.cwi.nl/~steven/css/hsl.html).
|
||||
--
|
||||
-- @param h hue (0-360)
|
||||
-- @param s saturation (0.0-1.0)
|
||||
-- @param L lightness (0.0-1.0)
|
||||
-- @return an R, G, and B component of RGB
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
function hsl_to_rgb(h, s, L)
|
||||
h = h/360
|
||||
local m1, m2
|
||||
if L<=0.5 then
|
||||
m2 = L*(s+1)
|
||||
else
|
||||
m2 = L+s-L*s
|
||||
end
|
||||
m1 = L*2-m2
|
||||
|
||||
local function _h2rgb(m1, m2, h)
|
||||
if h<0 then h = h+1 end
|
||||
if h>1 then h = h-1 end
|
||||
if h*6<1 then
|
||||
return m1+(m2-m1)*h*6
|
||||
elseif h*2<1 then
|
||||
return m2
|
||||
elseif h*3<2 then
|
||||
return m1+(m2-m1)*(2/3-h)*6
|
||||
else
|
||||
return m1
|
||||
end
|
||||
end
|
||||
|
||||
return _h2rgb(m1, m2, h+1/3), _h2rgb(m1, m2, h), _h2rgb(m1, m2, h-1/3)
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Converts an RGB triplet to HSL.
|
||||
-- (see http://easyrgb.com)
|
||||
--
|
||||
-- @param r red (0.0-1.0)
|
||||
-- @param g green (0.0-1.0)
|
||||
-- @param b blue (0.0-1.0)
|
||||
-- @return corresponding H, S and L components
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
function rgb_to_hsl(r, g, b)
|
||||
--r, g, b = r/255, g/255, b/255
|
||||
local min = math.min(r, g, b)
|
||||
local max = math.max(r, g, b)
|
||||
local delta = max - min
|
||||
|
||||
local h, s, l = 0, 0, ((min+max)/2)
|
||||
|
||||
if l > 0 and l < 0.5 then s = delta/(max+min) end
|
||||
if l >= 0.5 and l < 1 then s = delta/(2-max-min) end
|
||||
|
||||
if delta > 0 then
|
||||
if max == r and max ~= g then h = h + (g-b)/delta end
|
||||
if max == g and max ~= b then h = h + 2 + (b-r)/delta end
|
||||
if max == b and max ~= r then h = h + 4 + (r-g)/delta end
|
||||
h = h / 6;
|
||||
end
|
||||
|
||||
if h < 0 then h = h + 1 end
|
||||
if h > 1 then h = h - 1 end
|
||||
|
||||
return h * 360, s, l
|
||||
end
|
||||
|
||||
function rgb_string_to_hsl(rgb)
|
||||
return rgb_to_hsl(tonumber(rgb:sub(2,3), 16)/255,
|
||||
tonumber(rgb:sub(4,5), 16)/255,
|
||||
tonumber(rgb:sub(6,7), 16)/255)
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Converts the color to an RGB string.
|
||||
--
|
||||
-- @return a 6-digit RGB representation of the color prefixed
|
||||
-- with "#" (suitable for inclusion in HTML)
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
function Color:to_rgb()
|
||||
local rgb = {hsl_to_rgb(self.H, self.S, self.L)}
|
||||
local buffer = "#"
|
||||
for i,v in ipairs(rgb) do
|
||||
buffer = buffer..string.format("%02x",math.floor(v*255+0.5))
|
||||
end
|
||||
return buffer
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Creates a new color with hue different by delta.
|
||||
--
|
||||
-- @param delta a delta for hue.
|
||||
-- @return a new instance of Color.
|
||||
-----------------------------------------------------------------------------
|
||||
function Color:hue_offset(delta)
|
||||
return new((self.H + delta) % 360, self.S, self.L)
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Creates a complementary color.
|
||||
--
|
||||
-- @return a new instance of Color
|
||||
-----------------------------------------------------------------------------
|
||||
function Color:complementary()
|
||||
return self:hue_offset(180)
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Creates two neighboring colors (by hue), offset by "angle".
|
||||
--
|
||||
-- @param angle the difference in hue between this color and the
|
||||
-- neighbors
|
||||
-- @return two new instances of Color
|
||||
-----------------------------------------------------------------------------
|
||||
function Color:neighbors(angle)
|
||||
local angle = angle or 30
|
||||
return self:hue_offset(angle), self:hue_offset(360-angle)
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Creates two new colors to make a triadic color scheme.
|
||||
--
|
||||
-- @return two new instances of Color
|
||||
-----------------------------------------------------------------------------
|
||||
function Color:triadic()
|
||||
return self:neighbors(120)
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Creates two new colors, offset by angle from this colors complementary.
|
||||
--
|
||||
-- @param angle the difference in hue between the complementary and
|
||||
-- the returned colors
|
||||
-- @return two new instances of Color
|
||||
-----------------------------------------------------------------------------
|
||||
function Color:split_complementary(angle)
|
||||
return self:neighbors(180-(angle or 30))
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Creates a new color with saturation set to a new value.
|
||||
--
|
||||
-- @param saturation the new saturation value (0.0 - 1.0)
|
||||
-- @return a new instance of Color
|
||||
-----------------------------------------------------------------------------
|
||||
function Color:desaturate_to(saturation)
|
||||
return new(self.H, saturation, self.L)
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Creates a new color with saturation set to a old saturation times r.
|
||||
--
|
||||
-- @param r the multiplier for the new saturation
|
||||
-- @return a new instance of Color
|
||||
-----------------------------------------------------------------------------
|
||||
function Color:desaturate_by(r)
|
||||
return new(self.H, self.S*r, self.L)
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Creates a new color with lightness set to a new value.
|
||||
--
|
||||
-- @param lightness the new lightness value (0.0 - 1.0)
|
||||
-- @return a new instance of Color
|
||||
-----------------------------------------------------------------------------
|
||||
function Color:lighten_to(lightness)
|
||||
return new(self.H, self.S, lightness)
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Creates a new color with lightness set to a old lightness times r.
|
||||
--
|
||||
-- @param r the multiplier for the new lightness
|
||||
-- @return a new instance of Color
|
||||
-----------------------------------------------------------------------------
|
||||
function Color:lighten_by(r)
|
||||
return new(self.H, self.S, self.L*r)
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Creates n variations of this color using supplied function and returns
|
||||
-- them as a table.
|
||||
--
|
||||
-- @param f the function to create variations
|
||||
-- @param n the number of variations
|
||||
-- @return a table with n values containing the new colors
|
||||
-----------------------------------------------------------------------------
|
||||
function Color:variations(f, n)
|
||||
n = n or 5
|
||||
local results = {}
|
||||
for i=1,n do
|
||||
table.insert(results, f(self, i, n))
|
||||
end
|
||||
return results
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Creates n tints of this color and returns them as a table
|
||||
--
|
||||
-- @param n the number of tints
|
||||
-- @return a table with n values containing the new colors
|
||||
-----------------------------------------------------------------------------
|
||||
function Color:tints(n)
|
||||
local f = function (color, i, n)
|
||||
return color:lighten_to(color.L + (1-color.L)/n*i)
|
||||
end
|
||||
return self:variations(f, n)
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Creates n shades of this color and returns them as a table
|
||||
--
|
||||
-- @param n the number of shades
|
||||
-- @return a table with n values containing the new colors
|
||||
-----------------------------------------------------------------------------
|
||||
function Color:shades(n)
|
||||
local f = function (color, i, n)
|
||||
return color:lighten_to(color.L - (color.L)/n*i)
|
||||
end
|
||||
return self:variations(f, n)
|
||||
end
|
||||
|
||||
function Color:tint(r)
|
||||
return self:lighten_to(self.L + (1-self.L)*r)
|
||||
end
|
||||
|
||||
function Color:shade(r)
|
||||
return self:lighten_to(self.L - self.L*r)
|
||||
end
|
||||
|
||||
Color_mt.__tostring = Color.to_rgb
|
||||
223
cosmic rage/lua/commas.lua
Normal file
223
cosmic rage/lua/commas.lua
Normal file
@@ -0,0 +1,223 @@
|
||||
-- commas.lua
|
||||
-- ----------------------------------------------------------
|
||||
-- Rounding, duration, comma functions
|
||||
-- See forum thread:
|
||||
-- http://www.gammon.com.au/forum/?id=7805
|
||||
|
||||
--[[
|
||||
|
||||
This function rounds any number to the closest integer.
|
||||
The "tricky" case is exactly half-way.
|
||||
That is, should 1.5 round to 1 or 2? How about -1.5?
|
||||
|
||||
This function rounds 1.5 "up" to 2, and -1.5 "down" to -2.
|
||||
|
||||
--]]
|
||||
|
||||
-- ----------------------------------------------------------
|
||||
|
||||
|
||||
-- round "up" to absolute value, so we treat negative differently
|
||||
-- that is, round (-1.5) will return -2
|
||||
|
||||
function round (x)
|
||||
if x >= 0 then
|
||||
return math.floor (x + 0.5)
|
||||
end -- if positive
|
||||
|
||||
return math.ceil (x - 0.5)
|
||||
end -- function round
|
||||
|
||||
--[[
|
||||
|
||||
Duration
|
||||
|
||||
This function is designed to display a time interval in "short form".
|
||||
That is, rounded to the nearest major time interval. Some examples of intervals:
|
||||
|
||||
|
||||
* 3.6 days - displays "4 d"
|
||||
* 3.5 days - displays "4 d"
|
||||
* 3.4 days - displays "3 d"
|
||||
|
||||
* 3.6 hours - displays "4 h"
|
||||
* 3.5 hours - displays "4 h"
|
||||
* 3.4 hours - displays "3 h"
|
||||
|
||||
* 3.6 minutes - displays "4 m"
|
||||
* 3.5 minutes - displays "4 m"
|
||||
* 3.4 minutes - displays "3 m"
|
||||
|
||||
* 59 seconds - displays "59 s"
|
||||
* 58 seconds - displays "58 s"
|
||||
* 57 seconds - displays "57 s" ... and so on to "0 s"
|
||||
|
||||
|
||||
--]]
|
||||
|
||||
-- ----------------------------------------------------------
|
||||
|
||||
function convert_time (secs)
|
||||
|
||||
-- handle negative numbers
|
||||
local sign = ""
|
||||
if secs < 0 then
|
||||
secs = math.abs (secs)
|
||||
sign = "-"
|
||||
end -- if negative seconds
|
||||
|
||||
-- weeks
|
||||
if secs >= (60 * 60 * 24 * 6.5) then
|
||||
return sign .. round (secs / (60 * 60 * 24 * 7)) .. " w"
|
||||
end -- 6.5 or more days
|
||||
|
||||
-- days
|
||||
if secs >= (60 * 60 * 23.5) then
|
||||
return sign .. round (secs / (60 * 60 * 24)) .. " d"
|
||||
end -- 23.5 or more hours
|
||||
|
||||
-- hours
|
||||
if secs >= (60 * 59.5) then
|
||||
return sign .. round (secs / (60 * 60)) .. " h"
|
||||
end -- 59.5 or more minutes
|
||||
|
||||
-- minutes
|
||||
if secs >= 59.5 then
|
||||
return sign .. round (secs / 60) .. " m"
|
||||
end -- 59.5 or more seconds
|
||||
|
||||
-- seconds
|
||||
return sign .. round (secs) .. " s"
|
||||
end -- function convert_time
|
||||
|
||||
--[[
|
||||
|
||||
Commas in numbers
|
||||
|
||||
This function adds commas to big numbers.
|
||||
For example 123456 becomes "123,456".
|
||||
|
||||
--]]
|
||||
|
||||
-- ----------------------------------------------------------
|
||||
|
||||
function commas (num)
|
||||
assert (type (num) == "number" or
|
||||
type (num) == "string")
|
||||
|
||||
local result = ""
|
||||
|
||||
-- split number into 3 parts, eg. -1234.545e22
|
||||
-- sign = + or -
|
||||
-- before = 1234
|
||||
-- after = .545e22
|
||||
|
||||
local sign, before, after =
|
||||
string.match (tostring (num), "^([%+%-]?)(%d*)(%.?.*)$")
|
||||
|
||||
-- pull out batches of 3 digits from the end, put a comma before them
|
||||
|
||||
while string.len (before) > 3 do
|
||||
result = "," .. string.sub (before, -3, -1) .. result
|
||||
before = string.sub (before, 1, -4) -- remove last 3 digits
|
||||
end -- while
|
||||
|
||||
-- we want the original sign, any left-over digits, the comma part,
|
||||
-- and the stuff after the decimal point, if any
|
||||
return sign .. before .. result .. after
|
||||
|
||||
end -- function commas
|
||||
|
||||
|
||||
-- trim leading and trailing spaces from a string
|
||||
function trim (s)
|
||||
return (string.gsub (s, "^%s*(.-)%s*$", "%1"))
|
||||
end -- trim
|
||||
|
||||
|
||||
--[[
|
||||
|
||||
Shuffle a table
|
||||
|
||||
-- see: http://en.wikipedia.org/wiki/Fisher-Yates_shuffle
|
||||
|
||||
Example:
|
||||
|
||||
cards = { "Ace", "King", "Queen", "Jack", 10, 9, 8, 7, 6, 5, 4, 3, 2 }
|
||||
|
||||
shuffle (cards)
|
||||
|
||||
--]]
|
||||
|
||||
function shuffle(t)
|
||||
local n = #t
|
||||
|
||||
while n >= 2 do
|
||||
-- n is now the last pertinent index
|
||||
local k = math.random(n) -- 1 <= k <= n
|
||||
-- Quick swap
|
||||
t[n], t[k] = t[k], t[n]
|
||||
n = n - 1
|
||||
end
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
|
||||
--[[
|
||||
|
||||
Directory scanner.
|
||||
|
||||
Calls function "f" for every file found in a path starting at "path".
|
||||
Recurses to handle nested directories. Function "f" is called with two
|
||||
arguments: (full) filename, statistics
|
||||
|
||||
See utils.readdir for the exact names of the entries supplied for each file.
|
||||
|
||||
See: http://www.gammon.com.au/forum/?id=9906
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
plugins = {}
|
||||
|
||||
-- this function is called for every found file
|
||||
function load_file (name, stats)
|
||||
|
||||
if stats.size > 0 and
|
||||
string.match (name:lower (), "%.xml$") and
|
||||
not stats.hidden then
|
||||
table.insert (plugins, name)
|
||||
end -- if
|
||||
|
||||
end -- load_file
|
||||
|
||||
-- Scan plugins folder, passing each file found to "load_file" function.
|
||||
|
||||
scan_dir (GetInfo (60), load_file)
|
||||
|
||||
--]]
|
||||
|
||||
|
||||
function scan_dir (path, f)
|
||||
|
||||
-- find all files in that directory
|
||||
local t = assert (utils.readdir (path .. "\\*"))
|
||||
|
||||
for k, v in pairs (t) do
|
||||
|
||||
-- recurse to process subdirectory
|
||||
if v.directory then
|
||||
|
||||
if k ~= "." and k ~= ".." then
|
||||
scan_dir (path .. "\\" .. k, f)
|
||||
end -- not current or owner directory
|
||||
|
||||
else -- call supplied function
|
||||
f (path .. "\\" .. k, v)
|
||||
end -- if
|
||||
|
||||
end -- for
|
||||
|
||||
end -- scan_dir
|
||||
89
cosmic rage/lua/copytable.lua
Normal file
89
cosmic rage/lua/copytable.lua
Normal file
@@ -0,0 +1,89 @@
|
||||
-- copytable.lua
|
||||
|
||||
--[[
|
||||
|
||||
Table copying functions.
|
||||
|
||||
See: http://www.gammon.com.au/forum/?id=8042
|
||||
|
||||
Ideas by Shaun Biggs, David Haley, Nick Gammon
|
||||
|
||||
Date: 21st July 2007
|
||||
|
||||
This is intended to copy tables (make a real copy, rather than just the table
|
||||
reference).
|
||||
|
||||
You can do a deep or shallow copy.
|
||||
|
||||
Shallow: Simply copies the keys and values.
|
||||
If a value is a table, you will get the same table as in the original.
|
||||
|
||||
Deep: Copies keys and values recursively.
|
||||
If a value is a table, makes a copy of that table, and so on.
|
||||
|
||||
Deep copy based on: http://lua-users.org/wiki/CopyTable
|
||||
|
||||
Restrictions: Items must be "safe" to copy (eg. not file IO userdata).
|
||||
|
||||
The deep copied tables share the same metatable as the original ones.
|
||||
To change this, change the line:
|
||||
|
||||
return setmetatable(new_table, getmetatable(object))
|
||||
|
||||
to:
|
||||
|
||||
return setmetatable(new_table, _copy (getmetatable(object))
|
||||
|
||||
Example:
|
||||
|
||||
t1 = {
|
||||
m = { a = 1, b = 2 },
|
||||
n = { c = 3, d = 4 },
|
||||
}
|
||||
|
||||
require "copytable" -- load this file
|
||||
|
||||
t2 = copytable.shallow (t1) -- shallow copy
|
||||
t3 = copytable.deep (t1) -- copies sub tables as well
|
||||
|
||||
--]]
|
||||
|
||||
module (..., package.seeall)
|
||||
|
||||
|
||||
function deep (object)
|
||||
local lookup_table = {}
|
||||
|
||||
local function _copy (object)
|
||||
if type (object) ~= "table" then
|
||||
return object
|
||||
elseif lookup_table [object] then
|
||||
return lookup_table [object]
|
||||
end -- if
|
||||
|
||||
local new_table = {}
|
||||
lookup_table [object] = new_table
|
||||
|
||||
for index, value in pairs (object) do
|
||||
new_table [_copy (index)] = _copy (value)
|
||||
end -- for
|
||||
|
||||
return setmetatable (new_table, getmetatable (object))
|
||||
end -- function _copy
|
||||
|
||||
return _copy (object)
|
||||
end -- function deepcopy
|
||||
|
||||
function shallow (t)
|
||||
assert (type (t) == "table", "You must specify a table to copy")
|
||||
|
||||
local result = {}
|
||||
|
||||
for k, v in pairs (t) do
|
||||
result [k] = v
|
||||
end -- for each table element
|
||||
|
||||
-- copy the metatable
|
||||
return setmetatable (result, getmetatable (t))
|
||||
|
||||
end -- function shallow
|
||||
47
cosmic rage/lua/declare.lua
Normal file
47
cosmic rage/lua/declare.lua
Normal file
@@ -0,0 +1,47 @@
|
||||
-- declare.lua
|
||||
-- See: http://www.gammon.com.au/forum/?id=7327
|
||||
--
|
||||
-- If you use this inside a function you cannot access global variables that have
|
||||
-- not already been declared, and must declare all local variables
|
||||
|
||||
function force_declarations ()
|
||||
setfenv (2, setmetatable ({},
|
||||
{
|
||||
__index = function (t, n)
|
||||
error("variable '"..n.."' is not declared", 2)
|
||||
end,
|
||||
__newindex = function (t, n, v)
|
||||
error("assign to undeclared variable '"..n.."'", 2)
|
||||
end })
|
||||
)
|
||||
end -- force_declarations
|
||||
|
||||
return force_declarations
|
||||
|
||||
--[[
|
||||
|
||||
Example of use:
|
||||
|
||||
|
||||
require "declare"
|
||||
|
||||
function test (x)
|
||||
-- capture any global variables we want
|
||||
local print = print
|
||||
|
||||
-- after this we can't access global variables, and must declare local ones
|
||||
force_declarations ()
|
||||
|
||||
-- must declare every variable now before we use it
|
||||
local a, b, c
|
||||
|
||||
print (a)
|
||||
a = 1
|
||||
b = 2
|
||||
c = x * 2
|
||||
print (c)
|
||||
end -- test
|
||||
|
||||
test (1)
|
||||
|
||||
--]]
|
||||
186
cosmic rage/lua/gauge.lua
Normal file
186
cosmic rage/lua/gauge.lua
Normal file
@@ -0,0 +1,186 @@
|
||||
--[[
|
||||
|
||||
Function to draw a gauge (health bar).
|
||||
|
||||
You specify the starting point, width, and height.
|
||||
Also the name to show for the mouse-over window (eg. "HP).
|
||||
|
||||
Author: Nick Gammon
|
||||
Date: 14 February 2010
|
||||
|
||||
--]]
|
||||
|
||||
function gauge (win, -- miniwindow ID to draw in
|
||||
name, -- string, eg: "HP"
|
||||
current, max, -- current and max value (eg. 50, 100)
|
||||
-- if max is nil, then current is a percentage
|
||||
left, top, width, height, -- where to put it inside the window
|
||||
fg_colour, bg_colour, -- colour for bar, colour for unfilled part
|
||||
ticks, tick_colour, -- number of ticks to draw, and in what colour
|
||||
frame_colour, -- colour for frame around bar
|
||||
shadow_colour, -- colour for shadow, nil for no shadow
|
||||
no_gradient) -- don't use the gradient fill effect
|
||||
|
||||
local Fraction
|
||||
|
||||
if not current then
|
||||
return
|
||||
end -- if
|
||||
|
||||
-- max == nil, means current is a percentage
|
||||
if max then
|
||||
if max <= 0 then
|
||||
return
|
||||
end -- no divide by zero
|
||||
|
||||
Fraction = current / max
|
||||
else
|
||||
Fraction = current / 100
|
||||
end -- if
|
||||
|
||||
-- fraction in range 0 to 1
|
||||
Fraction = math.min (math.max (Fraction, 0), 1)
|
||||
|
||||
-- set up some defaults
|
||||
height = height or 15
|
||||
fg_colour = fg_colour or ColourNameToRGB "mediumblue"
|
||||
bg_colour = bg_colour or ColourNameToRGB "rosybrown"
|
||||
ticks = ticks or 5
|
||||
tick_colour = tick_colour or ColourNameToRGB "silver"
|
||||
frame_colour = frame_colour or ColourNameToRGB "lightgrey"
|
||||
|
||||
-- shadow
|
||||
if shadow_colour then
|
||||
WindowRectOp (win, miniwin.rect_fill, left + 2, top + 2, left + width + 2, top + height + 2, shadow_colour)
|
||||
end -- if
|
||||
|
||||
-- background colour - for un-filled part
|
||||
WindowRectOp (win, miniwin.rect_fill, left, top, left + width, top + height, bg_colour) -- fill entire box
|
||||
|
||||
-- how big filled part is
|
||||
local gauge_width = (width - 2) * Fraction
|
||||
|
||||
-- box size must be > 0 or WindowGradient fills the whole thing
|
||||
if math.floor (gauge_width) > 0 then
|
||||
|
||||
if no_gradient then
|
||||
WindowRectOp (win, miniwin.rect_fill, left+1, top, left+1+gauge_width, top+height, fg_colour)
|
||||
else
|
||||
-- top half
|
||||
WindowGradient (win, left, top,
|
||||
left + gauge_width, top + height / 2,
|
||||
0x000000, -- black
|
||||
fg_colour, 2) -- vertical top to bottom
|
||||
|
||||
-- bottom half
|
||||
WindowGradient (win, left, top + height / 2,
|
||||
left + gauge_width, top + height-1,
|
||||
fg_colour,
|
||||
0x000000, -- black
|
||||
2) -- vertical top to bottom
|
||||
end
|
||||
|
||||
end -- non-zero
|
||||
|
||||
-- draw tick marks if wanted
|
||||
if ticks > 0 then
|
||||
|
||||
-- show ticks (if there are 5 ticks there are 6 gaps)
|
||||
local ticks_at = width / (ticks + 1)
|
||||
|
||||
-- ticks
|
||||
for i = 1, ticks do
|
||||
WindowLine (win, left + (i * ticks_at), top,
|
||||
left + (i * ticks_at), top + height,
|
||||
tick_colour, 0, 1)
|
||||
end -- for
|
||||
|
||||
end -- ticks wanted
|
||||
|
||||
-- draw a box around it (frame)
|
||||
WindowRectOp (win, miniwin.rect_frame, left, top, left + width, top + height, frame_colour)
|
||||
|
||||
if name and #name > 0 then
|
||||
-- mouse-over information: add hotspot
|
||||
WindowAddHotspot (win, name, left, top, left + width, top + height,
|
||||
"", "", "", "", "", "", 0, 0)
|
||||
|
||||
-- store numeric values in case they mouse over it
|
||||
if max then
|
||||
WindowHotspotTooltip(win, name, string.format ("%s\t%i / %i (%i%%)",
|
||||
name, current, max, Fraction * 100) )
|
||||
else
|
||||
WindowHotspotTooltip(win, name, string.format ("%s\t(%i%%)",
|
||||
name, Fraction * 100) )
|
||||
end -- if
|
||||
|
||||
end -- hotspot wanted
|
||||
|
||||
end -- function gauge
|
||||
|
||||
-- find which element in an array has the largest text size
|
||||
function max_text_width (win, font_id, t, utf8)
|
||||
local max = 0
|
||||
for _, s in ipairs (t) do
|
||||
max = math.max (max, WindowTextWidth (win, font_id, s, utf8))
|
||||
end -- for each item
|
||||
return max
|
||||
end -- max_text_width
|
||||
|
||||
-- get font from preferred font list
|
||||
function get_preferred_font (t)
|
||||
local fonts = utils.getfontfamilies ()
|
||||
|
||||
-- convert to upper-case
|
||||
local f2 = {}
|
||||
for k in pairs (fonts) do
|
||||
f2 [k:upper ()] = true
|
||||
end -- for
|
||||
|
||||
for _, s in ipairs (t) do
|
||||
if f2 [s:upper ()] then
|
||||
return s
|
||||
end -- if
|
||||
end -- for each item
|
||||
|
||||
return "Courier"
|
||||
end -- get_preferred_font
|
||||
|
||||
function capitalize (x)
|
||||
return string.upper (string.sub(x, 1, 1)) .. string.lower (string.sub(x, 2))
|
||||
end -- capitalize
|
||||
|
||||
function draw_3d_box (win, left, top, width, height)
|
||||
local right = left + width
|
||||
local bottom = top + height
|
||||
|
||||
WindowCircleOp (win, miniwin.circle_round_rectangle, left, top, right, bottom, 0x505050, 0, 3, 0, 1) -- dark grey border (3 pixels)
|
||||
WindowCircleOp (win, miniwin.circle_round_rectangle, left + 1, top + 1, right - 1, bottom - 1, 0x7C7C7C, 0, 1, 0, 1) -- lighter inner border
|
||||
WindowCircleOp (win, miniwin.circle_round_rectangle, left + 2, top + 2, right - 2, bottom - 2, 0x000000, 0, 1, 0, 1) -- black inside that
|
||||
WindowLine (win, left + 1, top + 1, right - 1, top + 1, 0xC2C2C2, 0, 1) -- light top edge
|
||||
WindowLine (win, left + 1, top + 1, left + 1, bottom - 1, 0xC2C2C2, 0, 1) -- light left edge (for 3D look)
|
||||
end -- draw_3d_box
|
||||
|
||||
function draw_text_box (win, font, left, top, text, utf8, text_colour, fill_colour, border_colour)
|
||||
local width = WindowTextWidth (win, font, text, utf8)
|
||||
local font_height = WindowFontInfo (win, font, 1)
|
||||
|
||||
WindowRectOp (win, miniwin.rect_fill, left - 3, top, left + width + 3, top + font_height + 4, fill_colour) -- fill
|
||||
WindowText (win, font, text, left, top + 2, 0, 0, text_colour, utf8) -- draw text
|
||||
WindowRectOp (win, miniwin.rect_frame, left - 3, top, left + width + 3, top + font_height + 4, border_colour) -- border
|
||||
return width
|
||||
end -- draw_text_box
|
||||
|
||||
-- text with a black outline
|
||||
function outlined_text(win, font, text, startx, starty, endx, endy, color, utf8)
|
||||
WindowText(win, font, text, startx-1, starty-1, endx, endy, 0x000000, utf8)
|
||||
WindowText(win, font, text, startx-1, starty, endx, endy, 0x000000, utf8)
|
||||
WindowText(win, font, text, startx-1, starty+1, endx, endy, 0x000000, utf8)
|
||||
WindowText(win, font, text, startx, starty-1, endx, endy, 0x000000, utf8)
|
||||
WindowText(win, font, text, startx, starty+1, endx, endy, 0x000000, utf8)
|
||||
WindowText(win, font, text, startx+1, starty-1, endx, endy, 0x000000, utf8)
|
||||
WindowText(win, font, text, startx+1, starty, endx, endy, 0x000000, utf8)
|
||||
WindowText(win, font, text, startx+1, starty+1, endx, endy, 0x000000, utf8)
|
||||
WindowText(win, font, text, startx, starty, endx, endy, color, utf8)
|
||||
end
|
||||
|
||||
48
cosmic rage/lua/getlines.lua
Normal file
48
cosmic rage/lua/getlines.lua
Normal file
@@ -0,0 +1,48 @@
|
||||
-- getlines.lua
|
||||
-- getlines iterator - iterates over a string and returns one item per line
|
||||
|
||||
function getlines (str)
|
||||
|
||||
local pos = 0
|
||||
|
||||
-- the for loop calls this for every iteration
|
||||
-- returning nil terminates the loop
|
||||
local function iterator (s)
|
||||
|
||||
if not pos then
|
||||
return nil
|
||||
end -- end of string, exit loop
|
||||
|
||||
local oldpos = pos + 1 -- step past previous newline
|
||||
pos = string.find (s, "\n", oldpos) -- find next newline
|
||||
|
||||
if not pos then -- no more newlines, return rest of string
|
||||
return string.sub (s, oldpos)
|
||||
end -- no newline
|
||||
|
||||
return string.sub (s, oldpos, pos - 1)
|
||||
|
||||
end -- iterator
|
||||
|
||||
return iterator, str
|
||||
end -- getlines
|
||||
|
||||
return getlines
|
||||
|
||||
--[=[
|
||||
|
||||
Example of use:
|
||||
|
||||
require "getlines"
|
||||
|
||||
test = [[
|
||||
every good
|
||||
boy
|
||||
deserves
|
||||
fruit]]
|
||||
|
||||
for l in getlines (test) do
|
||||
print ('"' .. l .. '"')
|
||||
end -- for
|
||||
|
||||
--]=]
|
||||
49
cosmic rage/lua/getstyle.lua
Normal file
49
cosmic rage/lua/getstyle.lua
Normal file
@@ -0,0 +1,49 @@
|
||||
-- getstyle.lua
|
||||
--
|
||||
|
||||
--[[
|
||||
|
||||
See forum thread: http://www.gammon.com.au/forum/?id=7818
|
||||
|
||||
GetStyle:
|
||||
Finds a style run corresponding to a given column
|
||||
|
||||
Returns nil if style run not found (eg. column out of range)
|
||||
|
||||
If style run found returns:
|
||||
* the style table (see below)
|
||||
* the character at that column
|
||||
* the style run number (eg. style 3)
|
||||
|
||||
The style table should contain the following:
|
||||
|
||||
t.text --> text of that (entire) style run
|
||||
t.length --> length of the (entire) style run
|
||||
t.textcolour --> text colour (RGB number)
|
||||
t.backcolour --> background colour (RGB number)
|
||||
t.style --> style bits (1=bold, 2=underline, 4=italic)
|
||||
|
||||
--]]
|
||||
|
||||
function GetStyle (styleRuns, wantedColumn)
|
||||
local currentColumn = 1
|
||||
|
||||
-- check arguments
|
||||
assert (type (styleRuns) == "table",
|
||||
"First argument to GetStyle must be table of style runs")
|
||||
|
||||
assert (type (wantedColumn) == "number" and wantedColumn >= 1,
|
||||
"Second argument to GetStyle must be column number to find")
|
||||
|
||||
-- go through each style
|
||||
for item, style in ipairs (styleRuns) do
|
||||
local position = wantedColumn - currentColumn + 1 -- where letter is in style
|
||||
currentColumn = currentColumn + style.length -- next style starts here
|
||||
if currentColumn > wantedColumn then -- if we are within this style
|
||||
return style, string.sub (style.text, position, position), item -- done
|
||||
end -- if found column
|
||||
end -- for each style
|
||||
|
||||
-- if not found: result is nil
|
||||
|
||||
end -- function GetStyle
|
||||
111
cosmic rage/lua/getworld.lua
Normal file
111
cosmic rage/lua/getworld.lua
Normal file
@@ -0,0 +1,111 @@
|
||||
|
||||
-- table of worlds we couldn't open
|
||||
cannot_open_world = cannot_open_world or {} -- set flag here if can't open world
|
||||
-- getworld.lua
|
||||
--
|
||||
|
||||
--[[
|
||||
|
||||
See forum thread: http://www.gammon.com.au/forum/?id=7991
|
||||
|
||||
This simplifies sending triggered lines to another, dummy, world.
|
||||
|
||||
get_a_world (name) - returns a world pointer to the named world, opening it if necessary
|
||||
|
||||
send_to_world (name, styles) - sends the style runs to the named world, calling get_a_world
|
||||
to get it
|
||||
|
||||
--]]
|
||||
|
||||
|
||||
-- make the named world, if necessary - adds "extra" lines to the world file (eg. plugins)
|
||||
function make_world (name, extra, folder)
|
||||
|
||||
local filename = GetInfo (57)
|
||||
if folder then
|
||||
filename = filename .. folder .. "\\"
|
||||
end -- if folder wanted
|
||||
|
||||
filename = filename .. name .. ".mcl"
|
||||
local f = io.open (filename, "r")
|
||||
|
||||
if f then
|
||||
f:close ()
|
||||
return
|
||||
end -- world file exists
|
||||
|
||||
f = io.output (filename) -- create world file
|
||||
|
||||
assert (f:write ([[
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!DOCTYPE muclient>
|
||||
<!-- MUSHclient world file -->
|
||||
<!-- Written by Nick Gammon -->
|
||||
<!-- Home Page: http://www.mushclient.com/ -->
|
||||
<!-- Generated by getworld.lua plugin -->
|
||||
<muclient>
|
||||
<world defaults="y"
|
||||
name="]] .. name .. [["
|
||||
site="0.0.0.0"
|
||||
port="4000"
|
||||
/>
|
||||
]] .. extra .. [[
|
||||
|
||||
</muclient>
|
||||
]]))
|
||||
|
||||
f:close () -- close world file now
|
||||
|
||||
-- and open the file ;P
|
||||
Open (filename)
|
||||
|
||||
end -- make_world
|
||||
|
||||
-- open a world by name, return world object or nil if cannot
|
||||
function get_a_world (name, folder)
|
||||
|
||||
-- try to find world
|
||||
local w = GetWorld (name) -- get world
|
||||
|
||||
-- if not found, try to open it in worlds directory
|
||||
|
||||
if not cannot_open_world [name] and not w then
|
||||
local filename = GetInfo (57)
|
||||
if folder then
|
||||
filename = filename .. folder .. "\\"
|
||||
end -- if folder wanted
|
||||
|
||||
filename = filename .. name .. ".mcl"
|
||||
Open (filename) -- get MUSHclient to open it
|
||||
Activate () -- make our original world active again
|
||||
w = GetWorld (name) -- try again to get the world object
|
||||
if w then
|
||||
w:DeleteOutput () -- delete "welcome to MUSHclient" message
|
||||
else
|
||||
ColourNote ("white", "red", "Can't open world file: " .. filename)
|
||||
cannot_open_world [name] = true -- don't repeatedly show failure message
|
||||
end -- can't find world
|
||||
end -- can't find world first time around
|
||||
|
||||
return w
|
||||
|
||||
end -- get_a_world
|
||||
|
||||
-- send the styles (eg. from a trigger) to the named world, opening it if necessary
|
||||
function send_to_world (name, styles)
|
||||
|
||||
local w = get_a_world (name)
|
||||
|
||||
if w then -- if present
|
||||
for _, v in ipairs (styles) do
|
||||
w:ColourTell (RGBColourToName (v.textcolour),
|
||||
RGBColourToName (v.backcolour),
|
||||
v.text)
|
||||
end -- for each style run
|
||||
w:Note ("") -- wrap up line
|
||||
|
||||
end -- world found
|
||||
|
||||
return w -- so they can check if we succeeded
|
||||
|
||||
end -- send_to_world
|
||||
24
cosmic rage/lua/json.lua
Normal file
24
cosmic rage/lua/json.lua
Normal file
@@ -0,0 +1,24 @@
|
||||
--[[
|
||||
Licensed according to the included 'LICENSE' document
|
||||
Author: Thomas Harning Jr <harningt@gmail.com>
|
||||
]]
|
||||
local decode = require("json.decode")
|
||||
local encode = require("json.encode")
|
||||
local util = require("json.util")
|
||||
|
||||
local _G = _G
|
||||
|
||||
local _ENV = nil
|
||||
|
||||
local json = {
|
||||
_VERSION = "1.3.4",
|
||||
_DESCRIPTION = "LuaJSON : customizable JSON decoder/encoder",
|
||||
_COPYRIGHT = "Copyright (c) 2007-2014 Thomas Harning Jr. <harningt@gmail.com>",
|
||||
decode = decode,
|
||||
encode = encode,
|
||||
util = util
|
||||
}
|
||||
|
||||
_G.json = json
|
||||
|
||||
return json
|
||||
171
cosmic rage/lua/json/decode.lua
Normal file
171
cosmic rage/lua/json/decode.lua
Normal file
@@ -0,0 +1,171 @@
|
||||
--[[
|
||||
Licensed according to the included 'LICENSE' document
|
||||
Author: Thomas Harning Jr <harningt@gmail.com>
|
||||
]]
|
||||
local lpeg = require("lpeg")
|
||||
|
||||
local error = error
|
||||
local pcall = pcall
|
||||
|
||||
local jsonutil = require("json.util")
|
||||
local merge = jsonutil.merge
|
||||
local util = require("json.decode.util")
|
||||
|
||||
local decode_state = require("json.decode.state")
|
||||
|
||||
local setmetatable, getmetatable = setmetatable, getmetatable
|
||||
local assert = assert
|
||||
local ipairs, pairs = ipairs, pairs
|
||||
local string_char = require("string").char
|
||||
|
||||
local type = type
|
||||
|
||||
local require = require
|
||||
|
||||
local _ENV = nil
|
||||
|
||||
local modulesToLoad = {
|
||||
"composite",
|
||||
"strings",
|
||||
"number",
|
||||
"others"
|
||||
}
|
||||
local loadedModules = {
|
||||
}
|
||||
|
||||
local json_decode = {}
|
||||
|
||||
json_decode.default = {
|
||||
unicodeWhitespace = true,
|
||||
initialObject = false,
|
||||
nothrow = false
|
||||
}
|
||||
|
||||
local modes_defined = { "default", "strict", "simple" }
|
||||
|
||||
json_decode.simple = {}
|
||||
|
||||
json_decode.strict = {
|
||||
unicodeWhitespace = true,
|
||||
initialObject = true,
|
||||
nothrow = false
|
||||
}
|
||||
|
||||
for _,name in ipairs(modulesToLoad) do
|
||||
local mod = require("json.decode." .. name)
|
||||
if mod.mergeOptions then
|
||||
for _, mode in pairs(modes_defined) do
|
||||
mod.mergeOptions(json_decode[mode], mode)
|
||||
end
|
||||
end
|
||||
loadedModules[#loadedModules + 1] = mod
|
||||
end
|
||||
|
||||
-- Shift over default into defaultOptions to permit build optimization
|
||||
local defaultOptions = json_decode.default
|
||||
json_decode.default = nil
|
||||
|
||||
local function generateDecoder(lexer, options)
|
||||
-- Marker to permit detection of final end
|
||||
local marker = {}
|
||||
local parser = lpeg.Ct((options.ignored * lexer)^0 * lpeg.Cc(marker)) * options.ignored * (lpeg.P(-1) + util.unexpected())
|
||||
local decoder = function(data)
|
||||
local state = decode_state.create(options)
|
||||
local parsed = parser:match(data)
|
||||
assert(parsed, "Invalid JSON data")
|
||||
local i = 0
|
||||
while true do
|
||||
i = i + 1
|
||||
local item = parsed[i]
|
||||
if item == marker then break end
|
||||
if type(item) == 'function' and item ~= jsonutil.undefined and item ~= jsonutil.null then
|
||||
item(state)
|
||||
else
|
||||
state:set_value(item)
|
||||
end
|
||||
end
|
||||
if options.initialObject then
|
||||
assert(type(state.previous) == 'table', "Initial value not an object or array")
|
||||
end
|
||||
-- Make sure stack is empty
|
||||
assert(state.i == 0, "Unclosed elements present")
|
||||
return state.previous
|
||||
end
|
||||
if options.nothrow then
|
||||
return function(data)
|
||||
local status, rv = pcall(decoder, data)
|
||||
if status then
|
||||
return rv
|
||||
else
|
||||
return nil, rv
|
||||
end
|
||||
end
|
||||
end
|
||||
return decoder
|
||||
end
|
||||
|
||||
local function buildDecoder(mode)
|
||||
mode = mode and merge({}, defaultOptions, mode) or defaultOptions
|
||||
for _, mod in ipairs(loadedModules) do
|
||||
if mod.mergeOptions then
|
||||
mod.mergeOptions(mode)
|
||||
end
|
||||
end
|
||||
local ignored = mode.unicodeWhitespace and util.unicode_ignored or util.ascii_ignored
|
||||
-- Store 'ignored' in the global options table
|
||||
mode.ignored = ignored
|
||||
|
||||
--local grammar = {
|
||||
-- [1] = mode.initialObject and (ignored * (object_type + array_type)) or value_type
|
||||
--}
|
||||
local lexer
|
||||
for _, mod in ipairs(loadedModules) do
|
||||
local new_lexer = mod.generateLexer(mode)
|
||||
lexer = lexer and lexer + new_lexer or new_lexer
|
||||
end
|
||||
return generateDecoder(lexer, mode)
|
||||
end
|
||||
|
||||
-- Since 'default' is nil, we cannot take map it
|
||||
local defaultDecoder = buildDecoder(json_decode.default)
|
||||
local prebuilt_decoders = {}
|
||||
for _, mode in pairs(modes_defined) do
|
||||
if json_decode[mode] ~= nil then
|
||||
prebuilt_decoders[json_decode[mode]] = buildDecoder(json_decode[mode])
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
Options:
|
||||
number => number decode options
|
||||
string => string decode options
|
||||
array => array decode options
|
||||
object => object decode options
|
||||
initialObject => whether or not to require the initial object to be a table/array
|
||||
allowUndefined => whether or not to allow undefined values
|
||||
]]
|
||||
local function getDecoder(mode)
|
||||
mode = mode == true and json_decode.strict or mode or json_decode.default
|
||||
local decoder = mode == nil and defaultDecoder or prebuilt_decoders[mode]
|
||||
if decoder then
|
||||
return decoder
|
||||
end
|
||||
return buildDecoder(mode)
|
||||
end
|
||||
|
||||
local function decode(data, mode)
|
||||
local decoder = getDecoder(mode)
|
||||
return decoder(data)
|
||||
end
|
||||
|
||||
local mt = {}
|
||||
mt.__call = function(self, ...)
|
||||
return decode(...)
|
||||
end
|
||||
|
||||
json_decode.getDecoder = getDecoder
|
||||
json_decode.decode = decode
|
||||
json_decode.util = util
|
||||
setmetatable(json_decode, mt)
|
||||
|
||||
return json_decode
|
||||
64
cosmic rage/lua/json/decode/array.lua
Normal file
64
cosmic rage/lua/json/decode/array.lua
Normal file
@@ -0,0 +1,64 @@
|
||||
--[[
|
||||
Licensed according to the included 'LICENSE' document
|
||||
Author: Thomas Harning Jr <harningt@gmail.com>
|
||||
--]]
|
||||
local lpeg = require("lpeg")
|
||||
|
||||
local util = require("json.decode.util")
|
||||
local jsonutil = require("json.util")
|
||||
|
||||
local table_maxn = require("table").maxn
|
||||
|
||||
local unpack = unpack
|
||||
|
||||
module("json.decode.array")
|
||||
|
||||
-- Utility function to help manage slighly sparse arrays
|
||||
local function processArray(array)
|
||||
local max_n = table_maxn(array)
|
||||
-- Only populate 'n' if it is necessary
|
||||
if #array ~= max_n then
|
||||
array.n = max_n
|
||||
end
|
||||
if jsonutil.InitArray then
|
||||
array = jsonutil.InitArray(array) or array
|
||||
end
|
||||
return array
|
||||
end
|
||||
|
||||
local defaultOptions = {
|
||||
trailingComma = true
|
||||
}
|
||||
|
||||
default = nil -- Let the buildCapture optimization take place
|
||||
strict = {
|
||||
trailingComma = false
|
||||
}
|
||||
|
||||
local function buildCapture(options, global_options)
|
||||
local ignored = global_options.ignored
|
||||
-- arrayItem == element
|
||||
local arrayItem = lpeg.V(util.types.VALUE)
|
||||
local arrayElements = lpeg.Ct(arrayItem * (ignored * lpeg.P(',') * ignored * arrayItem)^0 + 0) / processArray
|
||||
|
||||
options = options and jsonutil.merge({}, defaultOptions, options) or defaultOptions
|
||||
local capture = lpeg.P("[")
|
||||
capture = capture * ignored
|
||||
* arrayElements * ignored
|
||||
if options.trailingComma then
|
||||
capture = capture * (lpeg.P(",") + 0) * ignored
|
||||
end
|
||||
capture = capture * lpeg.P("]")
|
||||
return capture
|
||||
end
|
||||
|
||||
function register_types()
|
||||
util.register_type("ARRAY")
|
||||
end
|
||||
|
||||
function load_types(options, global_options, grammar)
|
||||
local capture = buildCapture(options, global_options)
|
||||
local array_id = util.types.ARRAY
|
||||
grammar[array_id] = capture
|
||||
util.append_grammar_item(grammar, "VALUE", lpeg.V(array_id))
|
||||
end
|
||||
116
cosmic rage/lua/json/decode/calls.lua
Normal file
116
cosmic rage/lua/json/decode/calls.lua
Normal file
@@ -0,0 +1,116 @@
|
||||
--[[
|
||||
Licensed according to the included 'LICENSE' document
|
||||
Author: Thomas Harning Jr <harningt@gmail.com>
|
||||
--]]
|
||||
local lpeg = require("lpeg")
|
||||
local tostring = tostring
|
||||
local pairs, ipairs = pairs, ipairs
|
||||
local next, type = next, type
|
||||
local error = error
|
||||
|
||||
local util = require("json.decode.util")
|
||||
|
||||
local buildCall = require("json.util").buildCall
|
||||
|
||||
local getmetatable = getmetatable
|
||||
|
||||
module("json.decode.calls")
|
||||
|
||||
local defaultOptions = {
|
||||
defs = nil,
|
||||
-- By default, do not allow undefined calls to be de-serialized as call objects
|
||||
allowUndefined = false
|
||||
}
|
||||
|
||||
-- No real default-option handling needed...
|
||||
default = nil
|
||||
strict = nil
|
||||
|
||||
local isPattern
|
||||
if lpeg.type then
|
||||
function isPattern(value)
|
||||
return lpeg.type(value) == 'pattern'
|
||||
end
|
||||
else
|
||||
local metaAdd = getmetatable(lpeg.P("")).__add
|
||||
function isPattern(value)
|
||||
return getmetatable(value).__add == metaAdd
|
||||
end
|
||||
end
|
||||
|
||||
local function buildDefinedCaptures(argumentCapture, defs)
|
||||
local callCapture
|
||||
if not defs then return end
|
||||
for name, func in pairs(defs) do
|
||||
if type(name) ~= 'string' and not isPattern(name) then
|
||||
error("Invalid functionCalls name: " .. tostring(name) .. " not a string or LPEG pattern")
|
||||
end
|
||||
-- Allow boolean or function to match up w/ encoding permissions
|
||||
if type(func) ~= 'boolean' and type(func) ~= 'function' then
|
||||
error("Invalid functionCalls item: " .. name .. " not a function")
|
||||
end
|
||||
local nameCallCapture
|
||||
if type(name) == 'string' then
|
||||
nameCallCapture = lpeg.P(name .. "(") * lpeg.Cc(name)
|
||||
else
|
||||
-- Name matcher expected to produce a capture
|
||||
nameCallCapture = name * "("
|
||||
end
|
||||
-- Call func over nameCallCapture and value to permit function receiving name
|
||||
|
||||
-- Process 'func' if it is not a function
|
||||
if type(func) == 'boolean' then
|
||||
local allowed = func
|
||||
func = function(name, ...)
|
||||
if not allowed then
|
||||
error("Function call on '" .. name .. "' not permitted")
|
||||
end
|
||||
return buildCall(name, ...)
|
||||
end
|
||||
else
|
||||
local inner_func = func
|
||||
func = function(...)
|
||||
return (inner_func(...))
|
||||
end
|
||||
end
|
||||
local newCapture = (nameCallCapture * argumentCapture) / func * ")"
|
||||
if not callCapture then
|
||||
callCapture = newCapture
|
||||
else
|
||||
callCapture = callCapture + newCapture
|
||||
end
|
||||
end
|
||||
return callCapture
|
||||
end
|
||||
|
||||
local function buildCapture(options)
|
||||
if not options -- No ops, don't bother to parse
|
||||
or not (options.defs and (nil ~= next(options.defs)) or options.allowUndefined) then
|
||||
return nil
|
||||
end
|
||||
-- Allow zero or more arguments separated by commas
|
||||
local value = lpeg.V(util.types.VALUE)
|
||||
local argumentCapture = (value * (lpeg.P(",") * value)^0) + 0
|
||||
local callCapture = buildDefinedCaptures(argumentCapture, options.defs)
|
||||
if options.allowUndefined then
|
||||
local function func(name, ...)
|
||||
return buildCall(name, ...)
|
||||
end
|
||||
-- Identifier-type-match
|
||||
local nameCallCapture = lpeg.C(util.identifier) * "("
|
||||
local newCapture = (nameCallCapture * argumentCapture) / func * ")"
|
||||
if not callCapture then
|
||||
callCapture = newCapture
|
||||
else
|
||||
callCapture = callCapture + newCapture
|
||||
end
|
||||
end
|
||||
return callCapture
|
||||
end
|
||||
|
||||
function load_types(options, global_options, grammar)
|
||||
local capture = buildCapture(options, global_options)
|
||||
if capture then
|
||||
util.append_grammar_item(grammar, "VALUE", capture)
|
||||
end
|
||||
end
|
||||
190
cosmic rage/lua/json/decode/composite.lua
Normal file
190
cosmic rage/lua/json/decode/composite.lua
Normal file
@@ -0,0 +1,190 @@
|
||||
--[[
|
||||
Licensed according to the included 'LICENSE' document
|
||||
Author: Thomas Harning Jr <harningt@gmail.com>
|
||||
]]
|
||||
local pairs = pairs
|
||||
local type = type
|
||||
|
||||
local lpeg = require("lpeg")
|
||||
|
||||
local util = require("json.decode.util")
|
||||
local jsonutil = require("json.util")
|
||||
|
||||
local rawset = rawset
|
||||
|
||||
local assert = assert
|
||||
local tostring = tostring
|
||||
|
||||
local error = error
|
||||
local getmetatable = getmetatable
|
||||
|
||||
local _ENV = nil
|
||||
|
||||
local defaultOptions = {
|
||||
array = {
|
||||
trailingComma = true
|
||||
},
|
||||
object = {
|
||||
trailingComma = true,
|
||||
number = true,
|
||||
identifier = true,
|
||||
setObjectKey = rawset
|
||||
},
|
||||
calls = {
|
||||
defs = nil,
|
||||
-- By default, do not allow undefined calls to be de-serialized as call objects
|
||||
allowUndefined = false
|
||||
}
|
||||
}
|
||||
|
||||
local modeOptions = {
|
||||
default = nil,
|
||||
strict = {
|
||||
array = {
|
||||
trailingComma = false
|
||||
},
|
||||
object = {
|
||||
trailingComma = false,
|
||||
number = false,
|
||||
identifier = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
local function BEGIN_ARRAY(state)
|
||||
state:push()
|
||||
state:new_array()
|
||||
end
|
||||
local function END_ARRAY(state)
|
||||
state:end_array()
|
||||
state:pop()
|
||||
end
|
||||
|
||||
local function BEGIN_OBJECT(state)
|
||||
state:push()
|
||||
state:new_object()
|
||||
end
|
||||
local function END_OBJECT(state)
|
||||
state:end_object()
|
||||
state:pop()
|
||||
end
|
||||
|
||||
local function END_CALL(state)
|
||||
state:end_call()
|
||||
state:pop()
|
||||
end
|
||||
|
||||
local function SET_KEY(state)
|
||||
state:set_key()
|
||||
end
|
||||
|
||||
local function NEXT_VALUE(state)
|
||||
state:put_value()
|
||||
end
|
||||
|
||||
local function mergeOptions(options, mode)
|
||||
jsonutil.doOptionMerge(options, true, 'array', defaultOptions, mode and modeOptions[mode])
|
||||
jsonutil.doOptionMerge(options, true, 'object', defaultOptions, mode and modeOptions[mode])
|
||||
jsonutil.doOptionMerge(options, true, 'calls', defaultOptions, mode and modeOptions[mode])
|
||||
end
|
||||
|
||||
|
||||
local isPattern
|
||||
if lpeg.type then
|
||||
function isPattern(value)
|
||||
return lpeg.type(value) == 'pattern'
|
||||
end
|
||||
else
|
||||
local metaAdd = getmetatable(lpeg.P("")).__add
|
||||
function isPattern(value)
|
||||
return getmetatable(value).__add == metaAdd
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function generateSingleCallLexer(name, func)
|
||||
if type(name) ~= 'string' and not isPattern(name) then
|
||||
error("Invalid functionCalls name: " .. tostring(name) .. " not a string or LPEG pattern")
|
||||
end
|
||||
-- Allow boolean or function to match up w/ encoding permissions
|
||||
if type(func) ~= 'boolean' and type(func) ~= 'function' then
|
||||
error("Invalid functionCalls item: " .. name .. " not a function")
|
||||
end
|
||||
local function buildCallCapture(name)
|
||||
return function(state)
|
||||
if func == false then
|
||||
error("Function call on '" .. name .. "' not permitted")
|
||||
end
|
||||
state:push()
|
||||
state:new_call(name, func)
|
||||
end
|
||||
end
|
||||
local nameCallCapture
|
||||
if type(name) == 'string' then
|
||||
nameCallCapture = lpeg.P(name .. "(") * lpeg.Cc(name) / buildCallCapture
|
||||
else
|
||||
-- Name matcher expected to produce a capture
|
||||
nameCallCapture = name * "(" / buildCallCapture
|
||||
end
|
||||
-- Call func over nameCallCapture and value to permit function receiving name
|
||||
return nameCallCapture
|
||||
end
|
||||
|
||||
local function generateNamedCallLexers(options)
|
||||
if not options.calls or not options.calls.defs then
|
||||
return
|
||||
end
|
||||
local callCapture
|
||||
for name, func in pairs(options.calls.defs) do
|
||||
local newCapture = generateSingleCallLexer(name, func)
|
||||
if not callCapture then
|
||||
callCapture = newCapture
|
||||
else
|
||||
callCapture = callCapture + newCapture
|
||||
end
|
||||
end
|
||||
return callCapture
|
||||
end
|
||||
|
||||
local function generateCallLexer(options)
|
||||
local lexer
|
||||
local namedCapture = generateNamedCallLexers(options)
|
||||
if options.calls and options.calls.allowUndefined then
|
||||
lexer = generateSingleCallLexer(lpeg.C(util.identifier), true)
|
||||
end
|
||||
if namedCapture then
|
||||
lexer = lexer and lexer + namedCapture or namedCapture
|
||||
end
|
||||
if lexer then
|
||||
lexer = lexer + lpeg.P(")") * lpeg.Cc(END_CALL)
|
||||
end
|
||||
return lexer
|
||||
end
|
||||
|
||||
local function generateLexer(options)
|
||||
local ignored = options.ignored
|
||||
local array_options, object_options = options.array, options.object
|
||||
local lexer =
|
||||
lpeg.P("[") * lpeg.Cc(BEGIN_ARRAY)
|
||||
+ lpeg.P("]") * lpeg.Cc(END_ARRAY)
|
||||
+ lpeg.P("{") * lpeg.Cc(BEGIN_OBJECT)
|
||||
+ lpeg.P("}") * lpeg.Cc(END_OBJECT)
|
||||
+ lpeg.P(":") * lpeg.Cc(SET_KEY)
|
||||
+ lpeg.P(",") * lpeg.Cc(NEXT_VALUE)
|
||||
if object_options.identifier then
|
||||
-- Add identifier match w/ validation check that it is in key
|
||||
lexer = lexer + lpeg.C(util.identifier) * ignored * lpeg.P(":") * lpeg.Cc(SET_KEY)
|
||||
end
|
||||
local callLexers = generateCallLexer(options)
|
||||
if callLexers then
|
||||
lexer = lexer + callLexers
|
||||
end
|
||||
return lexer
|
||||
end
|
||||
|
||||
local composite = {
|
||||
mergeOptions = mergeOptions,
|
||||
generateLexer = generateLexer
|
||||
}
|
||||
|
||||
return composite
|
||||
100
cosmic rage/lua/json/decode/number.lua
Normal file
100
cosmic rage/lua/json/decode/number.lua
Normal file
@@ -0,0 +1,100 @@
|
||||
--[[
|
||||
Licensed according to the included 'LICENSE' document
|
||||
Author: Thomas Harning Jr <harningt@gmail.com>
|
||||
]]
|
||||
local lpeg = require("lpeg")
|
||||
local tonumber = tonumber
|
||||
local jsonutil = require("json.util")
|
||||
local merge = jsonutil.merge
|
||||
local util = require("json.decode.util")
|
||||
|
||||
local _ENV = nil
|
||||
|
||||
local digit = lpeg.R("09")
|
||||
local digits = digit^1
|
||||
|
||||
-- Illegal octal declaration
|
||||
local illegal_octal_detect = #(lpeg.P('0') * digits) * util.denied("Octal numbers")
|
||||
|
||||
local int = (lpeg.P('-') + 0) * (lpeg.R("19") * digits + illegal_octal_detect + digit)
|
||||
|
||||
local frac = lpeg.P('.') * digits
|
||||
|
||||
local exp = lpeg.S("Ee") * (lpeg.S("-+") + 0) * digits
|
||||
|
||||
local nan = lpeg.S("Nn") * lpeg.S("Aa") * lpeg.S("Nn")
|
||||
local inf = lpeg.S("Ii") * lpeg.P("nfinity")
|
||||
local ninf = lpeg.P('-') * lpeg.S("Ii") * lpeg.P("nfinity")
|
||||
local hex = (lpeg.P("0x") + lpeg.P("0X")) * lpeg.R("09","AF","af")^1
|
||||
|
||||
local defaultOptions = {
|
||||
nan = true,
|
||||
inf = true,
|
||||
frac = true,
|
||||
exp = true,
|
||||
hex = false
|
||||
}
|
||||
|
||||
local modeOptions = {}
|
||||
|
||||
modeOptions.strict = {
|
||||
nan = false,
|
||||
inf = false
|
||||
}
|
||||
|
||||
local nan_value = 0/0
|
||||
local inf_value = 1/0
|
||||
local ninf_value = -1/0
|
||||
|
||||
--[[
|
||||
Options: configuration options for number rules
|
||||
nan: match NaN
|
||||
inf: match Infinity
|
||||
frac: match fraction portion (.0)
|
||||
exp: match exponent portion (e1)
|
||||
DEFAULT: nan, inf, frac, exp
|
||||
]]
|
||||
local function mergeOptions(options, mode)
|
||||
jsonutil.doOptionMerge(options, false, 'number', defaultOptions, mode and modeOptions[mode])
|
||||
end
|
||||
|
||||
local function generateLexer(options)
|
||||
options = options.number
|
||||
local ret = int
|
||||
if options.frac then
|
||||
ret = ret * (frac + 0)
|
||||
else
|
||||
ret = ret * (#frac * util.denied("Fractions", "number.frac") + 0)
|
||||
end
|
||||
if options.exp then
|
||||
ret = ret * (exp + 0)
|
||||
else
|
||||
ret = ret * (#exp * util.denied("Exponents", "number.exp") + 0)
|
||||
end
|
||||
if options.hex then
|
||||
ret = hex + ret
|
||||
else
|
||||
ret = #hex * util.denied("Hexadecimal", "number.hex") + ret
|
||||
end
|
||||
-- Capture number now
|
||||
ret = ret / tonumber
|
||||
if options.nan then
|
||||
ret = ret + nan / function() return nan_value end
|
||||
else
|
||||
ret = ret + #nan * util.denied("NaN", "number.nan")
|
||||
end
|
||||
if options.inf then
|
||||
ret = ret + ninf / function() return ninf_value end + inf / function() return inf_value end
|
||||
else
|
||||
ret = ret + (#ninf + #inf) * util.denied("+/-Inf", "number.inf")
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
local number = {
|
||||
int = int,
|
||||
mergeOptions = mergeOptions,
|
||||
generateLexer = generateLexer
|
||||
}
|
||||
|
||||
return number
|
||||
103
cosmic rage/lua/json/decode/object.lua
Normal file
103
cosmic rage/lua/json/decode/object.lua
Normal file
@@ -0,0 +1,103 @@
|
||||
--[[
|
||||
Licensed according to the included 'LICENSE' document
|
||||
Author: Thomas Harning Jr <harningt@gmail.com>
|
||||
--]]
|
||||
local lpeg = require("lpeg")
|
||||
|
||||
local util = require("json.decode.util")
|
||||
local merge = require("json.util").merge
|
||||
|
||||
local tonumber = tonumber
|
||||
local unpack = unpack
|
||||
local print = print
|
||||
local tostring = tostring
|
||||
|
||||
local rawset = rawset
|
||||
|
||||
module("json.decode.object")
|
||||
|
||||
-- BEGIN LPEG < 0.9 SUPPORT
|
||||
local initObject, applyObjectKey
|
||||
if not (lpeg.Cg and lpeg.Cf and lpeg.Ct) then
|
||||
function initObject()
|
||||
return {}
|
||||
end
|
||||
function applyObjectKey(tab, key, val)
|
||||
tab[key] = val
|
||||
return tab
|
||||
end
|
||||
end
|
||||
-- END LPEG < 0.9 SUPPORT
|
||||
|
||||
local defaultOptions = {
|
||||
number = true,
|
||||
identifier = true,
|
||||
trailingComma = true
|
||||
}
|
||||
|
||||
default = nil -- Let the buildCapture optimization take place
|
||||
|
||||
strict = {
|
||||
number = false,
|
||||
identifier = false,
|
||||
trailingComma = false
|
||||
}
|
||||
|
||||
local function buildItemSequence(objectItem, ignored)
|
||||
return (objectItem * (ignored * lpeg.P(",") * ignored * objectItem)^0) + 0
|
||||
end
|
||||
|
||||
local function buildCapture(options, global_options)
|
||||
local ignored = global_options.ignored
|
||||
local string_type = lpeg.V(util.types.STRING)
|
||||
local integer_type = lpeg.V(util.types.INTEGER)
|
||||
local value_type = lpeg.V(util.types.VALUE)
|
||||
options = options and merge({}, defaultOptions, options) or defaultOptions
|
||||
local key = string_type
|
||||
if options.identifier then
|
||||
key = key + lpeg.C(util.identifier)
|
||||
end
|
||||
if options.number then
|
||||
key = key + integer_type
|
||||
end
|
||||
local objectItems
|
||||
local objectItem = (key * ignored * lpeg.P(":") * ignored * value_type)
|
||||
-- BEGIN LPEG < 0.9 SUPPORT
|
||||
if not (lpeg.Cg and lpeg.Cf and lpeg.Ct) then
|
||||
local set_key = applyObjectKey
|
||||
if options.setObjectKey then
|
||||
local setObjectKey = options.setObjectKey
|
||||
set_key = function(tab, key, val)
|
||||
setObjectKey(tab, key, val)
|
||||
return tab
|
||||
end
|
||||
end
|
||||
|
||||
objectItems = buildItemSequence(objectItem / set_key, ignored)
|
||||
objectItems = lpeg.Ca(lpeg.Cc(false) / initObject * objectItems)
|
||||
-- END LPEG < 0.9 SUPPORT
|
||||
else
|
||||
objectItems = buildItemSequence(lpeg.Cg(objectItem), ignored)
|
||||
objectItems = lpeg.Cf(lpeg.Ct(0) * objectItems, options.setObjectKey or rawset)
|
||||
end
|
||||
|
||||
|
||||
local capture = lpeg.P("{") * ignored
|
||||
capture = capture * objectItems * ignored
|
||||
if options.trailingComma then
|
||||
capture = capture * (lpeg.P(",") + 0) * ignored
|
||||
end
|
||||
capture = capture * lpeg.P("}")
|
||||
return capture
|
||||
end
|
||||
|
||||
function register_types()
|
||||
util.register_type("OBJECT")
|
||||
end
|
||||
|
||||
function load_types(options, global_options, grammar)
|
||||
local capture = buildCapture(options, global_options)
|
||||
local object_id = util.types.OBJECT
|
||||
grammar[object_id] = capture
|
||||
util.append_grammar_item(grammar, "VALUE", lpeg.V(object_id))
|
||||
end
|
||||
62
cosmic rage/lua/json/decode/others.lua
Normal file
62
cosmic rage/lua/json/decode/others.lua
Normal file
@@ -0,0 +1,62 @@
|
||||
--[[
|
||||
Licensed according to the included 'LICENSE' document
|
||||
Author: Thomas Harning Jr <harningt@gmail.com>
|
||||
]]
|
||||
local lpeg = require("lpeg")
|
||||
local jsonutil = require("json.util")
|
||||
local merge = jsonutil.merge
|
||||
local util = require("json.decode.util")
|
||||
|
||||
-- Container module for other JavaScript types (bool, null, undefined)
|
||||
|
||||
local _ENV = nil
|
||||
|
||||
-- For null and undefined, use the util.null value to preserve null-ness
|
||||
local booleanCapture =
|
||||
lpeg.P("true") * lpeg.Cc(true)
|
||||
+ lpeg.P("false") * lpeg.Cc(false)
|
||||
|
||||
local nullCapture = lpeg.P("null")
|
||||
local undefinedCapture = lpeg.P("undefined")
|
||||
|
||||
local defaultOptions = {
|
||||
allowUndefined = true,
|
||||
null = jsonutil.null,
|
||||
undefined = jsonutil.undefined
|
||||
}
|
||||
|
||||
local modeOptions = {}
|
||||
|
||||
modeOptions.simple = {
|
||||
null = false, -- Mapped to nil
|
||||
undefined = false -- Mapped to nil
|
||||
}
|
||||
modeOptions.strict = {
|
||||
allowUndefined = false
|
||||
}
|
||||
|
||||
local function mergeOptions(options, mode)
|
||||
jsonutil.doOptionMerge(options, false, 'others', defaultOptions, mode and modeOptions[mode])
|
||||
end
|
||||
|
||||
local function generateLexer(options)
|
||||
-- The 'or nil' clause allows false to map to a nil value since 'nil' cannot be merged
|
||||
options = options.others
|
||||
local valueCapture = (
|
||||
booleanCapture
|
||||
+ nullCapture * lpeg.Cc(options.null or nil)
|
||||
)
|
||||
if options.allowUndefined then
|
||||
valueCapture = valueCapture + undefinedCapture * lpeg.Cc(options.undefined or nil)
|
||||
else
|
||||
valueCapture = valueCapture + #undefinedCapture * util.denied("undefined", "others.allowUndefined")
|
||||
end
|
||||
return valueCapture
|
||||
end
|
||||
|
||||
local others = {
|
||||
mergeOptions = mergeOptions,
|
||||
generateLexer = generateLexer
|
||||
}
|
||||
|
||||
return others
|
||||
189
cosmic rage/lua/json/decode/state.lua
Normal file
189
cosmic rage/lua/json/decode/state.lua
Normal file
@@ -0,0 +1,189 @@
|
||||
--[[
|
||||
Licensed according to the included 'LICENSE' document
|
||||
Author: Thomas Harning Jr <harningt@gmail.com>
|
||||
]]
|
||||
|
||||
local setmetatable = setmetatable
|
||||
local jsonutil = require("json.util")
|
||||
local assert = assert
|
||||
local type = type
|
||||
local next = next
|
||||
local unpack = require("table").unpack or unpack
|
||||
|
||||
local _ENV = nil
|
||||
|
||||
local state_ops = {}
|
||||
local state_mt = {
|
||||
__index = state_ops
|
||||
}
|
||||
|
||||
function state_ops.pop(self)
|
||||
self.previous_set = true
|
||||
self.previous = self.active
|
||||
local i = self.i
|
||||
-- Load in this array into the active item
|
||||
self.active = self.stack[i]
|
||||
self.active_state = self.state_stack[i]
|
||||
self.active_key = self.key_stack[i]
|
||||
self.stack[i] = nil
|
||||
self.state_stack[i] = nil
|
||||
self.key_stack[i] = nil
|
||||
|
||||
self.i = i - 1
|
||||
end
|
||||
|
||||
function state_ops.push(self)
|
||||
local i = self.i + 1
|
||||
self.i = i
|
||||
|
||||
self.stack[i] = self.active
|
||||
self.state_stack[i] = self.active_state
|
||||
self.key_stack[i] = self.active_key
|
||||
end
|
||||
|
||||
function state_ops.put_object_value(self, trailing)
|
||||
local object_options = self.options.object
|
||||
if trailing and object_options.trailingComma then
|
||||
if not self.active_key then
|
||||
return
|
||||
end
|
||||
end
|
||||
assert(self.active_key, "Missing key value")
|
||||
object_options.setObjectKey(self.active, self.active_key, self:grab_value())
|
||||
self.active_key = nil
|
||||
end
|
||||
|
||||
function state_ops.put_array_value(self, trailing)
|
||||
-- Safety check
|
||||
if trailing and not self.previous_set and self.options.array.trailingComma then
|
||||
return
|
||||
end
|
||||
local new_index = self.active_state + 1
|
||||
self.active_state = new_index
|
||||
self.active[new_index] = self:grab_value()
|
||||
end
|
||||
|
||||
function state_ops.put_value(self, trailing)
|
||||
if self.active_state == 'object' then
|
||||
self:put_object_value(trailing)
|
||||
else
|
||||
self:put_array_value(trailing)
|
||||
end
|
||||
end
|
||||
|
||||
function state_ops.new_array(self)
|
||||
local new_array = {}
|
||||
if jsonutil.InitArray then
|
||||
new_array = jsonutil.InitArray(new_array) or new_array
|
||||
end
|
||||
self.active = new_array
|
||||
self.active_state = 0
|
||||
self.active_key = nil
|
||||
self:unset_value()
|
||||
end
|
||||
|
||||
function state_ops.end_array(self)
|
||||
if self.previous_set or self.active_state ~= 0 then
|
||||
-- Not an empty array
|
||||
self:put_value(true)
|
||||
end
|
||||
if self.active_state ~= #self.active then
|
||||
-- Store the length in
|
||||
self.active.n = self.active_state
|
||||
end
|
||||
end
|
||||
|
||||
function state_ops.new_object(self)
|
||||
local new_object = {}
|
||||
self.active = new_object
|
||||
self.active_state = 'object'
|
||||
self.active_key = nil
|
||||
self:unset_value()
|
||||
end
|
||||
|
||||
function state_ops.end_object(self)
|
||||
if self.previous_set or next(self.active) then
|
||||
-- Not an empty object
|
||||
self:put_value(true)
|
||||
end
|
||||
end
|
||||
|
||||
function state_ops.new_call(self, name, func)
|
||||
-- TODO setup properly
|
||||
local new_call = {}
|
||||
new_call.name = name
|
||||
new_call.func = func
|
||||
self.active = new_call
|
||||
self.active_state = 0
|
||||
self.active_key = nil
|
||||
self:unset_value()
|
||||
end
|
||||
|
||||
function state_ops.end_call(self)
|
||||
if self.previous_set or self.active_state ~= 0 then
|
||||
-- Not an empty array
|
||||
self:put_value(true)
|
||||
end
|
||||
if self.active_state ~= #self.active then
|
||||
-- Store the length in
|
||||
self.active.n = self.active_state
|
||||
end
|
||||
local func = self.active.func
|
||||
if func == true then
|
||||
func = jsonutil.buildCall
|
||||
end
|
||||
self.active = func(self.active.name, unpack(self.active, 1, self.active.n or #self.active))
|
||||
end
|
||||
|
||||
|
||||
function state_ops.unset_value(self)
|
||||
self.previous_set = false
|
||||
self.previous = nil
|
||||
end
|
||||
|
||||
function state_ops.grab_value(self)
|
||||
assert(self.previous_set, "Previous value not set")
|
||||
self.previous_set = false
|
||||
return self.previous
|
||||
end
|
||||
|
||||
function state_ops.set_value(self, value)
|
||||
assert(not self.previous_set, "Value set when one already in slot")
|
||||
self.previous_set = true
|
||||
self.previous = value
|
||||
end
|
||||
|
||||
function state_ops.set_key(self)
|
||||
assert(self.active_state == 'object', "Cannot set key on array")
|
||||
local value = self:grab_value()
|
||||
local value_type = type(value)
|
||||
if self.options.object.number then
|
||||
assert(value_type == 'string' or value_type == 'number', "As configured, a key must be a number or string")
|
||||
else
|
||||
assert(value_type == 'string', "As configured, a key must be a string")
|
||||
end
|
||||
self.active_key = value
|
||||
end
|
||||
|
||||
|
||||
local function create(options)
|
||||
local ret = {
|
||||
options = options,
|
||||
stack = {},
|
||||
state_stack = {},
|
||||
key_stack = {},
|
||||
i = 0,
|
||||
active = nil,
|
||||
active_key = nil,
|
||||
previous = nil,
|
||||
active_state = nil
|
||||
|
||||
}
|
||||
return setmetatable(ret, state_mt)
|
||||
end
|
||||
|
||||
local state = {
|
||||
create = create
|
||||
}
|
||||
|
||||
return state
|
||||
133
cosmic rage/lua/json/decode/strings.lua
Normal file
133
cosmic rage/lua/json/decode/strings.lua
Normal file
@@ -0,0 +1,133 @@
|
||||
--[[
|
||||
Licensed according to the included 'LICENSE' document
|
||||
Author: Thomas Harning Jr <harningt@gmail.com>
|
||||
]]
|
||||
local lpeg = require("lpeg")
|
||||
local jsonutil = require("json.util")
|
||||
local util = require("json.decode.util")
|
||||
local merge = jsonutil.merge
|
||||
|
||||
local tonumber = tonumber
|
||||
local string_char = require("string").char
|
||||
local floor = require("math").floor
|
||||
local table_concat = require("table").concat
|
||||
|
||||
local error = error
|
||||
|
||||
local _ENV = nil
|
||||
|
||||
local function get_error(item)
|
||||
local fmt_string = item .. " in string [%q] @ %i:%i"
|
||||
return lpeg.P(function(data, index)
|
||||
local line, line_index, bad_char, last_line = util.get_invalid_character_info(data, index)
|
||||
local err = fmt_string:format(bad_char, line, line_index)
|
||||
error(err)
|
||||
end) * 1
|
||||
end
|
||||
|
||||
local bad_unicode = get_error("Illegal unicode escape")
|
||||
local bad_hex = get_error("Illegal hex escape")
|
||||
local bad_character = get_error("Illegal character")
|
||||
local bad_escape = get_error("Illegal escape")
|
||||
|
||||
local knownReplacements = {
|
||||
["'"] = "'",
|
||||
['"'] = '"',
|
||||
['\\'] = '\\',
|
||||
['/'] = '/',
|
||||
b = '\b',
|
||||
f = '\f',
|
||||
n = '\n',
|
||||
r = '\r',
|
||||
t = '\t',
|
||||
v = '\v',
|
||||
z = '\z'
|
||||
}
|
||||
|
||||
-- according to the table at http://da.wikipedia.org/wiki/UTF-8
|
||||
local function utf8DecodeUnicode(code1, code2)
|
||||
code1, code2 = tonumber(code1, 16), tonumber(code2, 16)
|
||||
if code1 == 0 and code2 < 0x80 then
|
||||
return string_char(code2)
|
||||
end
|
||||
if code1 < 0x08 then
|
||||
return string_char(
|
||||
0xC0 + code1 * 4 + floor(code2 / 64),
|
||||
0x80 + code2 % 64)
|
||||
end
|
||||
return string_char(
|
||||
0xE0 + floor(code1 / 16),
|
||||
0x80 + (code1 % 16) * 4 + floor(code2 / 64),
|
||||
0x80 + code2 % 64)
|
||||
end
|
||||
|
||||
local function decodeX(code)
|
||||
code = tonumber(code, 16)
|
||||
return string_char(code)
|
||||
end
|
||||
|
||||
local doSimpleSub = lpeg.C(lpeg.S("'\"\\/bfnrtvz")) / knownReplacements
|
||||
local doUniSub = lpeg.P('u') * (lpeg.C(util.hexpair) * lpeg.C(util.hexpair) + bad_unicode)
|
||||
local doXSub = lpeg.P('x') * (lpeg.C(util.hexpair) + bad_hex)
|
||||
|
||||
local defaultOptions = {
|
||||
badChars = '',
|
||||
additionalEscapes = false, -- disallow untranslated escapes
|
||||
escapeCheck = #lpeg.S('bfnrtv/\\"xu\'z'), -- no check on valid characters
|
||||
decodeUnicode = utf8DecodeUnicode,
|
||||
strict_quotes = false
|
||||
}
|
||||
|
||||
local modeOptions = {}
|
||||
|
||||
modeOptions.strict = {
|
||||
badChars = '\b\f\n\r\t\v',
|
||||
additionalEscapes = false, -- no additional escapes
|
||||
escapeCheck = #lpeg.S('bfnrtv/\\"u'), --only these chars are allowed to be escaped
|
||||
strict_quotes = true
|
||||
}
|
||||
|
||||
local function mergeOptions(options, mode)
|
||||
jsonutil.doOptionMerge(options, false, 'strings', defaultOptions, mode and modeOptions[mode])
|
||||
end
|
||||
|
||||
local function buildCaptureString(quote, badChars, escapeMatch)
|
||||
local captureChar = (1 - lpeg.S("\\" .. badChars .. quote)) + (lpeg.P("\\") / "" * escapeMatch)
|
||||
-- During error, force end
|
||||
local captureString = captureChar^0 + (-#lpeg.P(quote) * bad_character + -1)
|
||||
return lpeg.P(quote) * lpeg.Cs(captureString) * lpeg.P(quote)
|
||||
end
|
||||
|
||||
local function generateLexer(options)
|
||||
options = options.strings
|
||||
local quotes = { '"' }
|
||||
if not options.strict_quotes then
|
||||
quotes[#quotes + 1] = "'"
|
||||
end
|
||||
local escapeMatch = doSimpleSub
|
||||
escapeMatch = escapeMatch + doXSub / decodeX
|
||||
escapeMatch = escapeMatch + doUniSub / options.decodeUnicode
|
||||
if options.escapeCheck then
|
||||
escapeMatch = options.escapeCheck * escapeMatch + bad_escape
|
||||
end
|
||||
if options.additionalEscapes then
|
||||
escapeMatch = options.additionalEscapes + escapeMatch
|
||||
end
|
||||
local captureString
|
||||
for i = 1, #quotes do
|
||||
local cap = buildCaptureString(quotes[i], options.badChars, escapeMatch)
|
||||
if captureString == nil then
|
||||
captureString = cap
|
||||
else
|
||||
captureString = captureString + cap
|
||||
end
|
||||
end
|
||||
return captureString
|
||||
end
|
||||
|
||||
local strings = {
|
||||
mergeOptions = mergeOptions,
|
||||
generateLexer = generateLexer
|
||||
}
|
||||
|
||||
return strings
|
||||
121
cosmic rage/lua/json/decode/util.lua
Normal file
121
cosmic rage/lua/json/decode/util.lua
Normal file
@@ -0,0 +1,121 @@
|
||||
--[[
|
||||
Licensed according to the included 'LICENSE' document
|
||||
Author: Thomas Harning Jr <harningt@gmail.com>
|
||||
]]
|
||||
local lpeg = require("lpeg")
|
||||
local select = select
|
||||
local pairs, ipairs = pairs, ipairs
|
||||
local tonumber = tonumber
|
||||
local string_char = require("string").char
|
||||
local rawset = rawset
|
||||
local jsonutil = require("json.util")
|
||||
|
||||
local error = error
|
||||
local setmetatable = setmetatable
|
||||
|
||||
local table_concat = require("table").concat
|
||||
|
||||
local merge = require("json.util").merge
|
||||
|
||||
local _ENV = nil
|
||||
|
||||
local function get_invalid_character_info(input, index)
|
||||
local parsed = input:sub(1, index)
|
||||
local bad_character = input:sub(index, index)
|
||||
local _, line_number = parsed:gsub('\n',{})
|
||||
local last_line = parsed:match("\n([^\n]+.)$") or parsed
|
||||
return line_number, #last_line, bad_character, last_line
|
||||
end
|
||||
|
||||
local function build_report(msg)
|
||||
local fmt = msg:gsub("%%", "%%%%") .. " @ character: %i %i:%i [%s] line:\n%s"
|
||||
return lpeg.P(function(data, pos)
|
||||
local line, line_index, bad_char, last_line = get_invalid_character_info(data, pos)
|
||||
local text = fmt:format(pos, line, line_index, bad_char, last_line)
|
||||
error(text)
|
||||
end) * 1
|
||||
end
|
||||
local function unexpected()
|
||||
local msg = "unexpected character"
|
||||
return build_report(msg)
|
||||
end
|
||||
local function denied(item, option)
|
||||
local msg
|
||||
if option then
|
||||
msg = ("'%s' denied by option set '%s'"):format(item, option)
|
||||
else
|
||||
msg = ("'%s' denied"):format(item)
|
||||
end
|
||||
return build_report(msg)
|
||||
end
|
||||
|
||||
-- 09, 0A, 0B, 0C, 0D, 20
|
||||
local ascii_space = lpeg.S("\t\n\v\f\r ")
|
||||
local unicode_space
|
||||
do
|
||||
local chr = string_char
|
||||
local u_space = ascii_space
|
||||
-- \u0085 \u00A0
|
||||
u_space = u_space + lpeg.P(chr(0xC2)) * lpeg.S(chr(0x85) .. chr(0xA0))
|
||||
-- \u1680 \u180E
|
||||
u_space = u_space + lpeg.P(chr(0xE1)) * (lpeg.P(chr(0x9A, 0x80)) + chr(0xA0, 0x8E))
|
||||
-- \u2000 - \u200A, also 200B
|
||||
local spacing_end = ""
|
||||
for i = 0x80,0x8b do
|
||||
spacing_end = spacing_end .. chr(i)
|
||||
end
|
||||
-- \u2028 \u2029 \u202F
|
||||
spacing_end = spacing_end .. chr(0xA8) .. chr(0xA9) .. chr(0xAF)
|
||||
u_space = u_space + lpeg.P(chr(0xE2, 0x80)) * lpeg.S(spacing_end)
|
||||
-- \u205F
|
||||
u_space = u_space + lpeg.P(chr(0xE2, 0x81, 0x9F))
|
||||
-- \u3000
|
||||
u_space = u_space + lpeg.P(chr(0xE3, 0x80, 0x80))
|
||||
-- BOM \uFEFF
|
||||
u_space = u_space + lpeg.P(chr(0xEF, 0xBB, 0xBF))
|
||||
unicode_space = u_space
|
||||
end
|
||||
|
||||
local identifier = lpeg.R("AZ","az","__") * lpeg.R("AZ","az", "__", "09") ^0
|
||||
|
||||
local hex = lpeg.R("09","AF","af")
|
||||
local hexpair = hex * hex
|
||||
|
||||
local comments = {
|
||||
cpp = lpeg.P("//") * (1 - lpeg.P("\n"))^0 * lpeg.P("\n"),
|
||||
c = lpeg.P("/*") * (1 - lpeg.P("*/"))^0 * lpeg.P("*/")
|
||||
}
|
||||
|
||||
local comment = comments.cpp + comments.c
|
||||
|
||||
local ascii_ignored = (ascii_space + comment)^0
|
||||
|
||||
local unicode_ignored = (unicode_space + comment)^0
|
||||
|
||||
-- Parse the lpeg version skipping patch-values
|
||||
-- LPEG <= 0.7 have no version value... so 0.7 is value
|
||||
local DecimalLpegVersion = lpeg.version and tonumber(lpeg.version():match("^(%d+%.%d+)")) or 0.7
|
||||
|
||||
local function setObjectKeyForceNumber(t, key, value)
|
||||
key = tonumber(key) or key
|
||||
return rawset(t, key, value)
|
||||
end
|
||||
|
||||
local util = {
|
||||
unexpected = unexpected,
|
||||
denied = denied,
|
||||
ascii_space = ascii_space,
|
||||
unicode_space = unicode_space,
|
||||
identifier = identifier,
|
||||
hex = hex,
|
||||
hexpair = hexpair,
|
||||
comments = comments,
|
||||
comment = comment,
|
||||
ascii_ignored = ascii_ignored,
|
||||
unicode_ignored = unicode_ignored,
|
||||
DecimalLpegVersion = DecimalLpegVersion,
|
||||
get_invalid_character_info = get_invalid_character_info,
|
||||
setObjectKeyForceNumber = setObjectKeyForceNumber
|
||||
}
|
||||
|
||||
return util
|
||||
161
cosmic rage/lua/json/encode.lua
Normal file
161
cosmic rage/lua/json/encode.lua
Normal file
@@ -0,0 +1,161 @@
|
||||
--[[
|
||||
Licensed according to the included 'LICENSE' document
|
||||
Author: Thomas Harning Jr <harningt@gmail.com>
|
||||
]]
|
||||
local type = type
|
||||
local assert, error = assert, error
|
||||
local getmetatable, setmetatable = getmetatable, setmetatable
|
||||
|
||||
local ipairs, pairs = ipairs, pairs
|
||||
local require = require
|
||||
|
||||
local output = require("json.encode.output")
|
||||
|
||||
local util = require("json.util")
|
||||
local util_merge, isCall = util.merge, util.isCall
|
||||
|
||||
local _ENV = nil
|
||||
|
||||
--[[
|
||||
List of encoding modules to load.
|
||||
Loaded in sequence such that earlier encoders get priority when
|
||||
duplicate type-handlers exist.
|
||||
]]
|
||||
local modulesToLoad = {
|
||||
"strings",
|
||||
"number",
|
||||
"calls",
|
||||
"others",
|
||||
"array",
|
||||
"object"
|
||||
}
|
||||
-- Modules that have been loaded
|
||||
local loadedModules = {}
|
||||
|
||||
local json_encode = {}
|
||||
|
||||
-- Configuration bases for client apps
|
||||
local modes_defined = { "default", "strict" }
|
||||
|
||||
json_encode.default = {}
|
||||
json_encode.strict = {
|
||||
initialObject = true -- Require an object at the root
|
||||
}
|
||||
|
||||
-- For each module, load it and its defaults
|
||||
for _,name in ipairs(modulesToLoad) do
|
||||
local mod = require("json.encode." .. name)
|
||||
if mod.mergeOptions then
|
||||
for _, mode in pairs(modes_defined) do
|
||||
mod.mergeOptions(json_encode[mode], mode)
|
||||
end
|
||||
end
|
||||
loadedModules[name] = mod
|
||||
end
|
||||
|
||||
-- NOTE: Nested not found, so assume unsupported until use case arises
|
||||
local function flattenOutput(out, value)
|
||||
assert(type(value) ~= 'table')
|
||||
out = out or {}
|
||||
out[#out + 1] = value
|
||||
return out
|
||||
end
|
||||
|
||||
-- Prepares the encoding map from the already provided modules and new config
|
||||
local function prepareEncodeMap(options)
|
||||
local map = {}
|
||||
for _, name in ipairs(modulesToLoad) do
|
||||
local encodermap = loadedModules[name].getEncoder(options[name])
|
||||
for valueType, encoderSet in pairs(encodermap) do
|
||||
map[valueType] = flattenOutput(map[valueType], encoderSet)
|
||||
end
|
||||
end
|
||||
return map
|
||||
end
|
||||
|
||||
--[[
|
||||
Encode a value with a given encoding map and state
|
||||
]]
|
||||
local function encodeWithMap(value, map, state, isObjectKey)
|
||||
local t = type(value)
|
||||
local encoderList = assert(map[t], "Failed to encode value, unhandled type: " .. t)
|
||||
for _, encoder in ipairs(encoderList) do
|
||||
local ret = encoder(value, state, isObjectKey)
|
||||
if false ~= ret then
|
||||
return ret
|
||||
end
|
||||
end
|
||||
error("Failed to encode value, encoders for " .. t .. " deny encoding")
|
||||
end
|
||||
|
||||
|
||||
local function getBaseEncoder(options)
|
||||
local encoderMap = prepareEncodeMap(options)
|
||||
if options.preProcess then
|
||||
local preProcess = options.preProcess
|
||||
return function(value, state, isObjectKey)
|
||||
local ret = preProcess(value, isObjectKey or false)
|
||||
if nil ~= ret then
|
||||
value = ret
|
||||
end
|
||||
return encodeWithMap(value, encoderMap, state)
|
||||
end
|
||||
end
|
||||
return function(value, state, isObjectKey)
|
||||
return encodeWithMap(value, encoderMap, state)
|
||||
end
|
||||
end
|
||||
--[[
|
||||
Retreive an initial encoder instance based on provided options
|
||||
the initial encoder is responsible for initializing state
|
||||
State has at least these values configured: encode, check_unique, already_encoded
|
||||
]]
|
||||
function json_encode.getEncoder(options)
|
||||
options = options and util_merge({}, json_encode.default, options) or json_encode.default
|
||||
local encode = getBaseEncoder(options)
|
||||
|
||||
local function initialEncode(value)
|
||||
if options.initialObject then
|
||||
local errorMessage = "Invalid arguments: expects a JSON Object or Array at the root"
|
||||
assert(type(value) == 'table' and not isCall(value, options), errorMessage)
|
||||
end
|
||||
|
||||
local alreadyEncoded = {}
|
||||
local function check_unique(value)
|
||||
assert(not alreadyEncoded[value], "Recursive encoding of value")
|
||||
alreadyEncoded[value] = true
|
||||
end
|
||||
|
||||
local outputEncoder = options.output and options.output() or output.getDefault()
|
||||
local state = {
|
||||
encode = encode,
|
||||
check_unique = check_unique,
|
||||
already_encoded = alreadyEncoded, -- To unmark encoding when moving up stack
|
||||
outputEncoder = outputEncoder
|
||||
}
|
||||
local ret = encode(value, state)
|
||||
if nil ~= ret then
|
||||
return outputEncoder.simple and outputEncoder.simple(ret) or ret
|
||||
end
|
||||
end
|
||||
return initialEncode
|
||||
end
|
||||
|
||||
-- CONSTRUCT STATE WITH FOLLOWING (at least)
|
||||
--[[
|
||||
encoder
|
||||
check_unique -- used by inner encoders to make sure value is unique
|
||||
already_encoded -- used to unmark a value as unique
|
||||
]]
|
||||
function json_encode.encode(data, options)
|
||||
return json_encode.getEncoder(options)(data)
|
||||
end
|
||||
|
||||
local mt = {}
|
||||
mt.__call = function(self, ...)
|
||||
return json_encode.encode(...)
|
||||
end
|
||||
|
||||
setmetatable(json_encode, mt)
|
||||
|
||||
return json_encode
|
||||
110
cosmic rage/lua/json/encode/array.lua
Normal file
110
cosmic rage/lua/json/encode/array.lua
Normal file
@@ -0,0 +1,110 @@
|
||||
--[[
|
||||
Licensed according to the included 'LICENSE' document
|
||||
Author: Thomas Harning Jr <harningt@gmail.com>
|
||||
]]
|
||||
local jsonutil = require("json.util")
|
||||
|
||||
local type = type
|
||||
local pairs = pairs
|
||||
local assert = assert
|
||||
|
||||
local table = require("table")
|
||||
local math = require("math")
|
||||
local table_concat = table.concat
|
||||
local math_floor, math_modf = math.floor, math.modf
|
||||
|
||||
local jsonutil = require("json.util")
|
||||
local util_IsArray = jsonutil.IsArray
|
||||
|
||||
local _ENV = nil
|
||||
|
||||
local defaultOptions = {
|
||||
isArray = util_IsArray
|
||||
}
|
||||
|
||||
local modeOptions = {}
|
||||
|
||||
local function mergeOptions(options, mode)
|
||||
jsonutil.doOptionMerge(options, false, 'array', defaultOptions, mode and modeOptions[mode])
|
||||
end
|
||||
|
||||
--[[
|
||||
Utility function to determine whether a table is an array or not.
|
||||
Criteria for it being an array:
|
||||
* ExternalIsArray returns true (or false directly reports not-array)
|
||||
* If the table has an 'n' value that is an integer >= 1 then it
|
||||
is an array... may result in false positives (should check some values
|
||||
before it)
|
||||
* It is a contiguous list of values with zero string-based keys
|
||||
]]
|
||||
local function isArray(val, options)
|
||||
local externalIsArray = options and options.isArray
|
||||
|
||||
if externalIsArray then
|
||||
local ret = externalIsArray(val)
|
||||
if ret == true or ret == false then
|
||||
return ret
|
||||
end
|
||||
end
|
||||
-- Use the 'n' element if it's a number
|
||||
if type(val.n) == 'number' and math_floor(val.n) == val.n and val.n >= 1 then
|
||||
return true
|
||||
end
|
||||
local len = #val
|
||||
for k,v in pairs(val) do
|
||||
if type(k) ~= 'number' then
|
||||
return false
|
||||
end
|
||||
local _, decim = math_modf(k)
|
||||
if not (decim == 0 and 1<=k) then
|
||||
return false
|
||||
end
|
||||
if k > len then -- Use Lua's length as absolute determiner
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
--[[
|
||||
Cleanup function to unmark a value as in the encoding process and return
|
||||
trailing results
|
||||
]]
|
||||
local function unmarkAfterEncode(tab, state, ...)
|
||||
state.already_encoded[tab] = nil
|
||||
return ...
|
||||
end
|
||||
local function getEncoder(options)
|
||||
options = options and jsonutil.merge({}, defaultOptions, options) or defaultOptions
|
||||
local function encodeArray(tab, state)
|
||||
if not isArray(tab, options) then
|
||||
return false
|
||||
end
|
||||
-- Make sure this value hasn't been encoded yet
|
||||
state.check_unique(tab)
|
||||
local encode = state.encode
|
||||
local compositeEncoder = state.outputEncoder.composite
|
||||
local valueEncoder = [[
|
||||
for i = 1, (composite.n or #composite) do
|
||||
local val = composite[i]
|
||||
PUTINNER(i ~= 1)
|
||||
val = encode(val, state)
|
||||
val = val or ''
|
||||
if val then
|
||||
PUTVALUE(val)
|
||||
end
|
||||
end
|
||||
]]
|
||||
return unmarkAfterEncode(tab, state, compositeEncoder(valueEncoder, '[', ']', ',', tab, encode, state))
|
||||
end
|
||||
return { table = encodeArray }
|
||||
end
|
||||
|
||||
local array = {
|
||||
mergeOptions = mergeOptions,
|
||||
isArray = isArray,
|
||||
getEncoder = getEncoder
|
||||
}
|
||||
|
||||
return array
|
||||
68
cosmic rage/lua/json/encode/calls.lua
Normal file
68
cosmic rage/lua/json/encode/calls.lua
Normal file
@@ -0,0 +1,68 @@
|
||||
--[[
|
||||
Licensed according to the included 'LICENSE' document
|
||||
Author: Thomas Harning Jr <harningt@gmail.com>
|
||||
]]
|
||||
local table = require("table")
|
||||
local table_concat = table.concat
|
||||
|
||||
local select = select
|
||||
local getmetatable, setmetatable = getmetatable, setmetatable
|
||||
local assert = assert
|
||||
|
||||
local jsonutil = require("json.util")
|
||||
|
||||
local isCall, decodeCall = jsonutil.isCall, jsonutil.decodeCall
|
||||
|
||||
local _ENV = nil
|
||||
|
||||
local defaultOptions = {
|
||||
}
|
||||
|
||||
-- No real default-option handling needed...
|
||||
local modeOptions = {}
|
||||
|
||||
local function mergeOptions(options, mode)
|
||||
jsonutil.doOptionMerge(options, false, 'calls', defaultOptions, mode and modeOptions[mode])
|
||||
end
|
||||
|
||||
|
||||
--[[
|
||||
Encodes 'value' as a function call
|
||||
Must have parameters in the 'callData' field of the metatable
|
||||
name == name of the function call
|
||||
parameters == array of parameters to encode
|
||||
]]
|
||||
local function getEncoder(options)
|
||||
options = options and jsonutil.merge({}, defaultOptions, options) or defaultOptions
|
||||
local function encodeCall(value, state)
|
||||
if not isCall(value) then
|
||||
return false
|
||||
end
|
||||
local encode = state.encode
|
||||
local name, params = decodeCall(value)
|
||||
local compositeEncoder = state.outputEncoder.composite
|
||||
local valueEncoder = [[
|
||||
for i = 1, (composite.n or #composite) do
|
||||
local val = composite[i]
|
||||
PUTINNER(i ~= 1)
|
||||
val = encode(val, state)
|
||||
val = val or ''
|
||||
if val then
|
||||
PUTVALUE(val)
|
||||
end
|
||||
end
|
||||
]]
|
||||
return compositeEncoder(valueEncoder, name .. '(', ')', ',', params, encode, state)
|
||||
end
|
||||
return {
|
||||
table = encodeCall,
|
||||
['function'] = encodeCall
|
||||
}
|
||||
end
|
||||
|
||||
local calls = {
|
||||
mergeOptions = mergeOptions,
|
||||
getEncoder = getEncoder
|
||||
}
|
||||
|
||||
return calls
|
||||
58
cosmic rage/lua/json/encode/number.lua
Normal file
58
cosmic rage/lua/json/encode/number.lua
Normal file
@@ -0,0 +1,58 @@
|
||||
--[[
|
||||
Licensed according to the included 'LICENSE' document
|
||||
Author: Thomas Harning Jr <harningt@gmail.com>
|
||||
]]
|
||||
local tostring = tostring
|
||||
local assert = assert
|
||||
local jsonutil = require("json.util")
|
||||
local huge = require("math").huge
|
||||
|
||||
local _ENV = nil
|
||||
|
||||
local defaultOptions = {
|
||||
nan = true,
|
||||
inf = true
|
||||
}
|
||||
|
||||
local modeOptions = {}
|
||||
modeOptions.strict = {
|
||||
nan = false,
|
||||
inf = false
|
||||
}
|
||||
|
||||
local function mergeOptions(options, mode)
|
||||
jsonutil.doOptionMerge(options, false, 'number', defaultOptions, mode and modeOptions[mode])
|
||||
end
|
||||
|
||||
|
||||
local function encodeNumber(number, options)
|
||||
if number ~= number then
|
||||
assert(options.nan, "Invalid number: NaN not enabled")
|
||||
return "NaN"
|
||||
end
|
||||
if number == huge then
|
||||
assert(options.inf, "Invalid number: Infinity not enabled")
|
||||
return "Infinity"
|
||||
end
|
||||
if number == -huge then
|
||||
assert(options.inf, "Invalid number: Infinity not enabled")
|
||||
return "-Infinity"
|
||||
end
|
||||
return tostring(number)
|
||||
end
|
||||
|
||||
local function getEncoder(options)
|
||||
options = options and jsonutil.merge({}, defaultOptions, options) or defaultOptions
|
||||
return {
|
||||
number = function(number, state)
|
||||
return encodeNumber(number, options)
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
local number = {
|
||||
mergeOptions = mergeOptions,
|
||||
getEncoder = getEncoder
|
||||
}
|
||||
|
||||
return number
|
||||
77
cosmic rage/lua/json/encode/object.lua
Normal file
77
cosmic rage/lua/json/encode/object.lua
Normal file
@@ -0,0 +1,77 @@
|
||||
--[[
|
||||
Licensed according to the included 'LICENSE' document
|
||||
Author: Thomas Harning Jr <harningt@gmail.com>
|
||||
]]
|
||||
local pairs = pairs
|
||||
local assert = assert
|
||||
|
||||
local type = type
|
||||
local tostring = tostring
|
||||
|
||||
local table_concat = require("table").concat
|
||||
local jsonutil = require("json.util")
|
||||
|
||||
local _ENV = nil
|
||||
|
||||
local defaultOptions = {
|
||||
}
|
||||
|
||||
local modeOptions = {}
|
||||
|
||||
local function mergeOptions(options, mode)
|
||||
jsonutil.doOptionMerge(options, false, 'object', defaultOptions, mode and modeOptions[mode])
|
||||
end
|
||||
|
||||
--[[
|
||||
Cleanup function to unmark a value as in the encoding process and return
|
||||
trailing results
|
||||
]]
|
||||
local function unmarkAfterEncode(tab, state, ...)
|
||||
state.already_encoded[tab] = nil
|
||||
return ...
|
||||
end
|
||||
--[[
|
||||
Encode a table as a JSON Object ( keys = strings, values = anything else )
|
||||
]]
|
||||
local function encodeTable(tab, options, state)
|
||||
-- Make sure this value hasn't been encoded yet
|
||||
state.check_unique(tab)
|
||||
local encode = state.encode
|
||||
local compositeEncoder = state.outputEncoder.composite
|
||||
local valueEncoder = [[
|
||||
local first = true
|
||||
for k, v in pairs(composite) do
|
||||
local ti = type(k)
|
||||
assert(ti == 'string' or ti == 'number' or ti == 'boolean', "Invalid object index type: " .. ti)
|
||||
local name = encode(tostring(k), state, true)
|
||||
if first then
|
||||
first = false
|
||||
else
|
||||
name = ',' .. name
|
||||
end
|
||||
PUTVALUE(name .. ':')
|
||||
local val = encode(v, state)
|
||||
val = val or ''
|
||||
if val then
|
||||
PUTVALUE(val)
|
||||
end
|
||||
end
|
||||
]]
|
||||
return unmarkAfterEncode(tab, state, compositeEncoder(valueEncoder, '{', '}', nil, tab, encode, state))
|
||||
end
|
||||
|
||||
local function getEncoder(options)
|
||||
options = options and jsonutil.merge({}, defaultOptions, options) or defaultOptions
|
||||
return {
|
||||
table = function(tab, state)
|
||||
return encodeTable(tab, options, state)
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
local object = {
|
||||
mergeOptions = mergeOptions,
|
||||
getEncoder = getEncoder
|
||||
}
|
||||
|
||||
return object
|
||||
66
cosmic rage/lua/json/encode/others.lua
Normal file
66
cosmic rage/lua/json/encode/others.lua
Normal file
@@ -0,0 +1,66 @@
|
||||
--[[
|
||||
Licensed according to the included 'LICENSE' document
|
||||
Author: Thomas Harning Jr <harningt@gmail.com>
|
||||
]]
|
||||
local tostring = tostring
|
||||
|
||||
local assert = assert
|
||||
local jsonutil = require("json.util")
|
||||
local type = type
|
||||
|
||||
local _ENV = nil
|
||||
|
||||
-- Shortcut that works
|
||||
local encodeBoolean = tostring
|
||||
|
||||
local defaultOptions = {
|
||||
allowUndefined = true,
|
||||
null = jsonutil.null,
|
||||
undefined = jsonutil.undefined
|
||||
}
|
||||
|
||||
local modeOptions = {}
|
||||
|
||||
modeOptions.strict = {
|
||||
allowUndefined = false
|
||||
}
|
||||
|
||||
local function mergeOptions(options, mode)
|
||||
jsonutil.doOptionMerge(options, false, 'others', defaultOptions, mode and modeOptions[mode])
|
||||
end
|
||||
local function getEncoder(options)
|
||||
options = options and jsonutil.merge({}, defaultOptions, options) or defaultOptions
|
||||
local function encodeOthers(value, state)
|
||||
if value == options.null then
|
||||
return 'null'
|
||||
elseif value == options.undefined then
|
||||
assert(options.allowUndefined, "Invalid value: Unsupported 'Undefined' parameter")
|
||||
return 'undefined'
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
local function encodeBoolean(value, state)
|
||||
return value and 'true' or 'false'
|
||||
end
|
||||
local nullType = type(options.null)
|
||||
local undefinedType = options.undefined and type(options.undefined)
|
||||
-- Make sure that all of the types handled here are handled
|
||||
local ret = {
|
||||
boolean = encodeBoolean,
|
||||
['nil'] = function() return 'null' end,
|
||||
[nullType] = encodeOthers
|
||||
}
|
||||
if undefinedType then
|
||||
ret[undefinedType] = encodeOthers
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
local others = {
|
||||
encodeBoolean = encodeBoolean,
|
||||
mergeOptions = mergeOptions,
|
||||
getEncoder = getEncoder
|
||||
}
|
||||
|
||||
return others
|
||||
91
cosmic rage/lua/json/encode/output.lua
Normal file
91
cosmic rage/lua/json/encode/output.lua
Normal file
@@ -0,0 +1,91 @@
|
||||
--[[
|
||||
Licensed according to the included 'LICENSE' document
|
||||
Author: Thomas Harning Jr <harningt@gmail.com>
|
||||
]]
|
||||
local type = type
|
||||
local assert, error = assert, error
|
||||
local table_concat = require("table").concat
|
||||
local loadstring = loadstring or load
|
||||
|
||||
local io = require("io")
|
||||
|
||||
local setmetatable = setmetatable
|
||||
|
||||
local output_utility = require("json.encode.output_utility")
|
||||
|
||||
local _ENV = nil
|
||||
|
||||
local tableCompositeCache = setmetatable({}, {__mode = 'v'})
|
||||
|
||||
local TABLE_VALUE_WRITER = [[
|
||||
ret[#ret + 1] = %VALUE%
|
||||
]]
|
||||
|
||||
local TABLE_INNER_WRITER = ""
|
||||
|
||||
--[[
|
||||
nextValues can output a max of two values to throw into the data stream
|
||||
expected to be called until nil is first return value
|
||||
value separator should either be attached to v1 or in innerValue
|
||||
]]
|
||||
local function defaultTableCompositeWriter(nextValues, beginValue, closeValue, innerValue, composite, encode, state)
|
||||
if type(nextValues) == 'string' then
|
||||
local fun = output_utility.prepareEncoder(defaultTableCompositeWriter, nextValues, innerValue, TABLE_VALUE_WRITER, TABLE_INNER_WRITER)
|
||||
local ret = {}
|
||||
fun(composite, ret, encode, state)
|
||||
return beginValue .. table_concat(ret, innerValue) .. closeValue
|
||||
end
|
||||
end
|
||||
|
||||
-- no 'simple' as default action is just to return the value
|
||||
local function getDefault()
|
||||
return { composite = defaultTableCompositeWriter }
|
||||
end
|
||||
|
||||
-- BEGIN IO-WRITER OUTPUT
|
||||
local IO_INNER_WRITER = [[
|
||||
if %WRITE_INNER% then
|
||||
state.__outputFile:write(%INNER_VALUE%)
|
||||
end
|
||||
]]
|
||||
local IO_VALUE_WRITER = [[
|
||||
state.__outputFile:write(%VALUE%)
|
||||
]]
|
||||
|
||||
local function buildIoWriter(output)
|
||||
if not output then -- Default to stdout
|
||||
output = io.output()
|
||||
end
|
||||
local function ioWriter(nextValues, beginValue, closeValue, innerValue, composite, encode, state)
|
||||
-- HOOK OUTPUT STATE
|
||||
state.__outputFile = output
|
||||
if type(nextValues) == 'string' then
|
||||
local fun = output_utility.prepareEncoder(ioWriter, nextValues, innerValue, IO_VALUE_WRITER, IO_INNER_WRITER)
|
||||
local ret = {}
|
||||
output:write(beginValue)
|
||||
fun(composite, ret, encode, state)
|
||||
output:write(closeValue)
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
local function ioSimpleWriter(encoded)
|
||||
if encoded then
|
||||
output:write(encoded)
|
||||
end
|
||||
return nil
|
||||
end
|
||||
return { composite = ioWriter, simple = ioSimpleWriter }
|
||||
end
|
||||
local function getIoWriter(output)
|
||||
return function()
|
||||
return buildIoWriter(output)
|
||||
end
|
||||
end
|
||||
|
||||
local output = {
|
||||
getDefault = getDefault,
|
||||
getIoWriter = getIoWriter
|
||||
}
|
||||
|
||||
return output
|
||||
54
cosmic rage/lua/json/encode/output_utility.lua
Normal file
54
cosmic rage/lua/json/encode/output_utility.lua
Normal file
@@ -0,0 +1,54 @@
|
||||
--[[
|
||||
Licensed according to the included 'LICENSE' document
|
||||
Author: Thomas Harning Jr <harningt@gmail.com>
|
||||
]]
|
||||
local setmetatable = setmetatable
|
||||
local assert, loadstring = assert, loadstring or load
|
||||
|
||||
local _ENV = nil
|
||||
|
||||
-- Key == weak, if main key goes away, then cache cleared
|
||||
local outputCache = setmetatable({}, {__mode = 'k'})
|
||||
-- TODO: inner tables weak?
|
||||
|
||||
local function buildFunction(nextValues, innerValue, valueWriter, innerWriter)
|
||||
local putInner = ""
|
||||
if innerValue and innerWriter then
|
||||
-- Prepare the lua-string representation of the separator to put in between values
|
||||
local formattedInnerValue = ("%q"):format(innerValue)
|
||||
-- Fill in the condition %WRITE_INNER% and the %INNER_VALUE% to actually write
|
||||
putInner = innerWriter:gsub("%%WRITE_INNER%%", "%%1"):gsub("%%INNER_VALUE%%", formattedInnerValue)
|
||||
end
|
||||
-- Template-in the value writer (if present) and its conditional argument
|
||||
local functionCode = nextValues:gsub("PUTINNER(%b())", putInner)
|
||||
-- %VALUE% is to be filled in by the value-to-write
|
||||
valueWriter = valueWriter:gsub("%%VALUE%%", "%%1")
|
||||
-- Template-in the value writer with its argument
|
||||
functionCode = functionCode:gsub("PUTVALUE(%b())", valueWriter)
|
||||
functionCode = [[
|
||||
return function(composite, ret, encode, state)
|
||||
]] .. functionCode .. [[
|
||||
end
|
||||
]]
|
||||
return assert(loadstring(functionCode))()
|
||||
end
|
||||
|
||||
local function prepareEncoder(cacheKey, nextValues, innerValue, valueWriter, innerWriter)
|
||||
local cache = outputCache[cacheKey]
|
||||
if not cache then
|
||||
cache = {}
|
||||
outputCache[cacheKey] = cache
|
||||
end
|
||||
local fun = cache[nextValues]
|
||||
if not fun then
|
||||
fun = buildFunction(nextValues, innerValue, valueWriter, innerWriter)
|
||||
cache[nextValues] = fun
|
||||
end
|
||||
return fun
|
||||
end
|
||||
|
||||
local output_utility = {
|
||||
prepareEncoder = prepareEncoder
|
||||
}
|
||||
|
||||
return output_utility
|
||||
88
cosmic rage/lua/json/encode/strings.lua
Normal file
88
cosmic rage/lua/json/encode/strings.lua
Normal file
@@ -0,0 +1,88 @@
|
||||
--[[
|
||||
Licensed according to the included 'LICENSE' document
|
||||
Author: Thomas Harning Jr <harningt@gmail.com>
|
||||
]]
|
||||
local string_char = require("string").char
|
||||
local pairs = pairs
|
||||
|
||||
local jsonutil = require("json.util")
|
||||
local util_merge = jsonutil.merge
|
||||
|
||||
local _ENV = nil
|
||||
|
||||
local normalEncodingMap = {
|
||||
['"'] = '\\"',
|
||||
['\\'] = '\\\\',
|
||||
['/'] = '\\/',
|
||||
['\b'] = '\\b',
|
||||
['\f'] = '\\f',
|
||||
['\n'] = '\\n',
|
||||
['\r'] = '\\r',
|
||||
['\t'] = '\\t',
|
||||
['\v'] = '\\v' -- not in official spec, on report, removing
|
||||
}
|
||||
|
||||
local xEncodingMap = {}
|
||||
for char, encoded in pairs(normalEncodingMap) do
|
||||
xEncodingMap[char] = encoded
|
||||
end
|
||||
|
||||
-- Pre-encode the control characters to speed up encoding...
|
||||
-- NOTE: UTF-8 may not work out right w/ JavaScript
|
||||
-- JavaScript uses 2 bytes after a \u... yet UTF-8 is a
|
||||
-- byte-stream encoding, not pairs of bytes (it does encode
|
||||
-- some letters > 1 byte, but base case is 1)
|
||||
for i = 0, 255 do
|
||||
local c = string_char(i)
|
||||
if c:match('[%z\1-\031\128-\255]') and not normalEncodingMap[c] then
|
||||
-- WARN: UTF8 specializes values >= 0x80 as parts of sequences...
|
||||
-- without \x encoding, do not allow encoding > 7F
|
||||
normalEncodingMap[c] = ('\\u%.4X'):format(i)
|
||||
xEncodingMap[c] = ('\\x%.2X'):format(i)
|
||||
end
|
||||
end
|
||||
|
||||
local defaultOptions = {
|
||||
xEncode = false, -- Encode single-bytes as \xXX
|
||||
processor = nil, -- Simple processor for the string prior to quoting
|
||||
-- / is not required to be quoted but it helps with certain decoding
|
||||
-- Required encoded characters, " \, and 00-1F (0 - 31)
|
||||
encodeSet = '\\"/%z\1-\031',
|
||||
encodeSetAppend = nil -- Chars to append to the default set
|
||||
}
|
||||
|
||||
local modeOptions = {}
|
||||
|
||||
local function mergeOptions(options, mode)
|
||||
jsonutil.doOptionMerge(options, false, 'strings', defaultOptions, mode and modeOptions[mode])
|
||||
end
|
||||
|
||||
local function getEncoder(options)
|
||||
options = options and util_merge({}, defaultOptions, options) or defaultOptions
|
||||
local encodeSet = options.encodeSet
|
||||
if options.encodeSetAppend then
|
||||
encodeSet = encodeSet .. options.encodeSetAppend
|
||||
end
|
||||
local encodingMap = options.xEncode and xEncodingMap or normalEncodingMap
|
||||
local encodeString
|
||||
if options.processor then
|
||||
local processor = options.processor
|
||||
encodeString = function(s, state)
|
||||
return '"' .. processor(s:gsub('[' .. encodeSet .. ']', encodingMap)) .. '"'
|
||||
end
|
||||
else
|
||||
encodeString = function(s, state)
|
||||
return '"' .. s:gsub('[' .. encodeSet .. ']', encodingMap) .. '"'
|
||||
end
|
||||
end
|
||||
return {
|
||||
string = encodeString
|
||||
}
|
||||
end
|
||||
|
||||
local strings = {
|
||||
mergeOptions = mergeOptions,
|
||||
getEncoder = getEncoder
|
||||
}
|
||||
|
||||
return strings
|
||||
152
cosmic rage/lua/json/util.lua
Normal file
152
cosmic rage/lua/json/util.lua
Normal file
@@ -0,0 +1,152 @@
|
||||
--[[
|
||||
Licensed according to the included 'LICENSE' document
|
||||
Author: Thomas Harning Jr <harningt@gmail.com>
|
||||
]]
|
||||
local type = type
|
||||
local print = print
|
||||
local tostring = tostring
|
||||
local pairs = pairs
|
||||
local getmetatable, setmetatable = getmetatable, setmetatable
|
||||
local select = select
|
||||
|
||||
local _ENV = nil
|
||||
|
||||
local function foreach(tab, func)
|
||||
for k, v in pairs(tab) do
|
||||
func(k,v)
|
||||
end
|
||||
end
|
||||
local function printValue(tab, name)
|
||||
local parsed = {}
|
||||
local function doPrint(key, value, space)
|
||||
space = space or ''
|
||||
if type(value) == 'table' then
|
||||
if parsed[value] then
|
||||
print(space .. key .. '= <' .. parsed[value] .. '>')
|
||||
else
|
||||
parsed[value] = key
|
||||
print(space .. key .. '= {')
|
||||
space = space .. ' '
|
||||
foreach(value, function(key, value) doPrint(key, value, space) end)
|
||||
end
|
||||
else
|
||||
if type(value) == 'string' then
|
||||
value = '[[' .. tostring(value) .. ']]'
|
||||
end
|
||||
print(space .. key .. '=' .. tostring(value))
|
||||
end
|
||||
end
|
||||
doPrint(name, tab)
|
||||
end
|
||||
|
||||
local function clone(t)
|
||||
local ret = {}
|
||||
for k,v in pairs(t) do
|
||||
ret[k] = v
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
local function inner_merge(t, remaining, from, ...)
|
||||
if remaining == 0 then
|
||||
return t
|
||||
end
|
||||
if from then
|
||||
for k,v in pairs(from) do
|
||||
t[k] = v
|
||||
end
|
||||
end
|
||||
return inner_merge(t, remaining - 1, ...)
|
||||
end
|
||||
|
||||
--[[*
|
||||
Shallow-merges tables in order onto the first table.
|
||||
|
||||
@param t table to merge entries onto
|
||||
@param ... sequence of 0 or more tables to merge onto 't'
|
||||
|
||||
@returns table 't' from input
|
||||
]]
|
||||
local function merge(t, ...)
|
||||
return inner_merge(t, select('#', ...), ...)
|
||||
end
|
||||
|
||||
-- Function to insert nulls into the JSON stream
|
||||
local function null()
|
||||
return null
|
||||
end
|
||||
|
||||
-- Marker for 'undefined' values
|
||||
local function undefined()
|
||||
return undefined
|
||||
end
|
||||
|
||||
local ArrayMT = {}
|
||||
|
||||
--[[
|
||||
Return's true if the metatable marks it as an array..
|
||||
Or false if it has no array component at all
|
||||
Otherwise nil to get the normal detection component working
|
||||
]]
|
||||
local function IsArray(value)
|
||||
if type(value) ~= 'table' then return false end
|
||||
local meta = getmetatable(value)
|
||||
local ret = meta == ArrayMT or (meta ~= nil and meta.__is_luajson_array)
|
||||
if not ret then
|
||||
if #value == 0 then return false end
|
||||
else
|
||||
return ret
|
||||
end
|
||||
end
|
||||
local function InitArray(array)
|
||||
setmetatable(array, ArrayMT)
|
||||
return array
|
||||
end
|
||||
|
||||
local CallMT = {}
|
||||
|
||||
local function isCall(value)
|
||||
return CallMT == getmetatable(value)
|
||||
end
|
||||
|
||||
local function buildCall(name, ...)
|
||||
local callData = {
|
||||
name = name,
|
||||
parameters = {n = select('#', ...), ...}
|
||||
}
|
||||
return setmetatable(callData, CallMT)
|
||||
end
|
||||
|
||||
local function decodeCall(callData)
|
||||
if not isCall(callData) then return nil end
|
||||
return callData.name, callData.parameters
|
||||
end
|
||||
|
||||
local function doOptionMerge(options, nested, name, defaultOptions, modeOptions)
|
||||
if nested then
|
||||
modeOptions = modeOptions and modeOptions[name]
|
||||
defaultOptions = defaultOptions and defaultOptions[name]
|
||||
end
|
||||
options[name] = merge(
|
||||
{},
|
||||
defaultOptions,
|
||||
modeOptions,
|
||||
options[name]
|
||||
)
|
||||
end
|
||||
|
||||
local json_util = {
|
||||
printValue = printValue,
|
||||
clone = clone,
|
||||
merge = merge,
|
||||
null = null,
|
||||
undefined = undefined,
|
||||
IsArray = IsArray,
|
||||
InitArray = InitArray,
|
||||
isCall = isCall,
|
||||
buildCall = buildCall,
|
||||
decodeCall = decodeCall,
|
||||
doOptionMerge = doOptionMerge
|
||||
}
|
||||
|
||||
return json_util
|
||||
292
cosmic rage/lua/ltn12.lua
Normal file
292
cosmic rage/lua/ltn12.lua
Normal file
@@ -0,0 +1,292 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- LTN12 - Filters, sources, sinks and pumps.
|
||||
-- LuaSocket toolkit.
|
||||
-- Author: Diego Nehab
|
||||
-- RCS ID: $Id: ltn12.lua,v 1.31 2006/04/03 04:45:42 diego Exp $
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Declare module
|
||||
-----------------------------------------------------------------------------
|
||||
local string = require("string")
|
||||
local table = require("table")
|
||||
local base = _G
|
||||
module("ltn12")
|
||||
|
||||
filter = {}
|
||||
source = {}
|
||||
sink = {}
|
||||
pump = {}
|
||||
|
||||
-- 2048 seems to be better in windows...
|
||||
BLOCKSIZE = 2048
|
||||
_VERSION = "LTN12 1.0.1"
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Filter stuff
|
||||
-----------------------------------------------------------------------------
|
||||
-- returns a high level filter that cycles a low-level filter
|
||||
function filter.cycle(low, ctx, extra)
|
||||
base.assert(low)
|
||||
return function(chunk)
|
||||
local ret
|
||||
ret, ctx = low(ctx, chunk, extra)
|
||||
return ret
|
||||
end
|
||||
end
|
||||
|
||||
-- chains a bunch of filters together
|
||||
-- (thanks to Wim Couwenberg)
|
||||
function filter.chain(...)
|
||||
local n = table.getn(arg)
|
||||
local top, index = 1, 1
|
||||
local retry = ""
|
||||
return function(chunk)
|
||||
retry = chunk and retry
|
||||
while true do
|
||||
if index == top then
|
||||
chunk = arg[index](chunk)
|
||||
if chunk == "" or top == n then return chunk
|
||||
elseif chunk then index = index + 1
|
||||
else
|
||||
top = top+1
|
||||
index = top
|
||||
end
|
||||
else
|
||||
chunk = arg[index](chunk or "")
|
||||
if chunk == "" then
|
||||
index = index - 1
|
||||
chunk = retry
|
||||
elseif chunk then
|
||||
if index == n then return chunk
|
||||
else index = index + 1 end
|
||||
else base.error("filter returned inappropriate nil") end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Source stuff
|
||||
-----------------------------------------------------------------------------
|
||||
-- create an empty source
|
||||
local function empty()
|
||||
return nil
|
||||
end
|
||||
|
||||
function source.empty()
|
||||
return empty
|
||||
end
|
||||
|
||||
-- returns a source that just outputs an error
|
||||
function source.error(err)
|
||||
return function()
|
||||
return nil, err
|
||||
end
|
||||
end
|
||||
|
||||
-- creates a file source
|
||||
function source.file(handle, io_err)
|
||||
if handle then
|
||||
return function()
|
||||
local chunk = handle:read(BLOCKSIZE)
|
||||
if not chunk then handle:close() end
|
||||
return chunk
|
||||
end
|
||||
else return source.error(io_err or "unable to open file") end
|
||||
end
|
||||
|
||||
-- turns a fancy source into a simple source
|
||||
function source.simplify(src)
|
||||
base.assert(src)
|
||||
return function()
|
||||
local chunk, err_or_new = src()
|
||||
src = err_or_new or src
|
||||
if not chunk then return nil, err_or_new
|
||||
else return chunk end
|
||||
end
|
||||
end
|
||||
|
||||
-- creates string source
|
||||
function source.string(s)
|
||||
if s then
|
||||
local i = 1
|
||||
return function()
|
||||
local chunk = string.sub(s, i, i+BLOCKSIZE-1)
|
||||
i = i + BLOCKSIZE
|
||||
if chunk ~= "" then return chunk
|
||||
else return nil end
|
||||
end
|
||||
else return source.empty() end
|
||||
end
|
||||
|
||||
-- creates rewindable source
|
||||
function source.rewind(src)
|
||||
base.assert(src)
|
||||
local t = {}
|
||||
return function(chunk)
|
||||
if not chunk then
|
||||
chunk = table.remove(t)
|
||||
if not chunk then return src()
|
||||
else return chunk end
|
||||
else
|
||||
table.insert(t, chunk)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function source.chain(src, f)
|
||||
base.assert(src and f)
|
||||
local last_in, last_out = "", ""
|
||||
local state = "feeding"
|
||||
local err
|
||||
return function()
|
||||
if not last_out then
|
||||
base.error('source is empty!', 2)
|
||||
end
|
||||
while true do
|
||||
if state == "feeding" then
|
||||
last_in, err = src()
|
||||
if err then return nil, err end
|
||||
last_out = f(last_in)
|
||||
if not last_out then
|
||||
if last_in then
|
||||
base.error('filter returned inappropriate nil')
|
||||
else
|
||||
return nil
|
||||
end
|
||||
elseif last_out ~= "" then
|
||||
state = "eating"
|
||||
if last_in then last_in = "" end
|
||||
return last_out
|
||||
end
|
||||
else
|
||||
last_out = f(last_in)
|
||||
if last_out == "" then
|
||||
if last_in == "" then
|
||||
state = "feeding"
|
||||
else
|
||||
base.error('filter returned ""')
|
||||
end
|
||||
elseif not last_out then
|
||||
if last_in then
|
||||
base.error('filter returned inappropriate nil')
|
||||
else
|
||||
return nil
|
||||
end
|
||||
else
|
||||
return last_out
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- creates a source that produces contents of several sources, one after the
|
||||
-- other, as if they were concatenated
|
||||
-- (thanks to Wim Couwenberg)
|
||||
function source.cat(...)
|
||||
local src = table.remove(arg, 1)
|
||||
return function()
|
||||
while src do
|
||||
local chunk, err = src()
|
||||
if chunk then return chunk end
|
||||
if err then return nil, err end
|
||||
src = table.remove(arg, 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Sink stuff
|
||||
-----------------------------------------------------------------------------
|
||||
-- creates a sink that stores into a table
|
||||
function sink.table(t)
|
||||
t = t or {}
|
||||
local f = function(chunk, err)
|
||||
if chunk then table.insert(t, chunk) end
|
||||
return 1
|
||||
end
|
||||
return f, t
|
||||
end
|
||||
|
||||
-- turns a fancy sink into a simple sink
|
||||
function sink.simplify(snk)
|
||||
base.assert(snk)
|
||||
return function(chunk, err)
|
||||
local ret, err_or_new = snk(chunk, err)
|
||||
if not ret then return nil, err_or_new end
|
||||
snk = err_or_new or snk
|
||||
return 1
|
||||
end
|
||||
end
|
||||
|
||||
-- creates a file sink
|
||||
function sink.file(handle, io_err)
|
||||
if handle then
|
||||
return function(chunk, err)
|
||||
if not chunk then
|
||||
handle:close()
|
||||
return 1
|
||||
else return handle:write(chunk) end
|
||||
end
|
||||
else return sink.error(io_err or "unable to open file") end
|
||||
end
|
||||
|
||||
-- creates a sink that discards data
|
||||
local function null()
|
||||
return 1
|
||||
end
|
||||
|
||||
function sink.null()
|
||||
return null
|
||||
end
|
||||
|
||||
-- creates a sink that just returns an error
|
||||
function sink.error(err)
|
||||
return function()
|
||||
return nil, err
|
||||
end
|
||||
end
|
||||
|
||||
-- chains a sink with a filter
|
||||
function sink.chain(f, snk)
|
||||
base.assert(f and snk)
|
||||
return function(chunk, err)
|
||||
if chunk ~= "" then
|
||||
local filtered = f(chunk)
|
||||
local done = chunk and ""
|
||||
while true do
|
||||
local ret, snkerr = snk(filtered, err)
|
||||
if not ret then return nil, snkerr end
|
||||
if filtered == done then return 1 end
|
||||
filtered = f(done)
|
||||
end
|
||||
else return 1 end
|
||||
end
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Pump stuff
|
||||
-----------------------------------------------------------------------------
|
||||
-- pumps one chunk from the source to the sink
|
||||
function pump.step(src, snk)
|
||||
local chunk, src_err = src()
|
||||
local ret, snk_err = snk(chunk, src_err)
|
||||
if chunk and ret then return 1
|
||||
else return nil, src_err or snk_err end
|
||||
end
|
||||
|
||||
-- pumps all data from a source to a sink, using a step function
|
||||
function pump.all(src, snk, step)
|
||||
base.assert(src and snk)
|
||||
step = step or pump.step
|
||||
while true do
|
||||
local ret, err = step(src, snk)
|
||||
if not ret then
|
||||
if err then return nil, err
|
||||
else return 1 end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
1001
cosmic rage/lua/luacom5.lua
Normal file
1001
cosmic rage/lua/luacom5.lua
Normal file
File diff suppressed because it is too large
Load Diff
1633
cosmic rage/lua/mapper.lua
Normal file
1633
cosmic rage/lua/mapper.lua
Normal file
File diff suppressed because it is too large
Load Diff
87
cosmic rage/lua/mime.lua
Normal file
87
cosmic rage/lua/mime.lua
Normal file
@@ -0,0 +1,87 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- MIME support for the Lua language.
|
||||
-- Author: Diego Nehab
|
||||
-- Conforming to RFCs 2045-2049
|
||||
-- RCS ID: $Id: mime.lua,v 1.29 2007/06/11 23:44:54 diego Exp $
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Declare module and import dependencies
|
||||
-----------------------------------------------------------------------------
|
||||
local base = _G
|
||||
local ltn12 = require("ltn12")
|
||||
local mime = require("mime.core")
|
||||
local io = require("io")
|
||||
local string = require("string")
|
||||
module("mime")
|
||||
|
||||
-- encode, decode and wrap algorithm tables
|
||||
encodet = {}
|
||||
decodet = {}
|
||||
wrapt = {}
|
||||
|
||||
-- creates a function that chooses a filter by name from a given table
|
||||
local function choose(table)
|
||||
return function(name, opt1, opt2)
|
||||
if base.type(name) ~= "string" then
|
||||
name, opt1, opt2 = "default", name, opt1
|
||||
end
|
||||
local f = table[name or "nil"]
|
||||
if not f then
|
||||
base.error("unknown key (" .. base.tostring(name) .. ")", 3)
|
||||
else return f(opt1, opt2) end
|
||||
end
|
||||
end
|
||||
|
||||
-- define the encoding filters
|
||||
encodet['base64'] = function()
|
||||
return ltn12.filter.cycle(b64, "")
|
||||
end
|
||||
|
||||
encodet['quoted-printable'] = function(mode)
|
||||
return ltn12.filter.cycle(qp, "",
|
||||
(mode == "binary") and "=0D=0A" or "\r\n")
|
||||
end
|
||||
|
||||
-- define the decoding filters
|
||||
decodet['base64'] = function()
|
||||
return ltn12.filter.cycle(unb64, "")
|
||||
end
|
||||
|
||||
decodet['quoted-printable'] = function()
|
||||
return ltn12.filter.cycle(unqp, "")
|
||||
end
|
||||
|
||||
local function format(chunk)
|
||||
if chunk then
|
||||
if chunk == "" then return "''"
|
||||
else return string.len(chunk) end
|
||||
else return "nil" end
|
||||
end
|
||||
|
||||
-- define the line-wrap filters
|
||||
wrapt['text'] = function(length)
|
||||
length = length or 76
|
||||
return ltn12.filter.cycle(wrp, length, length)
|
||||
end
|
||||
wrapt['base64'] = wrapt['text']
|
||||
wrapt['default'] = wrapt['text']
|
||||
|
||||
wrapt['quoted-printable'] = function()
|
||||
return ltn12.filter.cycle(qpwrp, 76, 76)
|
||||
end
|
||||
|
||||
-- function that choose the encoding, decoding or wrap algorithm
|
||||
encode = choose(encodet)
|
||||
decode = choose(decodet)
|
||||
wrap = choose(wrapt)
|
||||
|
||||
-- define the end-of-line normalization filter
|
||||
function normalize(marker)
|
||||
return ltn12.filter.cycle(eol, 0, marker)
|
||||
end
|
||||
|
||||
-- high level stuffing filter
|
||||
function stuff()
|
||||
return ltn12.filter.cycle(dot, 2)
|
||||
end
|
||||
410
cosmic rage/lua/movewindow.lua
Normal file
410
cosmic rage/lua/movewindow.lua
Normal file
@@ -0,0 +1,410 @@
|
||||
-- movewindow.lua
|
||||
|
||||
--[[
|
||||
|
||||
Miniwindow drag-to-move functions.
|
||||
|
||||
Author: Nick Gammon
|
||||
Date: 15th July 2009
|
||||
Modified: 16th November 2010 to add preprocessing
|
||||
Modified: 29th November 2010 by Fiendish to improve dragging offscreen
|
||||
Modified: 8th February 2018 by Nick to remember the flags setting (eg. absolute position)
|
||||
|
||||
This module is intended to make it easier to add drag handlers for miniwindows.
|
||||
|
||||
It implements the following:
|
||||
|
||||
-- find previous location
|
||||
nocheck: if true, don't check if offscreen (boolean)
|
||||
friends: other windows to move with this one (table)
|
||||
preprocess: preprocessing for mousedown, mouseup etc. (table)
|
||||
|
||||
Handler names for preprocess table:
|
||||
|
||||
mousedown
|
||||
mouseup
|
||||
mouseover
|
||||
cancelmouseover
|
||||
cancelmousedown
|
||||
dragmove
|
||||
dragrelease
|
||||
|
||||
If any preprocess handler returns true (that is, neither nil nor false) then the default handler
|
||||
in this module is not used. (The miniwindow name is the third argument)
|
||||
|
||||
function mousedown (flags, id, win)
|
||||
if bit.band (flags, miniwin.hotspot_got_rh_mouse) then
|
||||
-- do something different here
|
||||
return true
|
||||
end -- if
|
||||
|
||||
return false -- take normal movewindow behaviour
|
||||
end -- mousedown
|
||||
|
||||
windowinfo = movewindow.install (win, default_position, default_flags, nocheck, friends, preprocess)
|
||||
|
||||
movewindow.add_drag_handler (win, left, top, right, bottom, cursor) -- add a drag handler for the nominated rectangle
|
||||
|
||||
movewindow.save_state (win) -- saves the miniwindow location to the appropriate variables
|
||||
|
||||
It also installs a position-checker that moves the miniwindow into view after 5 seconds, in case
|
||||
you resize the main world window, and the window is no longer visible. The 5 seconds are to give
|
||||
the main world window's position and size time to stabilize. (Unless nocheck is true)
|
||||
|
||||
|
||||
Example of use:
|
||||
|
||||
require "movewindow" -- pull in this module
|
||||
|
||||
|
||||
-- CREATE WINDOW in OnPluginInstall
|
||||
|
||||
win = GetPluginID () -- miniwindow ID
|
||||
|
||||
windowinfo = movewindow.install (win, miniwin.pos_center_right, 0) -- default position / flags
|
||||
|
||||
-- make miniwindow (use locations returned from last time we saved the state)
|
||||
-- note that the width and height are not part of the window position info, and can thus change as required
|
||||
|
||||
WindowCreate (win,
|
||||
windowinfo.window_left,
|
||||
windowinfo.window_top,
|
||||
WINDOW_WIDTH,
|
||||
WINDOW_HEIGHT,
|
||||
windowinfo.window_mode,
|
||||
windowinfo.window_flags,
|
||||
ColourNameToRGB "slategray")
|
||||
|
||||
|
||||
|
||||
-- INSTALL DRAG HANDLER when required (eg. when drawing stuff to window)
|
||||
-- in this case we use 0,0,0,0 as the rectangle (ie. the whole window)
|
||||
-- typically the height would be the size of the title bar
|
||||
|
||||
movewindow.add_drag_handler (win, 0, 0, 0, 0, miniwin.cursor_both_arrow)
|
||||
|
||||
-- SAVE STATE in OnPluginSaveState
|
||||
|
||||
movewindow.save_state (win)
|
||||
|
||||
|
||||
The module makes one global variable (table) when installed. This is named:
|
||||
|
||||
mw_<window_id>_movewindow_info
|
||||
|
||||
|
||||
This contains handler functions (the table is an upvalue to the functions)
|
||||
|
||||
"check_map_position"=function: 023D9368 -- the position checker
|
||||
"dragmove"=function: 01AD1158 -- the dragmove handler
|
||||
"dragrelease"=function: 023E4238 -- the dragrelease handler
|
||||
"margin"=20 -- margin for dragging offscreen
|
||||
"mousedown"=function: 01AD1108 -- the mousedown handler
|
||||
"origx"=648 -- used during dragging
|
||||
"origy"=39
|
||||
"startx"=88
|
||||
"starty"=8
|
||||
"win"="23c3c91af0a26790c625f5d1" -- the supplied window ID
|
||||
"window_flags"=2 -- flags (eg. 2, absolute position)
|
||||
"window_left"=652 -- current left location
|
||||
"window_mode"=0 -- window mode
|
||||
"window_top"=31 -- current top location
|
||||
|
||||
|
||||
This table is returned from movewindow.install so you can find where to put the
|
||||
window the first time it is created.
|
||||
|
||||
--]]
|
||||
|
||||
movewindow = {} -- table to hold functions like movewindow.install
|
||||
|
||||
-- make a mouse-down handler with the movement information as an upvalue
|
||||
|
||||
local function make_mousedown_handler (mwi)
|
||||
|
||||
return function (flags, hotspot_id)
|
||||
|
||||
local win = mwi.win
|
||||
|
||||
-- see if other action wanted
|
||||
if mwi.preprocess.mousedown then
|
||||
if mwi.preprocess.mousedown (flags, hotspot_id, win) then
|
||||
return
|
||||
end -- if handled already
|
||||
end -- if handler
|
||||
|
||||
-- find where mouse is so we can adjust window relative to mouse
|
||||
mwi.startx = WindowInfo (win, 14)
|
||||
mwi.starty = WindowInfo (win, 15)
|
||||
|
||||
-- find where window is in case we drag it offscreen
|
||||
mwi.origx = WindowInfo (win, 10)
|
||||
mwi.origy = WindowInfo (win, 11)
|
||||
|
||||
-- find where the friends are relative to the window
|
||||
for i, v in ipairs (mwi.window_friends) do
|
||||
if v then
|
||||
mwi.window_friend_deltas [i] =
|
||||
{
|
||||
WindowInfo (v, 10) - mwi.origx,
|
||||
WindowInfo (v, 11) - mwi.origy
|
||||
}
|
||||
end -- if
|
||||
end -- for
|
||||
|
||||
end -- mousedown
|
||||
|
||||
end -- make_mousedown_handler
|
||||
|
||||
-- make a mouse drag-move handler with the movement information as an upvalue
|
||||
|
||||
local function make_dragmove_handler (mwi)
|
||||
|
||||
return function (flags, hotspot_id)
|
||||
|
||||
local win = mwi.win
|
||||
|
||||
-- see if other action wanted
|
||||
if mwi.preprocess.dragmove then
|
||||
if mwi.preprocess.dragmove (flags, hotspot_id, win) then
|
||||
return
|
||||
end -- if handled already
|
||||
end -- if handler
|
||||
|
||||
-- find where it is now
|
||||
local posx, posy = WindowInfo (win, 17) - mwi.startx,
|
||||
WindowInfo (win, 18) - mwi.starty
|
||||
|
||||
-- change the mouse cursor shape appropriately
|
||||
if posx < 0 or
|
||||
posx > GetInfo (281) - mwi.margin or
|
||||
posy < 0 or -- don't drag title out of view
|
||||
posy > GetInfo (280) - mwi.margin then
|
||||
SetCursor (miniwin.cursor_x) -- X cursor
|
||||
else
|
||||
SetCursor (miniwin.cursor_hand) -- hand cursor
|
||||
end -- if
|
||||
|
||||
if posx < 0 then
|
||||
posx = 0
|
||||
elseif posx > GetInfo (281) - mwi.margin then
|
||||
posx = GetInfo(281) - mwi.margin
|
||||
end
|
||||
if posy < 0 then
|
||||
posy = 0
|
||||
elseif posy > GetInfo(280) - mwi.margin then
|
||||
posy = GetInfo(280) - mwi.margin
|
||||
end
|
||||
|
||||
-- move the window to the new location - offset by how far mouse was into window
|
||||
WindowPosition(win, posx, posy, 0, miniwin.create_absolute_location);
|
||||
|
||||
-- move the friends if they still exist
|
||||
for i, v in ipairs(mwi.window_friends) do
|
||||
if v then
|
||||
WindowPosition (v, posx + mwi.window_friend_deltas [i] [1],
|
||||
posy + mwi.window_friend_deltas [i] [2],
|
||||
0,
|
||||
WindowInfo (v, 8))
|
||||
end -- if
|
||||
end -- for
|
||||
|
||||
mwi.window_left = posx -- remember for saving state
|
||||
mwi.window_top = posy
|
||||
mwi.window_mode = 0
|
||||
mwi.window_flags = miniwin.create_absolute_location -- absolute position
|
||||
|
||||
end -- dragmove
|
||||
|
||||
end -- make_dragmove_handler
|
||||
|
||||
-- make a mouse drag-release handler with the movement information as an upvalue
|
||||
|
||||
local function make_dragrelease_handler (mwi)
|
||||
|
||||
return function (flags, hotspot_id)
|
||||
|
||||
local win = mwi.win
|
||||
|
||||
-- see if other action wanted
|
||||
if mwi.preprocess.dragrelease then
|
||||
if mwi.preprocess.dragrelease (flags, hotspot_id, win) then
|
||||
return
|
||||
end -- if handled already
|
||||
end -- if handler
|
||||
|
||||
Repaint () -- update window location
|
||||
|
||||
end -- dragrelease
|
||||
|
||||
end -- make_dragrelease_handler
|
||||
|
||||
-- make other handler with the movement information as an upvalue
|
||||
|
||||
local function make_other_handler (mwi, name)
|
||||
|
||||
return function (flags, hotspot_id)
|
||||
|
||||
-- send to supplied handler
|
||||
if mwi.preprocess [name] then
|
||||
mwi.preprocess [name] (flags, hotspot_id, mwi.win)
|
||||
end -- if handler
|
||||
|
||||
end -- other
|
||||
|
||||
end -- make_other_handler
|
||||
|
||||
-- make a mouse position-checking function with the movement information as an upvalue
|
||||
|
||||
local function make_check_map_position_handler (mwi)
|
||||
|
||||
return function ()
|
||||
|
||||
local win = mwi.win
|
||||
|
||||
if not WindowInfo (win, 1) then
|
||||
ColourNote ("white", "red", "Error in make_check_map_position_handler: no window named: " .. win)
|
||||
return
|
||||
end -- no such window
|
||||
|
||||
-- check miniwindow visible
|
||||
if mwi.window_left < 0 or
|
||||
mwi.window_left > GetInfo (281) - mwi.margin or
|
||||
mwi.window_top < 0 or -- don't drag title out of view
|
||||
mwi.window_top > GetInfo (280) - mwi.margin then
|
||||
mwi.window_mode = miniwin.pos_center_right
|
||||
mwi.window_flags = 0
|
||||
end -- if not visible
|
||||
|
||||
WindowPosition (win,
|
||||
mwi.window_left,
|
||||
mwi.window_top,
|
||||
mwi.window_mode,
|
||||
mwi.window_flags)
|
||||
|
||||
end -- check_map_position
|
||||
|
||||
end -- make_check_map_position_handler
|
||||
|
||||
-- call movewindow.install in OnPluginInstall to find the position of the window, before creating it
|
||||
-- - it also creates the handler functions ready for use later
|
||||
|
||||
function movewindow.install (win, default_position, default_flags, nocheck, friends, preprocess, start_position)
|
||||
|
||||
win = win or GetPluginID () -- default to current plugin ID
|
||||
|
||||
assert (not string.match (win, "[^A-Za-z0-9_]"), "Invalid window name in movewindow.install: " .. win)
|
||||
|
||||
default_position = default_position or miniwin.pos_center_right -- on right, center top/bottom
|
||||
default_flags = default_flags or 0
|
||||
|
||||
-- set up handlers and where window should be shown (from saved state, if any)
|
||||
local movewindow_info = {
|
||||
win = win, -- save window ID
|
||||
|
||||
-- save current position in table (obtained from state file)
|
||||
window_left = tonumber (GetVariable ("mw_" .. win .. "_windowx")) or (start_position and start_position.x) or 0,
|
||||
window_top = tonumber (GetVariable ("mw_" .. win .. "_windowy")) or (start_position and start_position.y) or 0,
|
||||
window_mode = default_position,
|
||||
window_flags = tonumber (GetVariable ("mw_" .. win .. "_windowflags")) or default_flags,
|
||||
window_friends = friends or {},
|
||||
window_friend_deltas = {},
|
||||
margin = 20, -- how close we can put to the edge of the window
|
||||
preprocess = preprocess or {},
|
||||
}
|
||||
|
||||
-- check valid
|
||||
for k, v in pairs (movewindow_info.preprocess) do
|
||||
assert (type (v) == "function", "Handler '" .. k .. "' is not a function")
|
||||
end -- for
|
||||
|
||||
-- handler to reposition window
|
||||
movewindow_info.check_map_position = make_check_map_position_handler (movewindow_info) -- for startup
|
||||
|
||||
-- mouse handlers
|
||||
movewindow_info.mousedown = make_mousedown_handler (movewindow_info)
|
||||
movewindow_info.mouseup = make_other_handler (movewindow_info, "mouseup")
|
||||
movewindow_info.mouseover = make_other_handler (movewindow_info, "mouseover")
|
||||
movewindow_info.cancelmouseover = make_other_handler (movewindow_info, "cancelmouseover")
|
||||
movewindow_info.cancelmousedown = make_other_handler (movewindow_info, "cancelmousedown")
|
||||
movewindow_info.dragmove = make_dragmove_handler (movewindow_info)
|
||||
movewindow_info.dragrelease = make_dragrelease_handler (movewindow_info)
|
||||
|
||||
-- save table in global namespace
|
||||
_G ["mw_" .. win .. "_movewindow_info"] = movewindow_info
|
||||
|
||||
|
||||
-- give main world window time to stabilize its size and position
|
||||
-- eg. this might be: mw_23c3c91af0a26790c625f5d1_movewindow_info.check_map_position ()
|
||||
|
||||
if not nocheck then -- if wanted
|
||||
DoAfterSpecial (5, "mw_" .. win .. "_movewindow_info.check_map_position ()" , sendto.script)
|
||||
end -- if
|
||||
|
||||
return movewindow_info -- the caller might appreciate access to this table
|
||||
end -- movewindow.install
|
||||
|
||||
-- call movewindow.add_drag_handler after creating the window, and after deleting hotspots where applicable
|
||||
-- to add a drag hotspot
|
||||
|
||||
function movewindow.add_drag_handler (win, left, top, right, bottom, cursor)
|
||||
|
||||
win = win or GetPluginID () -- default to current plugin ID
|
||||
|
||||
-- the zz puts it under other hotspots on the drag area
|
||||
local hotspot_id = "zz_mw_" .. win .. "_movewindow_hotspot"
|
||||
|
||||
if not WindowInfo (win, 1) then
|
||||
ColourNote ("white", "red", "Error in movewindow.add_drag_handler: no window named: " .. win)
|
||||
return
|
||||
end -- no such window
|
||||
|
||||
-- make a hotspot
|
||||
WindowAddHotspot(win, hotspot_id,
|
||||
left or 0, top or 0, right or 0, bottom or 0, -- rectangle
|
||||
"mw_" .. win .. "_movewindow_info.mouseover", -- MouseOver
|
||||
"mw_" .. win .. "_movewindow_info.cancelmouseover", -- CancelMouseOver
|
||||
"mw_" .. win .. "_movewindow_info.mousedown", -- MouseDown
|
||||
"mw_" .. win .. "_movewindow_info.cancelmousedown", -- CancelMouseDown
|
||||
"mw_" .. win .. "_movewindow_info.mouseup", -- MouseUp
|
||||
"Drag to move window", -- tooltip text
|
||||
cursor or miniwin.cursor_hand, -- cursor
|
||||
0) -- flags
|
||||
|
||||
WindowDragHandler (win, hotspot_id,
|
||||
"mw_" .. win .. "_movewindow_info.dragmove",
|
||||
"mw_" .. win .. "_movewindow_info.dragrelease",
|
||||
0) -- flags
|
||||
|
||||
end -- movewindow.add_drag_handler
|
||||
|
||||
-- call movewindow.save_state in OnPluginSaveState
|
||||
|
||||
function movewindow.save_state (win)
|
||||
|
||||
win = win or GetPluginID () -- default to current plugin ID
|
||||
|
||||
-- get movewindow variable from global namespace
|
||||
local mwi = _G ["mw_" .. win .. "_movewindow_info"]
|
||||
|
||||
if not mwi then
|
||||
ColourNote ("white", "red", "Error in movewindow.save_state: no window movement info for: " .. win)
|
||||
return
|
||||
end -- no such window
|
||||
|
||||
-- remember where the window was
|
||||
|
||||
-- use actual last specified position, not where we happen to think it is, in case another plugin moves it
|
||||
-- suggested by Fiendish, 27 August 2012.
|
||||
if WindowInfo (win, 1) then
|
||||
mwi.window_left = WindowInfo(win, 1)
|
||||
end
|
||||
if WindowInfo (win, 2) then
|
||||
mwi.window_top = WindowInfo(win, 2)
|
||||
end
|
||||
|
||||
SetVariable ("mw_" .. win .. "_windowx", mwi.window_left)
|
||||
SetVariable ("mw_" .. win .. "_windowy", mwi.window_top)
|
||||
SetVariable ("mw_" .. win .. "_windowflags", mwi.window_flags)
|
||||
|
||||
end -- movewindow.save_state
|
||||
480
cosmic rage/lua/mw.lua
Normal file
480
cosmic rage/lua/mw.lua
Normal file
@@ -0,0 +1,480 @@
|
||||
-- mw.lua
|
||||
|
||||
-- Helper functions for miniwindows
|
||||
--
|
||||
|
||||
-- Author: Nick Gammon - 8th September 2008
|
||||
|
||||
--[[
|
||||
|
||||
Exposed functions are:
|
||||
|
||||
mw.colourtext - show a string with imbedded colour codes
|
||||
mw.colour_conversion - table with colour codes in it - add more if you want
|
||||
mw.strip_colours - remove colour codes from a string
|
||||
mw.popup - make a popup window
|
||||
mw.tooltip - make a tooltip window
|
||||
|
||||
EXAMPLE OF MAKING A POPUP WINDOW:
|
||||
|
||||
require "mw"
|
||||
|
||||
|
||||
-- SET UP FOR POPUP WINDOWS - define colours, add fonts, make window id
|
||||
-- (DO THIS ONCE ONLY, eg. in OnPluginInstall)
|
||||
|
||||
-- our window frame/background colours
|
||||
border_colour = 0xCCD148
|
||||
background_colour = 0x222222
|
||||
|
||||
-- a unique ID
|
||||
infowin = GetPluginID () .. ":info"
|
||||
|
||||
-- font IDs
|
||||
font_id = "popup_font"
|
||||
heading_font_id = "popup_heading_font"
|
||||
|
||||
font_size = 8
|
||||
|
||||
-- use 8 pt Dina or 10 pt Courier
|
||||
local fonts = utils.getfontfamilies ()
|
||||
|
||||
-- choose a font that exists
|
||||
|
||||
if fonts.Dina then
|
||||
font_name = "Dina"
|
||||
elseif fonts ["Lucida Sans Unicode"] then
|
||||
font_name = "Lucida Sans Unicode"
|
||||
else
|
||||
font_size = 10
|
||||
font_name = "Courier"
|
||||
end -- if
|
||||
|
||||
-- load fonts
|
||||
WindowCreate (infowin, 0, 0, 0, 0, 0, 0, 0) -- make initial window
|
||||
|
||||
-- install the fonts
|
||||
WindowFont (infowin, font_id, font_name, font_size, false, false, false, false,
|
||||
miniwin.font_charset_ansi,
|
||||
miniwin.font_family_modern + miniwin.font_pitch_fixed)
|
||||
WindowFont (infowin, heading_font_id, font_name, font_size + 2, false, false, false, false,
|
||||
miniwin.font_charset_ansi,
|
||||
miniwin.font_family_modern + miniwin.font_pitch_fixed)
|
||||
|
||||
-- NOW DISPLAY A WINDOW
|
||||
|
||||
-- what to say - one line per table entry, with imbedded colour codes
|
||||
|
||||
info = { "@Ctesting 1 2 3",
|
||||
"@GThis is a heading",
|
||||
"Line @Mwith @Bmultiple @Rcolours",
|
||||
}
|
||||
|
||||
heading = "@MHello, @Yworld"
|
||||
left, top = 40, 50
|
||||
align_right = false
|
||||
align_bottom = false
|
||||
capitalize = true
|
||||
|
||||
-- show it
|
||||
mw.popup (infowin, -- window name to use
|
||||
heading_font_id, -- font to use for the heading
|
||||
font_id, -- font to use for each line
|
||||
heading, -- heading text
|
||||
info, -- table of lines to show (with colour codes)
|
||||
left, top, -- where to put it
|
||||
border_colour, -- colour for round rectangle line
|
||||
background_colour, -- colour for background
|
||||
capitalize, -- if true, force the first letter to upper case
|
||||
align_right, -- if true, align right side on "Left" parameter
|
||||
align_bottom) -- if true, align bottom side on "Top" parameter
|
||||
|
||||
|
||||
EXAMPLE OF MAKING A TOOLTIP WINDOW:
|
||||
|
||||
-- SET UP FOR TOOLTIP WINDOWS - define colours, add fonts, make window id
|
||||
-- (DO THIS ONCE ONLY, eg. in OnPluginInstall)
|
||||
|
||||
-- Example setup code:
|
||||
require "mw"
|
||||
-- get ready for tooltips
|
||||
win_tooltip = GetPluginID () .. "_tooltip"
|
||||
font_tooltip = "tf"
|
||||
bold_font_tooltip = "tfb"
|
||||
-- make the window
|
||||
WindowCreate (win_tooltip, 0, 0, 0, 0, 0, 0, 0)
|
||||
-- load some fonts into it
|
||||
WindowFont (win_tooltip, font_tooltip, "Tahoma", 8)
|
||||
WindowFont (win_tooltip, bold_font_tooltip, "Tahoma", 8, true)
|
||||
|
||||
-- NOW DISPLAY A tooltip (Put bold lines inside asterisks)
|
||||
|
||||
mw.tooltip (win_tooltip, -- window name to use
|
||||
font_tooltip, -- font to use for each line
|
||||
bold_font_tooltip, -- bold font
|
||||
"*Info*\nHello, world!\nHave fun.", -- tooltip text
|
||||
45, 75, -- where to put it (x, y)
|
||||
0, -- colour for text (black)
|
||||
0, -- colour for border (black)
|
||||
ColourNameToRGB ("#FFFFE1")) -- colour for background
|
||||
|
||||
--]]
|
||||
|
||||
module (..., package.seeall)
|
||||
|
||||
DEFAULT_COLOUR = "@w"
|
||||
TRANSPARENCY_COLOUR = 0x080808
|
||||
BORDER_WIDTH = 2
|
||||
|
||||
local BLACK = 1
|
||||
local RED = 2
|
||||
local GREEN = 3
|
||||
local YELLOW = 4
|
||||
local BLUE = 5
|
||||
local MAGENTA = 6
|
||||
local CYAN = 7
|
||||
local WHITE = 8
|
||||
|
||||
-- colour styles (eg. @r is normal red, @R is bold red)
|
||||
|
||||
-- @- is shown as ~
|
||||
-- @@ is shown as @
|
||||
|
||||
-- This table uses the colours as defined in the MUSHclient ANSI tab, however the
|
||||
-- defaults are shown on the right if you prefer to use those.
|
||||
|
||||
colour_conversion = {
|
||||
k = GetNormalColour (BLACK) , -- 0x000000
|
||||
r = GetNormalColour (RED) , -- 0x000080
|
||||
g = GetNormalColour (GREEN) , -- 0x008000
|
||||
y = GetNormalColour (YELLOW) , -- 0x008080
|
||||
b = GetNormalColour (BLUE) , -- 0x800000
|
||||
m = GetNormalColour (MAGENTA) , -- 0x800080
|
||||
c = GetNormalColour (CYAN) , -- 0x808000
|
||||
w = GetNormalColour (WHITE) , -- 0xC0C0C0
|
||||
K = GetBoldColour (BLACK) , -- 0x808080
|
||||
R = GetBoldColour (RED) , -- 0x0000FF
|
||||
G = GetBoldColour (GREEN) , -- 0x00FF00
|
||||
Y = GetBoldColour (YELLOW) , -- 0x00FFFF
|
||||
B = GetBoldColour (BLUE) , -- 0xFF0000
|
||||
M = GetBoldColour (MAGENTA) , -- 0xFF00FF
|
||||
C = GetBoldColour (CYAN) , -- 0xFFFF00
|
||||
W = GetBoldColour (WHITE) , -- 0xFFFFFF
|
||||
|
||||
-- add custom colours here
|
||||
|
||||
|
||||
} -- end conversion table
|
||||
|
||||
|
||||
|
||||
-- displays text with colour codes imbedded
|
||||
--
|
||||
-- win: window to use
|
||||
-- font_id : font to use
|
||||
-- Text : what to display
|
||||
-- Left, Top, Right, Bottom : where to display it
|
||||
-- Capitalize : if true, turn the first letter into upper-case
|
||||
|
||||
function colourtext (win, font_id, Text, Left, Top, Right, Bottom, Capitalize, utf8)
|
||||
|
||||
if Text:match ("@") then
|
||||
local x = Left -- current x position
|
||||
local need_caps = Capitalize
|
||||
|
||||
Text = Text:gsub ("@%-", "~") -- fix tildes
|
||||
Text = Text:gsub ("@@", "\0") -- change @@ to 0x00
|
||||
|
||||
-- make sure we start with @ or gsub doesn't work properly
|
||||
if Text:sub (1, 1) ~= "@" then
|
||||
Text = DEFAULT_COLOUR .. Text
|
||||
end -- if
|
||||
|
||||
for colour, text in Text:gmatch ("@(%a)([^@]+)") do
|
||||
text = text:gsub ("%z", "@") -- put any @ characters back
|
||||
|
||||
if need_caps then
|
||||
local count
|
||||
text, count = text:gsub ("%a", string.upper, 1)
|
||||
need_caps = count == 0 -- if not done, still need to capitalize yet
|
||||
end -- if
|
||||
|
||||
if #text > 0 then
|
||||
x = x + WindowText (win, font_id, text, x, Top, Right, Bottom,
|
||||
colour_conversion [colour] or GetNormalColour (WHITE), utf8)
|
||||
end -- some text to display
|
||||
|
||||
end -- for each colour run
|
||||
|
||||
return x
|
||||
end -- if
|
||||
|
||||
|
||||
if Capitalize then
|
||||
Text = Text:gsub ("%a", string.upper, 1)
|
||||
end -- if leading caps wanted
|
||||
|
||||
return WindowText (win, font_id, Text, Left, Top, Right, Bottom,
|
||||
colour_conversion [DEFAULT_COLOUR] or GetNormalColour (WHITE))
|
||||
|
||||
end -- colourtext
|
||||
|
||||
-- converts text with colour styles in it into style runs
|
||||
|
||||
function ColoursToStyles (Text)
|
||||
|
||||
if Text:match ("@") then
|
||||
|
||||
astyles = {}
|
||||
|
||||
Text = Text:gsub ("@%-", "~") -- fix tildes
|
||||
Text = Text:gsub ("@@", "\0") -- change @@ to 0x00
|
||||
|
||||
-- make sure we start with @ or gsub doesn't work properly
|
||||
if Text:sub (1, 1) ~= "@" then
|
||||
Text = DEFAULT_COLOUR .. Text
|
||||
end -- if
|
||||
|
||||
for colour, text in Text:gmatch ("@(%a)([^@]+)") do
|
||||
|
||||
text = text:gsub ("%z", "@") -- put any @ characters back
|
||||
|
||||
if #text > 0 then
|
||||
table.insert (astyles, { text = text,
|
||||
length = #text,
|
||||
textcolour = colour_conversion [colour] or GetNormalColour (WHITE),
|
||||
backcolour = GetNormalColour (BLACK) })
|
||||
end -- if some text
|
||||
end -- for each colour run.
|
||||
|
||||
return astyles
|
||||
|
||||
end -- if any colour codes at all
|
||||
|
||||
-- No colour codes, create a single style.
|
||||
return { { text = Text,
|
||||
length = #Text,
|
||||
textcolour = GetNormalColour (WHITE),
|
||||
backcolour = GetNormalColour (BLACK) } }
|
||||
|
||||
end -- function ColoursToStyles
|
||||
|
||||
-- take a string, and remove colour codes from it (eg. "@Ghello" becomes "hello"
|
||||
function strip_colours (s)
|
||||
s = s:gsub ("@%-", "~") -- fix tildes
|
||||
s = s:gsub ("@@", "\0") -- change @@ to 0x00
|
||||
s = s:gsub ("@%a([^@]*)", "%1")
|
||||
return (s:gsub ("%z", "@")) -- put @ back
|
||||
end -- strip_colours
|
||||
|
||||
|
||||
function popup (win, -- window name to use
|
||||
heading_font_id, -- font to use for the heading
|
||||
font_id, -- font to use for each line
|
||||
heading, -- heading text
|
||||
info, -- table of lines to show (with colour codes)
|
||||
Left, Top, -- where to put it
|
||||
border_colour, -- colour for round rectangle line
|
||||
background_colour, -- colour for background
|
||||
capitalize, -- if true, force the first letter to be upper case
|
||||
align_right, -- if true, align right side on "Left" parameter
|
||||
align_bottom) -- if true, align bottom side on "Top" parameter
|
||||
|
||||
assert (WindowInfo (win, 1), "Window " .. win .. " must already exist")
|
||||
assert (WindowFontInfo (win, heading_font_id, 1), "No font " .. heading_font_id .. " in " .. win)
|
||||
assert (WindowFontInfo (win, font_id, 1), "No font " .. font_id .. " in " .. win)
|
||||
|
||||
local font_height = WindowFontInfo (win, font_id, 1)
|
||||
local font_leading = WindowFontInfo (win, font_id, 4) + WindowFontInfo (win, font_id, 5)
|
||||
local heading_font_height = WindowFontInfo (win, heading_font_id, 1)
|
||||
|
||||
-- find text width - minus colour codes
|
||||
local infowidth = 0
|
||||
local infoheight = 0
|
||||
|
||||
-- calculate heading width and height
|
||||
if heading and #heading > 0 then
|
||||
infowidth = WindowTextWidth (win, heading_font_id, strip_colours (heading))
|
||||
infoheight = heading_font_height
|
||||
end -- have a heading
|
||||
|
||||
-- calculate remaining width and height
|
||||
for _, v in ipairs (info) do
|
||||
infowidth = math.max (infowidth, WindowTextWidth (win, font_id, strip_colours (v)))
|
||||
infoheight = infoheight + font_height
|
||||
end -- for
|
||||
|
||||
infowidth = infowidth + (2 * BORDER_WIDTH) + -- leave room for border
|
||||
WindowFontInfo (win, font_id, 6) -- one character width extra
|
||||
|
||||
infoheight = infoheight + (2 * BORDER_WIDTH) + -- leave room for border
|
||||
font_leading + -- plus leading below bottom line,
|
||||
10 -- and 5 pixels top and bottom
|
||||
|
||||
if align_right then
|
||||
Left = Left - infowidth
|
||||
end -- if align_right
|
||||
|
||||
if align_bottom then
|
||||
Top = Top - infoheight
|
||||
end -- if align_bottom
|
||||
|
||||
WindowCreate (win,
|
||||
Left, Top, -- where
|
||||
infowidth, -- width (gap of 5 pixels per side)
|
||||
infoheight, -- height
|
||||
miniwin.pos_top_left, -- position mode: can't be 0 to 3
|
||||
miniwin.create_absolute_location + miniwin.create_transparent,
|
||||
TRANSPARENCY_COLOUR) -- background (transparent) colour
|
||||
|
||||
WindowCircleOp (win, miniwin.circle_round_rectangle,
|
||||
BORDER_WIDTH, BORDER_WIDTH, -BORDER_WIDTH, -BORDER_WIDTH, -- border inset
|
||||
border_colour, miniwin.pen_solid, BORDER_WIDTH, -- line
|
||||
background_colour, miniwin.brush_solid, -- fill
|
||||
5, 5) -- diameter of ellipse
|
||||
|
||||
local x = BORDER_WIDTH + WindowFontInfo (win, font_id, 6) / 2 -- start 1/2 character in
|
||||
local y = BORDER_WIDTH + 5 -- skip border, and leave 5 pixel gap
|
||||
|
||||
-- heading if wanted
|
||||
if heading and #heading > 0 then
|
||||
colourtext (win, heading_font_id, heading, x, y, 0, 0, capitalize)
|
||||
y = y + heading_font_height
|
||||
end -- have a heading
|
||||
|
||||
-- show each line
|
||||
for _, v in ipairs (info) do
|
||||
colourtext (win, font_id, v, x, y, 0, 0, capitalize)
|
||||
y = y + font_height
|
||||
end -- for
|
||||
|
||||
-- display popup window
|
||||
WindowShow (win, true)
|
||||
|
||||
end -- popup
|
||||
|
||||
-- --------------------------------------------------------------
|
||||
-- Displays a tooltip (small box with "arrow" pointing to something of interest)
|
||||
-- Bold lines are surrounded by asterisks (eg. *Info*)
|
||||
-- --------------------------------------------------------------
|
||||
function tooltip (win, -- window name to use
|
||||
font_id, -- font to use for each line
|
||||
bold_font_id, -- font to use for bold lines
|
||||
text, -- tooltip text
|
||||
Left, Top, -- where to put it (x, y)
|
||||
text_colour, -- colour for text
|
||||
border_colour, -- colour for border
|
||||
background_colour) -- colour for background
|
||||
|
||||
assert (WindowInfo (win, 1), "Window " .. win .. " must already exist")
|
||||
assert (WindowFontInfo (win, font_id, 1), "No font " .. font_id .. " in " .. win)
|
||||
assert (WindowFontInfo (win, bold_font_id, 1), "No font " .. bold_font_id .. " in " .. win)
|
||||
local font_height = WindowFontInfo (win, font_id, 1)
|
||||
local bold_font_height = WindowFontInfo (win, bold_font_id, 1)
|
||||
local MARGIN = 8
|
||||
local TIPSIZE = 12
|
||||
|
||||
-- break text into lines
|
||||
local t = utils.split (text, "\n")
|
||||
|
||||
-- tooltip height
|
||||
local height = MARGIN * 2 -- margin at top and bottom
|
||||
-- tooltip width
|
||||
local width = TIPSIZE * 2 -- must be at least large enough for the tip part
|
||||
for k, v in ipairs (t) do
|
||||
-- bold lines start and end with an asterisk
|
||||
local boldText = string.match (v, "^%*(.*)%*$")
|
||||
if boldText then
|
||||
width = math.max (width, WindowTextWidth (win, bold_font_id, boldText, true))
|
||||
height = height + bold_font_height
|
||||
else
|
||||
width = math.max (width, WindowTextWidth (win, font_id, v, true))
|
||||
height = height + font_height
|
||||
end -- if
|
||||
end -- for
|
||||
width = width + (MARGIN * 2) -- margin per side
|
||||
|
||||
-- the tooltip pointer starts TIPSIZE pixels to the right and descends TIPSIZE pixels
|
||||
WindowCreate (win,
|
||||
Left - TIPSIZE, Top - height - TIPSIZE,
|
||||
width + 2, height + TIPSIZE + 2, -- 2 pixels margin to allow for border + TIPSIZE downwards for the tip
|
||||
miniwin.pos_top_left, -- position
|
||||
miniwin.create_absolute_location + miniwin.create_transparent + miniwin.create_ignore_mouse,
|
||||
TRANSPARENCY_COLOUR)
|
||||
|
||||
-- mucking around here to get rounded rectangle
|
||||
local points = {
|
||||
-- top LH corner
|
||||
1, 6,
|
||||
2, 6,
|
||||
2, 4,
|
||||
3, 4,
|
||||
3, 3,
|
||||
4, 3,
|
||||
4, 2,
|
||||
6, 2,
|
||||
6, 1,
|
||||
|
||||
-- top RH corner
|
||||
width - 5, 1,
|
||||
width - 5, 2,
|
||||
width - 3, 2,
|
||||
width - 3, 3,
|
||||
width - 2, 3,
|
||||
width - 2, 4,
|
||||
width - 1, 4,
|
||||
width - 1, 6,
|
||||
width, 6,
|
||||
|
||||
-- bottom RH corner
|
||||
width, height - 5,
|
||||
width - 1, height - 5,
|
||||
width - 1, height - 3,
|
||||
width - 2, height - 3,
|
||||
width - 2, height - 2,
|
||||
width - 3, height - 2,
|
||||
width - 3, height - 1,
|
||||
width - 5, height - 1,
|
||||
width - 5, height,
|
||||
|
||||
(TIPSIZE * 2) + 1, height, -- RH side of tip
|
||||
TIPSIZE + 1, height + TIPSIZE, -- bottom of tip
|
||||
TIPSIZE + 1, height, -- LH side of tip
|
||||
|
||||
-- bottom LH corner
|
||||
6, height,
|
||||
6, height - 1,
|
||||
4, height - 1,
|
||||
4, height - 2,
|
||||
3, height - 2,
|
||||
3, height - 3,
|
||||
2, height - 3,
|
||||
2, height - 5,
|
||||
1, height - 5,
|
||||
}
|
||||
|
||||
-- make the tooltip polygon
|
||||
WindowPolygon(win, table.concat (points, ","),
|
||||
border_colour, -- pen colour
|
||||
miniwin.pen_solid, 1, -- pen (1 pixel wide)
|
||||
background_colour, -- brush colour
|
||||
miniwin.brush_solid, -- brush
|
||||
true) -- close it
|
||||
|
||||
-- put the text into it
|
||||
local top = MARGIN + 1
|
||||
for _, v in ipairs (t) do
|
||||
-- bold lines start and end with an asterisk
|
||||
local boldText = string.match (v, "^%*(.*)%*$")
|
||||
if boldText then
|
||||
WindowText (win, bold_font_id, boldText, MARGIN + 1, top, 0, 0, text_colour, true)
|
||||
top = top + bold_font_height
|
||||
else
|
||||
WindowText (win, font_id, v, MARGIN + 1, top, 0, 0, text_colour, true)
|
||||
top = top + font_height
|
||||
end -- if
|
||||
end -- for
|
||||
|
||||
WindowShow (win, true)
|
||||
|
||||
end -- tooltip
|
||||
43
cosmic rage/lua/pairsbykeys.lua
Normal file
43
cosmic rage/lua/pairsbykeys.lua
Normal file
@@ -0,0 +1,43 @@
|
||||
-- pairsbykeys.lua
|
||||
-- From Programming in Lua book, 2nd edition
|
||||
-- Gives you an iterator that moves through an ordinary table (eg. string keys)
|
||||
-- but sorted into key sequence.
|
||||
-- It does that by copying the table keys into a temporary table and sorting that.
|
||||
|
||||
-- If you need to sort keys other than strings, see:
|
||||
|
||||
-- See: http://lua-users.org/wiki/SortedIteration
|
||||
|
||||
function pairsByKeys (t, f)
|
||||
local a = {}
|
||||
-- build temporary table of the keys
|
||||
for n in pairs (t) do
|
||||
table.insert (a, n)
|
||||
end
|
||||
table.sort (a, f) -- sort using supplied function, if any
|
||||
local i = 0 -- iterator variable
|
||||
return function () -- iterator function
|
||||
i = i + 1
|
||||
return a[i], t[a[i]]
|
||||
end -- iterator function
|
||||
end -- pairsByKeys
|
||||
|
||||
return pairsByKeys
|
||||
|
||||
--[[
|
||||
|
||||
|
||||
-- This prints the math functions in random order
|
||||
for k, v in pairs (math) do
|
||||
print (k, v)
|
||||
end -- for
|
||||
|
||||
require "pairsbykeys"
|
||||
|
||||
-- This prints the math functions in key order
|
||||
for k, v in pairsByKeys (math) do
|
||||
print (k, v)
|
||||
end -- for
|
||||
|
||||
--]]
|
||||
|
||||
170
cosmic rage/lua/ppi.lua
Normal file
170
cosmic rage/lua/ppi.lua
Normal file
@@ -0,0 +1,170 @@
|
||||
--[[
|
||||
|
||||
PLUGIN-to-PLUGIN-INTERFACE (PPI)
|
||||
|
||||
Author: Twisol
|
||||
Date: 3rd January 2010
|
||||
|
||||
Amendments: Nick Gammon
|
||||
Date: 8th January 2010
|
||||
|
||||
Example of use:
|
||||
|
||||
SERVICE
|
||||
|
||||
-- require PPI module
|
||||
require "ppi"
|
||||
|
||||
-- exposed function
|
||||
function SomeMethodHere (a, b, c, d)
|
||||
-- do something with a, b, c, d
|
||||
return 1, 2, 3, 4
|
||||
end
|
||||
|
||||
-- notify PPI of this function
|
||||
ppi.Expose "SomeMethodHere"
|
||||
|
||||
-- Or, for anonymous functions:
|
||||
ppi.Expose ("DoSomethingElse", function () print "hello" end)
|
||||
|
||||
CLIENT
|
||||
|
||||
-- require PPI module
|
||||
require "ppi"
|
||||
|
||||
-- resolve dependencies
|
||||
function OnPluginListChanged ()
|
||||
|
||||
-- get PPI entries for all exposed function in this plugin
|
||||
my_service = ppi.Load ("15783160bde378741f9652d1") -- plugin ID of service plugin
|
||||
|
||||
if not my_service then
|
||||
Note ("Dependency plugin not installed!")
|
||||
end
|
||||
|
||||
end -- OnPluginListChanged
|
||||
|
||||
-- later on in plugin ...
|
||||
|
||||
-- call SomeMethodHere in other plugin, passing various data types, getting results
|
||||
|
||||
if my_service then
|
||||
w, x, y, z = my_service.SomeMethodHere (42, "Nick", true, { a = 63, b = 22 } )
|
||||
end -- if service installed
|
||||
|
||||
NOTES
|
||||
-----
|
||||
|
||||
ppi.Load returns a table with various values in it about the target plugin (see below
|
||||
for what they are). For example, _name is the plugin name of the target plugin, and
|
||||
_version is the version number of that plugin.
|
||||
|
||||
If ppi.Load returns no value (effectively, nil) then the target plugin was not installed.
|
||||
|
||||
Provided a non-nil result was returned, you can then call any exposed function in the
|
||||
target plugin. There is currently no mechanism for finding what functions are exposed, for
|
||||
simplicity's sake. However it would be possible to make a service function that returned all
|
||||
exposed functions. If service plugins evolve in functionality, checking the target plugin's
|
||||
version (the _version variable) should suffice for making sure plugins are synchronized.
|
||||
|
||||
To avoid clashes in variable names, you cannot expose a function starting with an underscore.
|
||||
|
||||
Communication with the target plugin is by global variables set up by the Expose function, along
|
||||
the lines of:
|
||||
|
||||
PPI_function_name_PPI_ (one for each exposed function)
|
||||
|
||||
Also:
|
||||
|
||||
PPI__returns__PPI_ is used for storing the returned values.
|
||||
|
||||
--]]
|
||||
|
||||
-- hide all except non-local variables
|
||||
module (..., package.seeall)
|
||||
|
||||
-- for transferring variables
|
||||
require "serialize"
|
||||
|
||||
-- PPI version
|
||||
local V_MAJOR, V_MINOR, V_PATCH = 1, 1, 0
|
||||
local VERSION = string.format ("%d.%d.%d", V_MAJOR, V_MINOR, V_PATCH)
|
||||
|
||||
-- called plugin uses this variable to store returned values
|
||||
local RETURNED_VALUE_VARIABLE = "PPI__returns__PPI_"
|
||||
|
||||
-- For any function in our PPI table, try to call that in the target plugin
|
||||
local PPI_meta = {
|
||||
__index = function (tbl, idx)
|
||||
if (idx:sub (1, 1) ~= "_") then
|
||||
return function(...)
|
||||
-- Call the method in the target plugin
|
||||
local status = CallPlugin (tbl._id, "PPI_" .. idx .. "_PPI_", serialize.save_simple {...})
|
||||
|
||||
-- explain a bit if we failed
|
||||
if status ~= error_code.eOK then
|
||||
ColourNote ("white", "red", "Error calling " .. idx ..
|
||||
" in plugin " .. tbl._name ..
|
||||
" using PPI from " .. GetPluginName () ..
|
||||
" (" .. error_desc [status] .. ")")
|
||||
check (status)
|
||||
end -- if
|
||||
|
||||
-- call succeeded, get any returned values
|
||||
local returns = {} -- table of returned values
|
||||
local s = GetPluginVariable(tbl._id, RETURNED_VALUE_VARIABLE) or "{}"
|
||||
local f = assert (loadstring ("t = " .. s)) -- convert serialized data back
|
||||
setfenv (f, returns) () -- load the returned values into 'returns'
|
||||
|
||||
-- unpack returned values to caller
|
||||
return unpack (returns.t)
|
||||
end -- generated function
|
||||
end -- not starting with underscore
|
||||
end -- __index function
|
||||
} -- end PPI_meta table
|
||||
|
||||
-- PPI request resolver
|
||||
local function PPI_resolver (func)
|
||||
return function (s) -- calling plugin serialized parameters into a single string argument
|
||||
local params = {} -- table of parameters
|
||||
local f = assert (loadstring ("t = " .. s)) -- convert serialized data back
|
||||
setfenv (f, params) () -- load the parameters into 'params'
|
||||
|
||||
-- call target function, get return values, serialize back into variable
|
||||
SetVariable(RETURNED_VALUE_VARIABLE, serialize.save_simple {func(unpack (params.t))})
|
||||
end -- generated function
|
||||
|
||||
end -- PPI_resolver
|
||||
|
||||
-- EXPOSED FUNCTIONS
|
||||
|
||||
-- We "load" a plugin by checking it exists, and creating a table saving the
|
||||
-- target plugin ID etc., and have a metatable which will handle function calls
|
||||
function Load (plugin_id)
|
||||
if IsPluginInstalled (plugin_id) then
|
||||
return setmetatable (
|
||||
{ _id = plugin_id, -- so we know which plugin to call
|
||||
_name = GetPluginInfo (plugin_id, 1),
|
||||
_author = GetPluginInfo (plugin_id, 2),
|
||||
_filename = GetPluginInfo (plugin_id, 6),
|
||||
_enabled = GetPluginInfo (plugin_id, 17),
|
||||
_version = GetPluginInfo (plugin_id, 18),
|
||||
_required_version = GetPluginInfo (plugin_id, 19),
|
||||
_directory = GetPluginInfo (plugin_id, 20),
|
||||
_PPI_V_MAJOR = V_MAJOR, -- version info
|
||||
_PPI_V_MINOR = V_MINOR,
|
||||
_PPI_V_PATCH = V_PATCH,
|
||||
_PPI_VERSION = VERSION,
|
||||
},
|
||||
PPI_meta) -- everything except the above will generate functions
|
||||
else
|
||||
return nil, "Plugin ID " .. plugin_id .. " not installed" -- in case you assert
|
||||
end -- if
|
||||
end -- function Load
|
||||
|
||||
-- Used by a plugin to expose methods to other plugins
|
||||
-- Each exposed function will be added to global namespace as PPI_<name>_PPI_
|
||||
function Expose (name, func)
|
||||
assert (type (func or _G [name]) == "function", "Function " .. name .. " does not exist.")
|
||||
_G ["PPI_" .. name .. "_PPI_"] = PPI_resolver (func or _G [name])
|
||||
end -- function Expose
|
||||
259
cosmic rage/lua/re.lua
Normal file
259
cosmic rage/lua/re.lua
Normal file
@@ -0,0 +1,259 @@
|
||||
-- $Id: re.lua,v 1.44 2013/03/26 20:11:40 roberto Exp $
|
||||
|
||||
-- imported functions and modules
|
||||
local tonumber, type, print, error = tonumber, type, print, error
|
||||
local setmetatable = setmetatable
|
||||
local m = require"lpeg"
|
||||
|
||||
-- 'm' will be used to parse expressions, and 'mm' will be used to
|
||||
-- create expressions; that is, 're' runs on 'm', creating patterns
|
||||
-- on 'mm'
|
||||
local mm = m
|
||||
|
||||
-- pattern's metatable
|
||||
local mt = getmetatable(mm.P(0))
|
||||
|
||||
|
||||
|
||||
-- No more global accesses after this point
|
||||
local version = _VERSION
|
||||
if version == "Lua 5.2" then _ENV = nil end
|
||||
|
||||
|
||||
local any = m.P(1)
|
||||
|
||||
|
||||
-- Pre-defined names
|
||||
local Predef = { nl = m.P"\n" }
|
||||
|
||||
|
||||
local mem
|
||||
local fmem
|
||||
local gmem
|
||||
|
||||
|
||||
local function updatelocale ()
|
||||
mm.locale(Predef)
|
||||
Predef.a = Predef.alpha
|
||||
Predef.c = Predef.cntrl
|
||||
Predef.d = Predef.digit
|
||||
Predef.g = Predef.graph
|
||||
Predef.l = Predef.lower
|
||||
Predef.p = Predef.punct
|
||||
Predef.s = Predef.space
|
||||
Predef.u = Predef.upper
|
||||
Predef.w = Predef.alnum
|
||||
Predef.x = Predef.xdigit
|
||||
Predef.A = any - Predef.a
|
||||
Predef.C = any - Predef.c
|
||||
Predef.D = any - Predef.d
|
||||
Predef.G = any - Predef.g
|
||||
Predef.L = any - Predef.l
|
||||
Predef.P = any - Predef.p
|
||||
Predef.S = any - Predef.s
|
||||
Predef.U = any - Predef.u
|
||||
Predef.W = any - Predef.w
|
||||
Predef.X = any - Predef.x
|
||||
mem = {} -- restart memoization
|
||||
fmem = {}
|
||||
gmem = {}
|
||||
local mt = {__mode = "v"}
|
||||
setmetatable(mem, mt)
|
||||
setmetatable(fmem, mt)
|
||||
setmetatable(gmem, mt)
|
||||
end
|
||||
|
||||
|
||||
updatelocale()
|
||||
|
||||
|
||||
|
||||
local I = m.P(function (s,i) print(i, s:sub(1, i-1)); return i end)
|
||||
|
||||
|
||||
local function getdef (id, defs)
|
||||
local c = defs and defs[id]
|
||||
if not c then error("undefined name: " .. id) end
|
||||
return c
|
||||
end
|
||||
|
||||
|
||||
local function patt_error (s, i)
|
||||
local msg = (#s < i + 20) and s:sub(i)
|
||||
or s:sub(i,i+20) .. "..."
|
||||
msg = ("pattern error near '%s'"):format(msg)
|
||||
error(msg, 2)
|
||||
end
|
||||
|
||||
local function mult (p, n)
|
||||
local np = mm.P(true)
|
||||
while n >= 1 do
|
||||
if n%2 >= 1 then np = np * p end
|
||||
p = p * p
|
||||
n = n/2
|
||||
end
|
||||
return np
|
||||
end
|
||||
|
||||
local function equalcap (s, i, c)
|
||||
if type(c) ~= "string" then return nil end
|
||||
local e = #c + i
|
||||
if s:sub(i, e - 1) == c then return e else return nil end
|
||||
end
|
||||
|
||||
|
||||
local S = (Predef.space + "--" * (any - Predef.nl)^0)^0
|
||||
|
||||
local name = m.R("AZ", "az", "__") * m.R("AZ", "az", "__", "09")^0
|
||||
|
||||
local arrow = S * "<-"
|
||||
|
||||
local seq_follow = m.P"/" + ")" + "}" + ":}" + "~}" + "|}" + (name * arrow) + -1
|
||||
|
||||
name = m.C(name)
|
||||
|
||||
|
||||
-- a defined name only have meaning in a given environment
|
||||
local Def = name * m.Carg(1)
|
||||
|
||||
local num = m.C(m.R"09"^1) * S / tonumber
|
||||
|
||||
local String = "'" * m.C((any - "'")^0) * "'" +
|
||||
'"' * m.C((any - '"')^0) * '"'
|
||||
|
||||
|
||||
local defined = "%" * Def / function (c,Defs)
|
||||
local cat = Defs and Defs[c] or Predef[c]
|
||||
if not cat then error ("name '" .. c .. "' undefined") end
|
||||
return cat
|
||||
end
|
||||
|
||||
local Range = m.Cs(any * (m.P"-"/"") * (any - "]")) / mm.R
|
||||
|
||||
local item = defined + Range + m.C(any)
|
||||
|
||||
local Class =
|
||||
"["
|
||||
* (m.C(m.P"^"^-1)) -- optional complement symbol
|
||||
* m.Cf(item * (item - "]")^0, mt.__add) /
|
||||
function (c, p) return c == "^" and any - p or p end
|
||||
* "]"
|
||||
|
||||
local function adddef (t, k, exp)
|
||||
if t[k] then
|
||||
error("'"..k.."' already defined as a rule")
|
||||
else
|
||||
t[k] = exp
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
local function firstdef (n, r) return adddef({n}, n, r) end
|
||||
|
||||
|
||||
local function NT (n, b)
|
||||
if not b then
|
||||
error("rule '"..n.."' used outside a grammar")
|
||||
else return mm.V(n)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local exp = m.P{ "Exp",
|
||||
Exp = S * ( m.V"Grammar"
|
||||
+ m.Cf(m.V"Seq" * ("/" * S * m.V"Seq")^0, mt.__add) );
|
||||
Seq = m.Cf(m.Cc(m.P"") * m.V"Prefix"^0 , mt.__mul)
|
||||
* (#seq_follow + patt_error);
|
||||
Prefix = "&" * S * m.V"Prefix" / mt.__len
|
||||
+ "!" * S * m.V"Prefix" / mt.__unm
|
||||
+ m.V"Suffix";
|
||||
Suffix = m.Cf(m.V"Primary" * S *
|
||||
( ( m.P"+" * m.Cc(1, mt.__pow)
|
||||
+ m.P"*" * m.Cc(0, mt.__pow)
|
||||
+ m.P"?" * m.Cc(-1, mt.__pow)
|
||||
+ "^" * ( m.Cg(num * m.Cc(mult))
|
||||
+ m.Cg(m.C(m.S"+-" * m.R"09"^1) * m.Cc(mt.__pow))
|
||||
)
|
||||
+ "->" * S * ( m.Cg((String + num) * m.Cc(mt.__div))
|
||||
+ m.P"{}" * m.Cc(nil, m.Ct)
|
||||
+ m.Cg(Def / getdef * m.Cc(mt.__div))
|
||||
)
|
||||
+ "=>" * S * m.Cg(Def / getdef * m.Cc(m.Cmt))
|
||||
) * S
|
||||
)^0, function (a,b,f) return f(a,b) end );
|
||||
Primary = "(" * m.V"Exp" * ")"
|
||||
+ String / mm.P
|
||||
+ Class
|
||||
+ defined
|
||||
+ "{:" * (name * ":" + m.Cc(nil)) * m.V"Exp" * ":}" /
|
||||
function (n, p) return mm.Cg(p, n) end
|
||||
+ "=" * name / function (n) return mm.Cmt(mm.Cb(n), equalcap) end
|
||||
+ m.P"{}" / mm.Cp
|
||||
+ "{~" * m.V"Exp" * "~}" / mm.Cs
|
||||
+ "{|" * m.V"Exp" * "|}" / mm.Ct
|
||||
+ "{" * m.V"Exp" * "}" / mm.C
|
||||
+ m.P"." * m.Cc(any)
|
||||
+ (name * -arrow + "<" * name * ">") * m.Cb("G") / NT;
|
||||
Definition = name * arrow * m.V"Exp";
|
||||
Grammar = m.Cg(m.Cc(true), "G") *
|
||||
m.Cf(m.V"Definition" / firstdef * m.Cg(m.V"Definition")^0,
|
||||
adddef) / mm.P
|
||||
}
|
||||
|
||||
local pattern = S * m.Cg(m.Cc(false), "G") * exp / mm.P * (-any + patt_error)
|
||||
|
||||
|
||||
local function compile (p, defs)
|
||||
if mm.type(p) == "pattern" then return p end -- already compiled
|
||||
local cp = pattern:match(p, 1, defs)
|
||||
if not cp then error("incorrect pattern", 3) end
|
||||
return cp
|
||||
end
|
||||
|
||||
local function match (s, p, i)
|
||||
local cp = mem[p]
|
||||
if not cp then
|
||||
cp = compile(p)
|
||||
mem[p] = cp
|
||||
end
|
||||
return cp:match(s, i or 1)
|
||||
end
|
||||
|
||||
local function find (s, p, i)
|
||||
local cp = fmem[p]
|
||||
if not cp then
|
||||
cp = compile(p) / 0
|
||||
cp = mm.P{ mm.Cp() * cp * mm.Cp() + 1 * mm.V(1) }
|
||||
fmem[p] = cp
|
||||
end
|
||||
local i, e = cp:match(s, i or 1)
|
||||
if i then return i, e - 1
|
||||
else return i
|
||||
end
|
||||
end
|
||||
|
||||
local function gsub (s, p, rep)
|
||||
local g = gmem[p] or {} -- ensure gmem[p] is not collected while here
|
||||
gmem[p] = g
|
||||
local cp = g[rep]
|
||||
if not cp then
|
||||
cp = compile(p)
|
||||
cp = mm.Cs((cp / rep + 1)^0)
|
||||
g[rep] = cp
|
||||
end
|
||||
return cp:match(s)
|
||||
end
|
||||
|
||||
|
||||
-- exported names
|
||||
local re = {
|
||||
compile = compile,
|
||||
match = match,
|
||||
find = find,
|
||||
gsub = gsub,
|
||||
updatelocale = updatelocale,
|
||||
}
|
||||
|
||||
if version == "Lua 5.1" then _G.re = re end
|
||||
|
||||
return re
|
||||
163
cosmic rage/lua/sandbox.lua
Normal file
163
cosmic rage/lua/sandbox.lua
Normal file
@@ -0,0 +1,163 @@
|
||||
-- sandbox.lua
|
||||
|
||||
--[[
|
||||
|
||||
MUSHclient sandbox (taken from versions 4.11 to 4.57)
|
||||
|
||||
To enable the sandbox for all Lua scripting add to:
|
||||
|
||||
File menu -> Global Preferences -> Lua -> Preliminary Code
|
||||
|
||||
... this line:
|
||||
|
||||
require "sandbox"
|
||||
|
||||
See: http://mushclient.com/security
|
||||
|
||||
Note that this sandbox only affects Lua, not other scripting languages.
|
||||
|
||||
--]]
|
||||
|
||||
|
||||
trust_all_worlds = false -- change to true to trust all the worlds
|
||||
trust_all_plugins = false -- change to true to trust all the plugins
|
||||
warn_if_not_trusted = false -- change to true to show warnings
|
||||
|
||||
--[[
|
||||
|
||||
-- Lua initialization (sandbox) --> please read comments carefully.
|
||||
|
||||
Use this to create a "sandbox" for safe execution of non-trusted scripts.
|
||||
|
||||
If you only run your own scripts or plugins then you may not need this.
|
||||
|
||||
The code in this area is executed after each Lua script space is created
|
||||
but before any of your scripts are done. This can be used to initialise things
|
||||
(eg. load DLLs, load files, set up variables) or to disable things as shown below.
|
||||
|
||||
By setting a function name to nil you effectively make it unavailable.
|
||||
|
||||
You can remove some functions from a library rather than all of them, eg.
|
||||
|
||||
os.execute = nil -- no operating system calls
|
||||
os.remove = nil -- no deleting files
|
||||
os.rename = nil -- no renaming files
|
||||
|
||||
This script will automatically be replaced if you completely delete it from
|
||||
the Global Preferences, and restart MUSHclient. To avoid this, leave a comment
|
||||
in (if you don't want any further action taken).
|
||||
|
||||
--]]
|
||||
|
||||
-- Example sandbox --
|
||||
|
||||
function MakeSandbox ()
|
||||
|
||||
local function ReportDisabled (pkg, func)
|
||||
return function ()
|
||||
error (string.format (
|
||||
"Function '%s.%s' disabled in Lua sandbox - see MUSHclient global preferences",
|
||||
pkg, func), 2)
|
||||
end -- function
|
||||
end -- ReportDisabled
|
||||
|
||||
package.loadlib = ReportDisabled ("package", "loadlib") -- disable loadlib function
|
||||
package.loaders [3] = nil -- disable DLL loader
|
||||
package.loaders [4] = nil -- disable all-in-one loader
|
||||
|
||||
for k, v in pairs (io) do
|
||||
if type (v) == "function" then
|
||||
io [k] = ReportDisabled ("io", k)
|
||||
end -- type is function
|
||||
end -- for
|
||||
|
||||
local orig_os = os -- so we know names of disabled ones
|
||||
|
||||
-- replace 'os' table with one containing only safe functions
|
||||
os = {
|
||||
date = os.date,
|
||||
time = os.time,
|
||||
setlocale = os.setlocale,
|
||||
clock = os.clock,
|
||||
difftime = os.difftime,
|
||||
}
|
||||
|
||||
for k, v in pairs (orig_os) do
|
||||
if not os [k] and type (v) == "function" then
|
||||
os [k] = ReportDisabled ("os", k)
|
||||
end -- not still active
|
||||
end -- for
|
||||
|
||||
if warn_if_not_trusted then
|
||||
ColourNote ("yellow", "black",
|
||||
"Lua sandbox created, some functions disabled.")
|
||||
end -- if warn_if_not_trusted
|
||||
|
||||
end -- end of function MakeSandbox
|
||||
|
||||
|
||||
-- default is to sandbox everything --
|
||||
|
||||
-- To trust individual worlds or plugins, add them to the lists below.
|
||||
|
||||
-- To find your current world ID, do this: /print (GetWorldID ())
|
||||
-- Plugin IDs are mentioned near the start of every plugin.
|
||||
|
||||
-- You can limit the behaviour to specific worlds, or specific plugins
|
||||
-- by doing something like this:
|
||||
|
||||
do
|
||||
|
||||
-- World IDs of worlds we trust - replace with your world IDs
|
||||
-- (and remove comment from start of line)
|
||||
|
||||
local trusted_worlds = {
|
||||
-- ["a4a1cc1801787ba88cd84f3a"] = true, -- example world A
|
||||
-- ["cdc8552d1b251e449b874b9a"] = true, -- example world B
|
||||
-- ["1ec5aac3265e472b97f0c103"] = true, -- example world C
|
||||
} -- end of trusted_worlds
|
||||
|
||||
-- Plugin IDs of plugins we trust - add your plugins to the table
|
||||
|
||||
local trusted_plugins = {
|
||||
[""] = "", -- trust main script (ie. if no plugin running)
|
||||
["03ca99c4e98d2a3e6d655c7d"] = "Chat",
|
||||
["982581e59ab42844527eec80"] = "Random_Socials",
|
||||
["4a267cd69ba59b5ecefe42d8"] = "Installer_sumcheck",
|
||||
["83beba4e37b3d0e7f63cedbc"] = "Reconnecter",
|
||||
} -- end of trusted_plugins
|
||||
|
||||
|
||||
-- check worlds
|
||||
if not trust_all_worlds then
|
||||
if not trusted_worlds [GetWorldID ()] then
|
||||
if warn_if_not_trusted then
|
||||
ColourNote ("yellow", "black", "Untrusted world " .. WorldName () ..
|
||||
", ID: " .. GetWorldID ())
|
||||
end -- if warn_if_not_trusted
|
||||
MakeSandbox ()
|
||||
end -- not trusted world or plugin
|
||||
end -- not trusting all worlds
|
||||
|
||||
-- check plugins - check name *and* plugin ID
|
||||
if not trust_all_plugins then
|
||||
if trusted_plugins [GetPluginID ()] ~= GetPluginName () then
|
||||
if warn_if_not_trusted then
|
||||
ColourNote ("yellow", "black", "Untrusted plugin " .. GetPluginName () ..
|
||||
", ID: " .. GetPluginID ())
|
||||
end -- if warn_if_not_trusted
|
||||
MakeSandbox ()
|
||||
end -- not trusted world or plugin
|
||||
end -- if not trusting all plugins
|
||||
|
||||
end -- local block
|
||||
|
||||
-- warn if we can't load DLLs (checkbox might be unchecked)
|
||||
if not package.loadlib and warn_if_not_trusted then
|
||||
local by_this_plugin = ""
|
||||
if GetPluginID () ~= "" then
|
||||
by_this_plugin = " by this plugin"
|
||||
end -- this is a plugin
|
||||
ColourNote ("yellow", "black",
|
||||
"Loading of DLLs" .. by_this_plugin .. " is disabled.")
|
||||
end -- if
|
||||
237
cosmic rage/lua/serialize.lua
Normal file
237
cosmic rage/lua/serialize.lua
Normal file
@@ -0,0 +1,237 @@
|
||||
|
||||
-- serialize.lua
|
||||
|
||||
module (..., package.seeall)
|
||||
|
||||
-- ----------------------------------------------------------
|
||||
-- serializer
|
||||
-- See "Programming In Lua" chapter 12.1.2.
|
||||
-- Also see forum thread:
|
||||
-- http://www.gammon.com.au/forum/?id=4960
|
||||
-- ----------------------------------------------------------
|
||||
|
||||
--[[
|
||||
|
||||
Example of use:
|
||||
|
||||
require "serialize"
|
||||
SetVariable ("mobs", serialize.save ("mobs")) --> serialize mobs table
|
||||
loadstring (GetVariable ("mobs")) () --> restore mobs table
|
||||
|
||||
If you need to serialize two tables where subsequent ones refer to earlier ones
|
||||
you can supply your own "saved tables" variable, like this:
|
||||
|
||||
require "serialize"
|
||||
result, t = serialize.save ("mobs")
|
||||
result = result .. "\n" .. serialize.save ("quests", nil, t)
|
||||
|
||||
In this example the serializing of "quests" also knows about the "mobs" table
|
||||
and will use references to it where necessary.
|
||||
|
||||
You can also supply the actual variable if the variable to be serialized does
|
||||
not exist in the global namespace (for instance, if the variable is a local
|
||||
variable to a function). eg.
|
||||
|
||||
require "serialize"
|
||||
do
|
||||
local myvar = { 1, 2, 8, 9 }
|
||||
print (serialize.save ("myvar", myvar))
|
||||
end
|
||||
|
||||
In this example, without supplying the location of "myvar" the serialize would fail
|
||||
because it would not be found in the _G namespace.
|
||||
|
||||
----- Added on 19 July 2007:
|
||||
|
||||
You can now do a "simple save" which is intended for tables without cycles. That is,
|
||||
tables, that do not refer to other tables. This is appropriate for "simple" data, like
|
||||
a straight table of keys/values, including subtables.
|
||||
|
||||
For a simple save, all you need to do is supply the value, like this:
|
||||
|
||||
print (serialize.save_simple ({ foo = 22, bar = "hi", t = { s = 9, k = 22 } }))
|
||||
|
||||
This produces:
|
||||
|
||||
{
|
||||
t = {
|
||||
s = 9,
|
||||
k = 22,
|
||||
},
|
||||
bar = "hi",
|
||||
foo = 22,
|
||||
}
|
||||
|
||||
--]]
|
||||
|
||||
local save_item -- forward declaration, function appears near the end
|
||||
local save_item_simple
|
||||
|
||||
function save (what, v, saved)
|
||||
|
||||
saved = saved or {} -- initial table of tables we have already done
|
||||
v = v or _G [what] -- default to "what" in global namespace
|
||||
|
||||
assert (type (what) == "string",
|
||||
"1st argument to serialize.save should be the *name* of a variable")
|
||||
|
||||
assert (v, "Variable '" .. what .. "' does not exist")
|
||||
|
||||
assert (type (saved) == "table" or saved == nil,
|
||||
"3rd argument to serialize.save should be a table or nil")
|
||||
|
||||
local out = {} -- output to this table
|
||||
save_item (what, v, out, 0, saved) -- do serialization
|
||||
return table.concat (out, "\n"), saved -- turn into a string (also return our table)
|
||||
end -- serialize.save
|
||||
|
||||
function save_simple (v)
|
||||
local out = {} -- output to this table
|
||||
save_item_simple (v, out, 2) -- do serialization
|
||||
return table.concat (out) -- turn into a string
|
||||
end -- serialize.save_simple
|
||||
|
||||
--- below are local functions for this module -------------
|
||||
|
||||
local function basicSerialize (o)
|
||||
if type(o) == "number" or type(o) == "boolean" then
|
||||
return tostring(o)
|
||||
else -- assume it is a string
|
||||
return string.format("%q", o)
|
||||
end
|
||||
end -- basicSerialize
|
||||
|
||||
--
|
||||
-- Lua keywords might look OK to not be quoted as keys but must be.
|
||||
-- So, we make a list of them.
|
||||
--
|
||||
|
||||
local lua_reserved_words = {}
|
||||
|
||||
for _, v in ipairs ({
|
||||
"and", "break", "do", "else", "elseif", "end",
|
||||
"for", "function", "if", "in", "local", "nil", "not", "or",
|
||||
"repeat", "return", "then", "until", "while"
|
||||
}) do lua_reserved_words [v] = true end
|
||||
|
||||
-- ----------------------------------------------------------
|
||||
-- save one variable (calls itself recursively)
|
||||
--
|
||||
-- Modified on 23 October 2005 to better handle keys (like table keys)
|
||||
-- ----------------------------------------------------------
|
||||
function save_item (name, value, out, indent, saved) -- important! no "local" keyword
|
||||
local iname = string.rep (" ", indent) .. name -- indented name
|
||||
|
||||
-- numbers, strings, and booleans can be simply serialized
|
||||
|
||||
if type (value) == "number" or
|
||||
type (value) == "string" or
|
||||
type (value) == "boolean" then
|
||||
table.insert (out, iname .. " = " .. basicSerialize(value))
|
||||
|
||||
-- tables need to be constructed, unless we have already done it
|
||||
|
||||
elseif type (value) == "table" then
|
||||
if saved[value] then -- value already saved?
|
||||
table.insert (out, iname .. " = " .. saved[value]) -- use its previous name
|
||||
else
|
||||
|
||||
-- remember we have created this table so we don't do it twice
|
||||
|
||||
saved [value] = name -- save name for next time
|
||||
|
||||
-- make the table constructor, and recurse to save its contents
|
||||
|
||||
table.insert (out, iname .. " = {}") -- create a new table
|
||||
|
||||
for k, v in pairs (value) do -- save its fields
|
||||
local fieldname
|
||||
|
||||
-- if key is a Lua variable name which is not a reserved word
|
||||
-- we can express it as tablename.keyname
|
||||
|
||||
if type (k) == "string"
|
||||
and string.find (k, "^[_%a][_%a%d]*$")
|
||||
and not lua_reserved_words [k] then
|
||||
fieldname = string.format("%s.%s", name, k)
|
||||
|
||||
-- if key is a table itself, and we know its name then we can use that
|
||||
-- eg. tablename [ tablekeyname ]
|
||||
|
||||
elseif type (k) == "table" and saved[k] then
|
||||
fieldname = string.format("%s[%s]", name, saved [k])
|
||||
|
||||
-- if key is an unknown table, we have to raise an error as we cannot
|
||||
-- deduce its name
|
||||
|
||||
elseif type (k) == "table" then
|
||||
error ("Key table entry " .. tostring (k) ..
|
||||
" in table " .. name .. " is not known")
|
||||
|
||||
-- if key is a number or a boolean it can simply go in brackets,
|
||||
-- like this: tablename [5] or tablename [true]
|
||||
|
||||
elseif type (k) == "number" or type (k) == "boolean" then
|
||||
fieldname = string.format("%s[%s]", name, tostring (k))
|
||||
|
||||
-- now key should be a string, otherwise an error
|
||||
|
||||
elseif type (k) ~= "string" then
|
||||
error ("Cannot serialize table keys of type '" .. type (k) ..
|
||||
"' in table " .. name)
|
||||
|
||||
-- if key is a non-variable name (eg. "++") then we have to put it
|
||||
-- in brackets and quote it, like this: tablename ["keyname"]
|
||||
|
||||
else
|
||||
fieldname = string.format("%s[%s]", name,
|
||||
basicSerialize(k))
|
||||
end
|
||||
|
||||
-- now we have finally worked out a suitable name for the key,
|
||||
-- recurse to save the value associated with it
|
||||
|
||||
save_item(fieldname, v, out, indent + 2, saved)
|
||||
end
|
||||
end
|
||||
|
||||
-- cannot serialize things like functions, threads
|
||||
|
||||
else
|
||||
error ("Cannot serialize '" .. name .. "' (" .. type (value) .. ")")
|
||||
end -- if type of variable
|
||||
end -- save_item
|
||||
|
||||
-- saves tables *without* cycles (produces smaller output)
|
||||
function save_item_simple (value, out, indent)
|
||||
-- numbers, strings, and booleans can be simply serialized
|
||||
|
||||
if type (value) == "number" or
|
||||
type (value) == "string" or
|
||||
type (value) == "boolean" then
|
||||
table.insert (out, basicSerialize(value))
|
||||
elseif type (value) == "table" then
|
||||
table.insert (out, "{\n")
|
||||
|
||||
for k, v in pairs (value) do -- save its fields
|
||||
table.insert (out, string.rep (" ", indent))
|
||||
if not string.find (k, '^[_%a][_%a%d]*$')
|
||||
or lua_reserved_words [k] then
|
||||
table.insert (out, "[" .. basicSerialize (k) .. "] = ")
|
||||
else
|
||||
table.insert (out, k .. " = ")
|
||||
end -- if
|
||||
save_item_simple (v, out, indent + 2)
|
||||
table.insert (out, ",\n")
|
||||
end -- for each table item
|
||||
|
||||
table.insert (out, string.rep (" ", indent) .. "}")
|
||||
|
||||
-- cannot serialize things like functions, threads
|
||||
|
||||
else
|
||||
error ("Cannot serialize " .. type (value))
|
||||
end -- if type of variable
|
||||
|
||||
end -- save_item_simple
|
||||
|
||||
25
cosmic rage/lua/show_loaded.lua
Normal file
25
cosmic rage/lua/show_loaded.lua
Normal file
@@ -0,0 +1,25 @@
|
||||
-- show_loaded.lua
|
||||
|
||||
--[[
|
||||
|
||||
Shows each plugin as it is loaded.
|
||||
|
||||
To enable this, add to:
|
||||
|
||||
File menu -> Global Preferences -> Lua -> Preliminary Code
|
||||
|
||||
... this line:
|
||||
|
||||
require "show_loaded"
|
||||
|
||||
|
||||
--]]
|
||||
|
||||
if GetPluginID () == "" then
|
||||
ColourNote ("gray", "", "Initializing main world script ...")
|
||||
else
|
||||
ColourNote ("gray", "", string.format ("Loading plugin '%s' (%s) version %0.2f ...",
|
||||
GetPluginInfo ( GetPluginID (), 7),
|
||||
GetPluginInfo ( GetPluginID (), 1),
|
||||
GetPluginInfo ( GetPluginID (), 19)))
|
||||
end -- if
|
||||
133
cosmic rage/lua/socket.lua
Normal file
133
cosmic rage/lua/socket.lua
Normal file
@@ -0,0 +1,133 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- LuaSocket helper module
|
||||
-- Author: Diego Nehab
|
||||
-- RCS ID: $Id: socket.lua,v 1.22 2005/11/22 08:33:29 diego Exp $
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Declare module and import dependencies
|
||||
-----------------------------------------------------------------------------
|
||||
local base = _G
|
||||
local string = require("string")
|
||||
local math = require("math")
|
||||
local socket = require("socket.core")
|
||||
module("socket")
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Exported auxiliar functions
|
||||
-----------------------------------------------------------------------------
|
||||
function connect(address, port, laddress, lport)
|
||||
local sock, err = socket.tcp()
|
||||
if not sock then return nil, err end
|
||||
if laddress then
|
||||
local res, err = sock:bind(laddress, lport, -1)
|
||||
if not res then return nil, err end
|
||||
end
|
||||
local res, err = sock:connect(address, port)
|
||||
if not res then return nil, err end
|
||||
return sock
|
||||
end
|
||||
|
||||
function bind(host, port, backlog)
|
||||
local sock, err = socket.tcp()
|
||||
if not sock then return nil, err end
|
||||
sock:setoption("reuseaddr", true)
|
||||
local res, err = sock:bind(host, port)
|
||||
if not res then return nil, err end
|
||||
res, err = sock:listen(backlog)
|
||||
if not res then return nil, err end
|
||||
return sock
|
||||
end
|
||||
|
||||
try = newtry()
|
||||
|
||||
function choose(table)
|
||||
return function(name, opt1, opt2)
|
||||
if base.type(name) ~= "string" then
|
||||
name, opt1, opt2 = "default", name, opt1
|
||||
end
|
||||
local f = table[name or "nil"]
|
||||
if not f then base.error("unknown key (".. base.tostring(name) ..")", 3)
|
||||
else return f(opt1, opt2) end
|
||||
end
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Socket sources and sinks, conforming to LTN12
|
||||
-----------------------------------------------------------------------------
|
||||
-- create namespaces inside LuaSocket namespace
|
||||
sourcet = {}
|
||||
sinkt = {}
|
||||
|
||||
BLOCKSIZE = 2048
|
||||
|
||||
sinkt["close-when-done"] = function(sock)
|
||||
return base.setmetatable({
|
||||
getfd = function() return sock:getfd() end,
|
||||
dirty = function() return sock:dirty() end
|
||||
}, {
|
||||
__call = function(self, chunk, err)
|
||||
if not chunk then
|
||||
sock:close()
|
||||
return 1
|
||||
else return sock:send(chunk) end
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
sinkt["keep-open"] = function(sock)
|
||||
return base.setmetatable({
|
||||
getfd = function() return sock:getfd() end,
|
||||
dirty = function() return sock:dirty() end
|
||||
}, {
|
||||
__call = function(self, chunk, err)
|
||||
if chunk then return sock:send(chunk)
|
||||
else return 1 end
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
sinkt["default"] = sinkt["keep-open"]
|
||||
|
||||
sink = choose(sinkt)
|
||||
|
||||
sourcet["by-length"] = function(sock, length)
|
||||
return base.setmetatable({
|
||||
getfd = function() return sock:getfd() end,
|
||||
dirty = function() return sock:dirty() end
|
||||
}, {
|
||||
__call = function()
|
||||
if length <= 0 then return nil end
|
||||
local size = math.min(socket.BLOCKSIZE, length)
|
||||
local chunk, err = sock:receive(size)
|
||||
if err then return nil, err end
|
||||
length = length - string.len(chunk)
|
||||
return chunk
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
sourcet["until-closed"] = function(sock)
|
||||
local done
|
||||
return base.setmetatable({
|
||||
getfd = function() return sock:getfd() end,
|
||||
dirty = function() return sock:dirty() end
|
||||
}, {
|
||||
__call = function()
|
||||
if done then return nil end
|
||||
local chunk, err, partial = sock:receive(socket.BLOCKSIZE)
|
||||
if not err then return chunk
|
||||
elseif err == "closed" then
|
||||
sock:close()
|
||||
done = 1
|
||||
return partial
|
||||
else return nil, err end
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
sourcet["default"] = sourcet["until-closed"]
|
||||
|
||||
source = choose(sourcet)
|
||||
|
||||
35
cosmic rage/lua/strict.lua
Normal file
35
cosmic rage/lua/strict.lua
Normal file
@@ -0,0 +1,35 @@
|
||||
-- strict.lua
|
||||
|
||||
-- by Roberto Ierusalimschy
|
||||
-- checks uses of undeclared global variables
|
||||
-- All global variables must be 'declared' through a regular assignment
|
||||
-- (even assigning nil will do) in a main chunk before being used
|
||||
-- anywhere or assigned to inside a function.
|
||||
--
|
||||
|
||||
local mt = getmetatable(_G)
|
||||
if mt == nil then
|
||||
mt = {}
|
||||
setmetatable(_G, mt)
|
||||
end
|
||||
|
||||
mt.__declared = {}
|
||||
|
||||
mt.__newindex = function (t, n, v)
|
||||
if not mt.__declared[n] then
|
||||
local w = debug.getinfo(2, "S").what
|
||||
if w ~= "main" and w ~= "C" then
|
||||
error("assign to undeclared variable '"..n.."'", 2)
|
||||
end
|
||||
mt.__declared[n] = true
|
||||
end
|
||||
rawset(t, n, v)
|
||||
end
|
||||
|
||||
mt.__index = function (t, n)
|
||||
if not mt.__declared[n] then
|
||||
error("variable '"..n.."' is not declared", 2)
|
||||
end
|
||||
return rawget(t, n)
|
||||
end
|
||||
|
||||
24
cosmic rage/lua/string_indexing.lua
Normal file
24
cosmic rage/lua/string_indexing.lua
Normal file
@@ -0,0 +1,24 @@
|
||||
--[[
|
||||
|
||||
string_indexing.lua
|
||||
|
||||
If you install this, then you can index into strings, like this:
|
||||
|
||||
|
||||
require "string_indexing"
|
||||
|
||||
a = "nick"
|
||||
print (a [3]) --> c
|
||||
|
||||
|
||||
|
||||
--]]
|
||||
|
||||
getmetatable ("").__index = function (str, i)
|
||||
|
||||
if (type (i) == "number") then
|
||||
return string.sub (str, i, i) -- index into str
|
||||
end -- if
|
||||
|
||||
return string [i] -- fallback (eg. string.match)
|
||||
end -- function
|
||||
313
cosmic rage/lua/tabbed_window.lua
Normal file
313
cosmic rage/lua/tabbed_window.lua
Normal file
@@ -0,0 +1,313 @@
|
||||
-- tabbed_window.lua
|
||||
|
||||
--[[
|
||||
|
||||
Author: Nick Gammon
|
||||
Date: 7th February 2018
|
||||
|
||||
Generic tabbed miniwindow drawer.
|
||||
|
||||
Exposed functions:
|
||||
|
||||
init (context) -- call once (from OnPluginInstall), supply:
|
||||
|
||||
context.win -- window ID for this tabbed window
|
||||
context.tabfont -- table of font info for non-active tabs (name, size, unicode, bold, italic)
|
||||
context.activefont -- table of font info for the selected tab (name, size, unicode, bold, italic)
|
||||
context.titlefont -- table of font info for the title bar (name, size, unicode, bold, italic)
|
||||
context.colours -- table of assorted colours (see below for examples)
|
||||
context.window -- table (width and height) of the desired window size
|
||||
context.tab_filler -- gap between tabs
|
||||
context.can_move -- can window be dragged around?
|
||||
context.active_tab -- the currently active tab
|
||||
context.tabs -- table of tables - one per tab (name, handler)
|
||||
|
||||
draw_window (context, which_tab) -- draws the window with which_tab as the active tab
|
||||
hide_window (context) -- hides this window
|
||||
save_state (context) -- for saving the window position (call from OnPluginSaveState)
|
||||
|
||||
Exposed variables:
|
||||
|
||||
VERSION -- version of this module
|
||||
|
||||
--]]
|
||||
|
||||
module (..., package.seeall)
|
||||
|
||||
VERSION = 1.1 -- -- for querying by plugins
|
||||
|
||||
require "movewindow" -- load the movewindow.lua module
|
||||
|
||||
local default_context = {
|
||||
|
||||
-- window ID
|
||||
win = "tabbed_window_" .. GetUniqueID (),
|
||||
|
||||
-- font for inactive tab text
|
||||
tabfont = {
|
||||
id = "fn",
|
||||
name = "Lucida Console",
|
||||
size = 10, -- points
|
||||
unicode = false, -- Unicode?
|
||||
bold = false, -- make bold?
|
||||
italic = false, -- make italic?
|
||||
}, -- end of tabfont
|
||||
|
||||
-- font for active tab text
|
||||
activefont = {
|
||||
id = "fa",
|
||||
name = "Lucida Console",
|
||||
size = 10, -- points
|
||||
unicode = false, -- Unicode?
|
||||
bold = false, -- make bold?
|
||||
italic = false, -- make italic?
|
||||
}, -- end of activefont
|
||||
|
||||
-- font for title text
|
||||
titlefont = {
|
||||
id = "ft",
|
||||
name = "Lucida Console",
|
||||
size = 12, -- points
|
||||
unicode = false, -- Unicode?
|
||||
bold = true, -- make bold?
|
||||
italic = false, -- make italic?
|
||||
}, -- end of titlefont
|
||||
|
||||
colours = {
|
||||
background = ColourNameToRGB ("lightskyblue"), -- entire window background
|
||||
frame = ColourNameToRGB ("mediumorchid"), -- frame around window
|
||||
title_bar = ColourNameToRGB ("palegoldenrod"), -- behind the title
|
||||
title = ColourNameToRGB ("mediumblue"), -- title text
|
||||
tab = ColourNameToRGB ("green"), -- tab background
|
||||
tab_text = ColourNameToRGB ("white"), -- inactive tab text
|
||||
active_tab_text = ColourNameToRGB ("lightgreen"), -- active tab text
|
||||
upper_line = ColourNameToRGB ("lightgray"), -- line above tab text
|
||||
lower_line = ColourNameToRGB ("lightgray"), -- line below tab text
|
||||
vertical_line = ColourNameToRGB ("lightgray"), -- line between tabs
|
||||
}, -- end of colours
|
||||
|
||||
-- miniwindow size
|
||||
window = {
|
||||
width = 300,
|
||||
height = 200,
|
||||
}, -- end of window info
|
||||
|
||||
-- gap between text in tabs
|
||||
tab_filler = 10,
|
||||
|
||||
-- can it be moved?
|
||||
can_move = true,
|
||||
|
||||
-- which tab is active
|
||||
active_tab = 1,
|
||||
|
||||
-- tabs
|
||||
-- table of tabs. For each one supply a table containing a name and a handler,
|
||||
-- eg. { name = "Circle", handler = DrawCircle },
|
||||
|
||||
tabs = { },
|
||||
|
||||
|
||||
} -- end of default_context
|
||||
|
||||
-- -------------------------------------------------------------------------------
|
||||
-- init - initialize this module ready for use (do once)
|
||||
-- -------------------------------------------------------------------------------
|
||||
function init (context)
|
||||
|
||||
-- make copy of colours, sizes etc.
|
||||
assert (type (context) == "table", "No 'context' table supplied to tabbed_window.")
|
||||
|
||||
-- force some context defaults if not supplied
|
||||
for k, v in pairs (default_context) do
|
||||
|
||||
-- some context items are tables so copy the table entries
|
||||
if type (v) == 'table' then
|
||||
if type (context [k]) ~= 'table' then
|
||||
assert (context [k] == nil, "tabbed window context item '" .. k .. "' should be a table or nil")
|
||||
context [k] = { } -- make table if required
|
||||
end -- not having a table there
|
||||
for k1, v1 in pairs (v) do
|
||||
context [k] [k1] = context [k] [k1] or v1
|
||||
end -- for each sub-table item
|
||||
else
|
||||
context [k] = context [k] or v
|
||||
end -- if table or not
|
||||
end -- for
|
||||
|
||||
if next (context.tabs) == nil then
|
||||
ColourNote ("orange", "", "Warning: no tabs define for tabbed window")
|
||||
end -- if
|
||||
|
||||
-- install the window movement handler, get back the window position
|
||||
context.windowinfo = movewindow.install (context.win, miniwin.pos_top_left, 0) -- default to top left
|
||||
|
||||
-- save a bit of typing
|
||||
local windowinfo = context.windowinfo
|
||||
local win = context.win
|
||||
local tabfont = context.tabfont
|
||||
local activefont = context.activefont
|
||||
local titlefont = context.titlefont
|
||||
local window = context.window
|
||||
local colours = context.colours
|
||||
|
||||
-- make window so I can grab the font info
|
||||
WindowCreate (win,
|
||||
windowinfo.window_left,
|
||||
windowinfo.window_top,
|
||||
window.width,
|
||||
window.height,
|
||||
windowinfo.window_mode,
|
||||
windowinfo.window_flags,
|
||||
colours.background)
|
||||
|
||||
WindowFont (win, tabfont.id, tabfont.name, tabfont.size, tabfont.bold, tabfont.italic, false, false, 0, 0)
|
||||
tabfont.height = WindowFontInfo (win, tabfont.id, 1) -- height
|
||||
WindowFont (win, activefont.id, activefont.name, activefont.size, activefont.bold, activefont.italic, false, false, 0, 0)
|
||||
activefont.height = WindowFontInfo (win, activefont.id, 1) -- height
|
||||
WindowFont (win, titlefont.id, titlefont.name, titlefont.size, titlefont.bold, titlefont.italic, false, false, 0, 0)
|
||||
titlefont.height = WindowFontInfo (win, titlefont.id, 1) -- height
|
||||
|
||||
context.client_bottom = context.window.height - context.tabfont.height - 8
|
||||
|
||||
end -- init
|
||||
|
||||
function save_state (context)
|
||||
-- save window current location for next time
|
||||
movewindow.save_state (context.win)
|
||||
end -- save_state
|
||||
|
||||
-- -------------------------------------------------------------------------------
|
||||
-- draw_window - draw the tabbed window
|
||||
-- -------------------------------------------------------------------------------
|
||||
function draw_window (context, whichTab)
|
||||
|
||||
context.active_tab = whichTab
|
||||
|
||||
-- save a bit of typing
|
||||
local windowinfo = context.windowinfo
|
||||
local win = context.win
|
||||
local tabfont = context.tabfont
|
||||
local activefont = context.activefont
|
||||
local titlefont = context.titlefont
|
||||
local window = context.window
|
||||
local colours = context.colours
|
||||
local tabs = context.tabs
|
||||
local client_bottom = context.client_bottom
|
||||
local tab_filler = context.tab_filler
|
||||
|
||||
-- clear window
|
||||
WindowRectOp (win, miniwin.rect_fill, 0, 0, 0, 0, colours.background)
|
||||
WindowDeleteAllHotspots (win)
|
||||
|
||||
-- draw drag bar rectangle
|
||||
WindowRectOp (win, miniwin.rect_fill, 1, 1, 0, titlefont.height + 2, colours.title_bar)
|
||||
|
||||
-- add the drag handler so they can move the window around
|
||||
if context.can_move then
|
||||
movewindow.add_drag_handler (win, 0, 0, 0, titlefont.height)
|
||||
end -- if can move the window
|
||||
|
||||
local thisTab = tabs [whichTab]
|
||||
|
||||
if not thisTab then
|
||||
ColourNote ("orange", "", "Tab " .. whichTab .. " does not exist")
|
||||
return
|
||||
end -- no such tab
|
||||
|
||||
-- find title width so we can center it
|
||||
local title_width = WindowTextWidth (win, titlefont.id, thisTab.name, titlefont.unicode)
|
||||
|
||||
-- draw title
|
||||
WindowText(win, titlefont.id, thisTab.name, (window.width - title_width )/ 2 + 1, 1, 0, 0, colours.title, titlefont.unicode)
|
||||
|
||||
-- frame window
|
||||
WindowRectOp (win, miniwin.rect_frame, 0, 0, 0, 0, colours.frame)
|
||||
|
||||
-- draw tabs
|
||||
|
||||
local left = 1
|
||||
|
||||
-- we have to make a function to handle each tabbed window, so its
|
||||
-- name must include the window ID
|
||||
local mouse_down_function_name = "tabbed_window_mouse_down_handler_" .. win
|
||||
|
||||
-- the function will have the context as a closure
|
||||
getfenv () [mouse_down_function_name] = function (flags, hotspot_id)
|
||||
local whichTab = string.match (hotspot_id, "^hs(%d)$")
|
||||
if not whichTab then -- tab unknown for some reason
|
||||
ColourNote ("orange", "", "Mousedown tab unknown " .. hotspot_id)
|
||||
return
|
||||
end -- if
|
||||
|
||||
draw_window (context, tonumber (whichTab))
|
||||
|
||||
end -- mouse_down_function_name
|
||||
|
||||
for k, v in ipairs (tabs) do
|
||||
local tab_width
|
||||
|
||||
if k == whichTab then
|
||||
tab_width = WindowTextWidth (win, activefont.id, v.name, activefont.unicode)
|
||||
else
|
||||
tab_width = WindowTextWidth (win, tabfont.id, v.name, tabfont.unicode)
|
||||
end -- if
|
||||
|
||||
-- tab background
|
||||
WindowRectOp (win, miniwin.rect_fill, left, client_bottom, left + tab_width + tab_filler, window.height - 1, colours.tab)
|
||||
|
||||
-- tab text
|
||||
if k == whichTab then
|
||||
WindowText(win, activefont.id, v.name, left + tab_filler / 2, client_bottom + 4, 0, 0, colours.active_tab_text, activefont.unicode)
|
||||
else
|
||||
WindowText(win, tabfont.id, v.name, left + tab_filler / 2, client_bottom + 4, 0, 0, colours.tab_text, tabfont.unicode)
|
||||
end -- if
|
||||
|
||||
-- draw upper line if not active tab
|
||||
if k ~= whichTab then
|
||||
WindowLine(win, left, client_bottom + 2, left + tab_width + tab_filler, client_bottom + 2,
|
||||
colours.upper_line, miniwin.pen_solid, 2)
|
||||
end -- if not active
|
||||
|
||||
-- draw lower line
|
||||
WindowLine(win, left, window.height - 1, left + tab_width + tab_filler, window.height - 1,
|
||||
colours.lower_line, miniwin.pen_solid, 1)
|
||||
|
||||
-- draw vertical lines
|
||||
WindowLine(win, left, client_bottom + 2, left, window.height - 1,
|
||||
colours.vertical_line, miniwin.pen_solid, 1)
|
||||
WindowLine(win, left + tab_width + tab_filler, client_bottom + 2, left + tab_width + tab_filler, window.height - 1,
|
||||
colours.vertical_line, miniwin.pen_solid, 1)
|
||||
|
||||
-- now add a hotspot for this tab
|
||||
WindowAddHotspot(win, "hs" .. k,
|
||||
left, client_bottom, left + tab_width + tab_filler, window.height - 1, -- rectangle
|
||||
"", -- mouseover
|
||||
"", -- cancelmouseover
|
||||
"tabbed_window." .. mouse_down_function_name,
|
||||
"", -- cancelmousedown
|
||||
"", -- mouseup
|
||||
"Show " .. v.name .. " tab", -- tooltip text
|
||||
miniwin.cursor_hand, 0) -- hand cursor
|
||||
|
||||
left = left + tab_width + tab_filler
|
||||
|
||||
end -- for each tab
|
||||
|
||||
-- call handler to draw rest of window
|
||||
local handler = thisTab.handler
|
||||
if handler then
|
||||
handler (win, 1, titlefont.height + 2, window.width - 1, client_bottom, context)
|
||||
else
|
||||
ColourNote ("orange", "", "No tab handler for " .. thisTab.name)
|
||||
end -- if
|
||||
|
||||
WindowShow (win, true)
|
||||
end -- draw_window
|
||||
|
||||
-- -------------------------------------------------------------------------------
|
||||
-- hide_window - hide the tabbed window
|
||||
-- -------------------------------------------------------------------------------
|
||||
function hide_window (context)
|
||||
WindowShow (context.win, false)
|
||||
end -- hide_window
|
||||
47
cosmic rage/lua/tprint.lua
Normal file
47
cosmic rage/lua/tprint.lua
Normal file
@@ -0,0 +1,47 @@
|
||||
--
|
||||
-- tprint.lua
|
||||
|
||||
--[[
|
||||
|
||||
For debugging what tables have in them, prints recursively
|
||||
|
||||
See forum thread: http://www.gammon.com.au/forum/?id=4903
|
||||
|
||||
eg.
|
||||
|
||||
require "tprint"
|
||||
|
||||
tprint (GetStyleInfo (20))
|
||||
|
||||
--]]
|
||||
|
||||
function tprint (t, indent, done)
|
||||
-- in case we run it standalone
|
||||
local Note = Note or print
|
||||
local Tell = Tell or io.write
|
||||
|
||||
-- show strings differently to distinguish them from numbers
|
||||
local function show (val)
|
||||
if type (val) == "string" then
|
||||
return '"' .. val .. '"'
|
||||
else
|
||||
return tostring (val)
|
||||
end -- if
|
||||
end -- show
|
||||
-- entry point here
|
||||
done = done or {}
|
||||
indent = indent or 0
|
||||
for key, value in pairs (t) do
|
||||
Tell (string.rep (" ", indent)) -- indent it
|
||||
if type (value) == "table" and not done [value] then
|
||||
done [value] = true
|
||||
Note (show (key), ":");
|
||||
tprint (value, indent + 2, done)
|
||||
else
|
||||
Tell (show (key), "=")
|
||||
print (show (value))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return tprint
|
||||
48
cosmic rage/lua/var.lua
Normal file
48
cosmic rage/lua/var.lua
Normal file
@@ -0,0 +1,48 @@
|
||||
-- var.lua
|
||||
-- ----------------------------------------------------------
|
||||
-- Accessing MUSHclient variables through the 'var' table.
|
||||
-- See forum thread:
|
||||
-- http://www.gammon.com.au/forum/?id=4904
|
||||
|
||||
--[[
|
||||
|
||||
* Set a variable by assigning something to it.
|
||||
* Delete a variable by assigning nil to it.
|
||||
* Get a variable by retrieving its value, will return nil if the variable does not exist.
|
||||
|
||||
Examples:
|
||||
|
||||
var.target = "kobold" -- set MUSHclient variable 'target' to kobold
|
||||
print (var.target) -- print contents of MUSHclient variable
|
||||
|
||||
--]]
|
||||
|
||||
-- ----------------------------------------------------------
|
||||
|
||||
var = {} -- variables table
|
||||
|
||||
setmetatable (var,
|
||||
{
|
||||
-- called to access an entry
|
||||
__index =
|
||||
function (t, name)
|
||||
return GetVariable (name)
|
||||
end,
|
||||
|
||||
-- called to change or delete an entry
|
||||
__newindex =
|
||||
function (t, name, val)
|
||||
local result
|
||||
if val == nil then -- nil deletes it
|
||||
result = DeleteVariable (name)
|
||||
else
|
||||
result = SetVariable (name, tostring (val))
|
||||
end
|
||||
-- warn if they are using bad variable names
|
||||
if result == error_code.eInvalidObjectLabel then
|
||||
error ("Bad variable name '" .. name .. "'", 2)
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
return var
|
||||
180
cosmic rage/lua/wait.lua
Normal file
180
cosmic rage/lua/wait.lua
Normal file
@@ -0,0 +1,180 @@
|
||||
-- wait.lua
|
||||
-- ----------------------------------------------------------
|
||||
-- 'wait' stuff - lets you build pauses into scripts.
|
||||
-- See forum thread:
|
||||
-- http://www.gammon.com.au/forum/?id=4957
|
||||
-- ----------------------------------------------------------
|
||||
|
||||
--[[
|
||||
|
||||
Example of an alias 'send to script':
|
||||
|
||||
|
||||
require "wait"
|
||||
|
||||
wait.make (function () --- coroutine below here
|
||||
|
||||
repeat
|
||||
Send "cast heal"
|
||||
line, wildcards =
|
||||
wait.regexp ("^(You heal .*|You lose your concentration)$")
|
||||
|
||||
until string.find (line, "heal")
|
||||
|
||||
-- wait a second for luck
|
||||
wait.time (1)
|
||||
|
||||
end) -- end of coroutine
|
||||
|
||||
--]]
|
||||
|
||||
require "check"
|
||||
|
||||
module (..., package.seeall)
|
||||
|
||||
-- ----------------------------------------------------------
|
||||
-- table of outstanding threads that are waiting
|
||||
-- ----------------------------------------------------------
|
||||
local threads = {}
|
||||
|
||||
-- ----------------------------------------------------------
|
||||
-- wait.timer_resume: called by a timer to resume a thread
|
||||
-- ----------------------------------------------------------
|
||||
function timer_resume (name)
|
||||
local thread = threads [name]
|
||||
if thread then
|
||||
threads [name] = nil
|
||||
local ok, err = coroutine.resume (thread)
|
||||
if not ok then
|
||||
ColourNote ("deeppink", "black", "Error raised in timer function (in wait module).")
|
||||
ColourNote ("darkorange", "black", debug.traceback (thread))
|
||||
error (err)
|
||||
end -- if
|
||||
end -- if
|
||||
end -- function timer_resume
|
||||
|
||||
-- ----------------------------------------------------------
|
||||
-- wait.trigger_resume: called by a trigger to resume a thread
|
||||
-- ----------------------------------------------------------
|
||||
function trigger_resume (name, line, wildcards, styles)
|
||||
local thread = threads [name]
|
||||
if thread then
|
||||
threads [name] = nil
|
||||
local ok, err = coroutine.resume (thread, line, wildcards, styles)
|
||||
if not ok then
|
||||
ColourNote ("deeppink", "black", "Error raised in trigger function (in wait module)")
|
||||
ColourNote ("darkorange", "black", debug.traceback (thread))
|
||||
error (err)
|
||||
end -- if
|
||||
end -- if
|
||||
end -- function trigger_resume
|
||||
|
||||
-- ----------------------------------------------------------
|
||||
-- convert x seconds to hours, minutes, seconds (for AddTimer)
|
||||
-- ----------------------------------------------------------
|
||||
local function convert_seconds (seconds)
|
||||
local hours = math.floor (seconds / 3600)
|
||||
seconds = seconds - (hours * 3600)
|
||||
local minutes = math.floor (seconds / 60)
|
||||
seconds = seconds - (minutes * 60)
|
||||
return hours, minutes, seconds
|
||||
end -- function convert_seconds
|
||||
|
||||
-- ----------------------------------------------------------
|
||||
-- wait.time: we call this to wait in a script
|
||||
-- ----------------------------------------------------------
|
||||
function time (seconds)
|
||||
local id = "wait_timer_" .. GetUniqueNumber ()
|
||||
threads [id] = assert (coroutine.running (), "Must be in coroutine")
|
||||
|
||||
local hours, minutes, seconds = convert_seconds (seconds)
|
||||
|
||||
check (AddTimer (id, hours, minutes, seconds, "",
|
||||
bit.bor (timer_flag.Enabled,
|
||||
timer_flag.OneShot,
|
||||
timer_flag.Temporary,
|
||||
timer_flag.ActiveWhenClosed,
|
||||
timer_flag.Replace),
|
||||
"wait.timer_resume"))
|
||||
|
||||
return coroutine.yield ()
|
||||
end -- function time
|
||||
|
||||
-- ----------------------------------------------------------
|
||||
-- wait.regexp: we call this to wait for a trigger with a regexp
|
||||
-- ----------------------------------------------------------
|
||||
function regexp (regexp, timeout, flags)
|
||||
local id = "wait_trigger_" .. GetUniqueNumber ()
|
||||
threads [id] = assert (coroutine.running (), "Must be in coroutine")
|
||||
|
||||
check (AddTriggerEx (id, regexp,
|
||||
"-- added by wait.regexp",
|
||||
bit.bor (flags or 0, -- user-supplied extra flags, like omit from output
|
||||
trigger_flag.Enabled,
|
||||
trigger_flag.RegularExpression,
|
||||
trigger_flag.Temporary,
|
||||
trigger_flag.Replace,
|
||||
trigger_flag.OneShot),
|
||||
custom_colour.NoChange,
|
||||
0, "", -- wildcard number, sound file name
|
||||
"wait.trigger_resume",
|
||||
12, 100)) -- send to script (in case we have to delete the timer)
|
||||
|
||||
-- if timeout specified, also add a timer
|
||||
if timeout and timeout > 0 then
|
||||
local hours, minutes, seconds = convert_seconds (timeout)
|
||||
|
||||
-- if timer fires, it deletes this trigger
|
||||
check (AddTimer (id, hours, minutes, seconds,
|
||||
"DeleteTrigger ('" .. id .. "')",
|
||||
bit.bor (timer_flag.Enabled,
|
||||
timer_flag.OneShot,
|
||||
timer_flag.Temporary,
|
||||
timer_flag.ActiveWhenClosed,
|
||||
timer_flag.Replace),
|
||||
"wait.timer_resume"))
|
||||
|
||||
check (SetTimerOption (id, "send_to", "12")) -- send to script
|
||||
|
||||
-- if trigger fires, it should delete the timer we just added
|
||||
check (SetTriggerOption (id, "send", "DeleteTimer ('" .. id .. "')"))
|
||||
|
||||
end -- if having a timeout
|
||||
|
||||
return coroutine.yield () -- return line, wildcards
|
||||
end -- function regexp
|
||||
|
||||
-- ----------------------------------------------------------
|
||||
-- wait.match: we call this to wait for a trigger (not a regexp)
|
||||
-- ----------------------------------------------------------
|
||||
function match (match, timeout, flags)
|
||||
return regexp (MakeRegularExpression (match), timeout, flags)
|
||||
end -- function match
|
||||
|
||||
-- ----------------------------------------------------------
|
||||
-- wait.make: makes a coroutine and resumes it
|
||||
-- ----------------------------------------------------------
|
||||
function make (f)
|
||||
assert (type (f) == "function", "wait.make requires a function")
|
||||
|
||||
-- More friendly failure, suggested by Fiendish
|
||||
local errors = {}
|
||||
if GetOption ("enable_timers") ~= 1 then
|
||||
table.insert (errors, "TIMERS")
|
||||
end -- if timers disabled
|
||||
if GetOption ("enable_triggers") ~= 1 then
|
||||
table.insert (errors, "TRIGGERS")
|
||||
end -- if triggers disabled
|
||||
if #errors ~= 0 then
|
||||
ColourNote("white", "red",
|
||||
"One of your scripts (in '" ..
|
||||
(GetPluginInfo(GetPluginID(), 1) or "World") ..
|
||||
"') just did something that requires " ..
|
||||
table.concat (errors, " and ") ..
|
||||
" to be enabled, but they aren't. " ..
|
||||
"Please check your configuration settings.")
|
||||
return nil, "Trigger/Timers not enabled" -- bad return
|
||||
end -- if have errors
|
||||
coroutine.wrap (f) () -- make coroutine, resume it
|
||||
return true -- good return
|
||||
end -- make
|
||||
298
cosmic rage/lua/words_to_numbers.lua
Normal file
298
cosmic rage/lua/words_to_numbers.lua
Normal file
@@ -0,0 +1,298 @@
|
||||
-- Convert a number to words
|
||||
-- Author: Nick Gammon
|
||||
-- Date: 18th March 2010
|
||||
|
||||
-- See: http://www.gammon.com.au/forum/?id=10155
|
||||
|
||||
-- Examples of use:
|
||||
-- words = convert_numbers_to_words ("94921277802687490518")
|
||||
-- number = convert_words_to_numbers ("one hundred eight thousand three hundred nine")
|
||||
|
||||
-- Both functions return nil and an error message so you can check for failure,
|
||||
-- or assert, eg. words = assert (convert_numbers_to_words ("2687490518"))
|
||||
|
||||
-- Units, must be in inverse order!
|
||||
-- The trailing space is required as the space between words
|
||||
|
||||
local inverse_units = {
|
||||
"vigintillion ", -- 10^63
|
||||
"novemdecillion ", -- 10^60
|
||||
"octodecillion ", -- 10^57
|
||||
"septendecillion ", -- 10^54
|
||||
"sexdecillion ", -- 10^51
|
||||
"quindecillion ", -- 10^48
|
||||
"quattuordecillion ",-- 10^45
|
||||
"tredecillion ", -- 10^42
|
||||
"duodecillion ", -- 10^39
|
||||
"undecillion ", -- 10^36
|
||||
"decillion ", -- 10^33
|
||||
"nonillion ", -- 10^30
|
||||
"octillion ", -- 10^27
|
||||
"septillion ", -- 10^24
|
||||
"sextillion ", -- 10^21
|
||||
"quintillion ", -- 10^18
|
||||
"quadrillion ", -- 10^15
|
||||
"trillion ", -- 10^12
|
||||
"billion ", -- 10^9
|
||||
"million ", -- 10^6
|
||||
"thousand ", -- 10^3
|
||||
} -- inverse_units
|
||||
|
||||
local inverse_numbers = {
|
||||
"one ",
|
||||
"two ",
|
||||
"three ",
|
||||
"four ",
|
||||
"five ",
|
||||
"six ",
|
||||
"seven ",
|
||||
"eight ",
|
||||
"nine ",
|
||||
"ten ",
|
||||
"eleven ",
|
||||
"twelve ",
|
||||
"thirteen ",
|
||||
"fourteen ",
|
||||
"fifteen ",
|
||||
"sixteen ",
|
||||
"seventeen ",
|
||||
"eighteen ",
|
||||
"nineteen ",
|
||||
"twenty ",
|
||||
[30] = "thirty ",
|
||||
[40] = "forty ",
|
||||
[50] = "fifty ",
|
||||
[60] = "sixty ",
|
||||
[70] = "seventy ",
|
||||
[80] = "eighty ",
|
||||
[90] = "ninety ",
|
||||
} -- inverse_numbers
|
||||
|
||||
local function convert_up_to_999 (n)
|
||||
|
||||
if n <= 0 then
|
||||
return ""
|
||||
end -- if zero
|
||||
|
||||
local hundreds = math.floor (n / 100)
|
||||
local tens = math.floor (n % 100)
|
||||
local result = ""
|
||||
|
||||
-- if over 99 we need to say x hundred
|
||||
if hundreds > 0 then
|
||||
|
||||
result = inverse_numbers [hundreds] .. "hundred "
|
||||
if tens == 0 then
|
||||
return result
|
||||
end -- if only a digit in the hundreds column
|
||||
|
||||
-- to have "and" between things like "hundred and ten"
|
||||
-- uncomment the next line
|
||||
-- result = result .. "and "
|
||||
|
||||
end -- if
|
||||
|
||||
-- up to twenty it is then just five hundred (and) fifteen
|
||||
if tens <= 20 then
|
||||
return result .. inverse_numbers [tens]
|
||||
end -- if
|
||||
|
||||
-- otherwise we need: thirty (something)
|
||||
result = result .. inverse_numbers [math.floor (tens / 10) * 10]
|
||||
|
||||
-- get final digit (eg. thirty four)
|
||||
local digits = math.floor (n % 10)
|
||||
|
||||
-- to put a hyphen between things like "forty-two"
|
||||
-- uncomment the WITH HYPHEN line and
|
||||
-- comment out the NO HYPHEN line
|
||||
|
||||
if digits > 0 then
|
||||
result = result .. inverse_numbers [digits] -- NO HYPHEN
|
||||
-- result = string.sub (result, 1, -2) .. "-" .. inverse_numbers [digits] -- WITH HYPHEN
|
||||
end -- if
|
||||
|
||||
return result
|
||||
|
||||
end -- convert_up_to_999
|
||||
|
||||
-- convert a number to words
|
||||
-- See: http://www.gammon.com.au/forum/?id=10155
|
||||
|
||||
function convert_numbers_to_words (n)
|
||||
local s = tostring (n)
|
||||
|
||||
-- preliminary sanity checks
|
||||
local c = string.match (s, "%D")
|
||||
if c then
|
||||
return nil, "Non-numeric digit '" .. c .. "' in number"
|
||||
end -- if
|
||||
|
||||
if #s == 0 then
|
||||
return nil, "No number supplied"
|
||||
elseif #s > 66 then
|
||||
return nil, "Number too big to convert to words"
|
||||
end -- if
|
||||
|
||||
-- make multiple of 3
|
||||
while #s % 3 > 0 do
|
||||
s = "0" .. s
|
||||
end -- while
|
||||
|
||||
local result = ""
|
||||
local start = #inverse_units - (#s / 3) + 2
|
||||
|
||||
for i = start, #inverse_units do
|
||||
local group = tonumber (string.sub (s, 1, 3))
|
||||
if group > 0 then
|
||||
result = result .. convert_up_to_999 (group) .. inverse_units [i]
|
||||
end -- if not zero
|
||||
s = string.sub (s, 4)
|
||||
end -- for
|
||||
|
||||
result = result .. convert_up_to_999 (tonumber (s))
|
||||
|
||||
if result == "" then
|
||||
result = "zero"
|
||||
end -- if
|
||||
|
||||
return (string.gsub (result, " +$", "")) -- trim trailing spaces
|
||||
|
||||
end -- convert_numbers_to_words
|
||||
|
||||
-- Convert words to a number
|
||||
-- Author: Nick Gammon
|
||||
-- Date: 18th March 2010
|
||||
|
||||
-- Does NOT handle decimal places (eg. four point six)
|
||||
|
||||
local numbers = {
|
||||
zero = bc.number (0),
|
||||
one = bc.number (1),
|
||||
two = bc.number (2),
|
||||
three = bc.number (3),
|
||||
four = bc.number (4),
|
||||
five = bc.number (5),
|
||||
six = bc.number (6),
|
||||
seven = bc.number (7),
|
||||
eight = bc.number (8),
|
||||
nine = bc.number (9),
|
||||
ten = bc.number (10),
|
||||
eleven = bc.number (11),
|
||||
twelve = bc.number (12),
|
||||
thirteen = bc.number (13),
|
||||
fourteen = bc.number (14),
|
||||
fifteen = bc.number (15),
|
||||
sixteen = bc.number (16),
|
||||
seventeen = bc.number (17),
|
||||
eighteen = bc.number (18),
|
||||
nineteen = bc.number (19),
|
||||
twenty = bc.number (20),
|
||||
thirty = bc.number (30),
|
||||
forty = bc.number (40),
|
||||
fifty = bc.number (50),
|
||||
sixty = bc.number (60),
|
||||
seventy = bc.number (70),
|
||||
eighty = bc.number (80),
|
||||
ninety = bc.number (90),
|
||||
} -- numbers
|
||||
|
||||
local units = {
|
||||
hundred = bc.number ("100"),
|
||||
thousand = bc.number ("1" .. string.rep ("0", 3)),
|
||||
million = bc.number ("1" .. string.rep ("0", 6)),
|
||||
billion = bc.number ("1" .. string.rep ("0", 9)),
|
||||
trillion = bc.number ("1" .. string.rep ("0", 12)),
|
||||
quadrillion = bc.number ("1" .. string.rep ("0", 15)),
|
||||
quintillion = bc.number ("1" .. string.rep ("0", 18)),
|
||||
sextillion = bc.number ("1" .. string.rep ("0", 21)),
|
||||
septillion = bc.number ("1" .. string.rep ("0", 24)),
|
||||
octillion = bc.number ("1" .. string.rep ("0", 27)),
|
||||
nonillion = bc.number ("1" .. string.rep ("0", 30)),
|
||||
decillion = bc.number ("1" .. string.rep ("0", 33)),
|
||||
undecillion = bc.number ("1" .. string.rep ("0", 36)),
|
||||
duodecillion = bc.number ("1" .. string.rep ("0", 39)),
|
||||
tredecillion = bc.number ("1" .. string.rep ("0", 42)),
|
||||
quattuordecillion = bc.number ("1" .. string.rep ("0", 45)),
|
||||
quindecillion = bc.number ("1" .. string.rep ("0", 48)),
|
||||
sexdecillion = bc.number ("1" .. string.rep ("0", 51)),
|
||||
septendecillion = bc.number ("1" .. string.rep ("0", 54)),
|
||||
octodecillion = bc.number ("1" .. string.rep ("0", 57)),
|
||||
novemdecillion = bc.number ("1" .. string.rep ("0", 60)),
|
||||
vigintillion = bc.number ("1" .. string.rep ("0", 63)),
|
||||
} -- units
|
||||
|
||||
-- convert a number in words to a numeric form
|
||||
-- See: http://www.gammon.com.au/forum/?id=10155
|
||||
-- Thanks to David Haley
|
||||
|
||||
function convert_words_to_numbers (s)
|
||||
|
||||
local stack = {}
|
||||
local previous_type
|
||||
|
||||
for word in string.gmatch (s:lower (), "[%a%d]+") do
|
||||
if word ~= "and" then -- skip "and" (like "hundred and fifty two")
|
||||
local top = #stack
|
||||
|
||||
-- If the current word is a number (English or numeric),
|
||||
-- and the previous word was also a number, pop the previous number
|
||||
-- from the stack and push the addition of the two numbers.
|
||||
-- Otherwise, push the new number.
|
||||
|
||||
local number = tonumber (word) -- try for numeric (eg. 22 thousand)
|
||||
|
||||
if number then
|
||||
number = bc.number (number) -- turn into "big number"
|
||||
else
|
||||
number = numbers [word]
|
||||
end -- if a number-word "like: twenty"
|
||||
|
||||
if number then
|
||||
if previous_type == "number" then -- eg. forty three
|
||||
local previous_number = table.remove (stack, top) -- get the forty
|
||||
number = number + previous_number -- add three
|
||||
end -- if
|
||||
table.insert (stack, number)
|
||||
previous_type = "number"
|
||||
else
|
||||
|
||||
-- If the current word is a unit, multiply the number on the top of the stack by the unit's magnitude.
|
||||
local unit = units [word]
|
||||
if not unit then
|
||||
return nil, "Unexpected word: " .. word
|
||||
end -- not unit
|
||||
previous_type = "unit"
|
||||
|
||||
-- It is an error to get a unit before a number.
|
||||
|
||||
if top == 0 then
|
||||
return nil, "Cannot have unit before a number: " .. word
|
||||
end -- starts of with something like "thousand"
|
||||
|
||||
-- pop until we get something larger on the stack
|
||||
local interim_result = bc.number (0)
|
||||
while top > 0 and stack [top] < unit do
|
||||
interim_result = interim_result + table.remove (stack, top)
|
||||
top = #stack
|
||||
end -- while
|
||||
table.insert (stack, interim_result * unit)
|
||||
|
||||
end -- if number or not
|
||||
end -- if 'and'
|
||||
|
||||
end -- for each word
|
||||
|
||||
if #stack == 0 then
|
||||
return nil, "No number found"
|
||||
end -- nothing
|
||||
|
||||
-- When the input has been parsed, sum all numbers on the stack.
|
||||
|
||||
local result = bc.number (0)
|
||||
for _, item in ipairs (stack) do
|
||||
result = result + item
|
||||
end -- for
|
||||
|
||||
return result
|
||||
end -- function convert_words_to_numbers
|
||||
Reference in New Issue
Block a user