Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local files = {
- ["/LICENSE"] = "The MIT License (MIT)\
- \
- Copyright (c) 2015 Ben\
- \
- Permission is hereby granted, free of charge, to any person obtaining a copy\
- of this software and associated documentation files (the \"Software\"), to deal\
- in the Software without restriction, including without limitation the rights\
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\
- copies of the Software, and to permit persons to whom the Software is\
- furnished to do so, subject to the following conditions:\
- \
- The above copyright notice and this permission notice shall be included in all\
- copies or substantial portions of the Software.\
- \
- THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\
- SOFTWARE.\
- ";
- ["/bin/build"] = "\
- local args = { ... }\
- \
- local paths = {}\
- local main = \"init\"\
- local output = \"./run\"\
- local includeFlare = false\
- local minify = false\
- \
- local help = [[build [options] [dirs]\
- \
- options:\
- -o output - the output file (default=./run) without .lua, '.' is replaced with the project path\
- -m main - the main file (default=init) without .lua\
- -c - minify source code\
- -f - include Flare source files in the build\
- \
- dirs:\
- Each directory given is added as a path that Flare looks in when requiring files\
- \
- examples:\
- build MyProject - builds MyProject with main file 'MyProject/init.lua' and outputs to 'MyProject/run.lua'\
- build Test -m main -o ./out - builds Test with main file 'Test/main.lua' and outputs to 'Test/out.lua']]\
- \
- if args[1] == \"-h\" or args[1] == \"help\" then\
- \9return print( help )\
- end\
- \
- local flag\
- for i = 1, #args do\
- \9if args[i] == \"-m\" then\
- \9\9flag = \"main\"\
- \9elseif args[i] == \"-o\" then\
- \9\9flag = \"output\"\
- \9elseif args[i] == \"-c\" then\
- \9\9minify = true\
- \9elseif args[i] == \"-f\" then\
- \9\9includeFlare = true\
- \9elseif flag == \"main\" then\
- \9\9flag = nil\
- \9\9main = args[i]\
- \9elseif flag == \"output\" then\
- \9\9flag = nil\
- \9\9output = args[i]\
- \9else\
- \9\9paths[#paths + 1] = args[i]\
- \9end\
- end\
- \
- local minAPI\
- if minify then\
- \9minAPI = setmetatable( {}, { __index = _ENV or getfenv() } )\
- \9local h = fs.open( \"Flare/minify.lua\", \"r\" )\
- \9if h then\
- \9\9local f, err = load( h.readAll(), \"minify\", nil, minAPI )\
- \9\9h.close()\
- \9\9if not f then\
- \9\9\9error( err, 0 )\
- \9\9end\
- \9\9f()\
- \9else\
- \9\9error( \"failed to open Flare/minify.lua\", 0 )\
- \9end\
- end\
- \
- local userpathcount = #paths\
- if userpathcount == 0 then\
- \9error( \"expected one or more paths\", 0 )\
- end\
- \
- for i = 1, userpathcount do\
- \9if not fs.isDir( paths[i] ) then\
- \9\9error( \"no such directory \" .. (\"%q\"):format( paths[i] ), 0 )\
- \9end\
- end\
- \
- local function formatFileContent( str )\
- \9if minify then\
- \9\9return (\"%q\"):format( minAPI.Rebuild.MinifyString( str ) ):gsub( \"\\\\\\n\", \"\\\\n\" )\
- \9end\
- \9return (\"%q\"):format( str )\
- end\
- \
- local FLAREPATH = \"Flare/lib;Flare/lib/elements\"\
- for seg in FLAREPATH:gmatch \"[^;]+\" do\
- \9paths[#paths + 1] = seg\
- end\
- \
- local required = {}\
- local lookup = {}\
- \
- local files = \"{\\n\"\
- \
- local function requirefile( file )\
- \9if required[file] then return end\
- \9required[file] = true\
- \
- \9local path = file:gsub( \"%.\", \"/\" )\
- \9local fpath, isFlareFile\
- \9\
- \9for i = 1, #paths do\
- \9\9if fs.exists( paths[i] .. \"/\" .. path .. \".lua\" ) and not fs.isDir( paths[i] .. \"/\" .. path .. \".lua\" ) then\
- \9\9\9fpath = paths[i] .. \"/\" .. path .. \".lua\"\
- \9\9elseif fs.exists( paths[i] .. \"/\" .. path .. \"/init.lua\" ) and not fs.isDir( paths[i] .. \"/\" .. path .. \"/init.lua\" ) then\
- \9\9\9fpath = paths[i] .. \"/\" .. path .. \"/init.lua\"\
- \9\9end\
- \9\9if fpath then\
- \9\9\9isFlareFile = i > userpathcount\
- \9\9\9break\
- \9\9end\
- \9end\
- \
- \9if not fpath then\
- \9\9error( \"failed to find file \" .. (\"%q\"):format( file ) .. \" in paths given\", 0 )\
- \9end\
- \
- \9lookup[file] = fpath\
- \
- \9local h = fs.open( fpath, \"r\" )\
- \9local content = h.readAll()\
- \9h.close()\
- \
- \9local function _err()\
- \9\9error( \"@\", 0 )\
- \9end\
- \9local env = setmetatable( { require = requirefile }, {} )\
- \
- \9local f, err = load( content, file, nil, env )\
- \9if not f then\
- \9\9error( err, 0 )\
- \9end\
- \
- \9getmetatable( env ).__index = _err\
- \9getmetatable( env ).__newindex = _err\
- \
- \9local ok, err = pcall( f, file )\
- \9if not ok and err ~= \"@\" then\
- \9\9error( err, 0 )\
- \9end\
- \
- \9if includeFlare or not isFlareFile then\
- \9\9print( \"Including file '\" .. file .. \"' (\" .. fpath .. \")\" )\
- \9\9files = files .. \"\\t[\" .. (\"%q\"):format( file ) .. \"] = \" .. formatFileContent( content ) .. \";\\n\"\
- \9end\
- end\
- \
- requirefile( main )\
- \
- if includeFlare then\
- \9requirefile \"class\"\
- \9requirefile \"UIView\"\
- \9requirefile \"Timer\"\
- \9requirefile \"graphics.ScreenCanvas\"\
- \9requirefile \"Event.Event\"\
- \9requirefile \"Event.MouseEvent\"\
- \9requirefile \"Event.KeyboardEvent\"\
- \9requirefile \"Event.TextEvent\"\
- \9requirefile \"Timer\"\
- end\
- \
- files = files .. \"}\"\
- \
- local FlareInstaller = [[\
- if not fs.exists \"Flare\" then\
- \9print \"Downloading Flare\"\
- \9local h = http.get \"http://pastebin.com/raw.php?i=SD25GhYf\"\
- \9if h then\
- \9\9local f, err = load( h.readAll(), \"installer\", nil, _ENV or getfenv() )\
- \9\9h.close()\
- \9\9f()\
- \9else\
- \9\9return error( \"Cannot install Flare\", 0 )\
- \9end\
- end\
- ]]\
- \
- local str = \"local files = \" .. files .. \"\\n\" .. ( includeFlare and \"\" or FlareInstaller ) .. \"local loader\"\
- \
- if includeFlare then\
- \9local run = fs.open( \"Flare/run.lua\", \"r\" )\
- \9if run then\
- \9\9str = str .. \" = \" .. formatFileContent( run.readAll() )\
- \9\9run.close()\
- \9else\
- \9\9error( \"failed to read Flare\", 0 )\
- \9end\
- else\
- \9str = str .. \"\\nlocal h = fs.open( \\\"Flare/run.lua\\\", \\\"r\\\" )\\nif h then\\n\\tloader = h.readAll()\\n\\th.close()\\nelse\\n\\terror( \\\"failed to read Flare\\\", 0 )\\nend\"\
- end\
- \
- str = str .. \"\\nlocal f, err = load( loader, \\\"Flare\\\", nil, _ENV or getfenv() )\\\
- if not f then\\\
- \9error( \\\"there was a problem with Flare!: \\\" .. err, 0 )\\\
- end\\\
- f( files, \" .. (\"%q\"):format( main ) .. \", ... )\"\
- \
- local w = {}\
- local path = output:gsub( \"%.\", paths[1] ) .. \".lua\"\
- if not w[path] then\
- \9w[path] = true\
- \9local h = fs.open( path, \"w\" )\
- \9if h then\
- \9\9h.write( str )\
- \9\9h.close()\
- \9else\
- \9\9print( \"Failed to open output file \" .. (\"%q\"):format( path ) )\
- \9end\
- end";
- ["/bin/debug"] = "\
- local args = { ... }\
- \
- local paths = {}\
- local main = \"init\"\
- local output = \"./run\"\
- \
- local help = [[debug [options] [dirs]\
- \
- options:\
- -o output - the output file (default=./run) without .lua, '.' is replaced with the project path\
- -m main - the main file (default=init) without .lua\
- -c - minify source code\
- -f - include Flare source files in the build\
- \
- dirs:\
- Each directory given is added as a path that Flare looks in when requiring files\
- \
- examples:\
- debug MyProject - builds MyProject with main file 'MyProject/init.lua' and outputs to 'MyProject/run.lua'\
- debug Test -m main -o ./out - builds Test with main file 'Test/main.lua' and outputs to 'Test/out.lua']]\
- \
- if args[1] == \"-h\" or args[1] == \"help\" then\
- \9return print( help )\
- end\
- \
- local flag\
- for i = 1, #args do\
- \9if args[i] == \"-m\" then\
- \9\9flag = \"main\"\
- \9elseif args[i] == \"-o\" then\
- \9\9flag = \"output\"\
- \9elseif args[i] == \"-c\" then\
- \9\9\
- \9elseif args[i] == \"-f\" then\
- \9\9\
- \9elseif flag == \"main\" then\
- \9\9flag = nil\
- \9elseif flag == \"output\" then\
- \9\9flag = nil\
- \9\9output = args[i]\
- \9else\
- \9\9paths[#paths + 1] = args[i]\
- \9end\
- end\
- \
- local userpathcount = #paths\
- if userpathcount == 0 then\
- \9error( \"expected one or more paths\", 0 )\
- end\
- \
- loadfile \"Flare/bin/build\" ( unpack( args ) )\
- shell.run( output:gsub( \"%.\", paths[1] ) .. \".lua\" )";
- ["/bin/extract"] = "\
- local path, output = ...\
- \
- local function assertype( a, b, c )\
- \9return type( a ) == b and a or error( c, 0 )\
- end\
- \
- assertype( path, \"string\", \"expected input path as arg#1\" )\
- assertype( output, \"string\", \"expected output path as arg#2\" )\
- \
- if fs.isDir( path ) then\
- \9error( \"input path is a directory\", 0 )\
- end\
- if fs.exists( output ) and not fs.isDir( output ) then\
- \9error( \"output path is not a directory\", 0 )\
- end\
- \
- local h = fs.open( path, \"r\" )\
- local content = h.readAll()\
- h.close()\
- \
- if content:find \"}\\nif not fs.exists \\\"Flare\\\" then\\n\" then\
- \9local pos = #content - content:reverse():find( (\"}\\nif not fs.exists \\\"Flare\\\" then\\n\"):reverse() ) - 30\
- \9local f = content:sub( #(\"local files = \") + 1, pos )\
- \9local t = textutils.unserialize( f )\
- \9if type( t ) == \"table\" then\
- \9\9for k, v in pairs( t ) do\
- \9\9\9local h = fs.open( output .. \"/\" .. k, \"w\" )\
- \9\9\9h.write( v )\
- \9\9\9h.close()\
- \9\9end\
- \9else\
- \9\9error( \"couldn't unserialize application\", 0 )\
- \9end\
- else\
- \9error( \"path is not a Flare application\", 0 )\
- end";
- ["/bin/pack"] = "\
- local args = { ... }\
- \
- local function assertype( a, b, c )\
- \9return type( a ) == b and a or error( c, 0 )\
- end\
- \
- local input = assertype( args[1], \"string\", \"expected input path as arg#1\" )\
- local output = assertype( args[2], \"string\", \"expected output path as arg#2\" )\
- \
- if not fs.isDir( input ) then\
- \9error( \"input path is not a directory\", 0 )\
- end\
- if fs.isDir( output ) then\
- \9error( \"output path is a directory\", 0 )\
- end\
- \
- local str = \"{\\n\"\
- \
- local function scandir( path, name )\
- \9for _, file in ipairs( fs.list( path ) ) do\
- \9\9local filename = name .. \"/\" .. file\
- \9\9if fs.isDir( path .. \"/\" .. file ) then\
- \9\9\9scandir( path .. \"/\" .. file, filename )\
- \9\9else\
- \9\9\9local h = fs.open( path .. \"/\" .. file, \"r\" )\
- \9\9\9local content = h.readAll()\
- \9\9\9h.close()\
- \9\9\9str = str .. \"\\t[\" .. (\"%q\"):format( filename ) .. \"] = \" .. (\"%q\"):format( content ):gsub( \"\\\\\\n\", \"\\\\n\" ) .. \";\\n\"\
- \9\9end\
- \9end\
- end\
- \
- scandir( input, \"\" )\
- \
- local h = fs.open( output, \"w\" )\
- if h then\
- \9h.write( str .. \"}\" )\
- \9h.close()\
- else\
- \9error( \"failed to open output file\", 0 )\
- end";
- ["/bin/unpack"] = "\
- local args = { ... }\
- \
- local function assertype( a, b, c )\
- \9return type( a ) == b and a or error( c, 0 )\
- end\
- \
- local input = assertype( args[1], \"string\", \"expected input path as arg#1\" )\
- local output = assertype( args[2], \"string\", \"expected output path as arg#2\" )\
- \
- if not fs.exists( input ) then\
- \9error( \"input path does not exist\", 0 )\
- elseif fs.isDir( input ) then\
- \9error( \"input path is a directory\", 0 )\
- end\
- if fs.exists( output ) and not fs.isDir( output ) then\
- \9error( \"output path is not a directory\", 0 )\
- end\
- \
- local h = fs.open( input, \"r\" )\
- local content = h.readAll()\
- h.close()\
- \
- if not fs.exists( output ) then\
- \9fs.makeDir( output )\
- end\
- \
- local files = textutils.unserialize( content )\
- if type( files ) ~= \"table\" then\
- \9error( \"input path is not an unpackable file\", 0 )\
- end\
- \
- for k, v in pairs( files ) do\
- \9local h = fs.open( output .. k, \"w\" )\
- \9if h then\
- \9\9h.write( v )\
- \9\9h.close()\
- \9else\
- \9\9error( \"failed to open file \" .. k, 0 )\
- \9end\
- end";
- ["/lib/Event/Event.lua"] = "\
- class \"Event\" {\
- \9handled = false;\
- \9name = \"\";\
- \9parameters = {};\
- \
- \9MOUSEDOWN = 0;\
- \9MOUSEUP = 1;\
- \9MOUSEDRAG = 2;\
- \9MOUSESCROLL = 3;\
- \9MOUSEPING = 4;\
- \9KEYDOWN = 5;\
- \9KEYUP = 6;\
- \9TEXT = 7;\
- \9PASTE = 8;\
- }\
- \
- function Event:init( name, parameters )\
- \9self.name = name\
- \9self.parameters = parameters or {}\
- end";
- ["/lib/Event/KeyboardEvent.lua"] = "\
- require \"Event.Event\"\
- \
- class \"KeyboardEvent\" extends \"Event\" {\
- \9key = 0;\
- \9modifiers = {};\
- }\
- \
- function KeyboardEvent:init( name, key, modifiers, parameters )\
- \9self.key = keys.getName( key )\
- \9self.modifiers = modifiers\
- \9self.super:init( name, parameters )\
- end\
- \
- function KeyboardEvent:matchesHotkey( hotkey )\
- \9for segment in ( hotkey:match \"^(.+)%-\" or \"\" ):gmatch \"[^%-]+\" do\
- \9\9if segment == \"ctrl\" and not self.modifiers.leftCtrl and not self.modifiers.rightCtrl then\
- \9\9\9return false\
- \9\9elseif segment == \"shift\" and not self.modifiers.leftShift and not self.modifiers.rightShift then\
- \9\9\9return false\
- \9\9elseif segment ~= \"ctrl\" and segment ~= \"shift\" and not self.modifiers[segment] then\
- \9\9\9return false\
- \9\9end\
- \9end\
- \9return self.key == hotkey:gsub( \".+%-\", \"\" )\
- end";
- ["/lib/Event/MouseEvent.lua"] = "\
- require \"Event.Event\"\
- \
- class \"MouseEvent\" extends \"Event\" { useSetters = true;\
- \9x = 0;\
- \9y = 0;\
- \9button = 1;\
- \
- \9parent = nil;\
- \
- \9within = true;\
- \
- \9BUTTONLEFT = 1;\
- \9BUTTONRIGHT = 2;\
- \9BUTTONMIDDLE = 3;\
- }\
- \
- function MouseEvent:init( name, x, y, button, within, parameters )\
- \9self.x = x\
- \9self.y = y\
- \9self.button = button\
- \9self.within = within\
- \9self.super:init( name, parameters )\
- \9self.mt.__tostring = self.tostring\
- end\
- \
- function MouseEvent:clone( x, y, within )\
- \9local new = MouseEvent( self.name, self.x - x, self.y - y, self.button, self.within and within, self.parameters )\
- \9new.parent = self\
- \9new.handled = self.handled\
- \9return new\
- end\
- \
- function MouseEvent:isInArea( x, y, width, height )\
- \9local sx, sy = self.x, self.y\
- \9return self.within and sx >= x and sy >= y and sx < x + width and sy < y + height\
- end\
- \
- function MouseEvent:setHandled( handled )\
- \9self.handled = handled\
- \9if handled and self.parent then\
- \9\9self.parent.handled = true\
- \9end\
- end\
- \
- function MouseEvent:tostring()\
- \9return \"[Instance] MouseEvent[\" .. self.button .. \"] @\" .. self.x .. \",\" .. self.y\
- end";
- ["/lib/Event/TextEvent.lua"] = "\
- require \"Event.Event\"\
- \
- class \"TextEvent\" extends \"Event\" {\
- \9text = \"\";\
- }\
- \
- function TextEvent:init( name, text, parameters )\
- \9self.text = text\
- \9self.super:init( name, parameters )\
- end";
- ["/lib/Timer.lua"] = "\
- class \"Timer\"\
- \
- local time, timers, fpstimer = 0, {}, nil\
- \
- local function getTimer( n )\
- \9local timeout = os.clock() + n\
- \9for i = 1, #timers do\
- \9\9if timers[i].timeout == timeout then\
- \9\9\9return timers[i]\
- \9\9end\
- \9end\
- \9local timer = {\
- \9\9timeout = timeout;\
- \9\9timer = os.startTimer( n );\
- \9}\
- \9timers[#timers + 1] = timer;\
- \9return timer\
- end\
- \
- local function updateTimers( timer )\
- \9for i = #timers, 1, -1 do\
- \9\9if timers[i].timer == timer then\
- \9\9\9for a = 1, #timers[i] do\
- \9\9\9\9timers[i][a]()\
- \9\9\9end\
- \9\9\9table.remove( timers, i )\
- \9\9\9return true\
- \9\9end\
- \9end\
- \9return false\
- end\
- \
- function Timer.step()\
- \9time = os.clock()\
- end\
- \
- function Timer.getDelta()\
- \9return os.clock() - time\
- end\
- \
- function Timer.getFPS()\
- \9return fps\
- end\
- \
- function Timer.setFPS( f )\
- \9fps = f\
- \9fpstimer = fpstimer or ( f and os.startTimer( 1 / f ) )\
- end\
- \
- function Timer.queue( n, action )\
- \9local t = getTimer( n )\
- \9t[#t + 1] = action\
- \9return t.timer\
- end\
- \
- function Timer.sleep( n )\
- \9local t = getTimer( n ).timer\
- \9repeat\
- \9\9local _, timer = coroutine.yield \"timer\"\
- \9until timer == t\
- end\
- \
- function Timer.update( event, timer )\
- \9if event == \"timer\" then\
- \9\9if timer == fpstimer then\
- \9\9\9fpstimer = fps and os.startTimer( 1 / fps ) or nil\
- \9\9\9os.queueEvent \"update\"\
- \9\9\9return true\
- \9\9else\
- \9\9\9return updateTimers( timer )\
- \9\9end\
- \9end\
- end";
- ["/lib/Tween.lua"] = "local tween = {\
- \9_VERSION = 'tween 2.0.0',\
- \9_DESCRIPTION = 'tweening for lua',\
- \9_URL = 'https://github.com/kikito/tween.lua',\
- \9_LICENSE = [[\
- \9\9MIT LICENSE\
- \9\9Copyright (c) 2014 Enrique GarcÃa Cota, Yuichi Tateno, Emmanuel Oga\
- \9\9Permission is hereby granted, free of charge, to any person obtaining a\
- \9\9copy of this software and associated documentation files (the\
- \9\9\"Software\"), to deal in the Software without restriction, including\
- \9\9without limitation the rights to use, copy, modify, merge, publish,\
- \9\9distribute, sublicense, and/or sell copies of the Software, and to\
- \9\9permit persons to whom the Software is furnished to do so, subject to\
- \9\9the following conditions:\
- \9\9The above copyright notice and this permission notice shall be included\
- \9\9in all copies or substantial portions of the Software.\
- \9\9THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\
- \9\9OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\
- \9\9MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\
- \9\9IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\
- \9\9CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\
- \9\9TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\
- \9\9SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\
- \9]]\
- }\
- \
- -- easing\
- \
- -- Adapted from https://github.com/EmmanuelOga/easing. See LICENSE.txt for credits.\
- -- For all easing functions:\
- -- t = time == how much time has to pass for the tweening to complete\
- -- b = begin == starting property value\
- -- c = change == ending - beginning\
- -- d = duration == running time. How much time has passed *right now*\
- \
- local pow, sin, cos, pi, sqrt, abs, asin = math.pow, math.sin, math.cos, math.pi, math.sqrt, math.abs, math.asin\
- \
- -- linear\
- local function linear(t, b, c, d) return c * t / d + b end\
- \
- -- quad\
- local function inQuad(t, b, c, d) return c * pow(t / d, 2) + b end\
- local function outQuad(t, b, c, d)\
- \9t = t / d\
- \9return -c * t * (t - 2) + b\
- end\
- local function inOutQuad(t, b, c, d)\
- \9t = t / d * 2\
- \9if t < 1 then return c / 2 * pow(t, 2) + b end\
- \9return -c / 2 * ((t - 1) * (t - 3) - 1) + b\
- end\
- local function outInQuad(t, b, c, d)\
- \9if t < d / 2 then return outQuad(t * 2, b, c / 2, d) end\
- \9return inQuad((t * 2) - d, b + c / 2, c / 2, d)\
- end\
- \
- -- cubic\
- local function inCubic (t, b, c, d) return c * pow(t / d, 3) + b end\
- local function outCubic(t, b, c, d) return c * (pow(t / d - 1, 3) + 1) + b end\
- local function inOutCubic(t, b, c, d)\
- \9t = t / d * 2\
- \9if t < 1 then return c / 2 * t * t * t + b end\
- \9t = t - 2\
- \9return c / 2 * (t * t * t + 2) + b\
- end\
- local function outInCubic(t, b, c, d)\
- \9if t < d / 2 then return outCubic(t * 2, b, c / 2, d) end\
- \9return inCubic((t * 2) - d, b + c / 2, c / 2, d)\
- end\
- \
- -- quart\
- local function inQuart(t, b, c, d) return c * pow(t / d, 4) + b end\
- local function outQuart(t, b, c, d) return -c * (pow(t / d - 1, 4) - 1) + b end\
- local function inOutQuart(t, b, c, d)\
- \9t = t / d * 2\
- \9if t < 1 then return c / 2 * pow(t, 4) + b end\
- \9return -c / 2 * (pow(t - 2, 4) - 2) + b\
- end\
- local function outInQuart(t, b, c, d)\
- \9if t < d / 2 then return outQuart(t * 2, b, c / 2, d) end\
- \9return inQuart((t * 2) - d, b + c / 2, c / 2, d)\
- end\
- \
- -- quint\
- local function inQuint(t, b, c, d) return c * pow(t / d, 5) + b end\
- local function outQuint(t, b, c, d) return c * (pow(t / d - 1, 5) + 1) + b end\
- local function inOutQuint(t, b, c, d)\
- \9t = t / d * 2\
- \9if t < 1 then return c / 2 * pow(t, 5) + b end\
- \9return c / 2 * (pow(t - 2, 5) + 2) + b\
- end\
- local function outInQuint(t, b, c, d)\
- \9if t < d / 2 then return outQuint(t * 2, b, c / 2, d) end\
- \9return inQuint((t * 2) - d, b + c / 2, c / 2, d)\
- end\
- \
- -- sine\
- local function inSine(t, b, c, d) return -c * cos(t / d * (pi / 2)) + c + b end\
- local function outSine(t, b, c, d) return c * sin(t / d * (pi / 2)) + b end\
- local function inOutSine(t, b, c, d) return -c / 2 * (cos(pi * t / d) - 1) + b end\
- local function outInSine(t, b, c, d)\
- \9if t < d / 2 then return outSine(t * 2, b, c / 2, d) end\
- \9return inSine((t * 2) -d, b + c / 2, c / 2, d)\
- end\
- \
- -- expo\
- local function inExpo(t, b, c, d)\
- \9if t == 0 then return b end\
- \9return c * pow(2, 10 * (t / d - 1)) + b - c * 0.001\
- end\
- local function outExpo(t, b, c, d)\
- \9if t == d then return b + c end\
- \9return c * 1.001 * (-pow(2, -10 * t / d) + 1) + b\
- end\
- local function inOutExpo(t, b, c, d)\
- \9if t == 0 then return b end\
- \9if t == d then return b + c end\
- \9t = t / d * 2\
- \9if t < 1 then return c / 2 * pow(2, 10 * (t - 1)) + b - c * 0.0005 end\
- \9return c / 2 * 1.0005 * (-pow(2, -10 * (t - 1)) + 2) + b\
- end\
- local function outInExpo(t, b, c, d)\
- \9if t < d / 2 then return outExpo(t * 2, b, c / 2, d) end\
- \9return inExpo((t * 2) - d, b + c / 2, c / 2, d)\
- end\
- \
- -- circ\
- local function inCirc(t, b, c, d) return(-c * (sqrt(1 - pow(t / d, 2)) - 1) + b) end\
- local function outCirc(t, b, c, d) return(c * sqrt(1 - pow(t / d - 1, 2)) + b) end\
- local function inOutCirc(t, b, c, d)\
- \9t = t / d * 2\
- \9if t < 1 then return -c / 2 * (sqrt(1 - t * t) - 1) + b end\
- \9t = t - 2\
- \9return c / 2 * (sqrt(1 - t * t) + 1) + b\
- end\
- local function outInCirc(t, b, c, d)\
- \9if t < d / 2 then return outCirc(t * 2, b, c / 2, d) end\
- \9return inCirc((t * 2) - d, b + c / 2, c / 2, d)\
- end\
- \
- -- elastic\
- local function calculatePAS(p,a,c,d)\
- \9p, a = p or d * 0.3, a or 0\
- \9if a < abs(c) then return p, c, p / 4 end -- p, a, s\
- \9return p, a, p / (2 * pi) * asin(c/a) -- p,a,s\
- end\
- local function inElastic(t, b, c, d, a, p)\
- \9local s\
- \9if t == 0 then return b end\
- \9t = t / d\
- \9if t == 1 then return b + c end\
- \9p,a,s = calculatePAS(p,a,c,d)\
- \9t = t - 1\
- \9return -(a * pow(2, 10 * t) * sin((t * d - s) * (2 * pi) / p)) + b\
- end\
- local function outElastic(t, b, c, d, a, p)\
- \9local s\
- \9if t == 0 then return b end\
- \9t = t / d\
- \9if t == 1 then return b + c end\
- \9p,a,s = calculatePAS(p,a,c,d)\
- \9return a * pow(2, -10 * t) * sin((t * d - s) * (2 * pi) / p) + c + b\
- end\
- local function inOutElastic(t, b, c, d, a, p)\
- \9local s\
- \9if t == 0 then return b end\
- \9t = t / d * 2\
- \9if t == 2 then return b + c end\
- \9p,a,s = calculatePAS(p,a,c,d)\
- \9t = t - 1\
- \9if t < 0 then return -0.5 * (a * pow(2, 10 * t) * sin((t * d - s) * (2 * pi) / p)) + b end\
- \9return a * pow(2, -10 * t) * sin((t * d - s) * (2 * pi) / p ) * 0.5 + c + b\
- end\
- local function outInElastic(t, b, c, d, a, p)\
- \9if t < d / 2 then return outElastic(t * 2, b, c / 2, d, a, p) end\
- \9return inElastic((t * 2) - d, b + c / 2, c / 2, d, a, p)\
- end\
- \
- -- back\
- local function inBack(t, b, c, d, s)\
- \9s = s or 1.70158\
- \9t = t / d\
- \9return c * t * t * ((s + 1) * t - s) + b\
- end\
- local function outBack(t, b, c, d, s)\
- \9s = s or 1.70158\
- \9t = t / d - 1\
- \9return c * (t * t * ((s + 1) * t + s) + 1) + b\
- end\
- local function inOutBack(t, b, c, d, s)\
- \9s = (s or 1.70158) * 1.525\
- \9t = t / d * 2\
- \9if t < 1 then return c / 2 * (t * t * ((s + 1) * t - s)) + b end\
- \9t = t - 2\
- \9return c / 2 * (t * t * ((s + 1) * t + s) + 2) + b\
- end\
- local function outInBack(t, b, c, d, s)\
- \9if t < d / 2 then return outBack(t * 2, b, c / 2, d, s) end\
- \9return inBack((t * 2) - d, b + c / 2, c / 2, d, s)\
- end\
- \
- -- bounce\
- local function outBounce(t, b, c, d)\
- \9t = t / d\
- \9if t < 1 / 2.75 then return c * (7.5625 * t * t) + b end\
- \9if t < 2 / 2.75 then\
- \9\9t = t - (1.5 / 2.75)\
- \9\9return c * (7.5625 * t * t + 0.75) + b\
- \9elseif t < 2.5 / 2.75 then\
- \9\9t = t - (2.25 / 2.75)\
- \9\9return c * (7.5625 * t * t + 0.9375) + b\
- \9end\
- \9t = t - (2.625 / 2.75)\
- \9return c * (7.5625 * t * t + 0.984375) + b\
- end\
- local function inBounce(t, b, c, d) return c - outBounce(d - t, 0, c, d) + b end\
- local function inOutBounce(t, b, c, d)\
- \9if t < d / 2 then return inBounce(t * 2, 0, c, d) * 0.5 + b end\
- \9return outBounce(t * 2 - d, 0, c, d) * 0.5 + c * .5 + b\
- end\
- local function outInBounce(t, b, c, d)\
- \9if t < d / 2 then return outBounce(t * 2, b, c / 2, d) end\
- \9return inBounce((t * 2) - d, b + c / 2, c / 2, d)\
- end\
- \
- tween.easing = {\
- \9linear = linear,\
- \9inQuad = inQuad, outQuad = outQuad, inOutQuad = inOutQuad, outInQuad = outInQuad,\
- \9inCubic = inCubic, outCubic = outCubic, inOutCubic = inOutCubic, outInCubic = outInCubic,\
- \9inQuart = inQuart, outQuart = outQuart, inOutQuart = inOutQuart, outInQuart = outInQuart,\
- \9inQuint = inQuint, outQuint = outQuint, inOutQuint = inOutQuint, outInQuint = outInQuint,\
- \9inSine = inSine, outSine = outSine, inOutSine = inOutSine, outInSine = outInSine,\
- \9inExpo = inExpo, outExpo = outExpo, inOutExpo = inOutExpo, outInExpo = outInExpo,\
- \9inCirc = inCirc, outCirc = outCirc, inOutCirc = inOutCirc, outInCirc = outInCirc,\
- \9inElastic = inElastic, outElastic = outElastic, inOutElastic = inOutElastic, outInElastic = outInElastic,\
- \9inBack = inBack, outBack = outBack, inOutBack = inOutBack, outInBack = outInBack,\
- \9inBounce = inBounce, outBounce = outBounce, inOutBounce = inOutBounce, outInBounce = outInBounce\
- }\
- \
- \
- \
- -- private stuff\
- \
- local function copyTables(destination, keysTable, valuesTable)\
- \9valuesTable = valuesTable or keysTable\
- \9local mt = getmetatable(keysTable)\
- \9if mt and getmetatable(destination) == nil then\
- \9\9setmetatable(destination, mt)\
- \9end\
- \9for k,v in pairs(keysTable) do\
- \9\9if type(v) == 'table' then\
- \9\9\9destination[k] = copyTables({}, v, valuesTable[k])\
- \9\9else\
- \9\9\9destination[k] = valuesTable[k]\
- \9\9end\
- \9end\
- \9return destination\
- end\
- \
- local function checkSubjectAndTargetRecursively(subject, target, path)\
- \9path = path or {}\
- \9local targetType, newPath\
- \9for k,targetValue in pairs(target) do\
- \9\9targetType, newPath = type(targetValue), copyTables({}, path)\
- \9\9table.insert(newPath, tostring(k))\
- \9\9if targetType == 'number' then\
- \9\9\9assert(type(subject[k]) == 'number', \"Parameter '\" .. table.concat(newPath,'/') .. \"' is missing from subject or isn't a number\")\
- \9\9elseif targetType == 'table' then\
- \9\9\9checkSubjectAndTargetRecursively(subject[k], targetValue, newPath)\
- \9\9else\
- \9\9\9assert(targetType == 'number', \"Parameter '\" .. table.concat(newPath,'/') .. \"' must be a number or table of numbers\")\
- \9\9end\
- \9end\
- end\
- \
- local function checkNewParams(duration, subject, target, easing)\
- \9assert(type(duration) == 'number' and duration > 0, \"duration must be a positive number. Was \" .. tostring(duration))\
- \9local tsubject = type(subject)\
- \9assert(tsubject == 'table' or tsubject == 'userdata', \"subject must be a table or userdata. Was \" .. tostring(subject))\
- \9assert(type(target)== 'table', \"target must be a table. Was \" .. tostring(target))\
- \9assert(type(easing)=='function', \"easing must be a function. Was \" .. tostring(easing))\
- \9checkSubjectAndTargetRecursively(subject, target)\
- end\
- \
- local function getEasingFunction(easing)\
- \9easing = easing or \"linear\"\
- \9if type(easing) == 'string' then\
- \9\9local name = easing\
- \9\9easing = tween.easing[name]\
- \9\9if type(easing) ~= 'function' then\
- \9\9\9error(\"The easing function name '\" .. name .. \"' is invalid\")\
- \9\9end\
- \9end\
- \9return easing\
- end\
- \
- local function performEasingOnSubject(subject, target, initial, clock, duration, easing, round )\
- \9local t,b,c,d\
- \9for k,v in pairs(target) do\
- \9\9if type(v) == 'table' then\
- \9\9\9performEasingOnSubject(subject[k], v, initial[k], clock, duration, easing, round)\
- \9\9else\
- \9\9\9t,b,c,d = clock, initial[k], v - initial[k], duration\
- \9\9\9subject[k] = round and math.floor( easing(t,b,c,d) + .5 ) or easing( t, b, c, d )\
- \9\9end\
- \9end\
- end\
- \
- -- Tween methods\
- \
- class \"Tween\" {\
- \9duration = 0;\
- \9subject = 0;\
- \9target = 0;\
- \9easing = 0;\
- \9initial = 0;\
- \9clock = 0;\
- \9round = false;\
- \
- \9DEFAULT_EASING = \"inOutSine\";\
- \9DEFAULT_DURATION = .3;\
- }\
- \
- function Tween:init( subject, target, duration, easing )\
- \9easing = easing or self.DEFAULT_EASING\
- \9duration = duration or self.DEFAULT_DURATION\
- \9easing = getEasingFunction( easing )\
- \9checkNewParams( duration, subject, target, easing )\
- \9self.duration = duration\
- \9self.subject = subject\
- \9self.target = target\
- \9self.easing = easing\
- \9self.initial = copyTables( {}, target, subject )\
- end\
- \
- function Tween:set( clock )\
- \9self.clock = clock\
- \9if self.clock <= 0 then\
- \9\9self.clock = 0\
- \9\9copyTables(self.subject, self.initial)\
- \9elseif self.clock >= self.duration then -- the tween has expired\
- \9\9self.clock = self.duration\
- \9\9copyTables(self.subject, self.target)\
- \9else\
- \9\9performEasingOnSubject( self.subject, self.target, self.initial, self.clock, self.duration, self.easing, self.round )\
- \9end\
- \9return self.clock >= self.duration\
- end\
- \
- function Tween:reset()\
- \9return self:set( 0 )\
- end\
- \
- function Tween:update( dt )\
- \9return self:set( self.clock + dt )\
- end";
- ["/lib/UIAnimationHandler.lua"] = "\
- require \"Tween\"\
- require \"Timer\"\
- \
- local function kickTimer( self )\
- \9local time = os.clock()\
- \9self.updateTimer = self.updateTimer or Timer.queue( .05, function()\
- \9\9self:update( os.clock() - time )\
- \9end )\
- end\
- \
- class \"UIAnimationHandler\" {\
- \9tweens = {};\
- }\
- \
- function UIAnimationHandler:init()\
- \9self.tweens = {}\
- end\
- \
- function UIAnimationHandler:killTween( label )\
- \9local tweens = self.tweens\
- \9for i = 1, #tweens do\
- \9\9if tweens[i].label == label then\
- \9\9\9table.remove( tweens, i )\
- \9\9\9break\
- \9\9end\
- \9end\
- end\
- \
- function UIAnimationHandler:createTween( label, object, target, duration, easing )\
- \9self:killTween( label )\
- \9kickTimer( self )\
- \
- \9local tween = Tween( object, target, duration, easing )\
- \9tween.label = label\
- \9self.tweens[#self.tweens + 1] = tween\
- \9return tween\
- end\
- \
- function UIAnimationHandler:createRoundedTween( label, object, target, duration, easing )\
- \9self:killTween( label )\
- \9kickTimer( self )\
- \
- \9local tween = Tween( object, target, duration, easing )\
- \9tween.round = true\
- \9tween.label = label\
- \9self.tweens[#self.tweens + 1] = tween\
- \
- \9return tween\
- end\
- \
- function UIAnimationHandler:update( dt )\
- \9self.updateTimer = nil\
- \9local tweens = self.tweens\
- \9for i = #tweens, 1, -1 do\
- \9\9if tweens[i]:update( dt ) then\
- \9\9\9if tweens[i].onFinish then\
- \9\9\9\9tweens[i]:onFinish()\
- \9\9\9end\
- \9\9\9table.remove( tweens, i )\
- \9\9end\
- \9end\
- \9if #tweens > 0 then\
- \9\9kickTimer( self )\
- \9end\
- end";
- ["/lib/UIElement.lua"] = "\
- require \"UIAnimationHandler\"\
- require \"Event.MouseEvent\"\
- require \"Event.KeyboardEvent\"\
- require \"Event.TextEvent\"\
- require \"graphics.DrawingCanvas\"\
- \
- local function copytable( t )\
- \9return { unpack( t ) }\
- end\
- \
- class \"UIElement\" { useSetters = true,\
- \9id = \"NOID\";\
- \9tags = {};\
- \
- \9x = 0;\
- \9y = 0;\
- \9width = 0;\
- \9height = 0;\
- \
- \9ox = 0;\
- \9oy = 0;\
- \
- \9children = {};\
- \9parent = nil;\
- \
- \9handlesMouse = true;\
- \9handlesKeyboard = false;\
- \9handlesText = false;\
- \
- \9canvas = nil;\
- \9animationHandler = nil;\
- \9changed = true;\
- \
- \9transitionTime = .3;\
- }\
- \
- function UIElement:init( x, y, width, height )\
- \9self.tags = {}\
- \9self.children = {}\
- \9self.raw.x = x\
- \9self.raw.y = y\
- \9self.raw.width = width\
- \9self.raw.height = height\
- \9self.animationHandler = UIAnimationHandler()\
- \9self.canvas = DrawingCanvas( 0, 0, width, height )\
- \
- \9self.mt.__tostring = self.tostring\
- end\
- \
- function UIElement:getChildById( id, recursive )\
- \
- \9for i = #self.children, 1, -1 do\
- \9\9local child = self.children[i]\
- \9\9if child.id == id then\
- \9\9\9return child\
- \9\9elseif recursive then\
- \9\9\9child = child:getChildById( id, true )\
- \9\9\9if child then\
- \9\9\9\9return child\
- \9\9\9end\
- \9\9end\
- \9end\
- \
- end\
- \
- UIElement.getElementById = UIElement.getChildById\
- \
- function UIElement:getChildrenByTag( tag, recursive )\
- \
- \9local children = {}\
- \9for i = 1, #self.children do\
- \9\9local child = self.children[i]\
- \9\9for t = 1, #child.tags do\
- \9\9\9if child.tags[t] == tag then\
- \9\9\9\9children[#children + 1] = child\
- \9\9\9\9break\
- \9\9\9end\
- \9\9end\
- \9\9if recursive then\
- \9\9\9local e = child:getChildrenByTag( tag, true )\
- \9\9\9for c = 1, #e do\
- \9\9\9\9children[#children + 1] = e[c]\
- \9\9\9end\
- \9\9end\
- \9end\
- \
- \9return children\
- \
- end\
- \
- function UIElement:childrenWithTag( tag, recursive )\
- \
- \9local children = self:getChildrenByTag( tag, recursive )\
- \9local i = 0\
- \9return function()\
- \9\9i = i + 1\
- \9\9return children[i]\
- \9end\
- \
- end\
- \
- function UIElement:getChildrenAt( x, y )\
- \
- \9local ping = MouseEvent( Event.MOUSEPING, x + self.ox, y + self.oy )\
- \9ping.elements = {}\
- \
- \9for i = #self.children, 1, -1 do\
- \9\9self.children[i]:handle( ping )\
- \9end\
- \9return unpack( ping.elements )\
- \
- end\
- \
- function UIElement:childrenAt( x, y )\
- \
- \9local children = self:getChildrenAt( tag, recursive )\
- \9local i = 0\
- \9return function()\
- \9\9i = i + 1\
- \9\9return children[i]\
- \9end\
- \
- end\
- \
- function UIElement:addTag( tag )\
- \9self.tags[#self.tags + 1] = tag\
- end\
- \
- function UIElement:removeTag( tag )\
- \
- \9for i = #self.tags, 1, -1 do\
- \9\9if self.tags[i] == tag then\
- \9\9\9table.remove( self.tags, i )\
- \9\9end\
- \9end\
- \
- end\
- \
- function UIElement:hasTag( tag )\
- \
- \9for i = #self.tags, 1, -1 do\
- \9\9if self.tags[i] == tag then\
- \9\9\9return true\
- \9\9end\
- \9end\
- \9return false\
- \
- end\
- \
- function UIElement:addChild( child )\
- \
- \9if child.parent then\
- \9\9child.parent:removeChild( child )\
- \9end\
- \
- \9self.children[#self.children + 1] = child\
- \9child.raw.parent = self\
- \9child:onParentChanged()\
- \
- \9self.changed = true\
- \
- \9return child\
- \
- end\
- \
- function UIElement:removeChild( child )\
- \
- \9for i = #self.children, 1, -1 do\
- \9\9if self.children[i] == child then\
- \9\9\9table.remove( self.children, i )\
- \9\9\9child.raw.parent = nil\
- \9\9\9child:onParentChanged()\
- \9\9\9self.changed = true\
- \9\9\9break\
- \9\9end\
- \9end\
- \9return child\
- \
- end\
- \
- function UIElement:setParent( parent )\
- \
- \9if parent then\
- \9\9parent:addChild( self )\
- \9elseif self.parent then\
- \9\9self.parent:removeChild( self )\
- \9end\
- \
- end\
- \
- function UIElement:remove()\
- \9if self.parent then\
- \9\9self.parent:removeChild( self )\
- \9end\
- end\
- \
- function UIElement:transitionInLeft( easing )\
- \9self.x = -self.width\
- \9return self.animationHandler:createRoundedTween( \"x\", self, { x = 0 }, self.transitionTime, easing )\
- end\
- \
- function UIElement:transitionInLeftFrom( x, easing )\
- \9self.x = x\
- \9return self.animationHandler:createRoundedTween( \"x\", self, { x = 0 }, self.transitionTime, easing )\
- end\
- \
- function UIElement:transitionInRight( easing )\
- \9self.x = self.parent.width\
- \9return self.animationHandler:createRoundedTween( \"x\", self, { x = self.parent.width - self.width }, self.transitionTime, easing )\
- end\
- \
- function UIElement:transitionInRightFrom( x, easing )\
- \9self.x = x\
- \9return self.animationHandler:createRoundedTween( \"x\", self, { x = self.parent.width - self.width }, self.transitionTime, easing )\
- end\
- \
- function UIElement:transitionInTop( easing )\
- \9self.y = -self.height\
- \9return self.animationHandler:createRoundedTween( \"y\", self, { y = 0 }, self.transitionTime, easing )\
- end\
- \
- function UIElement:transitionInTopFrom( y, easing )\
- \9self.y = y\
- \9return self.animationHandler:createRoundedTween( \"y\", self, { y = 0 }, self.transitionTime, easing )\
- end\
- \
- function UIElement:transitionInBottom( easing )\
- \9self.y = self.parent.height\
- \9return self.animationHandler:createRoundedTween( \"y\", self, { y = self.parent.height - self.height }, self.transitionTime, easing )\
- end\
- \
- function UIElement:transitionInBottomFrom( y, easing )\
- \9self.y = y\
- \9return self.animationHandler:createRoundedTween( \"y\", self, { y = self.parent.height - self.height }, self.transitionTime, easing )\
- end\
- \
- function UIElement:transitionOutLeft( easing )\
- \9local tween = self.animationHandler:createRoundedTween( \"x\", self, { x = -self.width }, self.transitionTime, easing )\
- \9function tween.onFinish() self:remove() end\
- end\
- \
- function UIElement:transitionOutLeftTo( x, easing )\
- \9local tween = self.animationHandler:createRoundedTween( \"x\", self, { x = x }, self.transitionTime, easing )\
- \9function tween.onFinish() self:remove() end\
- end\
- \
- function UIElement:transitionOutRight( easing )\
- \9local tween = self.animationHandler:createRoundedTween( \"x\", self, { x = self.parent.width }, self.transitionTime, easing )\
- \9function tween.onFinish() self:remove() end\
- end\
- \
- function UIElement:transitionOutRightTo( x, easing )\
- \9local tween = self.animationHandler:createRoundedTween( \"x\", self, { x = x }, self.transitionTime, easing )\
- \9function tween.onFinish() self:remove() end\
- end\
- \
- function UIElement:transitionOutTop( easing )\
- \9local tween = self.animationHandler:createRoundedTween( \"y\", self, { y = -self.height }, self.transitionTime, easing )\
- \9function tween.onFinish() self:remove() end\
- end\
- \
- function UIElement:transitionOutTopTo( y, easing )\
- \9local tween = self.animationHandler:createRoundedTween( \"y\", self, { y = y }, self.transitionTime, easing )\
- \9function tween.onFinish() self:remove() end\
- end\
- \
- function UIElement:transitionOutBottom( easing )\
- \9local tween = self.animationHandler:createRoundedTween( \"y\", self, { y = self.parent.height }, self.transitionTime, easing )\
- \9function tween.onFinish() self:remove() end\
- end\
- \
- function UIElement:transitionOutBottomTo( y, easing )\
- \9local tween = self.animationHandler:createRoundedTween( \"y\", self, { y = y }, self.transitionTime, easing )\
- \9function tween.onFinish() self:remove() end\
- end\
- \
- function UIElement:bringToFront()\
- \9if self.parent and self.parent.children[#self.parent.children] ~= self then\
- \9\9for i = #self.parent.children, 1, -1 do\
- \9\9\9if self.parent.children[i] == self then\
- \9\9\9\9table.remove( self.parent.children, i )\
- \9\9\9end\
- \9\9end\
- \9\9self.parent.children[#self.parent.children + 1] = self\
- \9\9self.parent.changed = true\
- \9end\
- end\
- \
- function UIElement:setChanged( changed )\
- \9self.changed = changed\
- \9if changed and self.parent then self.parent.changed = true end\
- end\
- \
- function UIElement:setX( x )\
- \9self.raw.x = x\
- \9if self.parent then self.parent.changed = true end\
- end\
- \
- function UIElement:setAnimatedX( x )\
- \9return self.animationHandler:createRoundedTween( \"x\", self, { x = x }, self.transitionTime )\
- end\
- \
- function UIElement:setY( y )\
- \9self.raw.y = y\
- \9if self.parent then self.parent.changed = true end\
- end\
- \
- function UIElement:setAnimatedY( y )\
- \9return self.animationHandler:createRoundedTween( \"y\", self, { y = y }, self.transitionTime )\
- end\
- \
- function UIElement:setOx( ox )\
- \9self.raw.ox = ox\
- \9self.changed = true\
- end\
- \
- function UIElement:setAnimatedOX( ox )\
- \9return self.animationHandler:createRoundedTween( \"ox\", self, { ox = ox }, self.transitionTime )\
- end\
- \
- function UIElement:setOy( oy )\
- \9self.raw.oy = oy\
- \9self.changed = true\
- end\
- \
- function UIElement:setAnimatedOY( oy )\
- \9return self.animationHandler:createRoundedTween( \"oy\", self, { oy = oy }, self.transitionTime )\
- end\
- \
- function UIElement:setWidth( width )\
- \9self.raw.width = width\
- \9for i = 1, #self.children do\
- \9\9self.children[i]:onParentResized()\
- \9end\
- \9self.canvas.width = width\
- \9self.changed = true\
- end\
- \
- function UIElement:setAnimatedWidth( width )\
- \9return self.animationHandler:createRoundedTween( \"width\", self, { width = width }, self.transitionTime )\
- end\
- \
- function UIElement:setHeight( height )\
- \9self.raw.height = height\
- \9for i = 1, #self.children do\
- \9\9self.children[i]:onParentResized()\
- \9end\
- \9self.canvas.height = height\
- \9self.changed = true\
- end\
- \
- function UIElement:setAnimatedHeight( height )\
- \9return self.animationHandler:createRoundedTween( \"height\", self, { height = height }, self.transitionTime )\
- end\
- \
- function UIElement:update( dt )\
- \9self:onUpdate( dt )\
- \9for i = 1, #self.children do\
- \9\9self.children[i]:update( dt )\
- \9end\
- end\
- \
- function UIElement:draw()\
- \
- \9if not self.changed then return end\
- \9self.changed = false\
- \
- \9local canvas = self.canvas\
- \9canvas.cursor = nil\
- \9self:onDraw()\
- \
- \9for i = 1, #self.children do\
- \9\9local child = self.children[i]\
- \9\9child:draw()\
- \9\9child.canvas:drawTo( canvas, child.x + self.ox, child.y + self.oy )\
- \9\9if child.canvas.cursor then\
- \9\9\9canvas.cursor = {\
- \9\9\9\9x = child.canvas.cursor.x + child.x + self.ox;\
- \9\9\9\9y = child.canvas.cursor.y + child.y + self.oy;\
- \9\9\9\9colour = child.canvas.cursor.colour;\
- \9\9\9}\
- \9\9end\
- \9end\
- \
- end\
- \
- function UIElement:handle( event )\
- \
- \9local children = {}\
- \9for i = 1, #self.children do\
- \9\9children[i] = self.children[i]\
- \9end\
- \9if event:typeOf( MouseEvent ) then\
- \9\9local within = event:isInArea( 0, 0, self.width, self.height )\
- \9\9for i = #children, 1, -1 do\
- \9\9\9local child = children[i]\
- \9\9\9child:handle( event:clone( child.x + self.ox, child.y + self.oy, within ) )\
- \9\9end\
- \9else\
- \9\9for i = #children, 1, -1 do\
- \9\9\9children[i]:handle( event )\
- \9\9end\
- \9end\
- \
- \9if event:typeOf( MouseEvent ) and self.handlesMouse then\
- \9\9if event.name == Event.MOUSEPING and event:isInArea( 0, 0, self.width, self.height ) then\
- \9\9\9local elements = event.elements\
- \9\9\9elements[#elements + 1] = self\
- \9\9end\
- \9\9self:onMouseEvent( event )\
- \9elseif event:typeOf( KeyboardEvent ) and self.handlesKeyboard then\
- \9\9self:onKeyboardEvent( event )\
- \9elseif event:typeOf( TextEvent ) and self.handlesText then\
- \9\9self:onTextEvent( event )\
- \9end\
- \
- end\
- \
- function UIElement:onUpdate( dt ) end\
- function UIElement:onDraw() end\
- function UIElement:onMouseEvent( event ) end\
- function UIElement:onKeyboardEvent( event ) end\
- function UIElement:onTextEvent( event ) end\
- function UIElement:onParentChanged() end\
- function UIElement:onParentResized() end\
- \
- function UIElement:tostring()\
- \9return \"[Instance] \" .. self.class.name .. \" \" .. tostring( self.id ) .. \" (\" .. self.x .. \",\" .. self.y .. \" \" .. self.width .. \"x\" .. self.height .. \")\"\
- end";
- ["/lib/class.lua"] = "\
- local type, sub, upper, unpack = type, string.sub, string.upper, unpack\
- \
- -- cache the names of the property getter/setter functions\
- local setters = setmetatable( {}, { __index = function( self, k ) if type( k ) ~= \"string\" then return end local v = \"set\" .. upper( sub( k, 1, 1 ) ) .. sub( k, 2 ) self[k] = v return v end; } )\
- local getters = setmetatable( {}, { __index = function( self, k ) if type( k ) ~= \"string\" then return end local v = \"get\" .. upper( sub( k, 1, 1 ) ) .. sub( k, 2 ) self[k] = v return v end; } )\
- \
- local last_created\
- local class = {}\
- \
- local function __tostring( self )\
- \9return \"[Class] \" .. self.name\
- end\
- \
- local function newSuper( object, super )\
- \9local t = {}\
- \9if super.super then\
- \9\9t.super = newSuper( object, super.super )\
- \9end\
- \9setmetatable( t, { __index = function( t, k )\
- \9\9if type( super[k] ) == \"function\" then\
- \9\9\9return function( self, ... )\
- \9\9\9\9if self == t then\
- \9\9\9\9\9self = object\
- \9\9\9\9end\
- \9\9\9\9object.super = t.super\
- \9\9\9\9local v = { super[k]( self, ... ) }\
- \9\9\9\9object.super = t\
- \9\9\9\9return unpack( v )\
- \9\9\9end\
- \9\9else\
- \9\9\9return super[k]\
- \9\9end\
- \9end, __newindex = super, __tostring = function( self )\
- \9\9return \"[Super] \" .. tostring( super ) .. \" of \" .. tostring( object )\
- \9end } )\
- \9return t\
- end\
- \
- function class:new( name ) -- creates a new class\
- \
- \9local mt = {}\
- \9mt.__index = self\
- \9mt.__type = name\
- \9mt.__isClass = true\
- \9mt.__tostring = __tostring\
- \
- \9local classobj = {} -- the class object\
- \9classobj.name = name\
- \9classobj.mt = mt\
- \
- \9function mt:__call( ... )\
- \9\9return self:new( ... )\
- \9end\
- \
- \9function classobj:new( ... ) -- creates an instance\
- \9\9local ID = sub( tostring {}, 8 )\
- \9\9local instance, classobj = {}, self\
- \
- \9\9local useProxy = self.useGetters or self.useSetters\
- \
- \9\9local raw = useProxy and setmetatable( {}, { __index = classobj } ) or instance\
- \9\9instance.raw = raw\
- \
- \9\9instance.class = self\
- \9\9if self.super then\
- \9\9\9instance.super = newSuper( instance, self.super )\
- \9\9end\
- \
- \9\9instance.mt = {}\
- \9\9instance.mt.__isInstance = true\
- \
- \9\9function instance.mt:__tostring()\
- \9\9\9return \"[Instance] \" .. self.class.name .. \" \" .. ID\
- \9\9end\
- \
- \9\9for k, v in pairs( self.mt ) do\
- \9\9\9if k ~= \"__isClass\" and ( k ~= \"__tostring\" or v ~= __tostring ) then\
- \9\9\9\9instance.mt[k] = v\
- \9\9\9end\
- \9\9end\
- \
- \9\9if self.useGetters then\
- \9\9\9local getting = {}\
- \
- \9\9\9if type( classobj.get ) == \"function\" then\
- \9\9\9\9local genericgetter = classobj.get\
- \9\9\9\9function instance.mt:__index( k )\
- \9\9\9\9\9if not getting[k] then\
- \9\9\9\9\9\9local getter = getters[k]\
- \9\9\9\9\9\9if getter and type( classobj[getter] ) == \"function\" then\
- \9\9\9\9\9\9\9getting[k] = true\
- \9\9\9\9\9\9\9local value = classobj[getter]( self )\
- \9\9\9\9\9\9\9getting[k] = nil\
- \9\9\9\9\9\9\9return value\
- \9\9\9\9\9\9end\
- \9\9\9\9\9\9getting[k] = true\
- \9\9\9\9\9\9local use, value = genericgetter( self, k )\
- \9\9\9\9\9\9getting[k] = nil\
- \9\9\9\9\9\9if use then return value end\
- \9\9\9\9\9end\
- \9\9\9\9\9return raw[k]\
- \9\9\9\9end\
- \9\9\9else\
- \9\9\9\9function instance.mt:__index( k )\
- \9\9\9\9\9if not getting[k] then\
- \9\9\9\9\9\9local getter = getters[k]\
- \9\9\9\9\9\9if getter and type( classobj[getter] ) == \"function\" then\
- \9\9\9\9\9\9\9getting[k] = true\
- \9\9\9\9\9\9\9local value = classobj[getter]( self )\
- \9\9\9\9\9\9\9getting[k] = nil\
- \9\9\9\9\9\9\9return value\
- \9\9\9\9\9\9end\
- \9\9\9\9\9end\
- \9\9\9\9\9return raw[k]\
- \9\9\9\9end\
- \9\9\9end\
- \9\9else\
- \9\9\9instance.mt.__index = useProxy and raw or self\
- \9\9end\
- \9\9if self.useSetters then\
- \9\9\9local setting = {}\
- \
- \9\9\9if type( classobj.set ) == \"function\" then\
- \9\9\9\9local genericsetter = classobj.set\
- \9\9\9\9function instance.mt:__newindex( k, v )\
- \9\9\9\9\9if not setting[k] then\
- \9\9\9\9\9\9local setter = setters[k]\
- \9\9\9\9\9\9if setter and type( classobj[setter] ) == \"function\" then\
- \9\9\9\9\9\9\9setting[k] = true\
- \9\9\9\9\9\9\9classobj[setter]( self, v )\
- \9\9\9\9\9\9\9setting[k] = nil\
- \9\9\9\9\9\9\9return\
- \9\9\9\9\9\9end\
- \9\9\9\9\9\9setting[k] = true\
- \9\9\9\9\9\9local use = genericsetter( self, k, v )\
- \9\9\9\9\9\9setting[k] = nil\
- \9\9\9\9\9\9if use then return end\
- \9\9\9\9\9end\
- \9\9\9\9\9raw[k] = v\
- \9\9\9\9end\
- \9\9\9else\
- \9\9\9\9function instance.mt:__newindex( k, v )\
- \9\9\9\9\9if not setting[k] then\
- \9\9\9\9\9\9local setter = setters[k]\
- \9\9\9\9\9\9if setter and type( classobj[setter] ) == \"function\" then\
- \9\9\9\9\9\9\9setting[k] = true\
- \9\9\9\9\9\9\9classobj[setter]( self, v )\
- \9\9\9\9\9\9\9setting[k] = nil\
- \9\9\9\9\9\9\9return\
- \9\9\9\9\9\9end\
- \9\9\9\9\9end\
- \9\9\9\9\9raw[k] = v\
- \9\9\9\9end\
- \9\9\9end\
- \9\9end\
- \
- \9\9for k, v in pairs( self.mt ) do\
- \9\9\9if k ~= \"__isClass\" and k ~= \"__call\" and k ~= \"__index\" and ( k ~= \"__tostring\" or v ~= __tostring ) then\
- \9\9\9\9instance.mt[k] = v\
- \9\9\9end\
- \9\9end\
- \
- \9\9setmetatable( instance, instance.mt )\
- \
- \9\9if type( instance.init ) == \"function\" then -- initialise the instance\
- \9\9\9instance:init( ... )\
- \9\9end\
- \
- \9\9return instance\
- \
- \9end\
- \
- \9setmetatable( classobj, mt )\
- \
- \9getfenv( 2 )[name] = classobj\
- \9last_created = classobj\
- \
- \9return function( data )\
- \9\9for k, v in pairs( data ) do\
- \9\9\9classobj[k] = v\
- \9\9end\
- \9end\
- end\
- \
- function class:typeOf( _class )\
- \9if type( self ) ~= \"table\" then return false end\
- \9if self.class then return self.class:typeOf( _class ) end\
- \9if self == _class then return true end\
- \9if self.super then return self.super:typeOf( _class ) end\
- \9return false\
- end\
- \
- function class:type()\
- \9local _type = type( self )\
- \9pcall( function()\
- \9\9_type = getmetatable( self ).__type or _type\
- \9end )\
- \9return _type\
- end\
- \
- function class:can( method )\
- \9return type( self[method] ) == \"function\"\
- end\
- \
- function class:has( t, weak )\
- \9for k, v in pairs( t ) do\
- \9\9if ( not weak and self[k] ~= v ) or type( self[k] ) ~= type( v ) then\
- \9\9\9return false\
- \9\9end\
- \9end\
- \9return true\
- end\
- \
- function class:mixin( t )\
- \9for k, v in pairs( t ) do\
- \9\9self[k] = v\
- \9end\
- end\
- \
- function class:extends( class )\
- \9self.super = class\
- \9for k, v in pairs( class.mt ) do -- things like __add\
- \9\9if k ~= \"__index\" and k ~= \"__type\" and k ~= \"__tostring\" and k ~= \"__call\" and k ~= \"__isClass\" then\
- \9\9\9self.mt[k] = v\
- \9\9end\
- \9end\
- \9self.mt.__index = class\
- end\
- \
- function class:isClass()\
- \9return pcall( function() if not getmetatable( self ).__isClass then error \"\" end end ), nil\
- end\
- \
- function class.last()\
- \9return last_created\
- end\
- \
- setmetatable( class, { __call = function( self, ... ) return self:new( ... ) end } )\
- \
- return class";
- ["/lib/clipboard.lua"] = "\
- local c = { { \"empty\" } }\
- \
- local clipboard = {}\
- \
- function clipboard.put( t )\
- \9c = t\
- end\
- \
- function clipboard.clear()\
- \9c = { { \"empty\" } }\
- end\
- \
- function clipboard.get( type )\
- \9for i = 1, #c do\
- \9\9if c[i][1] == type then\
- \9\9\9return c[i][2]\
- \9\9end\
- \9end\
- end\
- \
- return clipboard";
- ["/lib/elements/UIButton.lua"] = "\
- require \"UIElement\"\
- local shader = require \"graphics.shader\"\
- local UIEventHelpers = require \"util.UIEventHelpers\"\
- \
- class \"UIButton\" extends \"UIElement\" {\
- \9colour = 1;\
- \9textColour = colours.grey;\
- \9text = \"\";\
- \9holding = false;\
- \9noAlign = false;\
- }\
- \
- function UIButton:init( x, y, w, h, text )\
- \9self.super:init( x, y, w, h )\
- \9self.text = text\
- end\
- \
- function UIButton:onMouseEvent( event )\
- \9local mode = UIEventHelpers.clicking.handleMouseEvent( self, event )\
- \9if mode == \"down\" or mode == \"up\" then\
- \9\9self.holding = mode == \"down\"\
- \9elseif mode == \"click\" then\
- \9\9self.holding = false\
- \9\9if self.onClick then\
- \9\9\9self:onClick()\
- \9\9end\
- \9end\
- end\
- \
- function UIButton:onDraw()\
- \9if self.holding then\
- \9\9local colour = shader.lighten[self.colour]\
- \9\9self.canvas:clear( colour == self.colour and shader.darken[colour] or colour )\
- \9else\
- \9\9self.canvas:clear( self.colour )\
- \9end\
- \9self.canvas:drawWrappedText( 0, 0, self.width, self.height, {\
- \9\9text = self.text;\
- \9\9alignment = self.noAlign and \"left\" or \"centre\";\
- \9\9verticalAlignment = self.noAlign and \"top\" or \"centre\";\
- \9\9textColour = self.textColour;\
- \9} )\
- end\
- \
- function UIButton:setHolding( state )\
- \9self.raw.holding = state\
- \9self.changed = true\
- end\
- \
- function UIButton:setText( text )\
- \9self.raw.text = text == nil and \"\" or tostring( text )\
- \9self.changed = true\
- end\
- \
- function UIButton:setColour( colour )\
- \9self.raw.colour = colour\
- \9self.changed = true\
- end\
- \
- function UIButton:setTextColour( textColour )\
- \9self.raw.textColour = textColour\
- \9self.changed = true\
- end";
- ["/lib/elements/UICheckbox.lua"] = "\
- require \"UIElement\"\
- local UIEventHelpers = require \"util.UIEventHelpers\"\
- \
- class \"UICheckbox\" extends \"UIElement\" {\
- \9colour = colours.lightGrey;\
- \9checkColour = colours.black;\
- \9check = \"x\";\
- \9toggled = false;\
- }\
- \
- function UICheckbox:init( x, y )\
- \9self.super:init( x, y, 1, 1 )\
- end\
- \
- function UICheckbox:onLabelPressed()\
- \9self.toggled = not self.toggled\
- \9if self.onToggle then\
- \9\9self:onToggle()\
- \9end\
- end\
- \
- function UICheckbox:onMouseEvent( event )\
- \
- \9if UIEventHelpers.clicking.handleMouseEvent( self, event ) == \"click\" then\
- \9\9self.toggled = not self.toggled\
- \9\9if self.onToggle then\
- \9\9\9self:onToggle()\
- \9\9end\
- \9end\
- \
- end\
- \
- function UICheckbox:onDraw()\
- \
- \9self.canvas:clear( self.colour )\
- \9self.canvas:drawPoint( 0, 0, {\
- \9\9colour = self.colour\
- \9} )\
- \9if self.toggled then\
- \9\9self.canvas:drawText( 0, 0, {\
- \9\9\9textColour = self.checkColour;\
- \9\9\9text = self.check;\
- \9\9} )\
- \9end\
- \
- end\
- \
- function UICheckbox:setWidth() end\
- function UICheckbox:setHeight() end\
- \
- function UICheckbox:setHolding( state )\
- \9self.raw.holding = state\
- \9self.changed = true\
- end\
- \
- function UICheckbox:setColour( colour )\
- \9self.raw.colour = colour\
- \9self.changed = true\
- end\
- \
- function UICheckbox:setCheckColour( colour )\
- \9self.raw.checkColour = colour\
- \9self.changed = trur\
- end\
- \
- function UICheckbox:setCheck( check )\
- \9self.raw.check = tostring( check )\
- \9self.changed = true\
- end\
- \
- function UICheckbox:setToggled( bool )\
- \9self.raw.toggled = bool\
- \9self.changed = true\
- end";
- ["/lib/elements/UIColourSelector.lua"] = "\
- require \"UIElement\"\
- local UIEventHelpers = require \"util.UIEventHelpers\"\
- \
- local log4 = math.log( 4 )\
- \
- class \"UIColourSelector\" extends \"UIElement\" {\
- \9ratio = 4/4; -- 1/16, 2/8, 8/2, 16/1\
- }\
- \
- function UIColourSelector:init( x, y, w, h, ratio )\
- \9self.super:init( x, y, w, h )\
- \
- \9self.raw.ratio = ratio\
- \9self.width = w or 8\
- \9self.height = h or 4\
- end\
- \
- function UIColourSelector:onMouseEvent( event )\
- \9if UIEventHelpers.clicking.handleMouseEvent( self, event ) == \"click\" then\
- \9\9local columns = 2 ^ ( 2 + math.log( self.ratio ) / log4 )\
- \9\9local rows = 2 ^ ( 2 - math.log( self.ratio ) / log4 )\
- \9\9local pixelwidth = self.width / columns\
- \9\9local pixelheight = self.height / rows\
- \9\9local x = math.floor( event.x / pixelwidth )\
- \9\9local y = math.floor( event.y / pixelheight )\
- \9\9local index = x + y * columns\
- \9\9if self.onSelect then\
- \9\9\9self:onSelect( 2 ^ index, index )\
- \9\9end\
- \9end\
- end\
- \
- function UIColourSelector:onDraw()\
- \9local columns = 2 ^ ( 2 + math.log( self.ratio ) / log4 )\
- \9local rows = 2 ^ ( 2 - math.log( self.ratio ) / log4 )\
- \9local pixelwidth = self.width / columns\
- \9local pixelheight = self.height / rows\
- \9local x, y = 0, 0\
- \9for i = 0, 15 do\
- \9\9self.canvas:drawRectangle( x, y, pixelwidth, pixelheight, {\
- \9\9\9colour = 2 ^ i;\
- \9\9\9filled = true;\
- \9\9} )\
- \9\9x = x + pixelwidth\
- \9\9if x == self.width then\
- \9\9\9x = 0\
- \9\9\9y = y + pixelheight\
- \9\9end\
- \9end\
- end\
- \
- function UIColourSelector:setWidth( width )\
- \9local columns = 2 ^ ( 2 + math.log( self.ratio ) / log4 )\
- \9self.super:setWidth( math.max( columns, math.floor( width / columns ) * columns ) )\
- end\
- function UIColourSelector:setHeight( height )\
- \9local rows = 2 ^ ( 2 - math.log( self.ratio ) / log4 )\
- \9self.super:setHeight( math.max( rows, math.floor( height / rows ) * rows ) )\
- end\
- \
- function UIColourSelector:setRatio( ratio )\
- \9if ratio == 1/16 or ratio == 2/8 or ratio == 4/4 or ratio == 8/2 or ratio == 16/1 then\
- \9\9self.raw.ratio = ratio\
- \9\9self.width = self.width\
- \9\9self.height = self.height\
- \9else\
- \9\9error( \"unsupported ratio\", 3 )\
- \9end\
- end";
- ["/lib/elements/UIContainer.lua"] = "\
- require \"UIElement\"\
- require \"Event.MouseEvent\"\
- \
- local UIDrawingHelpers = require \"util.UIDrawingHelpers\"\
- local UIEventHelpers = require \"util.UIEventHelpers\"\
- \
- class \"UIContainer\" extends \"UIElement\" {\
- \9colour = 1;\
- \
- \9scrollbars = true;\
- }\
- \
- UIContainer:mixin( UIEventHelpers.scrollbar.mixin )\
- \
- function UIContainer:draw()\
- \
- \9if not self.changed then return end\
- \
- \9local canvas = self.canvas\
- \9canvas:clear( self.colour )\
- \
- \9self.super:draw()\
- \
- \9if self.scrollbars then\
- \9\9UIDrawingHelpers.scrollbar.drawScrollbars( self )\
- \9end\
- \
- end\
- \
- function UIContainer:onMouseEvent( event )\
- \9if event:isInArea( 0, 0, self.width, self.height ) then\
- \9\9UIEventHelpers.scrollbar.handleMouseScroll( self, event )\
- \9end\
- end\
- \
- function UIContainer:handle( event )\
- \
- \9if event:typeOf( MouseEvent ) and self.scrollbars then\
- \9\9UIEventHelpers.scrollbar.handleMouseEvent( self, event )\
- \9end\
- \9return UIElement.handle( self, event )\
- \
- end\
- \
- function UIContainer:getContentWidth()\
- \9local max = 0\
- \9for i = 1, #self.children do\
- \9\9max = math.max( max, self.children[i].x + self.children[i].width )\
- \9end\
- \9return max\
- end\
- \
- function UIContainer:getContentHeight()\
- \9local max = 0\
- \9for i = 1, #self.children do\
- \9\9max = math.max( max, self.children[i].y + self.children[i].height )\
- \9end\
- \9return max\
- end\
- \
- function UIContainer:setWidth( width )\
- \9self.super:setWidth( width )\
- \9if self:getContentWidth() + self.ox < self.width then\
- \9\9self.ox = math.min( 0, self.width - self:getContentWidth() )\
- \9end\
- end\
- \
- function UIContainer:setHeight( height )\
- \9self.super:setHeight( height )\
- \9if self:getContentHeight() + self.oy < self.height then\
- \9\9self.oy = math.min( 0, self.height - self:getContentHeight() )\
- \9end\
- end\
- \
- function UIContainer:setColour( colour )\
- \9self.raw.colour = colour\
- \9self.changed = true\
- end\
- \
- function UIContainer:setScrollbars( scrollbars )\
- \9self.raw.scrollbars = scrollbars\
- \9self.changed = true\
- end";
- ["/lib/elements/UIFileDialogue.lua"] = "\
- -- line 169\
- -- add in settings button (needs UIMenu first, which idk how to implement)\
- \
- require \"graphics.DrawingCanvas\"\
- \
- require \"UIElement\"\
- require \"UIButton\"\
- require \"UITextInput\"\
- require \"UIPanel\"\
- require \"UIContainer\"\
- require \"UIImage\"\
- require \"UIWindow\"\
- require \"UILabel\"\
- \
- local function getSize( path )\
- \9if fs.isDir( path ) then\
- \9\9local n = 0\
- \9\9for i, v in ipairs( fs.list( path ) ) do\
- \9\9\9n = n + getSize( path .. \"/\" .. v )\
- \9\9end\
- \9\9return n\
- \9end\
- \9return tonumber( select( 2, pcall( fs.getSize, path ) ) ) or 0\
- end\
- local function fmtSize( size )\
- \9local ending = \" bytes\"\
- \9if size >= 1024 then\
- \9\9size = math.floor( size / 1024 )\
- \9\9ending = \"KB\"\
- \9end\
- \9if size >= 1024 then\
- \9\9size = math.floor( size / 1024 )\
- \9\9ending = \"MB\"\
- \9end\
- \9return size .. ending\
- end\
- \
- class \"UIFileDialogue\" extends \"UIElement\" {\
- \9icons = {}; -- static\
- \
- \9sortBy = \"name\"; -- , size\
- \9sortOrder = \"ascending\"; -- , descending\
- \9custom_sorter = nil;\
- \
- \9mode = \"open\"; -- save\
- \
- \9showBackButton = true;\
- \9showForwardButton = true;\
- \9showUpButton = true;\
- \9showSettingsButton = true;\
- \9showButtons = true;\
- \9showFileName = true;\
- \
- \9backButton = nil;\
- \9forwardButton = nil;\
- \9upButton = nil;\
- \9settingsButton = nil;\
- \9addressBar = nil;\
- \9button1 = nil;\
- \9button2 = nil;\
- \9fileNameBox = nil;\
- \
- \9content = nil;\
- \
- \9header = nil;\
- \9background = nil;\
- \
- \9headerColour = colours.grey;\
- \9backgroundColour = colours.white;\
- \9headerButtonColour = colours.lightGrey;\
- \9buttonColour = colours.grey;\
- \
- \9path = nil;\
- \9history = {};\
- \9hindex = 0;\
- \
- \9fileobjects = {};\
- \9window = nil;\
- }\
- \
- UIFileDialogue.icons.default = DrawingCanvas( 4, 3 )\
- UIFileDialogue.icons.default:mapPixels {\
- \9{ 1, { 1, 256, \"-\" } };\
- \9{ 2, { 1, 256, \"-\" } };\
- \9{ 3, { 1, 256, \"-\" } };\
- \9{ 4, { 1, 256, \"-\" } };\
- \9{ 5, { 1, 256, \"f\" } };\
- \9{ 6, { 1, 256, \"i\" } };\
- \9{ 7, { 1, 256, \"l\" } };\
- \9{ 8, { 1, 256, \"e\" } };\
- \9{ 9, { 1, 256, \"-\" } };\
- \9{ 10, { 1, 256, \"-\" } };\
- \9{ 11, { 1, 256, \"-\" } };\
- \9{ 12, { 1, 256, \"-\" } };\
- }\
- \
- UIFileDialogue.icons.folder = DrawingCanvas( 4, 3 )\
- UIFileDialogue.icons.folder:mapPixels {\
- \9{ 1, { 16, 128, \"f\" } };\
- \9{ 2, { 16, 128, \"l\" } };\
- \9{ 3, { 16, 128, \"d\" } };\
- \9{ 4, { 16, 128, \"r\" } };\
- \9{ 5, { 16, 128, \" \" } };\
- \9{ 6, { 16, 128, \" \" } };\
- \9{ 7, { 16, 128, \" \" } };\
- \9{ 8, { 16, 128, \" \" } };\
- \9{ 9, { 16, 128, \" \" } };\
- \9{ 10, { 16, 128, \" \" } };\
- \9{ 11, { 16, 128, \" \" } };\
- \9{ 12, { 16, 128, \">\" } };\
- }\
- \
- function UIFileDialogue:init( x, y, w, h, path )\
- \
- \9self.super:init( x, y, w, h )\
- \
- \9self.backButton = UIButton( 0, 0, 3, 1, \" < \" )\
- \9self.forwardButton = UIButton( 0, 0, 3, 1, \" > \" )\
- \9self.upButton = UIButton( 0, 0, 3, 1, \" ^ \" )\
- \9self.settingsButton = UIButton( 0, 0, 3, 1, \" = \" )\
- \9self.addressBar = UITextInput( 0, 1, 0 )\
- \9self.button1 = UIButton( 0, 0, 8, 1, \"Open\" )\
- \9self.button2 = UIButton( 0, 0, 8, 1, \"Cancel\" )\
- \9self.fileNameBox = UITextInput( 0, 0, 0 )\
- \
- \9self.content = UIContainer( 1, 4, w - 2, h - 5 )\
- \9self.header = UIPanel( 0, 0, w, 3, self.headerColour )\
- \9self.background = UIPanel( 0, 3, w, h - 3, self.backgroundColour )\
- \
- \9self.backButton.colour = self.headerButtonColour\
- \9self.forwardButton.colour = self.headerButtonColour\
- \9self.upButton.colour = self.headerButtonColour\
- \9self.settingsButton.colour = self.headerButtonColour\
- \
- \9self.button1.colour = self.buttonColour\
- \9self.button1.textColour = colours.white\
- \9self.button2.colour = self.buttonColour\
- \9self.button2.textColour = colours.white\
- \
- \9self.fileNameBox.focussedColour = colours.lightGrey\
- \
- \9function self.addressBar:onTab()\
- \9\9self.parent.fileNameBox:focusOn()\
- \9end\
- \9function self.fileNameBox:onTab()\
- \9\9self.parent.addressBar:focusOn()\
- \9end\
- \9function self.addressBar:onEnter()\
- \9\9self.parent:goto( self.text )\
- \9end\
- \9function self.fileNameBox:onEnter()\
- \9\9self.parent.button1:onClick()\
- \9end\
- \
- \9function self.backButton:onClick()\
- \9\9self.parent:back()\
- \9end\
- \9function self.forwardButton:onClick()\
- \9\9self.parent:forward()\
- \9end\
- \9function self.upButton:onClick()\
- \9\9self.parent:goto( self.parent.path:match \"(.+)/\" or \"\" )\
- \9end\
- \
- \9function self.button1:onClick()\
- \9\9if self.parent.fileNameBox.text == \"\" then\
- \9\9\9-- error\
- \9\9elseif self.mode == \"save\" then\
- \9\9\9if self.onSave then\
- \9\9\9\9self:onSave( self.parent.path .. \"/\" .. self.parent.fileNameBox.text )\
- \9\9\9end\
- \9\9else\
- \9\9\9self.parent:goto( self.parent.path .. \"/\" .. self.parent.fileNameBox.text )\
- \9\9end\
- \9\9self.parent.fileNameBox.text = \"\"\
- \9end\
- \9function self.button2:onClick()\
- \9\9if self.parent.onCancel then\
- \9\9\9self.parent:onCancel()\
- \9\9end\
- \9end\
- \
- \9self.raw.history = {}\
- \9self.raw.fileobjects = {}\
- \
- \9self:goto( path or \"\" )\
- \
- \9self.width = w\
- \9self.height = h\
- end\
- \
- function UIFileDialogue:back()\
- \9self.hindex = math.max( 1, self.hindex - 1 )\
- \9self:show( self.history[self.hindex] )\
- end\
- \
- function UIFileDialogue:forward()\
- \9self.hindex = math.min( #self.history, self.hindex + 1 )\
- \9self:show( self.history[self.hindex] )\
- end\
- \
- function UIFileDialogue:show( path )\
- \9if fs.isDir( path ) then\
- \9\9self.raw.path = path\
- \9\9self.addressBar.text = path\
- \9\9self:calculateFiles()\
- \9\9return true\
- \9else\
- \9\9if self.window then\
- \9\9\9self.window:remove()\
- \9\9end\
- \
- \9\9local win = self:addChild( UIWindow( math.floor( self.width / 2 - 10.5 ), math.max( 4, math.floor( self.height / 2 - 4 ) ), 21, 8 ) )\
- \9\9win.title = \"Path not a folder\"\
- \
- \9\9win.resizeable = false\
- \9\9win.moveable = false\
- \
- \9\9win.content:addChild( UILabel( 1, 1, \"The path you input\" ) )\
- \9\9win.content:addChild( UILabel( 1, 2, \"does not exist!\" ) )\
- \
- \9\9local button = win.content:addChild( UIButton( math.floor( win.width / 2 - 2 ), 4, 4, 1, \"ok\" ) )\
- \9\9button.colour = colours.lightGrey\
- \9\9button.textColour = colours.black\
- \
- \9\9function button:onClick()\
- \9\9\9self.parent.parent:onClose()\
- \9\9\9self.parent.parent:remove()\
- \9\9end\
- \
- \9\9function win:onClose()\
- \9\9\9self.parent.window = nil\
- \9\9end\
- \
- \9\9self.window = win\
- \9end\
- end\
- \
- function UIFileDialogue:goto( path )\
- \9while self.history[self.hindex + 1] do\
- \9\9self.history[#self.history] = nil\
- \9end\
- \9if fs.exists( path ) and not fs.isDir( path ) then\
- \9\9if self.mode == \"open\" and self.onOpen then\
- \9\9\9self:onOpen( path )\
- \9\9elseif self.mode == \"save\" and self.onSave then\
- \9\9\9self:onSave( path )\
- \9\9end\
- \9else\
- \9\9if path ~= self.path then\
- \9\9\9if self:show( path ) then\
- \9\9\9\9self.hindex = self.hindex + 1\
- \9\9\9\9self.history[self.hindex] = path\
- \9\9\9end\
- \9\9end\
- \9end\
- end\
- \
- function UIFileDialogue:reposition()\
- \
- \9local function position( element, condition, x, y )\
- \9\9if condition then\
- \9\9\9self:addChild( element )\
- \9\9\9element.x = x\
- \9\9\9element.y = y\
- \9\9else\
- \9\9\9self:removeChild( element )\
- \9\9end\
- \9\9return condition\
- \9end\
- \
- \9local x = 1\
- \9self:addChild( self.header )\
- \9self:addChild( self.background )\
- \9self:addChild( self.content )\
- \9self:addChild( self.addressBar )\
- \
- \9if self.window then\
- \9\9self:addChild( self.window )\
- \9\9self.window.x = math.floor( self.width / 2 - self.window.width / 2 )\
- \9\9self.window.y = math.max( 4, math.floor( self.height / 2 - self.window.height / 2 ) )\
- \9end\
- \
- \9self.header.width = self.width\
- \9self.background.width = self.width\
- \9self.background.height = self.height - 3\
- \9self.content.width = self.width - 2\
- \
- \9if position( self.backButton, self.showBackButton, x, 1 ) then\
- \9\9x = x + 4\
- \9end\
- \9if position( self.forwardButton, self.showForwardButton, x, 1 ) then\
- \9\9x = x + 4\
- \9end\
- \9if position( self.upButton, self.showUpButton, x, 1 ) then\
- \9\9x = x + 4\
- \9end\
- \9self.addressBar.x = x\
- \9self.addressBar.y = 1\
- \9self.addressBar.width = self.width - x - 1\
- \9x = self.width\
- \9if position( self.settingsButton, self.showSettingsButton, x - 4, 1 ) then\
- \9\9self.addressBar.width = self.addressBar.width - 4\
- \9end\
- \
- \9self.content.height = self.height - 5 - ( ( self.showButtons or self.showFileName ) and 2 or 0 )\
- \
- \9local x = self.width\
- \9if position( self.button2, self.showButtons, x - 9, self.height - 2 ) then\
- \9\9x = x - 18\
- \9end\
- \9position( self.button1, self.showButtons, x, self.height - 2 )\
- \9if position( self.fileNameBox, self.showFileName, 1, self.height - 2 ) then\
- \9\9if self.width <= 30 then\
- \9\9\9self.fileNameBox.y = self.height - 4\
- \9\9\9self.content.height = self.content.height - 2\
- \9\9\9self.button1.x = 1\
- \9\9\9self.fileNameBox.width = self.width - 2\
- \9\9else\
- \9\9\9self.fileNameBox.width = x - 2\
- \9\9end\
- \9end\
- \
- \9local width = self.content.width\
- \9if #self.fileobjects * 4 - 1 > self.content.height then\
- \9\9width = width - 1\
- \9end\
- \9for i = 1, #self.fileobjects do\
- \9\9local ob = self.fileobjects[i]\
- \9\9ob.width = width\
- \9\9ob:getChildById \"clicker\" .width = width\
- \9\9ob:getChildById \"name\" .width = width - 4\
- \9\9ob:getChildById \"extn\" .width = width - 4\
- \9\9ob:getChildById \"size\" .width = width - 4\
- \9end\
- \
- end\
- \
- function UIFileDialogue:calculateFiles()\
- \
- \9self.fileobjects = {}\
- \9for i = #self.content.children, 1, -1 do\
- \9\9self.content.children[i]:remove()\
- \9end\
- \
- \9local path = self.path\
- \
- \9local files = fs.list( path )\
- \9local width = self.content.width\
- \9if #files * 4 - 1 > self.content.height then\
- \9\9width = width - 1\
- \9end\
- \
- \9local objects = {}\
- \9for i = 1, #files do\
- \9\9objects[i] = {\
- \9\9\9type = fs.isDir( path .. \"/\" .. files[i] ) and \"directory\" or \"file\";\
- \9\9\9name = files[i];\
- \9\9\9path = path .. \"/\" .. files[i];\
- \9\9\9extension = files[i]:match( \"%.(.-)$\" ) or \"\";\
- \9\9\9size = getSize( path .. \"/\" .. files[i] );\
- \9\9}\
- \9end\
- \
- \9if self.customSorter then\
- \9\9table.sort( objects, self.customSorter )\
- \9elseif self.sortBy == \"name\" then\
- \9\9table.sort( objects, function( a, b )\
- \9\9\9if a.type == b.type then\
- \9\9\9\9return a.name < b.name\
- \9\9\9end\
- \9\9\9return a.type == \"directory\"\
- \9\9end )\
- \9elseif self.sortBy == \"size\" then\
- \9\9table.sort( objects, function( a, b )\
- \9\9\9if a.type == b.type then\
- \9\9\9\9return a.size < b.size\
- \9\9\9end\
- \9\9\9return a.type == \"directory\"\
- \9\9end )\
- \9end\
- \
- \9local start, fin, step = 1, #objects, 1\
- \9if self.sortOrder == \"descending\" then\
- \9\9start, fin, step = #objects, 1, -1\
- \9end\
- \9local y = 0\
- \9for i = start, fin, step do\
- \9\9local container = self.content:addChild( UIElement( 0, y, width, 3 ) )\
- \9\9local icon = container:addChild( UIImage( 0, 0, objects[i].type == \"directory\" and self.icons.folder or self.icons[objects[i].extension] or self.icons.default ) )\
- \9\9local name = container:addChild( UILabel( 5, 0, objects[i].name ) )\
- \9\9local extn = container:addChild( UILabel( 5, 1, objects[i].extension ) )\
- \9\9local size = container:addChild( UILabel( 5, 2, fmtSize( objects[i].size ) ) )\
- \9\9local clicker = container:addChild( UIButton( 0, 0, container.width, container.height, \"\" ) )\
- \
- \9\9name.textColour = colours.grey\
- \
- \9\9icon.id = \"icon\"\
- \9\9name.id = \"name\"\
- \9\9extn.id = \"extn\"\
- \9\9size.id = \"size\"\
- \9\9clicker.id = \"clicker\"\
- \
- \9\9name.width = math.min( name.width, container.width - 4 )\
- \9\9extn.width = math.min( extn.width, container.width - 4 )\
- \9\9size.width = math.min( size.width, container.width - 4 )\
- \
- \9\9function clicker.canvas:drawTo() end\
- \
- \9\9function clicker:onClick()\
- \9\9\9self.parent.parent.parent:goto( path .. \"/\" .. objects[i].name )\
- \9\9end\
- \
- \9\9self.fileobjects[i] = container\
- \
- \9\9y = y + 4\
- \9end\
- end\
- \
- function UIFileDialogue:setWidth( width )\
- \9local min1 = 9 + ( self.showBackButton and 4 or 0 ) + ( self.showForwardButton and 4 or 0 ) + ( self.showUpButton and 4 or 0 ) + ( self.showSettingsButton and 4 or 0 )\
- \9local min2 = 13\
- \9local min3 = 1 + ( self.showButtons and 18 or 0 ) + ( ( self.width > 30 and self.showFileName and 10 ) or 0 )\
- \9self.super:setWidth( math.max( width, min1, min2, min3 ) )\
- \9self:reposition()\
- end\
- \
- function UIFileDialogue:setHeight( height )\
- \9local min = 13 + ( ( self.showButtons or self.showFileName ) and 2 ) + ( self.width <= 30 and 2 or 0 )\
- \9self.super:setHeight( math.max( height, min ) )\
- \9self:reposition()\
- end\
- \
- function UIFileDialogue:setPath( path )\
- \9self:goto( tostring( path ) )\
- end\
- \
- function UIFileDialogue:setShowBackButton( bool )\
- \9self.raw.showBackButton = bool\
- \9self:reposition()\
- end\
- \
- function UIFileDialogue:setShowForwardButton( bool )\
- \9self.raw.showForwardButton = bool\
- \9self:reposition()\
- end\
- \
- function UIFileDialogue:setShowUpButton( bool )\
- \9self.raw.showUpButton = bool\
- \9self:reposition()\
- end\
- \
- function UIFileDialogue:setShowSettingsButton( bool )\
- \9self.raw.showSettingsButton = bool\
- \9self:reposition()\
- end\
- \
- function UIFileDialogue:setShowFileName( bool )\
- \9self.raw.showFileName = bool\
- \9self:reposition()\
- end\
- \
- function UIFileDialogue:setShowButtons( bool )\
- \9self.raw.showButtons = bool\
- \9self:reposition()\
- end\
- \
- function UIFileDialogue:setIcons( icons )\
- \9self.raw.icons = icons\
- \9self:calculateFiles()\
- end\
- \
- function UIFileDialogue:setSortBy( sortBy )\
- \9self.raw.sortBy = sortBy\
- \9self:calculateFiles()\
- end\
- \
- function UIFileDialogue:setSortOrder( sortOrder )\
- \9self.raw.sortOrder = sortOrder\
- \9self:calculateFiles()\
- end\
- \
- function UIFileDialogue:setCustomSorter( sorter )\
- \9self.customSorter = sorter\
- \9self:calculateFiles()\
- end\
- \
- function UIFileDialogue:setMode( mode )\
- \9if mode == \"open\" then\
- \9\9self.raw.mode = \"open\"\
- \9\9self.button1.text = \"Open\"\
- \9elseif mode == \"save\" then\
- \9\9self.raw.mode = \"save\"\
- \9\9self.button1.text = \"Save\"\
- \9else\
- \9\9error( \"no such mode '\" .. tostring( mode ) .. \"'\", 3 )\
- \9end\
- end\
- \
- function UIFileDialogue:setHeaderColour( colour )\
- \9self.raw.headerColour = colour\
- \9self.header.colour = colour\
- end\
- \
- function UIFileDialogue:setBackgroundColour( colour )\
- \9self.raw.backgroundColour = colour\
- \9self.background.colour = colour\
- end\
- \
- function UIFileDialogue:setHeaderButtonColour( colour )\
- \9self.raw.headerButtonColour = colour\
- \9self.backButton.colour = colour\
- \9self.forwardButton.colour = colour\
- \9self.upButton.colour = colour\
- \9self.settingsButton.colour = colour\
- end\
- \
- function UIFileDialogue:setButtonColour( colour )\
- \9self.raw.buttonColour = colour\
- \9self.button1.colour = colour\
- \9self.button2.colour = colour\
- end";
- ["/lib/elements/UIImage.lua"] = "\
- require \"UIElement\"\
- require \"graphics.Image\"\
- local shader = require \"graphics.shader\"\
- local UIEventHelpers = require \"util.UIEventHelpers\"\
- local UIDrawingHelpers = require \"util.UIDrawingHelpers\"\
- \
- class \"UIImage\" extends \"UIElement\" {\
- \9image = nil;\
- }\
- \
- UIImage:mixin( UIEventHelpers.scrollbar.mixin )\
- \
- function UIImage:init( x, y, image )\
- \9self.image = image\
- \9self.super:init( x, y, self.image.width, self.image.height )\
- end\
- \
- function UIImage:onMouseEvent( event )\
- \9UIEventHelpers.scrollbar.handleMouseEvent( self, event )\
- \9UIEventHelpers.scrollbar.handleMouseScroll( self, event )\
- \9if UIEventHelpers.clicking.handleMouseEvent( self, event ) == \"click\" then\
- \9\9if self.onClick then\
- \9\9\9self:onClick()\
- \9\9end\
- \9end\
- end\
- \
- function UIImage:onDraw()\
- \9self.canvas:clear()\
- \9self.image:drawTo( self.canvas, self.ox, self.oy )\
- \9UIDrawingHelpers.scrollbar.drawScrollbars( self )\
- end\
- \
- function UIImage:setImage( image )\
- \9if type( image ) == \"string\" then\
- \9\9self.raw.image = Image( image )\
- \9else\
- \9\9self.raw.image = image\
- \9end\
- \9self.changed = true\
- end\
- \
- function UIImage:setWidth( width )\
- \9self.super:setWidth( width )\
- \9if self:getContentWidth() + self.ox < self.width then\
- \9\9self.ox = math.min( 0, self.width - self:getContentWidth() )\
- \9end\
- end\
- \
- function UIImage:setHeight( height )\
- \9self.super:setHeight( height )\
- \9if self:getContentHeight() + self.oy < self.height then\
- \9\9self.oy = math.min( 0, self.height - self:getContentHeight() )\
- \9end\
- end\
- \
- function UIImage:getContentWidth()\
- \9return self.image.width\
- end\
- \
- function UIImage:getContentHeight()\
- \9return self.image.height\
- end";
- ["/lib/elements/UILabel.lua"] = "\
- require \"UIElement\"\
- local UIEventHelpers = require \"util.UIEventHelpers\"\
- \
- class \"UILabel\" extends \"UIElement\" {\
- \9link = nil;\
- \9textColour = colours.lightGrey;\
- \9text = \"\";\
- }\
- \
- function UILabel:init( x, y, text, link )\
- \9self.super:init( x, y, #text, 1 )\
- \9self.raw.text = text\
- \9self.raw.link = link\
- end\
- \
- function UILabel:onMouseEvent( event )\
- \9local mode = UIEventHelpers.clicking.handleMouseEvent( self, event )\
- \9if mode == \"click\" then\
- \9\9if self.link and self.link.onLabelPressed then\
- \9\9\9self.link:onLabelPressed()\
- \9\9end\
- \9end\
- end\
- \
- function UILabel:onDraw()\
- \9self.canvas:drawText( 0, 0, {\
- \9\9text = self.text:sub( 1, self.width );\
- \9\9textColour = self.textColour;\
- \9} )\
- end\
- \
- function UILabel:setText( text )\
- \9self.width = #tostring( text )\
- \9self.raw.text = tostring( text )\
- \9self.changed = true\
- end\
- \
- function UILabel:setTextColour( textColour )\
- \9self.raw.textColour = textColour\
- \9self.changed = true\
- end";
- ["/lib/elements/UIMenu.lua"] = "\
- require \"UIElement\"\
- require \"UIContainer\"\
- require \"UIPanel\"\
- require \"UILabel\"\
- require \"UIButton\"\
- require \"Event.Event\"\
- local shader = require \"graphics.shader\"\
- local UIEventHelpers = require \"util.UIEventHelpers\"\
- \
- local function checkContent( options )\
- \9for i = 1, #options do\
- \9\9if options[i].type == \"button\" or options[i].type == \"menu\" or options[i].type == \"label\" then\
- \9\9\9if type( options[i].text ) ~= \"string\" then\
- \9\9\9\9error( \"expected string text for option \" .. i .. \", got \" .. type( options[i].text ), 4 )\
- \9\9\9end\
- \9\9end\
- \9end\
- end\
- \
- local function constructContent( menu, options, parent )\
- \9local width, height = 2, #options\
- \9for i = 1, #options do\
- \9\9if options[i].type == \"button\" or options[i].type == \"menu\" then\
- \9\9\9width = math.max( width, #options[i].text + 2 )\
- \9\9elseif options[i].type == \"label\" then\
- \9\9\9width = math.max( width, #options[i].text + 3 )\
- \9\9elseif options[i].type == \"custom\" then\
- \9\9\9width = math.max( width, 2 + options[i].element.width )\
- \9\9\9height = height + options[i].element.height - 1\
- \9\9end\
- \9end\
- \
- \9local container = UIContainer( 0, 0, width + 1, math.min( 10, height ) + 1 )\
- \9local shadow = container:addChild( UIPanel( 1, 1, width, math.min( 10, height ), colours.grey ) )\
- \9local content = container:addChild( UIContainer( 0, 0, width, math.min( 10, height ) ) )\
- \9content.id = \"content\"\
- \
- \9local children = {}\
- \
- \9function content:clickRegistered( sx, sy )\
- \9\9if not ( sx >= 0 and sy >= 0 and sx < self.width and sy < self.height ) then -- not within self\
- \9\9\9for i = 1, #children do\
- \9\9\9\9if children[i].parent == self.parent.parent and children[i]:getElementById \"content\" :clickRegistered( self.parent.x + sx - children[i].x, self.parent.y + sy - children[i].y ) then\
- \9\9\9\9\9return true\
- \9\9\9\9end\
- \9\9\9end\
- \9\9\9return false\
- \9\9end\
- \9\9return true\
- \9end\
- \
- \9function content:onMouseEvent( event )\
- \
- \9\9UIContainer.onMouseEvent( self, event )\
- \9\
- \9\9if event.name == Event.MOUSEUP and not self:clickRegistered( event.x, event.y ) then\
- \9\9\9if parent then\
- \9\9\9\9menu:closeSubframe( self.parent )\
- \9\9\9else\
- \9\9\9\9menu:close()\
- \9\9\9end\
- \9\9end\
- \
- \9end\
- \
- \9local y = 0\
- \
- \9for i = 1, #options do\
- \9\9local element\
- \9\9if options[i].type == \"button\" then\
- \9\9\9element = content:addChild( UIButton( 1, y, width - 2, 1, options[i].text ) )\
- \9\9\9element.noAlign = true\
- \9\9\9element.onClick = options[i].onClick\
- \9\9\9y = y + 1\
- \9\9elseif options[i].type == \"menu\" then\
- \9\9\9local _y = y\
- \9\9\9element = content:addChild( UIButton( 1, y, width - 2, 1, options[i].text ) )\
- \9\9\9element.noAlign = true\
- \9\9\9local content = constructContent( menu, options[i].content, true )\
- \
- \9\9\9function element:onClick()\
- \9\9\9\9menu:openSubframe( content )\
- \9\9\9\9content.x = self.parent.x + self.parent.width\
- \9\9\9\9content.y = self.parent.y + _y\
- \9\9\9end\
- \
- \9\9\9children[#children + 1] = content\
- \
- \9\9\9y = y + 1\
- \9\9elseif options[i].type == \"label\" then\
- \9\9\9element = content:addChild( UILabel( 2, y, options[i].text ) )\
- \9\9\9y = y + 1\
- \9\9elseif options[i].type == \"rule\" then\
- \9\9\9element = content:addChild( UILabel( 1, y, (\"-\"):rep( width - 2 ) ) )\
- \9\9\9y = y + 1\
- \9\9elseif options[i].type == \"space\" then\
- \9\9\9y = y + 1\
- \9\9elseif options[i].type == \"custom\" then\
- \9\9\9element = content:addChild( options[i].element )\
- \9\9\9element.x = 1\
- \9\9\9element.y = y\
- \9\9\9element.width = width - 2\
- \9\9\9y = y + element.height\
- \9\9end\
- \
- \
- \9\9if element then\
- \9\9\9container:addChild( element )\
- \9\9end\
- \9end\
- \
- \9return container\
- end\
- \
- class \"UIMenu\" extends \"UIElement\" {\
- \9colour = 1;\
- \9textColour = colours.grey;\
- \9text = \"\";\
- \9holding = false;\
- \9content = nil;\
- \9contentFrame = nil;\
- \9isOpen = false;\
- \9subframes = {};\
- }\
- \
- function UIMenu:init( x, y, w, h, text, options, contentFrame )\
- \9self.super:init( x, y, w, h )\
- \9self.text = text\
- \9checkContent( options )\
- \9self.raw.content = constructContent( self, options )\
- \9self.raw.contentFrame = contentFrame\
- \9self.raw.subframes = {}\
- end\
- \
- function UIMenu:open()\
- \9if self.closed ~= os.clock() then\
- \9\9if not self.isOpen then\
- \9\9\9( self.contentFrame or self.parent ):addChild( self.content )\
- \9\9\9if self.onOpened then\
- \9\9\9\9self:onOpened()\
- \9\9\9end\
- \9\9\9self.isOpen = true\
- \9\9end\
- \9end\
- end\
- \
- function UIMenu:openSubframe( frame )\
- \9if frame.closed ~= os.clock() then\
- \9\9frame.parent = self.contentFrame or self.parent\
- \9\9self.subframes[#self.subframes + 1] = frame\
- \9end\
- end\
- \
- function UIMenu:closeSubframe( frame )\
- \9frame:remove()\
- \9frame.closed = os.clock()\
- \9for i = #self.subframes, 1, -1 do\
- \9\9if self.subframes[i] == frame then\
- \9\9\9table.remove( self.subframes, i )\
- \9\9end\
- \9end\
- end\
- \
- function UIMenu:close()\
- \9self.closed = os.clock()\
- \9if self.isOpen then\
- \9\9self.content:remove()\
- \9\9for i = #self.subframes, 1, -1 do\
- \9\9\9self.subframes[i]:remove()\
- \9\9\9self.subframes[i] = nil\
- \9\9end\
- \9\9if self.onClosed then\
- \9\9\9self:onClosed()\
- \9\9end\
- \9\9self.isOpen = false\
- \9end\
- end\
- \
- function UIMenu:onMouseEvent( event )\
- \9local mode = UIEventHelpers.clicking.handleMouseEvent( self, event )\
- \9if mode == \"down\" or mode == \"up\" then\
- \9\9self.holding = mode == \"down\"\
- \9elseif mode == \"click\" then\
- \9\9self.holding = false\
- \9\9if self.isOpen then\
- \9\9\9self:close()\
- \9\9else\
- \9\9\9self:open()\
- \9\9end\
- \9end\
- end\
- \
- function UIMenu:onDraw()\
- \9if self.holding then\
- \9\9local colour = shader.lighten[self.colour]\
- \9\9self.canvas:clear( colour == self.colour and shader.darken[colour] or colour )\
- \9else\
- \9\9self.canvas:clear( self.colour )\
- \9end\
- \9self.canvas:drawWrappedText( 0, 0, self.width, self.height, {\
- \9\9text = self.text;\
- \9\9alignment = \"centre\";\
- \9\9verticalAlignment = \"centre\";\
- \9\9textColour = self.textColour;\
- \9} )\
- end\
- \
- function UIMenu:setHolding( state )\
- \9self.raw.holding = state\
- \9self.changed = true\
- end\
- \
- function UIMenu:setText( text )\
- \9self.raw.text = text == nil and \"\" or tostring( text )\
- \9self.changed = true\
- end\
- \
- function UIMenu:setColour( colour )\
- \9self.raw.colour = colour\
- \9self.changed = true\
- end\
- \
- function UIMenu:setTextColour( textColour )\
- \9self.raw.textColour = textColour\
- \9self.changed = true\
- end\
- \
- function UIMenu:setContentFrame( frame )\
- \9self.contentFrame = frame\
- \9if self.isOpen and frame then\
- \9\9self.content.parent = frame\
- \9\9for i = #self.subframes, 1, -1 do\
- \9\9\9self.subframes[i].parent = frame\
- \9\9end\
- \9end\
- end";
- ["/lib/elements/UIMultilineTextInput.lua"] = "\
- require \"UIElement\"\
- \
- local UIDrawingHelpers = require \"util.UIDrawingHelpers\"\
- local UIEventHelpers = require \"util.UIEventHelpers\"\
- \
- class \"UIMultilineTextInput\" extends \"UIElement\" {\
- \9lines = {};\
- \9fmtLines = {};\
- \
- \9cx = 1;\
- \9cy = 1;\
- \
- \9selected = false;\
- \9scx = 1;\
- \9scy = 1;\
- \
- \9focussed = true;\
- \
- \9handlesKeyboard = true;\
- \9handlesText = true;\
- }\
- \
- function UIMultilineTextInput:init( x, y, width, height )\
- \9self.super:init( x, y, width, height )\
- \9self.lines = {}\
- end\
- \
- function UIMultilineTextInput:recolourLine( i )\
- \9if self.lines[i] then\
- \9\9local f = self.colourer or function( _, line )\
- \9\9\9local t = {}\
- \9\9\9for i = 1, #line do\
- \9\9\9\9t[i] = { colours.white, colours.grey, line:sub( i, i ) }\
- \9\9\9end\
- \9\9\9return t\
- \9\9end\
- \9\9self.fmtLines[i] = f( self, self.lines[i] )\
- \9end\
- end\
- \
- function UIMultilineTextInput:setCursor( x, y )\
- \9\
- end\
- \
- function UIMultilineTextInput:write( text )\
- \
- \9if self.selected then\
- \9\9local y1, y2, x1, x2 = self.cy, self.scy, self.cx, self.scx\
- \9\9if y1 > y1 or ( y1 == y2 and x1 > x2 ) then\
- \9\9\9y2, y1, x2, x1 = y1, y2, x1, x2\
- \9\9end\
- \
- \9\9self.lines[y1] = self.lines[y1]:sub( 1, x1 - 1 ) .. self.lines[y2]:sub( x2 )\
- \
- \9\9for i = 1, y2 - y1 do\
- \9\9\9table.remove( self.lines, y1 + i )\
- \9\9\9table.remove( self.fmtLines, y1 + i )\
- \9\9end\
- \
- \9\9self.selected = false\
- \9\9self.cy = y1\
- \9\9self.cx = x1\
- \9end\
- \
- \9local newlines = select( 2, text:gsub( \"\\n\", \"\" ) )\
- \
- \9for i = 1, newlines do\
- \9\9table.insert( self.lines, self.cy + 1, \"\" )\
- \9\9table.insert( self.fmtLines, self.cy + 1, {} )\
- \9end\
- \
- \9local ending = self.lines[self.cy]:sub( self.cx )\
- \9self.lines[self.cy] = self.lines[self.cy]:sub( 1, self.cx - 1 )\
- \9local i = self.cy\
- \
- \9for line in text:gmatch \"[^\\n]+\" do\
- \9\9self.lines[i] = self.lines[i] .. line\
- \9\9i = i + 1\
- \9end\
- \9self.lines[self.cy + newlines] = self.lines[self.cy + newlines] .. ending\
- \
- \9for i = 0, newlines do\
- \9\9self:recolourLine( self.cy + i )\
- \9end\
- \
- \9self.cy = self.cy + newlines\
- \9if newlines > 0 then self.cx = 1 end\
- \9self.cx = self.cx + #( text:match \"^.*\\n(.-)$\" or text )\
- \
- end\
- \
- function UIMultilineTextInput:onKeyboardEvent( event )\
- \9do return end\
- \9if not event.handled and self.focussed and event.name == Event.KEYDOWN then\
- \9\9if event:matchesHotkey \"shift-left\" then\
- \9\9\9self.selection = self.selection or self.cursor\
- \9\9\9self.cursor = self.cursor - 1\
- \9\9\9self.changed = true\
- \9\9\9event.handled = true\
- \9\9elseif event:matchesHotkey \"shift-right\" then\
- \9\9\9self.selection = self.selection or self.cursor\
- \9\9\9self.cursor = self.cursor + 1\
- \9\9\9self.changed = true\
- \9\9\9event.handled = true\
- \9\9elseif event:matchesHotkey \"ctrl-a\" then\
- \9\9\9self.cursor = #self.text\
- \9\9\9self.selection = 0\
- \9\9\9self.changed = true\
- \9\9\9event.handled = true\
- \9\9elseif event:matchesHotkey \"ctrl-c\" then\
- \9\9\9if self.selection then\
- \9\9\9\9clipboard.put {\
- \9\9\9\9\9{ \"plaintext\", self.text:sub( math.min( self.cursor, self.selection ) + 1, math.max( self.cursor, self.selection ) + 1 ) }\
- \9\9\9\9}\
- \9\9\9else\
- \9\9\9\9clipboard.put {\
- \9\9\9\9\9{ \"plaintext\", self.text }\
- \9\9\9\9}\
- \9\9\9end\
- \9\9\9event.handled = true\
- \9\9elseif event:matchesHotkey \"ctrl-x\" then\
- \9\9\9if self.selection then\
- \9\9\9\9clipboard.put {\
- \9\9\9\9\9{ \"plaintext\", self.text:sub( math.min( self.cursor, self.selection ) + 1, math.max( self.cursor, self.selection ) + 1 ) }\
- \9\9\9\9}\
- \9\9\9\9self:write \"\"\
- \9\9\9else\
- \9\9\9\9clipboard.put {\
- \9\9\9\9\9{ \"plaintext\", self.text }\
- \9\9\9\9}\
- \9\9\9\9self.text = \"\"\
- \9\9\9end\
- \9\9\9self.changed = true\
- \9\9\9event.handled = true\
- \9\9elseif event:matchesHotkey \"ctrl-b\" then\
- \9\9\9local text = clipboard.get \"plaintext\"\
- \9\9\9if text then\
- \9\9\9\9self:write( text )\
- \9\9\9\9self.changed = true\
- \9\9\9end\
- \9\9\9event.handled = true\
- \9\9elseif event:matchesHotkey \"left\" then\
- \9\9\9if self.selection then\
- \9\9\9\9self.cursor = math.min( self.cursor, self.selection )\
- \9\9\9\9self.selection = nil\
- \9\9\9else\
- \9\9\9\9self.cursor = self.cursor - 1\
- \9\9\9end\
- \9\9\9self.changed = true\
- \9\9\9event.handled = true\
- \9\9elseif event:matchesHotkey \"right\" then\
- \9\9\9if self.selection then\
- \9\9\9\9self.cursor = math.max( self.cursor, self.selection )\
- \9\9\9\9self.selection = nil\
- \9\9\9else\
- \9\9\9\9self.cursor = self.cursor + 1\
- \9\9\9end\
- \9\9\9self.changed = true\
- \9\9\9event.handled = true\
- \9\9elseif event:matchesHotkey \"backspace\" then\
- \9\9\9if self.selection then\
- \9\9\9\9self:write \"\"\
- \9\9\9\9self.changed = true\
- \9\9\9elseif self.cursor > 0 then\
- \9\9\9\9self.cursor = self.cursor - 1\
- \9\9\9\9self.text = self.text:sub( 1, self.cursor ) .. self.text:sub( self.cursor + 2 )\
- \9\9\9end\
- \9\9\9event.handled = true\
- \9\9elseif event:matchesHotkey \"shift-home\" then\
- \9\9\9self.selection = self.cursor\
- \9\9\9self.cursor = 0\
- \9\9\9self.changed = true\
- \9\9\9event.handled = true\
- \9\9elseif event:matchesHotkey \"shift-end\" then\
- \9\9\9self.selection = self.cursor\
- \9\9\9self.cursor = #self.text\
- \9\9\9self.changed = true\
- \9\9\9event.handled = true\
- \9\9elseif event:matchesHotkey \"home\" then\
- \9\9\9self.cursor = 0\
- \9\9\9self.selection = nil\
- \9\9\9self.changed = true\
- \9\9\9event.handled = true\
- \9\9elseif event:matchesHotkey \"end\" then\
- \9\9\9self.cursor = #self.text\
- \9\9\9self.selection = nil\
- \9\9\9self.changed = true\
- \9\9\9event.handled = true\
- \9\9elseif event:matchesHotkey \"delete\" then\
- \9\9\9self.text = self.text:sub( 1, self.cursor) .. self.text:sub( self.cursor + 2 )\
- \9\9\9event.handled = true\
- \9\9elseif event:matchesHotkey \"enter\" then\
- \9\9\9self.focussed = false\
- \9\9\9self.changed = true\
- \9\9\9if self.onEnter then\
- \9\9\9\9self:onEnter()\
- \9\9\9end\
- \9\9\9event.handled = true\
- \9\9elseif event:matchesHotkey \"tab\" then\
- \9\9\9self.focussed = false\
- \9\9\9self.changed = true\
- \9\9\9if self.onTab then\
- \9\9\9\9self:onTab()\
- \9\9\9end\
- \9\9\9event.handled = true\
- \9\9end\
- \9end\
- end\
- \
- function UIMultilineTextInput:onTextEvent( event )\
- \9if not event.handled and self.focussed then\
- \9\9self:write( event.text )\
- \9\9self.changed = true\
- \9\9event.handled = true\
- \9end\
- end\
- \
- function UIMultilineTextInput:onDraw()\
- \
- \9self.canvas:clear( colours.lightGrey )\
- \9self.canvas:drawPreformattedText( self.ox, self.oy, self.width, self.height, {\
- \9\9text = self.fmtLines;\
- \9\9verticalAlignment = \"top\";\
- \9\9selectedColour = self.selectedColour;\
- \9\9selectedTextColour = self.selectedTextColour;\
- \9} )\
- \
- \9-- UIDrawingHelpers.scrollbar.drawScrollbars( self )\
- \
- end\
- \
- function UIMultilineTextInput:setText( text )\
- \9self.lines = {}\
- \9self.fmtLines = {}\
- \9for line in text:gmatch \"[^\\n]+\" do\
- \9\9self.lines[#self.lines + 1] = line\
- \9\9self.fmtLines[#self.fmtLines + 1] = {}\
- \9end\
- \
- \9for i = 1, #self.lines do\
- \9\9self:recolourLine( i )\
- \9end\
- end\
- \
- function UIMultilineTextInput:setTextColourer( colourer )\
- \
- end";
- ["/lib/elements/UIPanel.lua"] = "\
- require \"UIElement\"\
- \
- class \"UIPanel\" extends \"UIElement\" {\
- \9colour = 1;\
- }\
- \
- function UIPanel:init( x, y, w, h, col )\
- \9self.super:init( x, y, w, h )\
- \9self.colour = col\
- end\
- \
- function UIPanel:onDraw()\
- \9self.canvas:clear( self.colour )\
- end\
- \
- function UIPanel:onMouseEvent( event )\
- \9if not event.handled and event:isInArea( 0, 0, self.width, self.height ) and event.name == Event.MOUSEDOWN then\
- \9\9event.handled = true\
- \9end\
- end\
- \
- function UIPanel:setColour( colour )\
- \9self.raw.colour = colour\
- \9self.changed = true\
- end";
- ["/lib/elements/UIRadioButton.lua"] = "\
- require \"UIElement\"\
- local UIEventHelpers = require \"util.UIEventHelpers\"\
- \
- local groups = { [0] = {} }\
- \
- class \"UIRadioButton\" extends \"UIElement\" {\
- \9colour = colours.lightGrey;\
- \9checkColour = colours.black;\
- \9check = \"@\"; --set to \" \" for a whole pixel\
- \9group = 0;\
- \9toggled = false;\
- }\
- \
- function UIRadioButton:init( x, y )\
- \9self.super:init( x, y, 1, 1 )\
- \9groups[0][#groups[0] + 1] = self\
- end\
- \
- function UIRadioButton:onLabelPressed()\
- \9self.toggled = not self.toggled\
- end\
- \
- function UIRadioButton:onMouseEvent( event )\
- \
- \9if UIEventHelpers.clicking.handleMouseEvent( self, event ) == \"click\" then\
- \9\9self.holding = false\
- \9\9self.toggled = not self.toggled\
- \9end\
- \
- end\
- \
- function UIRadioButton:onDraw()\
- \
- \9self.canvas:clear( self.colour )\
- \9self.canvas:drawPoint( 0, 0, {\
- \9\9colour = self.colour\
- \9} )\
- \9if self.toggled then\
- \9\9self.canvas:drawText( 0, 0, {\
- \9\9\9textColour = self.checkColour;\
- \9\9\9text = self.check;\
- \9\9} )\
- \9end\
- \
- end\
- \
- function UIRadioButton:setWidth() end\
- function UIRadioButton:setHeight() end\
- \
- function UIRadioButton:setHolding( state )\
- \9self.raw.holding = state\
- \9self.changed = true\
- end\
- \
- function UIRadioButton:setColour( colour )\
- \9self.raw.colour = colour\
- \9self.changed = true\
- end\
- \
- function UIRadioButton:setCheckColour( colour )\
- \9self.raw.checkColour = colour\
- \9self.changed = trur\
- end\
- \
- function UIRadioButton:setCheck( check )\
- \9self.raw.check = check\
- \9self.changed = true\
- end\
- \
- function UIRadioButton:setToggled( toggled )\
- \9if toggled then\
- \9\9for i = 1, #groups[self.group] do\
- \9\9\9groups[self.group][i].toggled = false\
- \9\9end\
- \9end\
- \9local t = self.toggled\
- \9self.raw.toggled = toggled\
- \9if toggled ~= t and self.onToggle then\
- \9\9self:onToggle()\
- \9end\
- \9self.changed = true\
- end\
- \
- function UIRadioButton:setGroup( group )\
- \9for i = #groups[self.group], 1, -1 do\
- \9\9if groups[self.group][i] == self then\
- \9\9\9table.remove( groups[self.group], i )\
- \9\9end\
- \9end\
- \9if #groups[self.group] == 0 then\
- \9\9groups[self.group] = nil\
- \9end\
- \9groups[group] = groups[group] or {}\
- \9groups[group][#groups[group] + 1] = self\
- \9self.raw.group = group\
- end";
- ["/lib/elements/UITabs.lua"] = "\
- require \"Timer\"\
- require \"UIElement\"\
- \
- local function optionWidth( option )\
- \9return #option + 2\
- end\
- local function formatOptions( options )\
- \9local t, x = {}, 0\
- \9for i = 1, #options do\
- \9\9local width = optionWidth( options[i] )\
- \9\9t[i] = { x = x, width = width, text = options[i] }\
- \9\9x = x + width + 1\
- \9end\
- \9return t\
- end\
- local function getOptionTID( t, x )\
- \9for i = 1, #t do\
- \9\9if x >= t[i].x and x < t[i].x + t[i].width then\
- \9\9\9return t[i], i, x - t[i].x\
- \9\9end\
- \9end\
- end\
- \
- class \"UITabs\" extends \"UIElement\" {\
- \9options = {};\
- \9showButtons = false;\
- \
- \9selected = nil;\
- \9selectedOffset = 0;\
- \9selectedWidth = 0;\
- \
- \9colour = 1;\
- \9textColour = colours.grey;\
- \9selectedColour = colours.cyan;\
- \9selectedTextColour = colours.white;\
- \9buttonColour = colours.lightGrey;\
- \9buttonTextColour = colours.white;\
- \9separator = \"|\";\
- \9seperatorTextColour = colours.lightGrey;\
- \
- \9scrolling = nil;\
- \9scrollingTimer = nil;\
- }\
- \
- function UITabs:init( x, y, width, options )\
- \9self.super:init( x, y, width, 1 )\
- \9self.options = options\
- \9self.width = width\
- end\
- \
- function UITabs:startScrollingTimer()\
- \9if self.scrolling then\
- \9\9self.ox = math.min( math.max( self.width - self:getContentWidth() - ( self.showButtons and 2 or 0 ), self.ox + self.scrolling ), 0 )\
- \9\9self.scrollingTimer = self.scrollingTimer or Timer.queue( .05, function()\
- \9\9\9self.scrollingTimer = nil\
- \9\9\9self:startScrollingTimer()\
- \9\9end )\
- \9else\
- \9\9self.scrollingTimer = nil\
- \9end\
- end\
- \
- function UITabs:changeSelection( x, width )\
- \9self.animationHandler:createRoundedTween( \"selectedOffset\", self, { selectedOffset = x }, self.transitionTime )\
- \9self.animationHandler:createRoundedTween( \"selectedWidth\", self, { selectedWidth = width }, self.transitionTime )\
- \9if x < -self.ox then\
- \9\9self.animatedOX = -x\
- \9elseif x + width > self.width - self.ox - ( self.showButtons and 2 or 0 ) then\
- \9\9self.animatedOX = -( x + width + ( self.showButtons and 2 or 0 ) - self.width )\
- \9end\
- end\
- \
- function UITabs:select( index )\
- \9local t = formatOptions( self.options )[index]\
- \9if t then\
- \9\9self:changeSelection( t.x, t.width )\
- \9\9if self.selected ~= index and self.onSelect then\
- \9\9\9self.raw.selected = index\
- \9\9\9self:onSelect( index )\
- \9\9else\
- \9\9\9self.raw.selected = index\
- \9\9end\
- \9end\
- end\
- \
- function UITabs:onDraw()\
- \9self.canvas:clear( self.colour )\
- \9local x = 0\
- \9if self.showButtons then\
- \9\9self.canvas:drawPoint( 0, 0, {\
- \9\9\9colour = self.buttonColour;\
- \9\9\9textColour = self.buttonTextColour;\
- \9\9\9character = \"<\";\
- \9\9} )\
- \9\9self.canvas:drawPoint( self.width - 1, 0, {\
- \9\9\9colour = self.buttonColour;\
- \9\9\9textColour = self.buttonTextColour;\
- \9\9\9character = \">\";\
- \9\9} )\
- \9\9x = 1\
- \9end\
- \9local options = formatOptions( self.options )\
- \9for i = 0, self.width - ( self.showButtons and 3 or 1 ) do\
- \9\9local t, _, d = getOptionTID( options, i - self.ox )\
- \9\9if t then\
- \9\9\9local selected = i - self.ox >= self.selectedOffset and i - self.ox < self.selectedOffset + self.selectedWidth\
- \9\9\9self.canvas:drawPoint( x + i, 0, {\
- \9\9\9\9colour = selected and self.selectedColour or self.colour;\
- \9\9\9\9textColour = selected and self.selectedTextColour or self.textColour;\
- \9\9\9\9character = ( d == 0 or d == t.width - 1 ) and \" \" or t.text:sub( d, d );\
- \9\9\9} )\
- \9\9elseif i - self.ox < self:getContentWidth() then\
- \9\9\9local selected = i - self.ox >= self.selectedOffset and i - self.ox < self.selectedOffset + self.selectedWidth\
- \9\9\9self.canvas:drawPoint( x + i, 0, {\
- \9\9\9\9colour = selected and self.selectedColour or self.colour;\
- \9\9\9\9textColour = selected and self.selectedTextColour or self.seperatorTextColour;\
- \9\9\9\9character = selected and \" \" or self.separator;\
- \9\9\9} )\
- \9\9end\
- \9end\
- end\
- \
- function UITabs:onMouseEvent( event )\
- \9if event.handled then return end\
- \
- \9if event.name == Event.MOUSEDOWN and event:isInArea( 0, 0, self.width, 1 ) then\
- \9\9if event.x == 0 then\
- \9\9\9self.scrolling = 1\
- \9\9\9self:startScrollingTimer()\
- \9\9\9event.handled = true\
- \9\9elseif event.x == self.width - 1 then\
- \9\9\9self.scrolling = -1\
- \9\9\9self:startScrollingTimer()\
- \9\9\9event.handled = true\
- \9\9else\
- \9\9\9self.holding = {\
- \9\9\9\9button = event.button;\
- \9\9\9\9moved = false;\
- \9\9\9\9x = event.x - self.ox;\
- \9\9\9}\
- \9\9\9event.handled = true\
- \9\9end\
- \9elseif event.name == Event.MOUSEDRAG and self.holding then\
- \9\9self.holding.moved = true\
- \9\9self.ox = math.min( math.max( self.width - self:getContentWidth() - ( self.showButtons and 2 or 0 ), event.x - self.holding.x ), 0 )\
- \9\9event.handled = true\
- \9elseif event.name == Event.MOUSEUP and self.scrolling then\
- \9\9self.scrolling = nil\
- \9elseif event.name == Event.MOUSEUP and self.holding then\
- \9\9if not self.holding.moved then\
- \9\9\9local _, i = getOptionTID( formatOptions( self.options ), event.x - self.ox - ( self.showButtons and 1 or 0 ) )\
- \9\9\9if i then\
- \9\9\9\9self:select( i )\
- \9\9\9end\
- \9\9end\
- \9\9self.holding = nil\
- \9\9event.handled = true\
- \9end\
- end\
- \
- function UITabs:getContentWidth()\
- \9local w = 0\
- \9for i = 1, #self.options do\
- \9\9w = w + 2 + #self.options[i]\
- \9end\
- \9return w + #self.options - 1\
- end\
- \
- function UITabs:setHeight() end\
- \
- function UITabs:setWidth( width )\
- \9self.super:setWidth( width )\
- \9local w = self:getContentWidth()\
- \9if w > width then\
- \9\9self.showButtons = true\
- \9else\
- \9\9self.showButtons = false\
- \9end\
- \9if w + self.ox < self.width then\
- \9\9self.ox = math.min( 0, self.width - w )\
- \9end\
- end\
- \
- \
- function UITabs:setSelectedOffset( offset )\
- \9self.raw.selectedOffset = offset\
- \9self.changed = true\
- end\
- \
- function UITabs:setSelectedWidth( width )\
- \9self.raw.selectedWidth = width\
- \9self.changed = true\
- end\
- \
- function UITabs:setShowButtons( show )\
- \9self.raw.showButtons = show\
- \9self.changed = true\
- end\
- \
- function UITabs:setSelected( option )\
- \9return self:select( option )\
- end\
- \
- function UITabs:setColour( colour )\
- \9self.raw.colour = colour\
- \9self.changed = true\
- end\
- \
- function UITabs:setTextColour( colour )\
- \9self.raw.textColour = colour\
- \9self.changed = true\
- end\
- \
- function UITabs:setSelectedColour( colour )\
- \9self.raw.selectedColour = colour\
- \9self.changed = true\
- end\
- \
- function UITabs:setSelectedTextColour( colour )\
- \9self.raw.selectedTextColour = colour\
- \9self.changed = true\
- end\
- \
- function UITabs:setButtonColour( colour )\
- \9self.raw.buttonColour = colour\
- \9self.changed = true\
- end\
- \
- function UITabs:setButtonTextColour( colour )\
- \9self.raw.buttonTextColour = colour\
- \9self.changed = true\
- end\
- \
- function UITabs:setSeperator( separator )\
- \9self.raw.separator = separator\
- \9self.changed = true\
- end\
- \
- function UITabs:setSeperatorTextColour( colour )\
- \9self.raw.seperatorTextColour = colour\
- \9self.changed = true\
- end";
- ["/lib/elements/UITerminal.lua"] = "\
- require \"graphics.TermCanvas\"\
- \
- require \"Event.Event\"\
- \
- require \"UIElement\"\
- \
- class \"UITerminal\" extends \"UIElement\" {\
- \9handlesKeyboard = true;\
- \9handlesText = true;\
- \9\
- \9holding = false;\
- }\
- \
- function UITerminal:init( x, y, w, h )\
- \9self.super:init( x, y, w, h )\
- \
- \9self.canvas = TermCanvas( w, h )\
- \9self.term = self.canvas:getTermRedirect()\
- end\
- \
- function UITerminal:wrap()\
- \9return term.redirect( self.term )\
- end\
- \
- function UITerminal:onMouseEvent( event )\
- \9if event.handled or not self.onEvent then return end\
- \
- \9if event.name == Event.MOUSEDOWN and event:isInArea( 0, 0, self.width, self.height ) then\
- \9\9self.holding = true\
- \9\9self:onEvent( \"mouse_click\", event.button, event.x + 1, event.y + 1 )\
- \9\9event.handled = true\
- \9\9self.changed = true\
- \9elseif event.name == Event.MOUSESCROLL and event:isInArea( 0, 0, self.width, self.height ) then\
- \9\9self:onEvent( \"mouse_scroll\", event.button, event.x + 1, event.y + 1 )\
- \9\9event.handled = true\
- \9\9self.changed = true\
- \9elseif event.name == Event.MOUSEUP and self.holding then\
- \9\9self.holding = false\
- \9\9self:onEvent( \"mouse_up\", event.button, event.x + 1, event.y + 1 )\
- \9\9event.handled = true\
- \9\9self.changed = true\
- \9elseif event.name == Event.MOUSEDRAG and self.holding then\
- \9\9self:onEvent( \"mouse_drag\", event.button, event.x + 1, event.y + 1 )\
- \9\9event.handled = true\
- \9\9self.changed = true\
- \9end\
- end\
- \
- function UITerminal:onKeyboardEvent( event )\
- \9if event.handled or not self.onEvent then return end\
- \
- \9if event.name == Event.KEYUP then\
- \9\9self:onEvent( \"key_up\", keys[event.key] )\
- \9\9event.handled = true\
- \9\9self.changed = true\
- \9elseif event.name == Event.KEYDOWN then\
- \9\9self:onEvent( \"key\", keys[event.key], event.parameters.isRepeat )\
- \9\9event.handled = true\
- \9\9self.changed = true\
- \9end\
- end\
- \
- function UITerminal:onTextEvent( event )\
- \9if event.handled or not self.onEvent then return end\
- \
- \9if event.name == Event.TEXT then\
- \9\9self:onEvent( \"char\", event.text )\
- \9\9event.handled = true\
- \9\9self.changed = true\
- \9elseif event.name == Event.PASTE then\
- \9\9self:onEvent( \"paste\", event.text )\
- \9\9event.handled = true\
- \9\9self.changed = true\
- \9end\
- end\
- \
- function UITerminal:setWidth( width )\
- \9self.super:setWidth( width )\
- \9if self.onEvent then\
- \9\9self:onEvent \"term_resize\"\
- \9end\
- end\
- \
- function UITerminal:setHeight( height )\
- \9self.super:setHeight( height )\
- \9if self.onEvent then\
- \9\9self:onEvent \"term_resize\"\
- \9end\
- end\
- \
- function UITerminal:handle( event )\
- \
- \9if event.class == Event and self.onEvent then\
- \9\9self:onEvent( event.name, unpack( event.parameters ) )\
- \9\9self.changed = true\
- \9end\
- \9UIElement.handle( self, event )\
- \
- end\
- \
- function UITerminal:onDraw()\
- \9if self.canvas.term_cb then\
- \9\9self.canvas.cursor = {\
- \9\9\9x = self.canvas.term_x - 1;\
- \9\9\9y = self.canvas.term_y - 1;\
- \9\9\9colour = self.canvas.term_tc;\
- \9\9}\
- \9end\
- end";
- ["/lib/elements/UIText.lua"] = "\
- require \"UIElement\"\
- \
- local markup = require \"util.markup\"\
- local UIDrawingHelpers = require \"util.UIDrawingHelpers\"\
- local UIEventHelpers = require \"util.UIEventHelpers\"\
- \
- class \"UIText\" extends \"UIElement\" {\
- \9colour = 1;\
- \9textColour = colours.grey;\
- \9text = \"\";\
- \9selectedColour = colours.blue;\
- \9selectedTextColour = colours.white;\
- \
- \9alignment = \"top\";\
- \9wrap = true;\
- \9selectable = true;\
- \
- \9internalWidth = nil;\
- \9internalHeight = nil;\
- \
- \9formattedText = nil;\
- \9formattedTextInfo = nil;\
- \9handlesKeyboard = true;\
- }\
- \
- UIText:mixin( UIEventHelpers.scrollbar.mixin )\
- \
- function UIText:init( x, y, w, h, text )\
- \9self.super:init( x, y, w, h )\
- \9self.text = text\
- end\
- \
- function UIText:onMouseEvent( event )\
- \9\
- \9UIEventHelpers.scrollbar.handleMouseEvent( self, event )\
- \9UIEventHelpers.scrollbar.handleMouseScroll( self, event )\
- \
- \9if self.selectable then\
- \9\9UIEventHelpers.textSelection.handleMouseEvent( self, event, self.formattedTextInfo, self.alignment, math.max( self:getContentWidth(), self.internalWidth or self.width ), math.max( self:getContentHeight(), self.internalHeight or self.height ) )\
- \9end\
- end\
- \
- function UIText:onKeyboardEvent( event )\
- \9if self.selectable then\
- \9\9UIEventHelpers.textSelection.handleKeyboardEvent( self, event, self.stream )\
- \9end\
- end\
- \
- function UIText:onDraw()\
- \9self.canvas:clear( self.colour )\
- \9self.canvas:drawPreformattedText( self.ox, self.oy, math.max( self:getContentWidth(), self.internalWidth or self.width ), math.max( self:getContentHeight(), self.internalHeight or self.height ), {\
- \9\9text = self.formattedText;\
- \9\9verticalAlignment = self.alignment;\
- \9\9selectedColour = self.selectedColour;\
- \9\9selectedTextColour = self.selectedTextColour;\
- \9} )\
- \
- \9UIDrawingHelpers.scrollbar.drawScrollbars( self )\
- end\
- \
- function UIText:updateText()\
- \9local a, b, c = markup.parse( self.text, self.colour, self.textColour, self.wrap and ( self.internalWidth or self.width ), self.wrap and self.internalHeight, true )\
- \9self.formattedTextInfo, self.formattedText, self.stream = a, b, c\
- \9self.changed = true\
- end\
- \
- function UIText:getContentWidth()\
- \9local max = 0\
- \9for i = 1, #self.formattedText do\
- \9\9max = math.max( #self.formattedText[i] - ( self.formattedText[i][#self.formattedText[i]][3] == \"\\n\" and 1 or 0 ), max )\
- \9end\
- \9return max\
- end\
- \
- function UIText:getContentHeight()\
- \9return #self.formattedText\
- end\
- \
- function UIText:setText( text )\
- \9self.raw.text = tostring( text )\
- \9self:updateText()\
- end\
- \
- function UIText:setColour( colour )\
- \9self.raw.colour = colour\
- \9self:updateText()\
- end\
- \
- function UIText:setTextColour( textColour )\
- \9self.raw.textColour = textColour\
- \9self:updateText()\
- end\
- \
- function UIText:setWrap( wrap )\
- \9self.raw.wrap = wrap\
- \9self:updateText()\
- end\
- \
- function UIText:setInternalWidth( width )\
- \9self.raw.internalWidth = width\
- \9self:updateText()\
- end\
- \
- function UIText:setInternalHeight( height )\
- \9self.raw.internalHeight = height\
- \9self:updateText()\
- end\
- \
- function UIText:setWidth( width )\
- \9self.super:setWidth( width )\
- \9self:updateText()\
- \9if self:getContentWidth() + self.ox < self.width then\
- \9\9self.ox = math.min( 0, self.width - self:getContentWidth() )\
- \9end\
- end\
- \
- function UIText:setHeight( height )\
- \9self.super:setHeight( height )\
- \9self:updateText()\
- \9if self:getContentHeight() + self.oy < self.height then\
- \9\9self.oy = math.min( 0, self.height - self:getContentHeight() )\
- \9end\
- end\
- \
- function UIText:setAlignment( alignment )\
- \9self.raw.alignment = alignment\
- \9self.changed = true\
- end\
- \
- function UIText:setSelectedColour( colour )\
- \9self.raw.selectedColour = colour\
- \9self.changed = true\
- end\
- \
- function UIText:setSelectedTextColour( colour )\
- \9self.raw.selectedTextColour = colour\
- \9self.changed = true\
- end";
- ["/lib/elements/UITextInput.lua"] = "\
- require \"UIElement\"\
- \
- local clipboard = require \"clipboard\"\
- \
- class \"UITextInput\" extends \"UIElement\" {\
- \9text = \"\";\
- \9mask = nil;\
- \9colour = colours.lightGrey;\
- \9textColour = colours.grey;\
- \9focussedColour = colours.white;\
- \9focussedTextColour = colours.grey;\
- \9selectedColour = colours.blue;\
- \9selectedTextColour = colours.white;\
- \
- \9cursor = 0;\
- \9selection = nil;\
- \9focussed = false;\
- \
- \9handlesKeyboard = true;\
- \9handlesText = true;\
- }\
- \
- function UITextInput:init( x, y, w )\
- \9self.super:init( x, y, w, 1 )\
- end\
- \
- function UITextInput:write( text )\
- \
- \9if self.selection then\
- \9\9self.raw.text = self.text:sub( 1, math.min( self.selection, self.cursor ) )\
- \9\9.. text\
- \9\9.. self.text:sub( math.max( self.selection, self.cursor ) + 2 )\
- \9\9self.cursor = math.min( self.selection, self.cursor ) + #text\
- \9\9self.selection = nil\
- \
- \9else\
- \9\9self.raw.text = self.text:sub( 1, self.cursor ) .. text .. self.text:sub( self.cursor + 1 )\
- \9\9self.cursor = self.cursor + #text\
- \9end\
- \
- end\
- \
- function UITextInput:focusOn()\
- \9if #self.text > 0 then\
- \9\9self.selection = 0\
- \9end\
- \9self.cursor = #self.text\
- \9self.focussed = true\
- \9self.changed = true\
- end\
- \
- function UITextInput:onLabelPressed()\
- \9self:focusOn()\
- end\
- \
- function UITextInput:setCursor( cursor )\
- \
- \9self.raw.cursor = math.max( math.min( cursor, #self.text ), 0 )\
- \9if self.cursor + self.ox < 1 then\
- \9\9self.ox = math.min( -self.cursor, 0 )\
- \9elseif self.cursor + self.ox >= self.width - 1 then\
- \9\9self.ox = self.width - 1 - self.cursor\
- \9end\
- \
- end\
- \
- function UITextInput:onMouseEvent( event )\
- \
- \9if event.name == Event.MOUSEDOWN and event.handled then\
- \9\9self.focussed = false\
- \9\9self.changed = true\
- \9end\
- \9if event.handled then return end\
- \
- \9if event.name == Event.MOUSEDOWN then\
- \9\9if event:isInArea( 0, 0, self.width, self.height ) then\
- \9\9\9self.focussed = true\
- \9\9\9self.cursor = event.x - self.ox\
- \9\9\9event.handled = true\
- \9\9else\
- \9\9\9self.focussed = false\
- \9\9end\
- \9\9self.selection = nil\
- \9\9self.changed = true\
- \9elseif event.name == Event.MOUSEDRAG and self.focussed then\
- \9\9if not self.selection then\
- \9\9\9self.selection = self.cursor\
- \9\9end\
- \9\9event.handled = true\
- \9\9self.cursor = math.max( 0, math.min( event.x - self.ox, #self.text ) )\
- \9\9self.changed = true\
- \9end\
- \
- end\
- \
- function UITextInput:onKeyboardEvent( event )\
- \9if not event.handled and self.focussed and event.name == Event.KEYDOWN then\
- \9\9if event:matchesHotkey \"shift-left\" then\
- \9\9\9self.selection = self.selection or self.cursor\
- \9\9\9self.cursor = self.cursor - 1\
- \9\9\9self.changed = true\
- \9\9\9event.handled = true\
- \9\9elseif event:matchesHotkey \"shift-right\" then\
- \9\9\9self.selection = self.selection or self.cursor\
- \9\9\9self.cursor = self.cursor + 1\
- \9\9\9self.changed = true\
- \9\9\9event.handled = true\
- \9\9elseif event:matchesHotkey \"ctrl-a\" then\
- \9\9\9self.cursor = #self.text\
- \9\9\9self.selection = 0\
- \9\9\9self.changed = true\
- \9\9\9event.handled = true\
- \9\9elseif event:matchesHotkey \"ctrl-c\" then\
- \9\9\9if self.selection then\
- \9\9\9\9clipboard.put {\
- \9\9\9\9\9{ \"plaintext\", self.text:sub( math.min( self.cursor, self.selection ) + 1, math.max( self.cursor, self.selection ) + 1 ) }\
- \9\9\9\9}\
- \9\9\9else\
- \9\9\9\9clipboard.put {\
- \9\9\9\9\9{ \"plaintext\", self.text }\
- \9\9\9\9}\
- \9\9\9end\
- \9\9\9event.handled = true\
- \9\9elseif event:matchesHotkey \"ctrl-x\" then\
- \9\9\9if self.selection then\
- \9\9\9\9clipboard.put {\
- \9\9\9\9\9{ \"plaintext\", self.text:sub( math.min( self.cursor, self.selection ) + 1, math.max( self.cursor, self.selection ) + 1 ) }\
- \9\9\9\9}\
- \9\9\9\9self:write \"\"\
- \9\9\9else\
- \9\9\9\9clipboard.put {\
- \9\9\9\9\9{ \"plaintext\", self.text }\
- \9\9\9\9}\
- \9\9\9\9self.text = \"\"\
- \9\9\9end\
- \9\9\9self.changed = true\
- \9\9\9event.handled = true\
- \9\9elseif event:matchesHotkey \"ctrl-b\" then\
- \9\9\9local text = clipboard.get \"plaintext\"\
- \9\9\9if text then\
- \9\9\9\9self:write( text )\
- \9\9\9\9self.changed = true\
- \9\9\9end\
- \9\9\9event.handled = true\
- \9\9elseif event:matchesHotkey \"left\" then\
- \9\9\9if self.selection then\
- \9\9\9\9self.cursor = math.min( self.cursor, self.selection )\
- \9\9\9\9self.selection = nil\
- \9\9\9else\
- \9\9\9\9self.cursor = self.cursor - 1\
- \9\9\9end\
- \9\9\9self.changed = true\
- \9\9\9event.handled = true\
- \9\9elseif event:matchesHotkey \"right\" then\
- \9\9\9if self.selection then\
- \9\9\9\9self.cursor = math.max( self.cursor, self.selection )\
- \9\9\9\9self.selection = nil\
- \9\9\9else\
- \9\9\9\9self.cursor = self.cursor + 1\
- \9\9\9end\
- \9\9\9self.changed = true\
- \9\9\9event.handled = true\
- \9\9elseif event:matchesHotkey \"backspace\" then\
- \9\9\9if self.selection then\
- \9\9\9\9self:write \"\"\
- \9\9\9\9self.changed = true\
- \9\9\9elseif self.cursor > 0 then\
- \9\9\9\9self.cursor = self.cursor - 1\
- \9\9\9\9self.text = self.text:sub( 1, self.cursor ) .. self.text:sub( self.cursor + 2 )\
- \9\9\9end\
- \9\9\9event.handled = true\
- \9\9elseif event:matchesHotkey \"shift-home\" then\
- \9\9\9self.selection = self.cursor\
- \9\9\9self.cursor = 0\
- \9\9\9self.changed = true\
- \9\9\9event.handled = true\
- \9\9elseif event:matchesHotkey \"shift-end\" then\
- \9\9\9self.selection = self.cursor\
- \9\9\9self.cursor = #self.text\
- \9\9\9self.changed = true\
- \9\9\9event.handled = true\
- \9\9elseif event:matchesHotkey \"home\" then\
- \9\9\9self.cursor = 0\
- \9\9\9self.selection = nil\
- \9\9\9self.changed = true\
- \9\9\9event.handled = true\
- \9\9elseif event:matchesHotkey \"end\" then\
- \9\9\9self.cursor = #self.text\
- \9\9\9self.selection = nil\
- \9\9\9self.changed = true\
- \9\9\9event.handled = true\
- \9\9elseif event:matchesHotkey \"delete\" then\
- \9\9\9self.text = self.text:sub( 1, self.cursor) .. self.text:sub( self.cursor + 2 )\
- \9\9\9event.handled = true\
- \9\9elseif event:matchesHotkey \"enter\" then\
- \9\9\9self.focussed = false\
- \9\9\9self.changed = true\
- \9\9\9if self.onEnter then\
- \9\9\9\9self:onEnter()\
- \9\9\9end\
- \9\9\9event.handled = true\
- \9\9elseif event:matchesHotkey \"tab\" then\
- \9\9\9self.focussed = false\
- \9\9\9self.changed = true\
- \9\9\9if self.onTab then\
- \9\9\9\9self:onTab()\
- \9\9\9end\
- \9\9\9event.handled = true\
- \9\9end\
- \9end\
- end\
- \
- function UITextInput:onTextEvent( event )\
- \9if not event.handled and self.focussed then\
- \9\9self:write( event.text )\
- \9\9self.changed = true\
- \9\9event.handled = true\
- \9end\
- end\
- \
- function UITextInput:onDraw()\
- \9self.canvas:clear( self.focussed and self.focussedColour or self.colour )\
- \9if self.selection then\
- \9\9local min, max = math.min( self.selection, self.cursor ), math.max( self.selection, self.cursor )\
- \
- \9\9self.canvas:drawHorizontalLine( self.ox + min, 0, math.abs( self.selection - self.cursor ) + 1, {\
- \9\9\9colour = self.selectedColour;\
- \9\9} )\
- \9\9self.canvas:drawText( self.ox, 0, {\
- \9\9\9text = ( self.mask and self.mask:rep( #self.text ) or self.text ):sub( 1, min );\
- \9\9\9textColour = self.focussed and self.focussedTextColour or self.textColour\
- \9\9} )\
- \9\9self.canvas:drawText( self.ox + min, 0, {\
- \9\9\9text = ( self.mask and self.mask:rep( #self.text ) or self.text ):sub( min + 1, max + 1 );\
- \9\9\9textColour = self.selectedTextColour;\
- \9\9} )\
- \9\9self.canvas:drawText( self.ox + max + 1, 0, {\
- \9\9\9text = ( self.mask and self.mask:rep( #self.text ) or self.text ):sub( max + 2 );\
- \9\9\9textColour = self.focussed and self.focussedTextColour or self.textColour\
- \9\9} )\
- \9else\
- \9\9self.canvas:drawText( self.ox, 0, {\
- \9\9\9text = self.mask and self.mask:rep( #self.text ) or self.text;\
- \9\9\9textColour = self.focussed and self.focussedTextColour or self.textColour\
- \9\9} )\
- \9end\
- \9if self.focussed then\
- \9\9self.canvas.cursor = {\
- \9\9\9x = self.cursor + self.ox;\
- \9\9\9y = 0;\
- \9\9\9colour = self.focussedTextColour;\
- \9\9}\
- \9end\
- end\
- \
- function UITextInput:setText( text )\
- \9self.raw.text = tostring( text )\
- \9if #text < self.cursor then\
- \9\9self.cursor = #tostring( text )\
- \9end\
- \9self.changed = true\
- end\
- \
- function UITextInput:setMask( mask )\
- \9self.raw.mask = mask\
- \9self.changed = true\
- end\
- \
- function UITextInput:setFocussedColour( colour )\
- \9self.raw.focussedColour = colour\
- \9self.changed = true\
- end\
- \
- function UITextInput:setFocussedTextColour( textColour )\
- \9self.raw.focussedTextColour = textColour\
- \9self.changed = true\
- end\
- \
- function UITextInput:setColour( colour )\
- \9self.raw.colour = colour\
- \9self.changed = true\
- end\
- \
- function UITextInput:setTextColour( textColour )\
- \9self.raw.textColour = textColour\
- \9self.changed = true\
- end\
- \
- function UITextInput:setSelectedColour( colour )\
- \9self.raw.selectedColour = colour\
- \9self.changed = true\
- end\
- \
- function UITextInput:setSelectedTextColour( colour )\
- \9self.raw.selectedTextColour = colour\
- \9self.changed = true\
- end\
- \
- function UITextInput:setFocussed( focussed )\
- \9self.raw.focussed = focussed\
- \9self.changed = true\
- end\
- \
- function UITextInput:setHeight() end";
- ["/lib/elements/UIToggle.lua"] = "\
- require \"UIElement\"\
- local shader = require \"graphics.shader\"\
- local UIEventHelpers = require \"util.UIEventHelpers\"\
- \
- class \"UIToggle\" extends \"UIElement\" {\
- \9colour = colours.grey;\
- \9activeColour = colours.green;\
- \9inactiveColour = colours.red;\
- \9holding = false;\
- \9toggled = false;\
- }\
- \
- function UIToggle:init( x, y, w, h )\
- \9self.super:init( x, y, w, h )\
- end\
- \
- function UIToggle:onLabelPressed()\
- \9self.toggled = not self.toggled\
- \9if self.onToggle then\
- \9\9self:onToggle()\
- \9end\
- end\
- \
- function UIToggle:onMouseEvent( event )\
- \
- \9local mode = UIEventHelpers.clicking.handleMouseEvent( self, event )\
- \9if mode == \"down\" or mode == \"up\" then\
- \9\9self.holding = mode == \"down\"\
- \
- \9elseif mode == \"click\" then\
- \9\9self.holding = false\
- \9\9self.toggled = not self.toggled\
- \9\9if self.onToggle then\
- \9\9\9self:onToggle()\
- \9\9end\
- \9end\
- \
- end\
- \
- function UIToggle:onDraw()\
- \
- \9self.canvas:clear( self.colour )\
- \9local size = math.floor( self.width * 1 / 3 )\
- \9local x = ( self.holding and math.floor( self.width / 2 - size / 2 ) ) or ( not self.toggled and 0 ) or self.width - size\
- \9local colour = self.toggled and self.activeColour or self.inactiveColour\
- \
- \9self.canvas:drawRectangle( x, 0, size, self.height, {\
- \9\9colour = colour;\
- \9\9filled = true;\
- \9} )\
- \
- end\
- \
- function UIToggle:setWidth( width )\
- \9self.width = math.min( width, 4 )\
- end\
- \
- function UIToggle:setHolding( state )\
- \9self.raw.holding = state\
- \9self.changed = true\
- end\
- \
- function UIToggle:setColour( colour )\
- \9self.raw.colour = colour\
- \9self.changed = true\
- end\
- \
- function UIToggle:setActiveColour( colour )\
- \9self.raw.activeColour = colour\
- \9self.changed = true\
- end\
- \
- function UIToggle:setInactiveColour( colour )\
- \9self.raw.inactiveColour = colour\
- \9self.changed = true\
- end\
- \
- function UIToggle:setToggled( bool )\
- \9self.raw.toggled = bool\
- \9self.changed = true\
- end";
- ["/lib/elements/UIView.lua"] = "\
- require \"UIElement\"\
- \
- class \"UIView\" extends \"UIElement\" {\
- \9shortcuts = {};\
- }\
- \
- function UIView:init( ... )\
- \9self.shortcuts = {}\
- \9return self.super:init( ... )\
- end\
- \
- function UIView:createShortcut( identifier, shortcut, action )\
- \9if self:shortcutExists( identifier ) then\
- \9\9self:setShortcut( identifier, shortcut )\
- \9\9self:setShortcutAction( identifier, action )\
- \9else\
- \9\9self.shortcuts[#self.shortcuts + 1] = { identifier, shortcut, action }\
- \9end\
- end\
- \
- function UIView:shortcutExists( identifier )\
- \9for i = 1, #self.shortcuts do\
- \9\9if self.shortcuts[i][1] == identifier then\
- \9\9\9return true\
- \9\9end\
- \9end\
- \9return false\
- end\
- \
- function UIView:deleteShortcut( identifier )\
- \9for i = 1, #self.shortcuts do\
- \9\9if self.shortcuts[i][1] == identifier then\
- \9\9\9table.remove( self.shortcuts, i )\
- \9\9\9return true\
- \9\9end\
- \9end\
- end\
- \
- function UIView:getShortcuts()\
- \9local t = {}\
- \9for i = 1, #self.shortcuts do\
- \9\9t[i] = self.shortcuts[i][1]\
- \9end\
- \9return t\
- end\
- \
- function UIView:setShortcut( identifier, shortut )\
- \9for i = 1, #self.shortcuts do\
- \9\9if self.shortcuts[i][1] == identifier then\
- \9\9\9self.shortcuts[i][2] = shortcut\
- \9\9\9return true\
- \9\9end\
- \9end\
- \9return false\
- end\
- \
- function UIView:getShortcut( identifier )\
- \9for i = 1, #self.shortcuts do\
- \9\9if self.shortcuts[i][1] == identifier then\
- \9\9\9return self.shortcuts[i][2]\
- \9\9end\
- \9end\
- end\
- \
- function UIView:setShortcutAction( identifier, action )\
- \9for i = 1, #self.shortcuts do\
- \9\9if self.shortcuts[i][1] == identifier then\
- \9\9\9self.shortcuts[i][3] = action\
- \9\9\9return true\
- \9\9end\
- \9end\
- \9return false\
- end\
- \
- function UIView:getShortcutAction( identifier )\
- \9for i = 1, #self.shortcuts do\
- \9\9if self.shortcuts[i][1] == identifier then\
- \9\9\9return self.shortcuts[i][3]\
- \9\9end\
- \9end\
- end\
- \
- function UIView:handle( event )\
- \
- \9if event.name == Event.KEYDOWN then\
- \9\9local a, l = nil, -1\
- \9\9for i = 1, #self.shortcuts do\
- \9\9\9if #self.shortcuts[i][2] > l and event:matchesHotkey( self.shortcuts[i][2] ) then\
- \9\9\9\9a, l = self.shortcuts[i][3], #self.shortcuts[i][2]\
- \9\9\9end\
- \9\9end\
- \9\9if a then\
- \9\9\9event.handled = true\
- \9\9\9a()\
- \9\9end\
- \9end\
- \
- \9return UIElement.handle( self, event )\
- \
- end";
- ["/lib/elements/UIWindow.lua"] = "\
- require \"UIElement\"\
- require \"UIContainer\"\
- require \"Event.MouseEvent\"\
- \
- local UIDrawingHelpers = require \"util.UIDrawingHelpers\"\
- local UIEventHelpers = require \"util.UIEventHelpers\"\
- \
- class \"UIWindow\" extends \"UIElement\" {\
- \9minWidth = 15;\
- \9minHeight = 6;\
- \9maxWidth = term.getSize();\
- \9maxHeight = select( 2, term.getSize() );\
- \
- \9title = \"Window\";\
- \9titleColour = colours.cyan;\
- \9titleTextColour = colours.white;\
- \
- \9shadowColour = colours.grey;\
- \
- \9closeable = true;\
- \9resizeable = true;\
- \9moveable = true;\
- \
- \9content = nil;\
- }\
- \
- UIWindow:mixin( UIEventHelpers.scrollbar.mixin )\
- \
- function UIWindow:init( x, y, w, h )\
- \9self.super:init( x, y, w, h )\
- \9self.content = self:addChild( UIContainer( 0, 1, w - 1, h - 2 ) )\
- end\
- \
- function UIWindow:onMouseEvent( event )\
- \9if event.handled then return end\
- \
- \9if event.name == Event.MOUSEDOWN and event:isInArea( 0, 0, self.width, self.height ) then\
- \
- \9\9if event.y == 0 and event.x == self.width - 2 and self.closeable then\
- \9\9\9if self.onClose then\
- \9\9\9\9self:onClose()\
- \9\9\9end\
- \9\9\9self:remove()\
- \9\9elseif event.x == self.width - 1 and event.y == self.height - 1 and self.resizeable then\
- \9\9\9self.dragging = {\
- \9\9\9\9button = event.button;\
- \9\9\9\9mode = \"resize\";\
- \9\9\9}\
- \
- \9\9elseif event.y == 0 and event.x < self.width - 1 and self.moveable then\
- \9\9\9self.dragging = {\
- \9\9\9\9x = event.x;\
- \9\9\9\9y = event.y;\
- \9\9\9\9button = event.button;\
- \9\9\9\9mode = \"move\";\
- \9\9\9}\
- \
- \9\9end\
- \9\9event.handled = true\
- \
- \9elseif event.name == Event.MOUSEDRAG and self.dragging and self.dragging.mode == \"move\" then\
- \9\9self.x = self.x + event.x - self.dragging.x\
- \9\9self.y = self.y + event.y - self.dragging.y\
- \9\9if self.onMove then\
- \9\9\9self:onMove()\
- \9\9end\
- \
- \9elseif event.name == Event.MOUSEDRAG and self.dragging and self.dragging.mode == \"resize\" then\
- \9\9self.width = event.x + 1\
- \9\9self.height = event.y + 1\
- \9\9if self.onResize then\
- \9\9\9self:onResize()\
- \9\9end\
- \
- \9elseif event.name == Event.MOUSEUP and self.dragging then\
- \9\9if event.button == self.dragging.button then\
- \9\9\9event.handled = true\
- \9\9\9self.dragging = nil\
- \9\9end\
- \
- \9end\
- end\
- \
- function UIWindow:onDraw()\
- \
- \9self.canvas:clear()\
- \9self.canvas:drawVerticalLine( self.width - 1, 1, self.height - 1, {\
- \9\9colour = self.shadowColour;\
- \9} )\
- \9self.canvas:drawHorizontalLine( 1, self.height - 1, self.width - 1, {\
- \9\9colour = self.shadowColour;\
- \9} )\
- \9self.canvas:drawHorizontalLine( 0, 0, self.width - 1, {\
- \9\9colour = self.titleColour\
- \9} )\
- \9self.canvas:drawText( 0, 0, {\
- \9\9text = self.title;\
- \9\9textColour = self.titleTextColour;\
- \9} )\
- \9if self.closeable then\
- \9\9self.canvas:drawPoint( self.width - 2, 0, {\
- \9\9\9character = \"x\";\
- \9\9\9colour = colours.red;\
- \9\9\9textColour = colours.white;\
- \9\9} )\
- \9end\
- \
- end\
- \
- function UIWindow:handle( event )\
- \
- \9if not event.handled and event:typeOf( MouseEvent ) and event.name == Event.MOUSEDOWN and event:isInArea( 0, 0, self.width, self.height ) then\
- \9\9local parent = self.parent\
- \9\9if parent and parent.children[#parent.children] ~= self then\
- \9\9\9parent:addChild( self )\
- \9\9end\
- \9end\
- \9return UIElement.handle( self, event )\
- \
- end\
- \
- function UIWindow:setWidth( width )\
- \9self.super:setWidth( math.max( math.min( width, self.maxWidth ), self.minWidth ) )\
- \9self.content.width = self.width - 1\
- end\
- \
- function UIWindow:setHeight( height )\
- \9self.super:setHeight( math.max( math.min( height, self.maxHeight ), self.minHeight ) )\
- \9self.content.height = self.height - 2\
- end\
- \
- function UIWindow:setTitle( title )\
- \9self.raw.title = tostring( title )\
- \9self.changed = true\
- end\
- \
- function UIWindow:setTitleColour( colour )\
- \9self.raw.titleColour = colour\
- \9self.changed = true\
- end\
- \
- function UIWindow:setTitleTextColour( colour )\
- \9self.raw.titleTextColour = colour\
- \9self.changed = true\
- end\
- \
- function UIWindow:setCloseable( closeable )\
- \9self.raw.closeable = closeable\
- \9self.changed = true\
- end\
- \
- function UIWindow:setShadowColour( colour )\
- \9self.raw.shadowColour = colour\
- \9self.changed = true\
- end\
- \
- function UIWindow:setMinWidth( width )\
- \9if width > self.width then\
- \9\9self.width = width\
- \9end\
- \9self.raw.minWidth = width\
- end\
- \
- function UIWindow:setMaxWidth( width )\
- \9if width < self.width then\
- \9\9self.width = width\
- \9end\
- \9self.raw.maxWidth = width\
- end\
- \
- function UIWindow:setMinHeight( height )\
- \9if height > self.height then\
- \9\9self.height = height\
- \9end\
- \9self.raw.minHeight = height\
- end\
- \
- function UIWindow:setMaxHeight( height )\
- \9if height < self.height then\
- \9\9self.height = height\
- \9end\
- \9self.raw.maxHeight = height\
- end";
- ["/lib/graphics/Canvas.lua"] = "\
- local insert = table.insert\
- local remove = table.remove\
- local min, max = math.min, math.max\
- local unpack = unpack\
- \
- class \"Canvas\" { useSetters = true,\
- \9x = 0;\
- \9y = 0;\
- \9width = 0;\
- \9height = 0;\
- \
- \9colour = 0;\
- \
- \9buffer = nil;\
- \
- \9cursor = nil;\
- }\
- \
- function Canvas:init( x, y, width, height )\
- \9self.raw.x = width and x or 0\
- \9self.raw.y = height and y or 0\
- \9self.raw.width = width or x or select( 1, term.getSize() )\
- \9self.raw.height = height or y or select( 2, term.getSize() )\
- \
- \9local buffer = {}\
- \9for i = 1, self.raw.width * self.raw.height do\
- \9\9buffer[i] = { self.colour, 1, \" \" }\
- \9end\
- \9self.raw.buffer = buffer\
- \
- \9self.mt.__tostring = self.tostring\
- end\
- \
- function Canvas:mapPixels( pixels )\
- \9local buffer = self.buffer\
- \9for i = 1, #pixels do\
- \9\9buffer[pixels[i][1]] = pixels[i][2]\
- \9end\
- end\
- \
- function Canvas:mapColour( pixels, colour )\
- \9local buffer = self.buffer\
- \9local px = { colour or 0, 1, \" \" }\
- \9for i = 1, #pixels do\
- \9\9buffer[pixels[i]] = px\
- \9end\
- end\
- \
- function Canvas:mapStaticPixel( pixels, px )\
- \9local buffer = self.buffer\
- \9for i = 1, #pixels do\
- \9\9buffer[pixels[i]] = px\
- \9end\
- end\
- \
- function Canvas:drawPixels( pixels )\
- \9local buffer = self.buffer\
- \9for i = 1, #pixels do\
- \9\9local pos = pixels[i][1]\
- \9\9if buffer[pos] then\
- \9\9\9local bc, tc, char = unpack( pixels[i][2] )\
- \9\9\9if bc == 0 then\
- \9\9\9\9bc = buffer[pos][1]\
- \9\9\9end\
- \9\9\9if tc == 0 or char == \"\" then\
- \9\9\9\9tc = buffer[pos][2]\
- \9\9\9\9char = buffer[pos][3]\
- \9\9\9end\
- \9\9\9buffer[pos] = { bc, tc, char }\
- \9\9end\
- \9end\
- end\
- \
- function Canvas:drawColour( pixels, colour )\
- \9if colour == 0 then return end\
- \9return self:mapColour( pixels, colour )\
- end\
- \
- function Canvas:drawStaticPixel( pixels, px )\
- \9local buffer = self.buffer\
- \9local tbc, tchar = px.bc == 0, px.tc == 0 or px.char == \"\"\
- \9if tbc or tchar and not ( tbc and tchar ) then\
- \9\9local bc, tc, char = unpack( px )\
- \9\9for i = 1, #pixels do\
- \9\9\9local pos = pixels[i]\
- \9\9\9buffer[pos] = { tbc and buffer[pos][1] or bc, tchar and buffer[pos][2] or tc, tchar and buffer[pos][3] or char }\
- \9\9end\
- \9elseif not tbc and not tchar then\
- \9\9for i = 1, #pixels do\
- \9\9\9buffer[pixels[i]] = px\
- \9\9end\
- \9end\
- end\
- \
- function Canvas:clear( colour )\
- \9local buffer = self.buffer\
- \9local px = { colour or self.colour, 1, \" \" }\
- \9for i = 1, self.width * self.height do\
- \9\9buffer[i] = px\
- \9end\
- end\
- \
- function Canvas:clone()\
- \9local new = self.class( self.width, self.height )\
- \9local b1, b2 = new.buffer, self.buffer\
- \9for i = 1, #b2 do\
- \9\9b1[i] = b2[i]\
- \9end\
- \9return new\
- end\
- \
- function Canvas:cloneAs( class )\
- \9local new = class( self.width, self.height )\
- \9local b1, b2 = new.buffer, self.buffer\
- \9for i = 1, #b2 do\
- \9\9b1[i] = b2[i]\
- \9end\
- \9return new\
- end\
- \
- function Canvas:drawTo( canvas, x, y )\
- \9x = ( x or 0 ) + self.x\
- \9y = ( y or 0 ) + self.y\
- \9local pixels = {}\
- \9local buffer = self.buffer\
- \9local width = self.width\
- \9local swidth = canvas.width\
- \
- \9local i = 1\
- \9local spos = 1\
- \
- \9for py = 0, self.height - 1 do\
- \9\9local pos = ( py + y ) * swidth + x + 1\
- \9\9for px = 1, width do\
- \9\9\9if px + x > 0 and px + x <= swidth then\
- \9\9\9\9pixels[i] = { pos, buffer[spos] }\
- \9\9\9\9i = i + 1\
- \9\9\9end\
- \9\9\9pos = pos + 1\
- \9\9\9spos = spos + 1\
- \9\9end\
- \9end\
- \
- \9canvas:drawPixels( pixels )\
- end\
- \
- function Canvas:setWidth( width )\
- \9local height, buffer, raw = self.height, self.buffer, self.raw\
- \9local px = { self.colour, 1, \" \" }\
- \
- \9while raw.width < width do\
- \9\9for i = 1, height do\
- \9\9\9insert( buffer, ( raw.width + 1 ) * i, px )\
- \9\9end\
- \9\9raw.width = raw.width + 1\
- \9end\
- \
- \9while raw.width > width do\
- \9\9for i = height, 1, -1 do\
- \9\9\9remove( buffer, raw.width * i )\
- \9\9end\
- \9\9raw.width = raw.width - 1\
- \9end\
- end\
- \
- function Canvas:setHeight( height )\
- \9local width, buffer, raw = self.width, self.buffer, self.raw\
- \9local px = { self.colour, 1, \" \" }\
- \9\
- \9while raw.height < height do\
- \9\9for i = 1, width do\
- \9\9\9buffer[#buffer + 1] = px\
- \9\9end\
- \9\9raw.height = raw.height + 1\
- \9end\
- \
- \9while raw.height > height do\
- \9\9for i = 1, width do\
- \9\9\9buffer[#buffer] = nil\
- \9\9end\
- \9\9raw.height = raw.height - 1\
- \9end\
- end\
- \
- function Canvas:tostring()\
- \9return \"[Instance] \" .. self.class.name .. \" (\" .. self.x .. \",\" .. self.y .. \" \" .. self.width .. \"x\" .. self.height .. \")\"\
- end";
- ["/lib/graphics/DrawingCanvas.lua"] = "\
- --[[\
- \
- function DrawingCanvas:drawLine( x1, y1, x2, y2, options )\
- \9local dx, dy = x2 - x1, y2 - y1\
- \9if dx == 0 then\
- \9\9return self:drawVerticalLine( x1, min( y1, y2 ), abs( y2 - y1 ) + 1, options )\
- \9elseif dy == 0 then\
- \9\9return self:drawHorizontalLine( min( x1, x2 ), y1, abs( x2 - x1 ) + 1, options )\
- \9end\
- \
- \9local m = dy / dx\
- \9local c = y1 - m * x1 + .5\
- \9local step = min( 1, 1 / abs( m ) )\
- \
- \9local drawPoint = self.drawPoint\
- \
- \9drawPoint( self, x1, y1, options )\
- \9drawPoint( self, x2, y2, options )\
- \
- \9for x = min( x1, x2 ), max( x1, x2 ), step do\
- \9\9drawPoint( self, floor( x + .5 ), floor( m * x + c ), options )\
- \9end\
- end\
- \
- function DrawingCanvas:drawWeightlessLine( x1, y1, x2, y2, options )\
- \9local width = self.width\
- \9local px = { options.colour or 1, options.textColour or 1, options.text or \" \" }\
- \9local pixels = { { floor( y1 + .5 ) * width + x1 + 1, px }, { floor( y2 + .5 ) * width + x2 + 1, px } }\
- \9local dx, dy = x2 - x1, y2 - y1\
- \9if abs( dx ) < .0001 then\
- \9\9local pos = floor( ( min( y1, y2 ) ) * width + x1 + 1.5 )\
- \9\9for i = 0, abs( y2 - y1 ) do\
- \9\9\9pixels[#pixels + 1] = { pos, px }\
- \9\9\9pos = pos + width\
- \9\9end\
- \9elseif abs( dy ) < .0001 then\
- \9\9local pos = floor( y1 + .5 ) * width + floor( min( x1, x2 ) + 1.5 )\
- \9\9for i = 0, abs( x2 - x1 ) do\
- \9\9\9pixels[#pixels + 1] = { pos, px }\
- \9\9\9pos = pos + 1\
- \9\9end\
- \9else\
- \9\9local m = dy / dx\
- \9\9local c = y1 - m * x1 + .5\
- \9\9local step = min( 1, 1 / abs( m ) )\
- \
- \9\9for x = min( x1, x2 ), max( x1, x2 ), step do\
- \9\9\9pixels[#pixels + 1] = { floor( m * x + c ) * width + floor( x + 1.5 ), px }\
- \9\9end\
- \9end\
- \
- \9self:mapPixels( pixels )\
- end\
- \
- function DrawingCanvas:drawTriangle( x1, y1, x2, y2, x3, y3, options )\
- \9if options.filled then\
- \9\9self:drawWeightlessLine( x1, y1, x2, y2, options )\
- \9\9self:drawWeightlessLine( x1, y1, x3, y3, options )\
- \9\9self:drawWeightlessLine( x2, y2, x3, y3, options )\
- \
- \9\9local dx1, dy1 = x2 - x1, y2 - y1\
- \9\9local m1 = dy1 / dx1\
- \9\9local c1 = y1 - m1 * x1\
- \9\9local dx2, dy2 = x3 - x1, y3 - y1\
- \9\9local m2 = dy2 / dx2\
- \9\9local c2 = y3 - m2 * x3\
- \9\9local dx3, dy3 = x3 - x2, y3 - y2\
- \9\9local m3 = dy3 / dx3\
- \9\9local c3 = y2 - m3 * x2\
- \
- \9\9local inf = 1/0\
- \9\9local nan = inf / inf\
- \
- \9\9local pixels = {}\
- \9\9local px = { options.colour or 1, options.textColour or 1, options.text or \" \" }\
- \9\9local width = self.width\
- \
- \9\9for y = floor( min( y1, y2, y3 ) ), ceil( max( y1, y2, y3 ) ) do\
- \9\9\9local yo = ( y ) * width\
- \9\9\9local a = ( y - c1 ) / m1\
- \9\9\9if x1 == x2 then\
- \9\9\9\9a = y >= min( y1, y2 ) and y <= max( y1, y2 ) and x1\
- \9\9\9elseif a < min( x1, x2 ) or a > max( x1, x2 ) then\
- \9\9\9\9a = nil\
- \9\9\9end\
- \9\9\9local b = ( y - c2 ) / m2\
- \9\9\9if x1 == x3 then\
- \9\9\9\9b = y >= min( y1, y3 ) and y <= max( y1, y3 ) and x1\
- \9\9\9elseif b < min( x1, x3 ) or b > max( x1, x3 ) then\
- \9\9\9\9b = nil\
- \9\9\9end\
- \9\9\9local c = ( y - c3 ) / m3\
- \9\9\9if x2 == x3 then\
- \9\9\9\9c = y >= min( y2, y3 ) and y <= max( y2, y3 ) and x2\
- \9\9\9elseif c < min( x2, x3 ) or c > max( x2, x3 ) then\
- \9\9\9\9c = nil\
- \9\9\9end\
- \9\9\9local v1, v2 = a or b or c, c or b or a\
- \9\9\9if v1 and v2 and abs( v1 - v2 ) < .00001 then v2 = b or a or c end\
- \9\9\9if v1 then\
- \9\9\9\9for i = floor( min( v1, v2 ) + .5 ), ceil( max( v1, v2 ) - .5 ) do\
- \9\9\9\9\9pixels[#pixels + 1] = { yo + i, px }\
- \9\9\9\9end\
- \9\9\9end\
- \9\9end\
- \
- \9\9self:mapPixels( pixels )\
- \9else\
- \9\9if not options.weight or options.weight == 1 then\
- \9\9\9self:drawWeightlessLine( x1, y1, x2, y2, options )\
- \9\9\9self:drawWeightlessLine( x1, y1, x3, y3, options )\
- \9\9\9self:drawWeightlessLine( x2, y2, x3, y3, options )\
- \9\9else\
- \9\9\9self:drawLine( x1, y1, x2, y2, options )\
- \9\9\9self:drawLine( x1, y1, x3, y3, options )\
- \9\9\9self:drawLine( x2, y2, x3, y3, options )\
- \9\9end\
- \9end\
- end\
- \
- function DrawingCanvas:drawPolygon( x, y, options )\
- \9if options.filled then\
- \9\9return error \"not yet supported\"\
- \9else\
- \9\9local points = options.points\
- \9\9x = ( x or 1 ) - 1\
- \9\9y = ( y or 1 ) - 1\
- \9\9if #points % 2 == 1 then\
- \9\9\9points[#points] = nil\
- \9\9end\
- \9\9for i = 1, #points / 2 do\
- \9\9\9local p1x, p1y = points[i * 2 - 1], points[i * 2]\
- \9\9\9local p2x, p2y = points[i * 2 + 1] or points[1], points[i * 2 + 2] or points[2]\
- \9\9\9self:drawLine( p1x + x, p1y + y, p2x + x, p2y + y, options )\
- \9\9end\
- \9end\
- end\
- \
- ]]\
- \
- require \"graphics.Canvas\"\
- \
- local max, min, floor, abs = math.max, math.min, math.floor, math.abs\
- \
- local function getpixel( options )\
- \9return { options.colour or 0, options.textColour or 0, options.character or \"\" }\
- end\
- local function isColourOnly( options )\
- \9return options.textColour == nil and options.character == nil\
- end\
- \
- local function pointInPolygon( polyCorners, polyX, polyY, x, y, d )\
- \9local oddNodes = false\
- \9local j = polyCorners\
- \
- \9for i = 1, polyCorners do\
- \9\9local polyYi = polyY[i]\
- \9\9if ( ( polyYi < y and polyY[j] >= y ) or ( polyY[j] < y and polyYi >= y ) ) then\
- \9\9\9if polyX[i] + d[i] * ( y - polyYi ) < x then\
- \9\9\9\9oddNodes = not oddNodes\
- \9\9\9end\
- \9\9end\
- \9\9j = i\
- \9end\
- \
- \9return oddNodes\
- end\
- \
- local function linewrap( str, w )\
- \9if w <= 0 then\
- \9\9return \"\", #str > 1 and str:sub( 2 )\
- \9end\
- \9for i = 1, w + 1 do\
- \9\9if str:sub( i, i ) == \"\\n\" then\
- \9\9\9return str:sub( 1, i - 1 ), str:sub( i + 1 )\
- \9\9end\
- \9end\
- \9if #str <= w then\
- \9\9return str\
- \9end\
- \9for i = w + 1, 1, -1 do\
- \9\9if str:sub( i, i ):find \"%s\" then\
- \9\9\9return str:sub( 1, i - 1 ) .. str:match( \"%s+\", i ), str:match( \"%S.+$\", i + 1 )\
- \9\9end\
- \9end\
- \9return str:sub( 1, w )\
- end\
- \
- local function wordwrap( str, w, h )\
- \9local lines, line = {}\
- \9while str do\
- \9\9line, str = linewrap( str, w )\
- \9\9lines[#lines + 1] = line\
- \9end\
- \9while h and #lines > math.max( h, 1 ) do\
- \9\9lines[#lines] = nil\
- \9end\
- \9return lines\
- end\
- \
- local function drawPixels( self, pixels, options )\
- \9if isColourOnly( options ) then\
- \9\9self:mapColour( pixels, options.colour or 1 )\
- \9else\
- \9\9self:mapStaticPixel( pixels, getpixel( options ) )\
- \9end\
- end\
- \
- local function drawRectOutline( self, x, y, width, height, options )\
- \9local pixels = {}\
- \9local swidth, sheight = self.width, self.height\
- \9local weight = options.weight or 1\
- \9local i = 1\
- \
- \9-- horizontal lines\
- \9for n = 0, weight - 1 do\
- \9\9local pos1 = ( y + n ) * swidth + x + 1\
- \9\9local pos2 = ( y + height - n - 1 ) * swidth + x + 1\
- \9\9for m = 1, width do\
- \9\9\9if x + m <= swidth and x + m > 0 then\
- \9\9\9\9pixels[i] = pos1\
- \9\9\9\9pixels[i + 1] = pos2\
- \9\9\9\9i = i + 2\
- \9\9\9end\
- \9\9\9pos1 = pos1 + 1\
- \9\9\9pos2 = pos2 + 1\
- \9\9end\
- \9end\
- \9-- vertical lines\
- \9for n = weight, height - weight - 1 do\
- \9\9local pos = ( y + n ) * swidth + x\
- \9\9for m = 0, weight - 1 do\
- \9\9\9if x + m < swidth and x + m > 0 then\
- \9\9\9\9pixels[i] = pos + m + 1\
- \9\9\9\9i = i + 1\
- \9\9\9end\
- \9\9\9if x + width - m < swidth and x + width - m > 0 then\
- \9\9\9\9pixels[i] = pos + width - m\
- \9\9\9\9i = i + 1\
- \9\9\9end\
- \9\9end\
- \9end\
- \
- \9drawPixels( self, pixels, options )\
- end\
- local function drawRectFilled( self, x, y, width, height, options )\
- \9local pixels = {}\
- \9local swidth, sheight = self.width, self.height\
- \9local i = 1\
- \
- \9if x < 0 then\
- \9\9width = width + x\
- \9\9x = 0\
- \9end\
- \9if x + width >= swidth then\
- \9\9width = swidth - x\
- \9end\
- \
- \9for _ = 1, height do\
- \9\9local pos = y * swidth + x + 1\
- \9\9y = y + 1\
- \9\9for _ = 1, width do\
- \9\9\9pixels[i] = pos\
- \9\9\9i = i + 1\
- \9\9\9pos = pos + 1\
- \9\9end\
- \9end\
- \9drawPixels( self, pixels, options )\
- end\
- \
- local function drawCircOutline( self, x, y, radius, options )\
- \9local pixels = {}\
- \9local swidth = self.width\
- \9local weight = options.weight or 1\
- \9local i = 1\
- \
- \9local r2 = radius * radius\
- \
- \9for py = 0, radius do\
- \9\9local ix = floor( ( r2 - py ^ 2 ) ^ .5 * self.correction + .5 )\
- \9\9local pos11 = ( y - py ) * swidth + x - ix + 1\
- \9\9local pos21 = ( y + py ) * swidth + x - ix + 1\
- \9\9local pos12 = ( y - py ) * swidth + x + ix + 1\
- \9\9local pos22 = ( y + py ) * swidth + x + ix + 1\
- \9\9for _ = 0, weight do\
- \9\9\9pixels[i] = pos11\
- \9\9\9pixels[i + 1] = pos21\
- \9\9\9pixels[i + 2] = pos12\
- \9\9\9pixels[i + 3] = pos22\
- \9\9\9pos11 = pos11 + 1\
- \9\9\9pos21 = pos21 + 1\
- \9\9\9pos12 = pos12 - 1\
- \9\9\9pos22 = pos22 - 1\
- \9\9\9i = i + 4\
- \9\9end\
- \9end\
- \
- \9drawPixels( self, pixels, options )\
- end\
- local function drawCircFilled( self, x, y, radius, options )\
- \9local pixels = {}\
- \9local swidth = self.width\
- \9local i = 1\
- \
- \9local r2 = radius * radius\
- \
- \9for py = 1, radius do\
- \9\9local ix = floor( ( r2 - py ^ 2 ) ^ .5 * self.correction + .5 )\
- \9\9local pos1 = ( y - py ) * swidth + max( 0, x - ix ) + 1\
- \9\9local pos2 = ( y + py ) * swidth + max( 0, x - ix ) + 1\
- \9\9for _ = max( 0, x - ix ), min( swidth - 1, x + ix ) do\
- \9\9\9pixels[i] = pos1\
- \9\9\9pixels[i + 1] = pos2\
- \9\9\9pos1 = pos1 + 1\
- \9\9\9pos2 = pos2 + 1\
- \9\9\9i = i + 2\
- \9\9end\
- \9end\
- \
- \9local rc = floor( radius * self.correction + .5 )\
- \9local pos = y * swidth + max( 0, x - rc ) + 1\
- \9for _ = max( 0, x - rc ), min( swidth - 1, x + rc ) do\
- \9\9pixels[i] = pos\
- \9\9pos = pos + 1\
- \9\9i = i + 1\
- \9end\
- \
- \9drawPixels( self, pixels, options )\
- end\
- \
- class \"DrawingCanvas\" extends \"Canvas\" {\
- \9correction = cclite and 1.5 or 1.67;\
- }\
- \
- function DrawingCanvas:drawPoint( x, y, options )\
- \
- \9local weight = options.weight or 1\
- \9if weight == 1 then\
- \9\9self.buffer[y * self.width + x + 1] = getpixel( options )\
- \9else\
- \9\9self:drawCircle( x, y, weight - 1, {\
- \9\9\9colour = colour;\
- \9\9\9textColour = textColour;\
- \9\9\9character = character;\
- \9\9\9filled = true;\
- \9\9} )\
- \9end\
- \
- end\
- \
- function DrawingCanvas:drawRectangle( x, y, width, height, options )\
- \
- \9if options.filled or options.outline == false then\
- \9\9drawRectFilled( self, x, y, width, height, options )\
- \9else\
- \9\9drawRectOutline( self, x, y, width, height, options )\
- \9end\
- \
- end\
- \
- function DrawingCanvas:drawCircle( x, y, radius, options )\
- \
- \9if options.filled or options.outline == false then\
- \9\9drawCircFilled( self, x, y, radius, options )\
- \9else\
- \9\9drawCircOutline( self, x, y, radius, options )\
- \9end\
- \
- end\
- \
- function DrawingCanvas:drawLine( x1, y1, x2, y2, options )\
- \
- \9local dx, dy = x2 - x1, y2 - y1\
- \9if abs( dx ) < .0001 then\
- \9\9return self:drawVerticalLine( x1, min( y1, y2 ), abs( y2 - y1 ) + 1, options )\
- \9elseif abs( dy ) < .0001 then\
- \9\9return self:drawHorizontalLine( min( x1, x2 ), y1, abs( x2 - x1 ) + 1, options )\
- \9end\
- \
- \9local m = dy / dx\
- \9local c = y1 - m * x1 + .5\
- \9local step = min( 1, 1 / abs( m ) )\
- \
- \9-- draw 2 circles at ends\
- \9-- semicircles, actually\
- \
- \9for x = min( x1, x2 ), max( x1, x2 ), step do\
- \9\9local y = floor( m * x + c )\
- \9\9\
- \9\9-- draw line between x-r and x+r at this y\
- \9end\
- \
- end\
- \
- function DrawingCanvas:drawHorizontalLine( x, y, width, options )\
- \
- \9local weight = options.weight or 1\
- \9local swidth = self.width\
- \9local pixels = {}\
- \9local radius = math.floor( weight / 2 - .5 )\
- \9local r2 = radius * radius\
- \9local i = 1\
- \
- \9for r = 0, radius do\
- \9\9local ix = radius - floor( ( r2 - r ^ 2 ) ^ .5 * self.correction + .5 )\
- \9\9local pos1 = ( y + r ) * swidth + x + ix + 1\
- \9\9local pos2 = ( y - r ) * swidth + x + ix + 1\
- \9\9for _ = x + ix, x + width - ix - 1 do\
- \9\9\9pixels[i] = pos1\
- \9\9\9pixels[i + 1] = pos2\
- \9\9\9pos1 = pos1 + 1\
- \9\9\9pos2 = pos2 + 1\
- \9\9\9i = i + 2\
- \9\9end\
- \9end\
- \
- \9drawPixels( self, pixels, options )\
- \
- end\
- \
- function DrawingCanvas:drawVerticalLine( x, y, size, options )\
- \
- \9local weight = options.weight or 1\
- \9local swidth = self.width\
- \9local pixels = {}\
- \9local radius = math.floor( weight / 2 - .5 )\
- \9local r2 = radius * radius\
- \9local i = 1\
- \
- \9for _y = 0, size - 1 do\
- \9\9if _y < radius then\
- \
- \9\9elseif _y >= size - radius then\
- \
- \9\9else\
- \9\9\9local pos = ( y + _y ) * swidth + x - radius + 1\
- \9\9\9for _ = 1, weight do\
- \9\9\9\9pixels[i] = pos\
- \9\9\9\9pos = pos + 1\
- \9\9\9\9i = i + 1\
- \9\9\9end\
- \9\9end\
- \9end\
- \
- \9drawPixels( self, pixels, options )\
- \
- end\
- \
- function DrawingCanvas:drawTriangle()\
- \
- end\
- \
- function DrawingCanvas:drawTopLeftTriangle()\
- \
- end\
- \
- function DrawingCanvas:drawTopRightTriangle()\
- \
- end\
- \
- function DrawingCanvas:drawBottomLeftTriangle()\
- \
- end\
- \
- function DrawingCanvas:drawBottomRightTriangle()\
- \
- end\
- \
- function DrawingCanvas:drawText( x, y, options )\
- \
- \9local bc = options.colour or 0\
- \9local tc = options.textColour or 0\
- \9local pixels = {}\
- \9local width = self.width\
- \9local pos = y * width + x\
- \
- \9for i = math.max( 1, 1 - x ), #options.text do\
- \9\9if x + i > width then\
- \9\9\9break\
- \9\9end\
- \9\9pixels[#pixels + 1] = { pos + i, { bc, tc, options.text:sub( i, i ) } }\
- \9end\
- \
- \9self:drawPixels( pixels )\
- end\
- \
- function DrawingCanvas:drawWrappedText( x, y, width, height, options )\
- \
- \9local bc = options.colour or 0\
- \9local tc = options.textColour or 0\
- \9local pixels = {}\
- \9local alignment = options.alignment\
- \9local verticalAlignment = options.verticalAlignment\
- \9local lines = wordwrap( options.text, width, height )\
- \9local cwidth = self.width\
- \9local pos = ( ( verticalAlignment == \"centre\" and math.floor( height / 2 - #lines / 2 + .5 ) or ( verticalAlignment == \"bottom\" and height - #lines ) or 0 ) + y ) * cwidth + x\
- \9local i = 1\
- \
- \9for line = 1, #lines do\
- \9\9local offset = ( alignment == \"centre\" and math.floor( width / 2 - #lines[line] / 2 + .5 ) ) or ( alignment == \"right\" and width - #lines[line] ) or 0\
- \9\9for c = 1, #lines[line] do\
- \9\9\9pixels[i] = { pos + c + offset, { bc, tc, lines[line]:sub( c, c ) } }\
- \9\9\9i = i + 1\
- \9\9end\
- \9\9pos = pos + cwidth\
- \9end\
- \
- \9self:drawPixels( pixels )\
- \
- end\
- \
- function DrawingCanvas:drawPreformattedText( x, y, width, height, options )\
- \
- \9local pixels = {}\
- \9local verticalAlignment = options.verticalAlignment\
- \9local selectedBC = options.selectedColour or colours.blue\
- \9local selectedTC = options.selectedTextColour or 1\
- \9local lines = options.text\
- \9local cwidth = self.width\
- \9local pos = ( ( ( verticalAlignment == \"centre\" and math.floor( height / 2 - #lines / 2 + .5 ) ) or ( verticalAlignment == \"bottom\" and height - #lines ) or 0 ) + y ) * cwidth + x\
- \9local i = 1\
- \
- \9for l = 1, #lines do\
- \9\9local line = lines[l]\
- \9\9local offset = ( line.alignment == \"centre\" and math.floor( width / 2 - #line / 2 + .5 ) ) or ( line.alignment == \"right\" and width - #line ) or 0\
- \9\9for c = 1, #line do\
- \9\9\9if c + x + offset >= 0 and c + x + offset <= cwidth then\
- \9\9\9\9if line[c][3] ~= \"\\n\" then\
- \9\9\9\9\9if line[c].selected then\
- \9\9\9\9\9\9pixels[i] = { pos + c + offset, { selectedBC, selectedTC, line[c][3] } }\
- \9\9\9\9\9else\
- \9\9\9\9\9\9pixels[i] = { pos + c + offset, line[c] }\
- \9\9\9\9\9end\
- \9\9\9\9\9i = i + 1\
- \9\9\9\9end\
- \9\9\9end\
- \9\9end\
- \9\9pos = pos + cwidth\
- \9end\
- \
- \9self:drawPixels( pixels )\
- \
- end";
- ["/lib/graphics/Image.lua"] = "\
- require \"graphics.Canvas\"\
- \
- local col = {}\
- for i = 0, 15 do\
- \9col[(\"%x\"):format( i )] = 2 ^ i\
- \9col[2 ^ i] = (\"%x\"):format( i )\
- end\
- \
- class \"Image\" extends \"Canvas\" {\
- \9filepath = \"\";\
- }\
- \
- function Image:init( filepath )\
- \9self.super:init( 0, 0 )\
- \9if not fs.exists( filepath ) or fs.isDir( filepath ) then\
- \9\9error( \"path is not a file\", 3 )\
- \9end\
- \9self.filepath = filepath\
- \9if filepath:find \"%.nfp$\" then\
- \9\9self:loadNFP()\
- \9else\
- \9\9self:load()\
- \9end\
- end\
- \
- function Image:loadNFP()\
- \9local lines = {}\
- \9local h = fs.open( self.filepath, \"r\" )\
- \9local content = h.readAll()\
- \9h.close()\
- \9for line in content:gmatch \"[^\\n]+\" do\
- \9\9lines[#lines + 1] = line\
- \9end\
- \
- \9local width, height = #( lines[1] or \"\" ), #lines\
- \9self.width = width\
- \9self.height = height\
- \
- \9local pixels = {}\
- \9local i = 1\
- \9for y = 1, #lines do\
- \9\9local pos = ( y - 1 ) * width\
- \9\9for x = 1, #lines[y] do\
- \9\9\9pixels[i] = { pos + x, { col[lines[y]:sub( x, x )] or 0, 1, \" \" } }\
- \9\9\9i = i + 1\
- \9\9end\
- \9end\
- \9self:mapPixels( pixels )\
- end\
- \
- function Image:load()\
- \
- end\
- \
- function Image:saveNFP()\
- \
- end\
- \
- function Image:save()\
- \
- end";
- ["/lib/graphics/ScreenCanvas.lua"] = "\
- require \"graphics.Canvas\"\
- \
- local col_lookup = {}\
- for i = 0, 15 do\
- \9col_lookup[2 ^ i] = (\"%x\"):format( i )\
- end\
- local concat = table.concat\
- local insert = table.insert\
- local remove = table.remove\
- local min, max = math.min, math.max\
- local unpack = unpack\
- \
- class \"ScreenCanvas\" extends \"Canvas\" {\
- \9colour = 1;\
- \9last = nil;\
- }\
- \
- function ScreenCanvas:init( ... )\
- \9self.super:init( ... )\
- \9self.last = {}\
- \9for i = 1, self.width * self.height do\
- \9\9self.last[i] = {}\
- \9end\
- end\
- \
- function ScreenCanvas:setWidth( width )\
- \9local height, buffer, raw = self.height, self.buffer, self.raw\
- \9local last = self.last\
- \9local px = { 0, 1, \" \" }\
- \
- \9while raw.width < width do\
- \9\9for i = 1, height do\
- \9\9\9insert( last, ( raw.width + 1 ) * i, {} )\
- \9\9\9insert( buffer, ( raw.width + 1 ) * i, px )\
- \9\9end\
- \9\9raw.width = raw.width + 1\
- \9end\
- \
- \9while raw.width > width do\
- \9\9for i = height, 1, -1 do\
- \9\9\9remove( last, raw.width * i )\
- \9\9\9remove( buffer, raw.width * i )\
- \9\9end\
- \9\9raw.width = raw.width - 1\
- \9end\
- end\
- \
- function ScreenCanvas:setHeight( height )\
- \9local width, buffer, raw = self.width, self.buffer, self.raw\
- \9local last = self.last\
- \9local px = { 0, 1, \" \" }\
- \
- \9local px = { 0, 1, \" \" }\
- \9while raw.height < height do\
- \9\9for i = 1, width do\
- \9\9\9last[#last + 1] = {}\
- \9\9\9buffer[#buffer + 1] = px\
- \9\9end\
- \9\9raw.height = raw.height + 1\
- \9end\
- \
- \9while raw.height > height do\
- \9\9for i = 1, width do\
- \9\9\9last[#last] = nil\
- \9\9\9buffer[#buffer] = nil\
- \9\9end\
- \9\9raw.height = raw.height - 1\
- \9end\
- end\
- \
- function ScreenCanvas:drawToTerminal( term, _x, _y )\
- \9_x = ( _x or 0 ) + self.x\
- \9_y = ( _y or 0 ) + self.y\
- \9local pos = 1\
- \9local width = self.width\
- \9local buffer = self.buffer\
- \9local last = self.last\
- \
- \9local blit, setCursorPos = term.blit, term.setCursorPos\
- \
- \9for y = 1, self.height do\
- \9\9local px\
- \9\9local text, bc, tc = {}, {}, {}\
- \9\9for x = 1, width do\
- \9\9\9local pxl = buffer[pos]\
- \9\9\9local lst = last[pos]\
- \9\9\9if pxl[1] ~= lst[1] or pxl[2] ~= lst[2] or pxl[3] ~= lst[3] then\
- \9\9\9\9if not px then\
- \9\9\9\9\9px = x\
- \9\9\9\9\9tx = x\
- \9\9\9\9\9setCursorPos( _x + x, y + _y )\
- \9\9\9\9end\
- \9\9\9\9bc[#bc + 1] = col_lookup[pxl[1]]\
- \9\9\9\9tc[#tc + 1] = col_lookup[pxl[2]]\
- \9\9\9\9text[#text + 1] = #pxl[3] == 0 and \" \" or pxl[3]\
- \9\9\9\9last[pos] = buffer[pos]\
- \9\9\9elseif px then\
- \9\9\9\9blit( concat( text ), concat( tc ), concat( bc ) )\
- \9\9\9\9px = nil\
- \9\9\9\9text = {}\
- \9\9\9\9tc = {}\
- \9\9\9\9bc = {}\
- \9\9\9end\
- \9\9\9pos = pos + 1\
- \9\9end\
- \9\9blit( concat( text ), concat( tc ), concat( bc ) )\
- \9end\
- end\
- \
- function ScreenCanvas:drawToScreen( x, y )\
- \9return self:drawToTerminal( term, x, y )\
- end";
- ["/lib/graphics/TermCanvas.lua"] = "\
- require \"graphics.Canvas\"\
- \
- local col_lookup = {}\
- for i = 0, 15 do\
- \9col_lookup[(\"%x\"):format( i ):byte()] = 2 ^ i\
- end\
- \
- local isColour = term.isColour()\
- \
- class \"TermCanvas\" extends \"Canvas\" {\
- \9colour = 32768;\
- \9term_bc = 32768;\
- \9term_tc = 1;\
- \9term_x = 1;\
- \9term_y = 1;\
- \9term_cb = false;\
- }\
- \
- function TermCanvas:getTermRedirect()\
- \9local term = {}\
- \
- \9function term.write( s )\
- \9\9s = tostring( s )\
- \9\9local pos = ( self.term_y - 1 ) * self.width + self.term_x\
- \9\9local pixels = {}\
- \9\9local bc, tc = self.term_bc, self.term_tc\
- \9\9for i = 1, math.min( #s, self.width - self.term_x + 1 ) do\
- \9\9\9pixels[#pixels + 1] = { pos, { bc, tc, s:sub( i, i ) } }\
- \9\9\9pos = pos + 1\
- \9\9end\
- \9\9self.term_x = self.term_x + #s\
- \9\9self:mapPixels( pixels )\
- \9end\
- \9function term.blit( s, t, b )\
- \9\9if #s ~= #b or #s ~= #t then\
- \9\9\9return error \"arguments must be the same length\"\
- \9\9end\
- \9\9local pixels = {}\
- \9\9local pos = ( self.term_y - 1 ) * self.width + self.term_x\
- \9\9for i = 1, math.min( #s, self.width - self.term_x + 1 ) do\
- \9\9\9pixels[#pixels + 1] = { pos, { col_lookup[b:byte( i )], col_lookup[t:byte( i )], s:sub( i, i ) } }\
- \9\9\9pos = pos + 1\
- \9\9end\
- \9\9self.term_x = self.term_x + #s\
- \9\9self:mapPixels( pixels )\
- \9end\
- \
- \9function term.clear()\
- \9\9self:clear( self.term_bc )\
- \9end\
- \9function term.clearLine()\
- \9\9local px = { self.term_bc, 1, \" \" }\
- \9\9local pixels = {}\
- \9\9local offset = self.width * ( self.term_y - 1 )\
- \9\9for i = 1, self.width do\
- \9\9\9pixels[#pixels + 1] = { i + offset, px }\
- \9\9end\
- \9\9self:mapPixels( pixels )\
- \9end\
- \
- \9function term.getCursorPos()\
- \9\9return self.term_x, self.term_y\
- \9end\
- \9function term.setCursorPos( x, y )\
- \9\9self.term_x = math.floor( x )\
- \9\9self.term_y = math.floor( y )\
- \9end\
- \
- \9function term.setCursorBlink( state )\
- \9\9self.term_cb = state\
- \9end\
- \
- \9function term.getSize()\
- \9\9return self.width, self.height\
- \9end\
- \
- \9function term.scroll( n )\
- \9\9local buffer = self.buffer\
- \9\9local offset = n * self.width\
- \9\9local n, f, s = n < 0 and self.width * self.height or 1, n < 0 and 1 or self.width * self.height, n < 0 and -1 or 1\
- \9\9local pixels = {}\
- \9\9local px = { self.term_bc, self.term_tc, \" \" }\
- \9\9for i = n, f, s do\
- \9\9\9pixels[#pixels + 1] = { i, buffer[i + offset] or px }\
- \9\9end\
- \9\9self:mapPixels( pixels )\
- \9end\
- \
- \9function term.isColour()\
- \9\9return isColour\
- \9end\
- \
- \9function term.setBackgroundColour( colour )\
- \9\9self.term_bc = colour\
- \9end\
- \9function term.setTextColour( colour )\
- \9\9self.term_tc = colour\
- \9end\
- \9function term.getBackgroundColour()\
- \9\9return self.term_bc\
- \9end\
- \9function term.getTextColour()\
- \9\9return self.term_tc\
- \9end\
- \
- \9term.isColor = term.isColour\
- \9term.setBackgroundColor = term.setBackgroundColour\
- \9term.setTextColor = term.setTextColour\
- \9term.getBackgroundColor = term.getBackgroundColour\
- \9term.getTextColor = term.getTextColour\
- \
- \9return term\
- end\
- \
- function TermCanvas:drawTo( other, x, y )\
- \9self.super:drawTo( other, x, y )\
- \9if self.term_cb then\
- \9\9other.term_cb = true\
- \9\9other.term_x = self.term_x + ( x or 1 ) - 1\
- \9\9other.term_y = self.term_y + ( y or 1 ) - 1\
- \9end\
- end\
- \
- function TermCanvas:redirect()\
- \9return term.redirect( self:getTermRedirect() )\
- end\
- \
- function TermCanvas:wrap( f )\
- \9local old = self:redirect()\
- \9f()\
- \9term.redirect( old )\
- end";
- ["/lib/graphics/shader.lua"] = "\
- local shader = {}\
- \
- shader.darken = {\
- \9[colours.white] = colours.lightGrey, [colours.orange] = colours.brown, [colours.magenta] = colours.purple, [colours.lightBlue] = colours.cyan;\
- \9[colours.yellow] = colours.orange, [colours.lime] = colours.green, [colours.pink] = colours.magenta, [colours.grey] = colours.black;\
- \9[colours.lightGrey] = colours.grey, [colours.cyan] = colours.blue, [colours.purple] = colours.grey, [colours.blue] = colours.grey;\
- \9[colours.brown] = colours.black, [colours.green] = colours.grey, [colours.red] = colours.brown, [colours.black] = colours.black;\
- }\
- shader.lighten = {\
- \9[colours.white] = colours.white, [colours.orange] = colours.yellow, [colours.magenta] = colours.pink, [colours.lightBlue] = colours.white;\
- \9[colours.yellow] = colours.white, [colours.lime] = colours.white, [colours.pink] = colours.white, [colours.grey] = colours.lightGrey;\
- \9[colours.lightGrey] = colours.white, [colours.cyan] = colours.lightBlue, [colours.purple] = colours.magenta, [colours.blue] = colours.cyan;\
- \9[colours.brown] = colours.red, [colours.green] = colours.lime, [colours.red] = colours.orange, [colours.black] = colours.grey;\
- }\
- shader.greyscale = {\
- \9[colours.white] = 1, [colours.orange] = 256, [colours.magenta] = 256, [colours.lightBlue] = 256;\
- \9[colours.yellow] = 1, [colours.lime] = 256, [colours.pink] = 1, [colours.grey] = 256;\
- \9[colours.lightGrey] = 256, [colours.cyan] = 128, [colours.purple] = 128, [colours.blue] = 32768;\
- \9[colours.brown] = 32768, [colours.green] = 128, [colours.red] = 128, [colours.black] = 32768;\
- }\
- shader.sepia = {\
- \9[colours.white] = 1, [colours.orange] = 2, [colours.magenta] = 2, [colours.lightBlue] = 2;\
- \9[colours.yellow] = 1, [colours.lime] = 2, [colours.pink] = 1, [colours.grey] = 2;\
- \9[colours.lightGrey] = 2, [colours.cyan] = 16, [colours.purple] = 16, [colours.blue] = 4096;\
- \9[colours.brown] = 4096, [colours.green] = 16, [colours.red] = 16, [colours.black] = 4096;\
- }\
- \
- return shader";
- ["/lib/util/UIDrawingHelpers.lua"] = "\
- -- TODO:\
- \9-- add colour support\
- \
- local UIEventHelpers = require \"util.UIEventHelpers\"\
- \
- local UIDrawingHelpers = {}\
- UIDrawingHelpers.scrollbar = {}\
- \
- function UIDrawingHelpers.scrollbar:drawScrollbars()\
- \
- \9local scrollright, scrollbottom = UIEventHelpers.scrollbar.active( self )\
- \9local rpos, rsize = UIEventHelpers.scrollbar.getBarInfo( self, \"right\", scrollright, scrollbottom, x, y )\
- \9local bpos, bsize = UIEventHelpers.scrollbar.getBarInfo( self, \"bottom\", scrollright, scrollbottom, x, y )\
- \
- \9if scrollright then\
- \9\9self.canvas:drawVerticalLine( self.width - 1, 0, scrollbottom and self.height - 1 or self.height, {\
- \9\9\9colour = colours.grey;\
- \9\9} )\
- \9\9self.canvas:drawVerticalLine( self.width - 1, rpos, rsize, {\
- \9\9\9colour = self.scrollbar_mounted_side == \"right\" and colours.lightBlue or colours.lightGrey\
- \9\9} )\
- \9end\
- \9if scrollbottom then\
- \9\9self.canvas:drawHorizontalLine( 0, self.height - 1, scrollright and self.width - 1 or self.width, {\
- \9\9\9colour = colours.grey;\
- \9\9} )\
- \9\9self.canvas:drawHorizontalLine( bpos, self.height - 1, bsize, {\
- \9\9\9colour = self.scrollbar_mounted_side == \"bottom\" and colours.lightBlue or colours.lightGrey\
- \9\9} )\
- \9end\
- \9if scrollright and scrollbottom then\
- \9\9self.canvas:drawPoint( self.width - 1, self.height - 1, {\
- \9\9\9colour = colours.grey;\
- \9\9} )\
- \9end\
- \
- end\
- \
- return UIDrawingHelpers";
- ["/lib/util/UIEventHelpers.lua"] = "\
- require \"Event.Event\"\
- \
- local clipboard = require \"clipboard\"\
- \
- local UIEventHelpers = {}\
- UIEventHelpers.clicking = {}\
- UIEventHelpers.scrollbar = {}\
- UIEventHelpers.scrollbar.mixin = {}\
- UIEventHelpers.textSelection = {}\
- \
- function UIEventHelpers.clicking:handleMouseEvent( event )\
- \
- \9if event.handled then return end\
- \
- \9if event.name == Event.MOUSEDOWN and event:isInArea( 0, 0, self.width, self.height ) then\
- \9\9self.clicking = {\
- \9\9\9button = event.button;\
- \9\9\9moved = false;\
- \9\9}\
- \9\9event.handled = true\
- \9\9return \"down\"\
- \
- \9elseif event.name == Event.MOUSEDRAG and self.clicking then\
- \9\9if not event.handled and event:isInArea( 0, 0, self.width, self.height ) then\
- \9\9\9event.handled = true\
- \9\9end\
- \9\9self.clicking.moved = true\
- \
- \9elseif event.name == Event.MOUSEUP and self.clicking then\
- \9\9if event.button == self.clicking.button then\
- \9\9\9event.handled = true\
- \9\9\9if event:isInArea( 0, 0, self.width, self.height ) and not self.clicking.moved then\
- \9\9\9\9self.clicking = nil\
- \9\9\9\9return \"click\"\
- \9\9\9end\
- \9\9\9self.clicking = nil\
- \9\9\9return \"up\"\
- \9\9end\
- \
- \9end\
- \
- end\
- \
- function UIEventHelpers.scrollbar:active()\
- \
- \9local cw, ch = self:getContentWidth(), self:getContentHeight()\
- \9local dw, dh = self:getDisplayWidth(), self:getDisplayHeight()\
- \9if cw > dw or ch > dh then\
- \9\9return ch > dh - 1, cw > dw - 1\
- \9end\
- \9return false, false\
- \
- end\
- \
- function UIEventHelpers.scrollbar:getBarInfo( side, scrollright, scrollbottom ) -- pos, size\
- \9local traysize = side == \"bottom\" and self.width - ( scrollright and 1 or 0 ) or self.height - ( scrollbottom and 1 or 0 )\
- \9local contentsize = side == \"bottom\" and self:getContentWidth() or self:getContentHeight()\
- \9local scale = traysize / contentsize\
- \
- \9local displaysize = ( side == \"bottom\" and self:getDisplayWidth() - ( scrollright and 1 or 0 ) or self:getDisplayHeight() - ( scrollbottom and 1 or 0 ) )\
- \9local scroll = ( side == \"bottom\" and self:getHorizontalOffset() or self:getVerticalOffset() )\
- \
- \9return contentsize == 0 and 0 or math.floor( scroll * scale + .5 ), contentsize == 0 and 0 or math.floor( displaysize * scale + .5 )\
- end\
- \
- function UIEventHelpers.scrollbar:updateScrollbarPosition( side, pos, scrollright, scrollbottom )\
- \9local traysize = side == \"bottom\" and self.width - ( scrollright and 1 or 0 ) or self.height - ( scrollbottom and 1 or 0 )\
- \9local contentsize = side == \"bottom\" and self:getContentWidth() or self:getContentHeight()\
- \9local displaysize = ( side == \"bottom\" and self:getDisplayWidth() - ( scrollright and 1 or 0 ) or self:getDisplayHeight() - ( scrollbottom and 1 or 0 ) )\
- \9local newpos = math.floor( ( pos - self.scrollbar_mounted ) / traysize * contentsize + .5 )\
- \
- \9if side == \"right\" then\
- \9\9self:setVerticalOffset( math.max( 0, math.min( contentsize - displaysize, newpos ) ) )\
- \9else\
- \9\9self:setHorizontalOffset( math.max( 0, math.min( contentsize - displaysize, newpos ) ) )\
- \9end\
- end\
- \
- function UIEventHelpers.scrollbar:handleMouseEvent( event )\
- \9if event.handled then return end\
- \
- \9local x, y = event.x, event.y\
- \9local scrollright, scrollbottom = UIEventHelpers.scrollbar.active( self )\
- \
- \9if event.name == Event.MOUSEDOWN and event:isInArea( 0, 0, self.width, self.height ) then\
- \9\9if scrollright and x == self.width - 1 then\
- \9\9\9local pos, size = UIEventHelpers.scrollbar.getBarInfo( self, \"right\", scrollright, scrollbottom )\
- \9\9\9if y <= pos then\
- \9\9\9\9self.scrollbar_mounted = 0\
- \9\9\9\9self.scrollbar_mounted_side = \"right\"\
- \
- \9\9\9elseif y >= pos + size then\
- \9\9\9\9self.scrollbar_mounted = size - 1\
- \9\9\9\9self.scrollbar_mounted_side = \"right\"\
- \
- \9\9\9else\
- \9\9\9\9self.scrollbar_mounted = y - pos\
- \9\9\9\9self.scrollbar_mounted_side = \"right\"\
- \
- \9\9\9end\
- \9\9\9self.scrollbar_scrollright = scrollright\
- \9\9\9self.scrollbar_scrollbottom = scrollbottom\
- \9\9\9UIEventHelpers.scrollbar.updateScrollbarPosition( self, \"right\", y, scrollright, scrollbottom )\
- \9\9\9event.handled = true\
- \
- \9\9elseif scrollbottom and y == self.height - 1 then\
- \9\9\9local pos, size = UIEventHelpers.scrollbar.getBarInfo( self, \"bottom\", scrollright, scrollbottom )\
- \9\9\9if x < pos then\
- \9\9\9\9self.scrollbar_mounted = 0\
- \9\9\9\9self.scrollbar_mounted_side = \"bottom\"\
- \
- \9\9\9elseif x >= pos + size then\
- \9\9\9\9self.scrollbar_mounted = size - 1\
- \9\9\9\9self.scrollbar_mounted_side = \"bottom\"\
- \
- \9\9\9else\
- \9\9\9\9self.scrollbar_mounted = x - pos\
- \9\9\9\9self.scrollbar_mounted_side = \"bottom\"\
- \
- \9\9\9end\
- \9\9\9self.scrollbar_scrollright = scrollright\
- \9\9\9self.scrollbar_scrollbottom = scrollbottom\
- \9\9\9UIEventHelpers.scrollbar.updateScrollbarPosition( self, \"bottom\", x, scrollright, scrollbottom )\
- \9\9\9event.handled = true\
- \
- \9\9end\
- \9elseif event.name == Event.MOUSEDRAG and self.scrollbar_mounted then\
- \9\9local side = self.scrollbar_mounted_side\
- \9\9UIEventHelpers.scrollbar.updateScrollbarPosition( self, side, side == \"right\" and y or x, self.scrollbar_scrollright, self.scrollbar_scrollbottom )\
- \9\9event.handled = true\
- \
- \9elseif event.name == Event.MOUSEUP and self.scrollbar_mounted then\
- \9\9self.scrollbar_mounted = nil\
- \9\9self.scrollbar_mounted_side = nil\
- \9\9event.handled = true\
- \9\9self.changed = true\
- \
- \9end\
- end\
- \
- function UIEventHelpers.scrollbar:handleMouseScroll( event )\
- \
- \9if not event.handled and event.name == Event.MOUSESCROLL and event:isInArea( 0, 0, self.width, self.height ) then\
- \
- \9\9local right, bottom = UIEventHelpers.scrollbar.active( self )\
- \9\9local max = self:getContentHeight() - self.height + ( bottom and 1 or 0 )\
- \9\9if event.button == 1 and self:getVerticalOffset() < max then\
- \9\9\9self:setVerticalOffset( math.max( 0, math.min( max, self:getVerticalOffset() + 1 ) ) )\
- \9\9\9event.handled = true\
- \9\9elseif event.button == -1 and self:getVerticalOffset() > 0 then\
- \9\9\9self:setVerticalOffset( math.max( 0, math.min( max, self:getVerticalOffset() - 1 ) ) )\
- \9\9\9event.handled = true\
- \9\9end\
- \
- \9end\
- end\
- \
- function UIEventHelpers.scrollbar.mixin:getDisplayWidth()\
- \9return self.width\
- end\
- function UIEventHelpers.scrollbar.mixin:getDisplayHeight()\
- \9return self.height\
- end\
- \
- function UIEventHelpers.scrollbar.mixin:getContentWidth()\
- \9return 0\
- end\
- function UIEventHelpers.scrollbar.mixin:getContentHeight()\
- \9return 0\
- end\
- \
- function UIEventHelpers.scrollbar.mixin:getHorizontalOffset()\
- \9return -self.ox\
- end\
- function UIEventHelpers.scrollbar.mixin:getVerticalOffset()\
- \9return -self.oy\
- end\
- function UIEventHelpers.scrollbar.mixin:setHorizontalOffset( scroll )\
- \9self.ox = -scroll\
- end\
- function UIEventHelpers.scrollbar.mixin:setVerticalOffset( scroll )\
- \9self.oy = -scroll\
- end\
- \
- function UIEventHelpers.textSelection:getCharacter( x, y, lines, alignment, width, height )\
- \
- \9local line = math.max( 1, math.min( #lines, 1 + y - ( ( alignment == \"centre\" and math.floor( height / 2 - #lines / 2 + .5 ) ) or ( alignment == \"bottom\" and height - #lines ) or 0 ) ) )\
- \9if not lines[line] then return nil end\
- \
- \9local pos = 0\
- \9for l = 1, line - 1 do\
- \9\9pos = pos + #lines[l]\
- \9end\
- \
- \9local a = lines[line].alignment\
- \9local char = math.max( 1, math.min( 1 + x - ( ( a == \"centre\" and math.floor( width / 2 - #lines[line] / 2 + .5 ) ) or ( a == \"right\" and width - #lines[line] ) or 0 ), #lines[line] ) )\
- \
- \9return pos + char, line, char\
- \
- end\
- \
- function UIEventHelpers.textSelection:deselectAll( lines )\
- \
- \9for l = 1, #lines do\
- \9\9local line = lines[l]\
- \9\9for c = 1, #line do\
- \9\9\9line[c].pixel.selected = false\
- \9\9end\
- \9end\
- \
- end\
- \
- function UIEventHelpers.textSelection:selectRange( lines, line1, char1, line2, char2 )\
- \
- \9if line1 > line2 then\
- \9\9line1, line2 = line2, line1\
- \9\9char1, char2 = char2, char1\
- \9elseif line1 == line2 and char1 > char2 then\
- \9\9char1, char2 = char2, char1\
- \9end\
- \9for l = 1, #lines do\
- \9\9local line = lines[l]\
- \9\9for c = 1, #line do\
- \9\9\9if l < line1 or l > line2 then\
- \9\9\9\9line[c].pixel.selected = false\
- \9\9\9elseif line1 == line2 then\
- \9\9\9\9line[c].pixel.selected = c >= char1 and c <= char2\
- \9\9\9elseif l == line1 then\
- \9\9\9\9line[c].pixel.selected = c >= char1\
- \9\9\9elseif l == line2 then\
- \9\9\9\9line[c].pixel.selected = c <= char2\
- \9\9\9else\
- \9\9\9\9line[c].pixel.selected = true\
- \9\9\9end\
- \9\9end\
- \9end\
- \
- end\
- \
- function UIEventHelpers.textSelection:getCharacterLink( x, y, lines, alignment, width, height )\
- \
- \9local line = math.max( 1, math.min( #lines, 1 + y - ( ( alignment == \"centre\" and math.floor( height / 2 - #lines / 2 + .5 ) ) or ( alignment == \"bottom\" and height - #lines ) or 0 ) ) )\
- \9if not lines[line] then return nil end\
- \
- \9local pos = 0\
- \9for l = 1, line - 1 do\
- \9\9pos = pos + #lines[l]\
- \9end\
- \
- \9local a = lines[line].alignment\
- \9local c = 1 + x - ( ( a == \"centre\" and math.floor( width / 2 - #lines[line] / 2 + .5 ) ) or ( a == \"right\" and width - #lines[line] ) or 0 ), #lines[line]\
- \
- \9if lines[line][c] then\
- \9\9return lines[line][c].link\
- \9end\
- \
- end\
- \
- function UIEventHelpers.textSelection:handleMouseEvent( event, lines, alignment, width, height )\
- \
- \9-- getCharacterLink( self, event.x - self.ox, event.y - self.oy, lines, alignment, width, height )\
- \
- \9if event.name == Event.MOUSEDOWN then\
- \9\9UIEventHelpers.textSelection.deselectAll( self, lines )\
- \9\9if event.handled or not event:isInArea( 0, 0, self.width, self.height ) then\
- \9\9\9self.selection = nil\
- \9\9else\
- \9\9\9local pos, line, char = UIEventHelpers.textSelection.getCharacter( self, event.x - self.ox, event.y - self.oy, lines, alignment, width, height )\
- \9\9\9if pos then\
- \9\9\9\9self.selection = {\
- \9\9\9\9\9line1 = line;\
- \9\9\9\9\9char1 = char;\
- \9\9\9\9\9pos1 = pos;\
- \9\9\9\9\9line2 = line;\
- \9\9\9\9\9char2 = char;\
- \9\9\9\9\9pos2 = pos;\
- \9\9\9\9\9holding = true;\
- \9\9\9\9\9button = event.button;\
- \9\9\9\9\9initialised = false;\
- \9\9\9\9}\
- \9\9\9end\
- \9\9\9event.handled = true\
- \9\9end\
- \9\9self.changed = true\
- \
- \9elseif not event.handled and event.name == Event.MOUSEUP and self.selection and event.button == self.selection.button then\
- \9\9self.selection.holding = false\
- \9\9if not self.selection.initialised then\
- \9\9\9if self.onLinkPressed then\
- \9\9\9\9local link = UIEventHelpers.textSelection.getCharacterLink( self, event.x - self.ox, event.y - self.oy, lines, alignment, width, height )\
- \9\9\9\9if link then\
- \9\9\9\9\9self:onLinkPressed( link )\
- \9\9\9\9end\
- \9\9\9end\
- \9\9\9self.selection = nil\
- \9\9end\
- \
- \9\9event.handled = true\
- \
- \9elseif not event.handled and event.name == Event.MOUSEDRAG and self.selection and self.selection.holding then\
- \9\9local pos, line, char = UIEventHelpers.textSelection.getCharacter( self, event.x - self.ox, event.y - self.oy, lines, alignment, width, height )\
- \9\9self.selection.pos2 = pos\
- \9\9self.selection.char2 = char\
- \9\9self.selection.line2 = line\
- \9\9self.selection.initialised = true\
- \9\9UIEventHelpers.textSelection.selectRange( self, lines, self.selection.line1, self.selection.char1, line, char )\
- \9\9self.changed = true\
- \
- \9end\
- end\
- \
- function UIEventHelpers.textSelection:handleKeyboardEvent( event, stream )\
- \9if not event.handled and event.name == Event.KEYDOWN and self.selection and self.selection.initialised then\
- \9\9if event:matchesHotkey \"ctrl-c\" or event:matchesHotkey \"ctrl-x\" then\
- \9\9\9local text = \"\"\
- \9\9\9local rtext = {}\
- \9\9\9local pos1, pos2 = self.selection.pos1, self.selection.pos2\
- \9\9\9for i = math.min( pos1, pos2 ), math.max( pos1, pos2 ) do\
- \9\9\9\9text = text .. stream[i].pixel[3]\
- \9\9\9\9rtext[#rtext + 1] = stream[i].pixel\
- \9\9\9end\
- \9\9\9clipboard.put {\
- \9\9\9\9{ \"plaintext\", text };\
- \9\9\9\9{ \"text\", rtext };\
- \9\9\9}\
- \9\9\9event.handled = true\
- \9\9end\
- \9end\
- end\
- \
- return UIEventHelpers";
- ["/lib/util/markup.lua"] = "\
- local col_lookup = {}\
- for i = 0, 15 do\
- \9col_lookup[(\"%x\"):format( i )] = 2 ^ i\
- \9col_lookup[(\"%X\"):format( i )] = 2 ^ i\
- end\
- col_lookup[\" \"] = 0\
- \
- local function parseStream( text, bc, tc, allowCodes )\
- \9local stream = {}\
- \9local n = 1\
- \9local alignment, tag = \"left\", nil\
- \9local i = 1\
- \9while i <= #text do\
- \9\9if text:sub( i, i ) == \"@\" and allowCodes then\
- \9\9\9i = i + 1\
- \9\9\9local ctrl = text:sub( i, i )\
- \9\9\9if ctrl == \"b\" then\
- \9\9\9\9bc = col_lookup[text:sub( i + 1, i + 1 )]\
- \9\9\9\9i = i + 2\
- \
- \9\9\9elseif ctrl == \"t\" then\
- \9\9\9\9tc = col_lookup[text:sub( i + 1, i + 1 )]\
- \9\9\9\9i = i + 2\
- \
- \9\9\9elseif ctrl == \"a\" then\
- \9\9\9\9local a = text:sub( i + 1, i + 1 )\
- \9\9\9\9alignment = ( a == \"c\" and \"centre\" ) or ( a == \"r\" and \"right\" ) or \"left\"\
- \9\9\9\9i = i + 2\
- \
- \9\9\9elseif ctrl == \"m\" then\
- \9\9\9\9if text:sub( i + 1, i + 1 ) == \"-\" then\
- \9\9\9\9\9tag = nil\
- \9\9\9\9\9i = i + 2\
- \9\9\9\9else\
- \9\9\9\9\9local t = text:match( \"m%((.-)%)\", i )\
- \9\9\9\9\9if t then\
- \9\9\9\9\9\9tag = t\
- \9\9\9\9\9\9i = i + 3 + #t\
- \9\9\9\9\9else\
- \9\9\9\9\9\9stream[n] = { pixel = { bc, tc, \"m\" }, tag = tag, alignment = alignment }\
- \9\9\9\9\9\9n = n + 1\
- \9\9\9\9\9\9i = i + 1\
- \9\9\9\9\9end\
- \9\9\9\9end\
- \
- \9\9\9elseif ctrl == \"l\" then -- link\
- \9\9\9\9local display, link = text:match( \"l%[(.-)%]%((.-)%)\", i )\
- \9\9\9\9if display then\
- \9\9\9\9\9i = i + 5 + #display + #link\
- \9\9\9\9\9for c = 1, #display do\
- \9\9\9\9\9\9stream[n] = { pixel = { bc, tc, display:sub( c, c ) }, tag = tag, alignment = alignment, link = link }\
- \9\9\9\9\9\9n = n + 1\
- \9\9\9\9\9end\
- \9\9\9\9else\
- \9\9\9\9\9stream[n] = { pixel = { bc, tc, \"l\" }, tag = tag, alignment = alignment }\
- \9\9\9\9\9n = n + 1\
- \9\9\9\9\9i = i + 1\
- \9\9\9\9end\
- \
- \9\9\9else\
- \9\9\9\9stream[n] = { pixel = { bc, tc, text:sub( i, i ) }, tag = tag, alignment = alignment }\
- \9\9\9\9n = n + 1\
- \9\9\9\9i = i + 1\
- \9\9\9end\
- \9\9else\
- \9\9\9stream[n] = { pixel = { bc, tc, text:sub( i, i ) }, tag = tag, alignment = alignment }\
- \9\9\9n = n + 1\
- \9\9\9i = i + 1\
- \9\9end\
- \9end\
- \9return stream\
- end\
- \
- local function wrapStreamLine( stream, pos, width )\
- \9for i = pos, pos + math.min( width or #stream - pos, #stream - pos ) do\
- \9\9if stream[i].pixel[3] == \"\\n\" then\
- \9\9\9return i\
- \9\9end\
- \9end\
- \9if not width or #stream - pos + 1 <= width then\
- \9\9return #stream\
- \9end\
- \9for i = pos + width, pos, -1 do\
- \9\9if stream[i].pixel[3]:find \"%s\" then\
- \9\9\9while stream[i + 1].pixel[3]:find \"%s\" do\
- \9\9\9\9i = i + 1\
- \9\9\9end\
- \9\9\9return i\
- \9\9end\
- \9end\
- \9return pos + width - 1\
- end\
- \
- local function wrapStream( stream, width, height )\
- \9local lines, pos = {}, 1\
- \9while pos <= #stream do\
- \9\9local newpos = wrapStreamLine( stream, pos, width )\
- \9\9lines[#lines + 1] = { pos, newpos }\
- \9\9pos = newpos + 1\
- \9end\
- \9local t, d = {}, {}\
- \9if height then\
- \9\9error( height )\
- \9end\
- \9for i = 1, math.min( height or #lines, #lines ) do\
- \9\9local alignment = stream[lines[i][1]].alignment\
- \9\9t[i] = { alignment = alignment }\
- \9\9d[i] = { alignment = alignment }\
- \9\9local p = 1\
- \9\9for n = lines[i][1], lines[i][2] do\
- \9\9\9t[i][p] = stream[n]\
- \9\9\9d[i][p] = stream[n].pixel\
- \9\9\9p = p + 1\
- \9\9end\
- \9end\
- \9return t, d, stream\
- end\
- \
- local markup = {}\
- \
- function markup.parse( text, bc, tc, width, height, allowCodes )\
- \9return wrapStream( parseStream( text, bc, tc, allowCodes ), width, height )\
- end\
- \
- return markup";
- ["/minify.lua"] = "local function _W(f) local e=setmetatable({}, {__index = getfenv()}) return setfenv(f,e)() or e end\
- Utils=_W(function()\
- return {\
- \9CreateLookup = function(tbl)\
- \9\9for _, v in ipairs(tbl) do\
- \9\9\9tbl[v] = true\
- \9\9end\
- \9\9return tbl\
- \9end\
- }\
- end)\
- Constants=_W(function()\
- --- Lexer constants\
- -- @module lexer.Constants\
- \
- createLookup = Utils.CreateLookup\
- \
- --- List of white chars\
- WhiteChars = createLookup{' ', '\\n', '\\t', '\\r'}\
- \
- --- Lookup of escape characters\
- EscapeLookup = {['\\r'] = '\\\\r', ['\\n'] = '\\\\n', ['\\t'] = '\\\\t', ['\"'] = '\\\\\"', [\"'\"] = \"\\\\'\"}\
- \
- --- Lookup of lower case characters\
- LowerChars = createLookup{\
- \9'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',\
- \9'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'\
- }\
- \
- --- Lookup of upper case characters\
- UpperChars = createLookup{\
- \9'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',\
- \9'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'\
- }\
- \
- --- Lookup of digits\
- Digits = createLookup{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}\
- \
- --- Lookup of hex digits\
- HexDigits = createLookup{\
- \9'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',\
- \9'A', 'a', 'B', 'b', 'C', 'c', 'D', 'd', 'E', 'e', 'F', 'f'\
- }\
- \
- --- Lookup of valid symbols\
- Symbols = createLookup{'+', '-', '*', '/', '^', '%', ',', '{', '}', '[', ']', '(', ')', ';', '#'}\
- \
- --- Lookup of valid keywords\
- Keywords = createLookup{\
- \9'and', 'break', 'do', 'else', 'elseif',\
- \9'end', 'false', 'for', 'function', 'goto', 'if',\
- \9'in', 'local', 'nil', 'not', 'or', 'repeat',\
- \9'return', 'then', 'true', 'until', 'while',\
- }\
- \
- --- Keywords that end a block\
- StatListCloseKeywords = createLookup{'end', 'else', 'elseif', 'until'}\
- \
- --- Unary operators\
- UnOps = createLookup{'-', 'not', '#'}\
- end)\
- Scope=_W(function()\
- --- Holds variables for one scope\
- -- This implementation is inefficient. Instead of using hashes,\
- -- a linear search is used instead to look up variables\
- -- @module lexer.Scope\
- \
- local keywords = Constants.Keywords\
- \
- --- Holds the data for one variable\
- -- @table Variable\
- -- @tfield Scope Scope The parent scope\
- -- @tfield string Name The name of the variable\
- -- @tfield boolean IsGlobal Is the variable global\
- -- @tfield boolean CanRename If the variable can be renamed\
- -- @tfield int References Number of references\
- \
- --- Holds variables for one scope\
- -- @type Scope\
- -- @tfield ?|Scope Parent The parent scope\
- -- @tfield table Locals A list of locals variables\
- -- @tfield table Globals A list of global variables\
- -- @tfield table Children A list of children @{Scope|scopes}\
- \
- local Scope = {}\
- \
- --- Add a local to this scope\
- -- @tparam Variable variable The local object\
- function Scope:AddLocal(variable)\
- \9table.insert(self.Locals, variable)\
- end\
- \
- --- Create a @{Variable} and add it to the scope\
- -- @tparam string name The name of the local\
- -- @treturn Variable The created local\
- function Scope:CreateLocal(name)\
- \9local variable = self:GetLocal(name)\
- \9if variable then return variable end\
- \
- \9variable = {\
- \9\9Scope = self,\
- \9\9Name= name,\
- \9\9IsGlobal = false,\
- \9\9CanRename = true,\
- \9\9References = 1,\
- \9}\
- \
- \9self:AddLocal(variable)\
- \9return variable\
- end\
- \
- --- Get a local variable\
- -- @tparam string name The name of the local\
- -- @treturn ?|Variable The variable\
- function Scope:GetLocal(name)\
- \9for k, var in pairs(self.Locals) do\
- \9\9if var.Name == name then return var end\
- \9end\
- \
- \9if self.Parent then\
- \9\9return self.Parent:GetLocal(name)\
- \9end\
- end\
- \
- --- Find an local variable by its old name\
- -- @tparam string name The old name of the local\
- -- @treturn ?|Variable The local variable\
- function Scope:GetOldLocal(name)\
- \9if self.oldLocalNamesMap[name] then\
- \9\9return self.oldLocalNamesMap[name]\
- \9end\
- \9return self:GetLocal(name)\
- end\
- \
- --- Rename a local variable\
- -- @tparam string|Variable oldName The old variable name\
- -- @tparam string newName The new variable name\
- function Scope:RenameLocal(oldName, newName)\
- \9oldName = type(oldName) == 'string' and oldName or oldName.Name\
- \9local found = false\
- \9local var = self:GetLocal(oldName)\
- \9if var then\
- \9\9var.Name = newName\
- \9\9self.oldLocalNamesMap[oldName] = var\
- \9\9found = true\
- \9end\
- \9if not found and self.Parent then\
- \9\9self.Parent:RenameLocal(oldName, newName)\
- \9end\
- end\
- \
- --- Add a global to this scope\
- -- @tparam Variable name The name of the global\
- function Scope:AddGlobal(name)\
- \9table.insert(self.Globals, name)\
- end\
- \
- --- Create a @{Variable} and add it to the scope\
- -- @tparam string name The name of the global\
- -- @treturn Variable The created global\
- function Scope:CreateGlobal(name)\
- \9local variable = self:GetGlobal(name)\
- \9if variable then return variable end\
- \
- \9variable = {\
- \9\9Scope = self,\
- \9\9Name= name,\
- \9\9IsGlobal = true,\
- \9\9CanRename = true,\
- \9\9References = 1,\
- \9}\
- \
- \9self:AddGlobal(variable)\
- \9return variable\
- end\
- \
- --- Get a global variable\
- -- @tparam string name The name of the global\
- -- @treturn ?|Variable The variable\
- function Scope:GetGlobal(name)\
- \9for k, v in pairs(self.Globals) do\
- \9\9if v.Name == name then return v end\
- \9end\
- \
- \9if self.Parent then\
- \9\9return self.Parent:GetGlobal(name)\
- \9end\
- end\
- \
- --- Find a Global by its old name\
- -- @tparam string name The old name of the global\
- -- @treturn ?|Variable The variable\
- function Scope:GetOldGlobal(name)\
- \9if self.oldGlobalNamesMap[name] then\
- \9\9return self.oldGlobalNamesMap[name]\
- \9end\
- \9return self:GetGlobal(name)\
- end\
- \
- --- Rename a global variable\
- -- @tparam string|Variable oldName The old variable name\
- -- @tparam string newName The new variable name\
- function Scope:RenameGlobal(oldName, newName)\
- \9oldName = type(oldName) == 'string' and oldName or oldName.Name\
- \9local found = false\
- \9local var = self:GetGlobal(oldName)\
- \9if var then\
- \9\9var.Name = newName\
- \9\9self.oldGlobalNamesMap[oldName] = var\
- \9\9found = true\
- \9end\
- \9if not found and self.Parent then\
- \9\9self.Parent:RenameGlobal(oldName, newName)\
- \9end\
- end\
- \
- --- Get a variable by name\
- -- @tparam string name The name of the variable\
- -- @treturn ?|Variable The found variable\
- -- @fixme This is a very inefficient implementation, as with @{Scope:GetLocal} and @{Scope:GetGlocal}\
- function Scope:GetVariable(name)\
- \9return self:GetLocal(name) or self:GetGlobal(name)\
- end\
- \
- --- Find an variable by its old name\
- -- @tparam string name The old name of the variable\
- -- @treturn ?|Variable The variable\
- function Scope:GetOldVariable(name)\
- \9return self:GetOldLocal(name) or self:GetOldGlobal(name)\
- end\
- \
- --- Rename a variable\
- -- @tparam string|Variable oldName The old variable name\
- -- @tparam string newName The new variable name\
- function Scope:RenameVariable(oldName, newName)\
- \9oldName = type(oldName) == 'string' and oldName or oldName.Name\
- \9if self:GetLocal(oldName) then\
- \9\9self:RenameLocal(oldName, newName)\
- \9else\
- \9\9self:RenameGlobal(oldName, newName)\
- \9end\
- end\
- \
- --- Get all variables in the scope\
- -- @treturn table A list of @{Variable|variables}\
- function Scope:GetAllVariables()\
- \9return self:getVars(true, self:getVars(true))\
- end\
- \
- --- Get all variables\
- -- @tparam boolean top If this values is the 'top' of the function stack\
- -- @tparam table ret Table to fill with return values (optional)\
- -- @treturn table The variables\
- -- @local\
- function Scope:getVars(top, ret)\
- \9local ret = ret or {}\
- \9if top then\
- \9\9for k, v in pairs(self.Children) do\
- \9\9\9v:getVars(true, ret)\
- \9\9end\
- \9else\
- \9\9for k, v in pairs(self.Locals) do\
- \9\9\9table.insert(ret, v)\
- \9\9end\
- \9\9for k, v in pairs(self.Globals) do\
- \9\9\9table.insert(ret, v)\
- \9\9end\
- \9\9if self.Parent then\
- \9\9\9self.Parent:getVars(false, ret)\
- \9\9end\
- \9end\
- \9return ret\
- end\
- \
- --- Rename all locals to smaller values\
- -- @tparam string validNameChars All characters that can be used to make a variable name\
- -- @fixme Some of the string generation happens a lot, this could be looked at\
- function Scope:ObfuscateLocals(validNameChars)\
- \9-- Use values sorted for letter frequency instead\
- \9local startChars = validNameChars or \"etaoinshrdlucmfwypvbgkqjxz_ETAOINSHRDLUCMFWYPVBGKQJXZ\"\
- \9local otherChars = validNameChars or \"etaoinshrdlucmfwypvbgkqjxz_0123456789ETAOINSHRDLUCMFWYPVBGKQJXZ\"\
- \
- \9local startCharsLength, otherCharsLength = #startChars, #otherChars\
- \9local index = 0\
- \9local floor = math.floor\
- \9for _, var in pairs(self.Locals) do\
- \9\9local name\
- \
- \9\9repeat\
- \9\9\9if index < startCharsLength then\
- \9\9\9\9index = index + 1\
- \9\9\9\9name = startChars:sub(index, index)\
- \9\9\9else\
- \9\9\9\9if index < startCharsLength then\
- \9\9\9\9\9index = index + 1\
- \9\9\9\9\9name = startChars:sub(index, index)\
- \9\9\9\9else\
- \9\9\9\9\9local varIndex = floor(index / startCharsLength)\
- \9\9\9\9\9local offset = index % startCharsLength\
- \9\9\9\9\9name = startChars:sub(offset, offset)\
- \
- \9\9\9\9\9while varIndex > 0 do\
- \9\9\9\9\9\9offset = varIndex % otherCharsLength\
- \9\9\9\9\9\9name = otherChars:sub(offset, offset) .. name\
- \9\9\9\9\9\9varIndex = floor(varIndex / otherCharsLength)\
- \9\9\9\9\9end\
- \9\9\9\9\9index = index + 1\
- \9\9\9\9end\
- \9\9\9end\
- \9\9until not (keywords[name] or self:GetVariable(name))\
- \9\9self:RenameLocal(var.Name, name)\
- \9end\
- end\
- \
- --- Converts the scope to a string\
- -- No, it actually just returns '<scope>'\
- -- @treturn string '<scope>'\
- function Scope:ToString()\
- \9return '<Scope>'\
- end\
- \
- --- Create a new scope\
- -- @tparam Scope parent The parent scope\
- -- @treturn Scope The created scope\
- local function NewScope(parent)\
- \9local scope = setmetatable({\
- \9\9Parent = parent,\
- \9\9Locals = { },\
- \9\9Globals = { },\
- \9\9oldLocalNamesMap = { },\
- \9\9oldGlobalNamesMap = { },\
- \9\9Children = { },\
- \9}, { __index = Scope })\
- \
- \9if parent then\
- \9\9table.insert(parent.Children, scope)\
- \9end\
- \
- \9return scope\
- end\
- \
- return NewScope\
- end)\
- TokenList=_W(function()\
- --- Provides utilities for reading tokens from a 'stream'\
- -- @module lexer.TokenList\
- \
- --- Stores a list of tokens\
- -- @type TokenList\
- -- @tfield table tokens List of tokens\
- -- @tfield number pointer Pointer to the current\
- -- @tfield table savedPointers A save point\
- local TokenList = {}\
- \
- --- Get this element in the token list\
- -- @tparam int offset The offset in the token list\
- function TokenList:Peek(offset)\
- \9local tokens = self.tokens\
- \9offset = offset or 0\
- \9return tokens[math.min(#tokens, self.pointer+offset)]\
- end\
- \
- --- Get the next token in the list\
- -- @tparam table tokenList Add the token onto this table\
- -- @treturn Token The token\
- function TokenList:Get(tokenList)\
- \9local tokens = self.tokens\
- \9local pointer = self.pointer\
- \9local token = tokens[pointer]\
- \9self.pointer = math.min(pointer + 1, #tokens)\
- \9if tokenList then\
- \9\9table.insert(tokenList, token)\
- \9end\
- \9return token\
- end\
- \
- --- Check if the next token is of a type\
- -- @tparam string type The type to compare it with\
- -- @treturn bool If the type matches\
- function TokenList:Is(type)\
- \9return self:Peek().Type == type\
- end\
- \
- --- Save position in a stream\
- function TokenList:Save()\
- \9table.insert(self.savedPointers, self.pointer)\
- end\
- \
- --- Remove the last position in the stream\
- function TokenList:Commit()\
- \9local savedPointers = self.savedPointers\
- \9savedPointers[#savedPointers] = nil\
- end\
- \
- --- Restore to the previous save point\
- function TokenList:Restore()\
- \9local savedPointers = self.savedPointers\
- \9local sPLength = #savedPointers\
- \9self.pointer = savedP[sPLength]\
- \9savedPointers[sPLength] = nil\
- end\
- \
- --- Check if the next token is a symbol and return it\
- -- @tparam string symbol Symbol to check (Optional)\
- -- @tparam table tokenList Add the token onto this table\
- -- @treturn[0] ?|token If symbol is not specified, return the token\
- -- @treturn[1] boolean If symbol is specified, return true if it matches\
- function TokenList:ConsumeSymbol(symbol, tokenList)\
- \9local token = self:Peek()\
- \9if token.Type == 'Symbol' then\
- \9\9if symbol then\
- \9\9\9if token.Data == symbol then\
- \9\9\9\9self:Get(tokenList)\
- \9\9\9\9return true\
- \9\9\9else\
- \9\9\9\9return nil\
- \9\9\9end\
- \9\9else\
- \9\9\9self:Get(tokenList)\
- \9\9\9return token\
- \9\9end\
- \9else\
- \9\9return nil\
- \9end\
- end\
- \
- --- Check if the next token is a keyword and return it\
- -- @tparam string kw Keyword to check (Optional)\
- -- @tparam table tokenList Add the token onto this table\
- -- @treturn[0] ?|token If kw is not specified, return the token\
- -- @treturn[1] boolean If kw is specified, return true if it matches\
- function TokenList:ConsumeKeyword(kw, tokenList)\
- \9local token = self:Peek()\
- \9if token.Type == 'Keyword' and token.Data == kw then\
- \9\9self:Get(tokenList)\
- \9\9return true\
- \9else\
- \9\9return nil\
- \9end\
- end\
- \
- --- Check if the next token matches is a keyword\
- -- @tparam string kw The particular keyword\
- -- @treturn boolean If it matches or not\
- function TokenList:IsKeyword(kw)\
- \9local token = self:Peek()\
- \9return token.Type == 'Keyword' and token.Data == kw\
- end\
- \
- --- Check if the next token matches is a symbol\
- -- @tparam string symbol The particular symbol\
- -- @treturn boolean If it matches or not\
- function TokenList:IsSymbol(symbol)\
- \9local token = self:Peek()\
- \9return token.Type == 'Symbol' and token.Data == symbol\
- end\
- \
- --- Check if the next token is an end of file\
- -- @treturn boolean If the next token is an end of file\
- function TokenList:IsEof()\
- \9return self:Peek().Type == 'Eof'\
- end\
- \
- --- Produce a string off all tokens\
- -- @tparam boolean includeLeading Include the leading whitespace\
- -- @treturn string The resulting string\
- function TokenList:Print(includeLeading)\
- \9includeLeading = (includeLeading == nil and true or includeLeading)\
- \
- \9local out = \"\"\
- \9for _, token in ipairs(self.tokens) do\
- \9\9if includeLeading then\
- \9\9\9for _, whitespace in ipairs(token.LeadingWhite) do\
- \9\9\9\9out = out .. whitespace:Print() .. \"\\n\"\
- \9\9\9end\
- \9\9end\
- \9\9out = out .. token:Print() .. \"\\n\"\
- \9end\
- \
- \9return out\
- end\
- \
- return TokenList\
- end)\
- Parse=_W(function()\
- --- The main lua parser and lexer.\
- -- LexLua returns a Lua token stream, with tokens that preserve\
- -- all whitespace formatting information.\
- -- ParseLua returns an AST, internally relying on LexLua.\
- -- @module lexer.Parse\
- \
- local createLookup = Utils.CreateLookup\
- \
- local lowerChars = Constants.LowerChars\
- local upperChars = Constants.UpperChars\
- local digits = Constants.Digits\
- local symbols = Constants.Symbols\
- local hexDigits = Constants.HexDigits\
- local keywords = Constants.Keywords\
- local statListCloseKeywords = Constants.StatListCloseKeywords\
- local unops = Constants.UnOps\
- local setmeta = setmetatable\
- \
- --- One token\
- -- @table Token\
- -- @tparam string Type The token type\
- -- @param Data Data about the token\
- -- @tparam string CommentType The type of comment (Optional)\
- -- @tparam number Line Line number (Optional)\
- -- @tparam number Char Character number (Optional)\
- local Token = {}\
- \
- --- Creates a string representation of the token\
- -- @treturn string The resulting string\
- function Token:Print()\
- \9return \"<\"..(self.Type .. string.rep(' ', math.max(3, 12-#self.Type)))..\" \"..(self.Data or '')..\" >\"\
- end\
- \
- local tokenMeta = { __index = Token }\
- \
- --- Create a list of @{Token|tokens} from a Lua source\
- -- @tparam string src Lua source code\
- -- @treturn TokenList The list of @{Token|tokens}\
- local function LexLua(src)\
- \9--token dump\
- \9local tokens = {}\
- \
- \9do -- Main bulk of the work\
- \9\9--line / char / pointer tracking\
- \9\9local pointer = 1\
- \9\9local line = 1\
- \9\9local char = 1\
- \
- \9\9--get / peek functions\
- \9\9local function get()\
- \9\9\9local c = src:sub(pointer,pointer)\
- \9\9\9if c == '\\n' then\
- \9\9\9\9char = 1\
- \9\9\9\9line = line + 1\
- \9\9\9else\
- \9\9\9\9char = char + 1\
- \9\9\9end\
- \9\9\9pointer = pointer + 1\
- \9\9\9return c\
- \9\9end\
- \9\9local function peek(n)\
- \9\9\9n = n or 0\
- \9\9\9return src:sub(pointer+n,pointer+n)\
- \9\9end\
- \9\9local function consume(chars)\
- \9\9\9local c = peek()\
- \9\9\9for i = 1, #chars do\
- \9\9\9\9if c == chars:sub(i,i) then return get() end\
- \9\9\9end\
- \9\9end\
- \
- \9\9--shared stuff\
- \9\9local function generateError(err)\
- \9\9\9error(\">> :\"..line..\":\"..char..\": \"..err, 0)\
- \9\9end\
- \
- \9\9local function tryGetLongString()\
- \9\9\9local start = pointer\
- \9\9\9if peek() == '[' then\
- \9\9\9\9local equalsCount = 0\
- \9\9\9\9local depth = 1\
- \9\9\9\9while peek(equalsCount+1) == '=' do\
- \9\9\9\9\9equalsCount = equalsCount + 1\
- \9\9\9\9end\
- \9\9\9\9if peek(equalsCount+1) == '[' then\
- \9\9\9\9\9--start parsing the string. Strip the starting bit\
- \9\9\9\9\9for _ = 0, equalsCount+1 do get() end\
- \
- \9\9\9\9\9--get the contents\
- \9\9\9\9\9local contentStart = pointer\
- \9\9\9\9\9while true do\
- \9\9\9\9\9\9--check for eof\
- \9\9\9\9\9\9if peek() == '' then\
- \9\9\9\9\9\9\9generateError(\"Expected `]\"..string.rep('=', equalsCount)..\"]` near <eof>.\", 3)\
- \9\9\9\9\9\9end\
- \
- \9\9\9\9\9\9--check for the end\
- \9\9\9\9\9\9local foundEnd = true\
- \9\9\9\9\9\9if peek() == ']' then\
- \9\9\9\9\9\9\9for i = 1, equalsCount do\
- \9\9\9\9\9\9\9\9if peek(i) ~= '=' then foundEnd = false end\
- \9\9\9\9\9\9\9end\
- \9\9\9\9\9\9\9if peek(equalsCount+1) ~= ']' then\
- \9\9\9\9\9\9\9\9foundEnd = false\
- \9\9\9\9\9\9\9end\
- \9\9\9\9\9\9else\
- \9\9\9\9\9\9\9if peek() == '[' then\
- \9\9\9\9\9\9\9\9-- is there an embedded long string?\
- \9\9\9\9\9\9\9\9local embedded = true\
- \9\9\9\9\9\9\9\9for i = 1, equalsCount do\
- \9\9\9\9\9\9\9\9\9if peek(i) ~= '=' then\
- \9\9\9\9\9\9\9\9\9\9embedded = false\
- \9\9\9\9\9\9\9\9\9\9break\
- \9\9\9\9\9\9\9\9\9end\
- \9\9\9\9\9\9\9\9end\
- \9\9\9\9\9\9\9\9if peek(equalsCount + 1) == '[' and embedded then\
- \9\9\9\9\9\9\9\9\9-- oh look, there was\
- \9\9\9\9\9\9\9\9\9depth = depth + 1\
- \9\9\9\9\9\9\9\9\9for i = 1, (equalsCount + 2) do\
- \9\9\9\9\9\9\9\9\9\9get()\
- \9\9\9\9\9\9\9\9\9end\
- \9\9\9\9\9\9\9\9end\
- \9\9\9\9\9\9\9end\
- \9\9\9\9\9\9\9foundEnd = false\
- \9\9\9\9\9\9end\
- \
- \9\9\9\9\9\9if foundEnd then\
- \9\9\9\9\9\9\9depth = depth - 1\
- \9\9\9\9\9\9\9if depth == 0 then\
- \9\9\9\9\9\9\9\9break\
- \9\9\9\9\9\9\9else\
- \9\9\9\9\9\9\9\9for i = 1, equalsCount + 2 do\
- \9\9\9\9\9\9\9\9\9get()\
- \9\9\9\9\9\9\9\9end\
- \9\9\9\9\9\9\9end\
- \9\9\9\9\9\9else\
- \9\9\9\9\9\9\9get()\
- \9\9\9\9\9\9end\
- \9\9\9\9\9end\
- \
- \9\9\9\9\9--get the interior string\
- \9\9\9\9\9local contentString = src:sub(contentStart, pointer-1)\
- \
- \9\9\9\9\9--found the end. Get rid of the trailing bit\
- \9\9\9\9\9for i = 0, equalsCount+1 do get() end\
- \
- \9\9\9\9\9--get the exterior string\
- \9\9\9\9\9local longString = src:sub(start, pointer-1)\
- \
- \9\9\9\9\9--return the stuff\
- \9\9\9\9\9return contentString, longString\
- \9\9\9\9else\
- \9\9\9\9\9return nil\
- \9\9\9\9end\
- \9\9\9else\
- \9\9\9\9return nil\
- \9\9\9end\
- \9\9end\
- \
- \9\9--main token emitting loop\
- \9\9while true do\
- \9\9\9--get leading whitespace. The leading whitespace will include any comments\
- \9\9\9--preceding the token. This prevents the parser needing to deal with comments\
- \9\9\9--separately.\
- \9\9\9local leading = { }\
- \9\9\9local leadingWhite = ''\
- \9\9\9local longStr = false\
- \9\9\9while true do\
- \9\9\9\9local c = peek()\
- \9\9\9\9if c == '#' and peek(1) == '!' and line == 1 then\
- \9\9\9\9\9-- #! shebang for linux scripts\
- \9\9\9\9\9get()\
- \9\9\9\9\9get()\
- \9\9\9\9\9leadingWhite = \"#!\"\
- \9\9\9\9\9while peek() ~= '\\n' and peek() ~= '' do\
- \9\9\9\9\9\9leadingWhite = leadingWhite .. get()\
- \9\9\9\9\9end\
- \
- \9\9\9\9\9table.insert(leading, setmeta({\
- \9\9\9\9\9\9Type = 'Comment',\
- \9\9\9\9\9\9CommentType = 'Shebang',\
- \9\9\9\9\9\9Data = leadingWhite,\
- \9\9\9\9\9\9Line = line,\
- \9\9\9\9\9\9Char = char\
- \9\9\9\9\9}, tokenMeta))\
- \9\9\9\9\9leadingWhite = \"\"\
- \9\9\9\9end\
- \9\9\9\9if c == ' ' or c == '\\t' then\
- \9\9\9\9\9--whitespace\
- \9\9\9\9\9--leadingWhite = leadingWhite..get()\
- \9\9\9\9\9local c2 = get() -- ignore whitespace\
- \9\9\9\9\9table.insert(leading, setmeta({\
- \9\9\9\9\9\9Type = 'Whitespace',\
- \9\9\9\9\9\9Line = line,\
- \9\9\9\9\9\9Char = char,\
- \9\9\9\9\9\9Data = c2\
- \9\9\9\9\9}, tokenMeta))\
- \9\9\9\9elseif c == '\\n' or c == '\\r' then\
- \9\9\9\9\9local nl = get()\
- \9\9\9\9\9if leadingWhite ~= \"\" then\
- \9\9\9\9\9\9table.insert(leading, setmeta({\
- \9\9\9\9\9\9\9Type = 'Comment',\
- \9\9\9\9\9\9\9CommentType = longStr and 'LongComment' or 'Comment',\
- \9\9\9\9\9\9\9Data = leadingWhite,\
- \9\9\9\9\9\9\9Line = line,\
- \9\9\9\9\9\9\9Char = char,\
- \9\9\9\9\9\9}, tokenMeta))\
- \9\9\9\9\9\9leadingWhite = \"\"\
- \9\9\9\9\9end\
- \9\9\9\9\9table.insert(leading, setmeta({\
- \9\9\9\9\9\9Type = 'Whitespace',\
- \9\9\9\9\9\9Line = line,\
- \9\9\9\9\9\9Char = char,\
- \9\9\9\9\9\9Data = nl,\
- \9\9\9\9\9}, tokenMeta))\
- \9\9\9\9elseif c == '-' and peek(1) == '-' then\
- \9\9\9\9\9--comment\
- \9\9\9\9\9get()\
- \9\9\9\9\9get()\
- \9\9\9\9\9leadingWhite = leadingWhite .. '--'\
- \9\9\9\9\9local _, wholeText = tryGetLongString()\
- \9\9\9\9\9if wholeText then\
- \9\9\9\9\9\9leadingWhite = leadingWhite..wholeText\
- \9\9\9\9\9\9longStr = true\
- \9\9\9\9\9else\
- \9\9\9\9\9\9while peek() ~= '\\n' and peek() ~= '' do\
- \9\9\9\9\9\9\9leadingWhite = leadingWhite..get()\
- \9\9\9\9\9\9end\
- \9\9\9\9\9end\
- \9\9\9\9else\
- \9\9\9\9\9break\
- \9\9\9\9end\
- \9\9\9end\
- \9\9\9if leadingWhite ~= \"\" then\
- \9\9\9\9table.insert(leading, setmeta(\
- \9\9\9\9{\
- \9\9\9\9\9Type = 'Comment',\
- \9\9\9\9\9CommentType = longStr and 'LongComment' or 'Comment',\
- \9\9\9\9\9Data = leadingWhite,\
- \9\9\9\9\9Line = line,\
- \9\9\9\9\9Char = char,\
- \9\9\9\9}, tokenMeta))\
- \9\9\9end\
- \
- \9\9\9--get the initial char\
- \9\9\9local thisLine = line\
- \9\9\9local thisChar = char\
- \9\9\9local errorAt = \":\"..line..\":\"..char..\":> \"\
- \9\9\9local c = peek()\
- \
- \9\9\9--symbol to emit\
- \9\9\9local toEmit = nil\
- \
- \9\9\9--branch on type\
- \9\9\9if c == '' then\
- \9\9\9\9--eof\
- \9\9\9\9toEmit = { Type = 'Eof' }\
- \
- \9\9\9elseif upperChars[c] or lowerChars[c] or c == '_' then\
- \9\9\9\9--ident or keyword\
- \9\9\9\9local start = pointer\
- \9\9\9\9repeat\
- \9\9\9\9\9get()\
- \9\9\9\9\9c = peek()\
- \9\9\9\9until not (upperChars[c] or lowerChars[c] or digits[c] or c == '_')\
- \9\9\9\9local dat = src:sub(start, pointer-1)\
- \9\9\9\9if keywords[dat] then\
- \9\9\9\9\9toEmit = {Type = 'Keyword', Data = dat}\
- \9\9\9\9else\
- \9\9\9\9\9toEmit = {Type = 'Ident', Data = dat}\
- \9\9\9\9end\
- \
- \9\9\9elseif digits[c] or (peek() == '.' and digits[peek(1)]) then\
- \9\9\9\9--number const\
- \9\9\9\9local start = pointer\
- \9\9\9\9if c == '0' and peek(1) == 'x' then\
- \9\9\9\9\9get();get()\
- \9\9\9\9\9while hexDigits[peek()] do get() end\
- \9\9\9\9\9if consume('Pp') then\
- \9\9\9\9\9\9consume('+-')\
- \9\9\9\9\9\9while digits[peek()] do get() end\
- \9\9\9\9\9end\
- \9\9\9\9else\
- \9\9\9\9\9while digits[peek()] do get() end\
- \9\9\9\9\9if consume('.') then\
- \9\9\9\9\9\9while digits[peek()] do get() end\
- \9\9\9\9\9end\
- \9\9\9\9\9if consume('Ee') then\
- \9\9\9\9\9\9consume('+-')\
- \9\9\9\9\9\9while digits[peek()] do get() end\
- \9\9\9\9\9end\
- \9\9\9\9end\
- \9\9\9\9toEmit = {Type = 'Number', Data = src:sub(start, pointer-1)}\
- \
- \9\9\9elseif c == '\\'' or c == '\\\"' then\
- \9\9\9\9local start = pointer\
- \9\9\9\9--string const\
- \9\9\9\9local delim = get()\
- \9\9\9\9local contentStart = pointer\
- \9\9\9\9while true do\
- \9\9\9\9\9local c = get()\
- \9\9\9\9\9if c == '\\\\' then\
- \9\9\9\9\9\9get() --get the escape char\
- \9\9\9\9\9elseif c == delim then\
- \9\9\9\9\9\9break\
- \9\9\9\9\9elseif c == '' then\
- \9\9\9\9\9\9generateError(\"Unfinished string near <eof>\")\
- \9\9\9\9\9end\
- \9\9\9\9end\
- \9\9\9\9local content = src:sub(contentStart, pointer-2)\
- \9\9\9\9local constant = src:sub(start, pointer-1)\
- \9\9\9\9toEmit = {Type = 'String', Data = constant, Constant = content}\
- \
- \9\9\9elseif c == '[' then\
- \9\9\9\9local content, wholetext = tryGetLongString()\
- \9\9\9\9if wholetext then\
- \9\9\9\9\9toEmit = {Type = 'String', Data = wholetext, Constant = content}\
- \9\9\9\9else\
- \9\9\9\9\9get()\
- \9\9\9\9\9toEmit = {Type = 'Symbol', Data = '['}\
- \9\9\9\9end\
- \
- \9\9\9elseif consume('>=<') then\
- \9\9\9\9if consume('=') then\
- \9\9\9\9\9toEmit = {Type = 'Symbol', Data = c..'='}\
- \9\9\9\9else\
- \9\9\9\9\9toEmit = {Type = 'Symbol', Data = c}\
- \9\9\9\9end\
- \
- \9\9\9elseif consume('~') then\
- \9\9\9\9if consume('=') then\
- \9\9\9\9\9toEmit = {Type = 'Symbol', Data = '~='}\
- \9\9\9\9else\
- \9\9\9\9\9generateError(\"Unexpected symbol `~` in source.\", 2)\
- \9\9\9\9end\
- \
- \9\9\9elseif consume('.') then\
- \9\9\9\9if consume('.') then\
- \9\9\9\9\9if consume('.') then\
- \9\9\9\9\9\9toEmit = {Type = 'Symbol', Data = '...'}\
- \9\9\9\9\9else\
- \9\9\9\9\9\9toEmit = {Type = 'Symbol', Data = '..'}\
- \9\9\9\9\9end\
- \9\9\9\9else\
- \9\9\9\9\9toEmit = {Type = 'Symbol', Data = '.'}\
- \9\9\9\9end\
- \
- \9\9\9elseif consume(':') then\
- \9\9\9\9if consume(':') then\
- \9\9\9\9\9toEmit = {Type = 'Symbol', Data = '::'}\
- \9\9\9\9else\
- \9\9\9\9\9toEmit = {Type = 'Symbol', Data = ':'}\
- \9\9\9\9end\
- \
- \9\9\9elseif symbols[c] then\
- \9\9\9\9get()\
- \9\9\9\9toEmit = {Type = 'Symbol', Data = c}\
- \
- \9\9\9else\
- \9\9\9\9local contents, all = tryGetLongString()\
- \9\9\9\9if contents then\
- \9\9\9\9\9toEmit = {Type = 'String', Data = all, Constant = contents}\
- \9\9\9\9else\
- \9\9\9\9\9generateError(\"Unexpected Symbol `\"..c..\"` in source.\", 2)\
- \9\9\9\9end\
- \9\9\9end\
- \
- \9\9\9--add the emitted symbol, after adding some common data\
- \9\9\9toEmit.LeadingWhite = leading -- table of leading whitespace/comments\
- \
- \9\9\9toEmit.Line = thisLine\
- \9\9\9toEmit.Char = thisChar\
- \9\9\9tokens[#tokens+1] = setmeta(toEmit, tokenMeta)\
- \
- \9\9\9--halt after eof has been emitted\
- \9\9\9if toEmit.Type == 'Eof' then break end\
- \9\9end\
- \9end\
- \
- \9--public interface:\
- \9local tokenList = setmetatable({\
- \9\9tokens = tokens,\
- \9\9savedPointers = {},\
- \9\9pointer = 1\
- \9}, {__index = TokenList})\
- \
- \9return tokenList\
- end\
- \
- --- Create a AST tree from a Lua Source\
- -- @tparam TokenList tok List of tokens from @{LexLua}\
- -- @treturn table The AST tree\
- local function ParseLua(tok)\
- \9--- Generate an error\
- \9-- @tparam string msg The error message\
- \9-- @raise The produces error message\
- \9local function GenerateError(msg)\
- \9\9local err = \">> :\"..tok:Peek().Line..\":\"..tok:Peek().Char..\": \"..msg..\"\\n\"\
- \9\9--find the line\
- \9\9local lineNum = 0\
- \9\9if type(src) == 'string' then\
- \9\9\9for line in src:gmatch(\"[^\\n]*\\n?\") do\
- \9\9\9\9if line:sub(-1,-1) == '\\n' then line = line:sub(1,-2) end\
- \9\9\9\9lineNum = lineNum+1\
- \9\9\9\9if lineNum == tok:Peek().Line then\
- \9\9\9\9\9err = err..\">> `\"..line:gsub('\\t',' ')..\"`\\n\"\
- \9\9\9\9\9for i = 1, tok:Peek().Char do\
- \9\9\9\9\9\9local c = line:sub(i,i)\
- \9\9\9\9\9\9if c == '\\t' then\
- \9\9\9\9\9\9\9err = err..' '\
- \9\9\9\9\9\9else\
- \9\9\9\9\9\9\9err = err..' '\
- \9\9\9\9\9\9end\
- \9\9\9\9\9end\
- \9\9\9\9\9err = err..\" ^^^^\"\
- \9\9\9\9\9break\
- \9\9\9\9end\
- \9\9\9end\
- \9\9end\
- \9\9error(err)\
- \9end\
- \
- \9local ParseExpr,\
- \9 ParseStatementList,\
- \9 ParseSimpleExpr,\
- \9 ParsePrimaryExpr,\
- \9 ParseSuffixedExpr\
- \
- \9--- Parse the function definition and its arguments\
- \9-- @tparam Scope.Scope scope The current scope\
- \9-- @tparam table tokenList A table to fill with tokens\
- \9-- @treturn Node A function Node\
- \9local function ParseFunctionArgsAndBody(scope, tokenList)\
- \9\9local funcScope = Scope(scope)\
- \9\9if not tok:ConsumeSymbol('(', tokenList) then\
- \9\9\9GenerateError(\"`(` expected.\")\
- \9\9end\
- \
- \9\9--arg list\
- \9\9local argList = {}\
- \9\9local isVarArg = false\
- \9\9while not tok:ConsumeSymbol(')', tokenList) do\
- \9\9\9if tok:Is('Ident') then\
- \9\9\9\9local arg = funcScope:CreateLocal(tok:Get(tokenList).Data)\
- \9\9\9\9argList[#argList+1] = arg\
- \9\9\9\9if not tok:ConsumeSymbol(',', tokenList) then\
- \9\9\9\9\9if tok:ConsumeSymbol(')', tokenList) then\
- \9\9\9\9\9\9break\
- \9\9\9\9\9else\
- \9\9\9\9\9\9GenerateError(\"`)` expected.\")\
- \9\9\9\9\9end\
- \9\9\9\9end\
- \9\9\9elseif tok:ConsumeSymbol('...', tokenList) then\
- \9\9\9\9isVarArg = true\
- \9\9\9\9if not tok:ConsumeSymbol(')', tokenList) then\
- \9\9\9\9\9GenerateError(\"`...` must be the last argument of a function.\")\
- \9\9\9\9end\
- \9\9\9\9break\
- \9\9\9else\
- \9\9\9\9GenerateError(\"Argument name or `...` expected\")\
- \9\9\9end\
- \9\9end\
- \
- \9\9--body\
- \9\9local body = ParseStatementList(funcScope)\
- \
- \9\9--end\
- \9\9if not tok:ConsumeKeyword('end', tokenList) then\
- \9\9\9GenerateError(\"`end` expected after function body\")\
- \9\9end\
- \
- \9\9return {\
- \9\9\9AstType = 'Function',\
- \9\9\9Scope = funcScope,\
- \9\9\9Arguments = argList,\
- \9\9\9Body = body,\
- \9\9\9VarArg = isVarArg,\
- \9\9\9Tokens = tokenList,\
- \9\9}\
- \9end\
- \
- \9--- Parse a simple expression\
- \9-- @tparam Scope.Scope scope The current scope\
- \9-- @treturn Node the resulting node\
- \9function ParsePrimaryExpr(scope)\
- \9\9local tokenList = {}\
- \
- \9\9if tok:ConsumeSymbol('(', tokenList) then\
- \9\9\9local ex = ParseExpr(scope)\
- \9\9\9if not tok:ConsumeSymbol(')', tokenList) then\
- \9\9\9\9GenerateError(\"`)` Expected.\")\
- \9\9\9end\
- \
- \9\9\9return {\
- \9\9\9\9AstType = 'Parentheses',\
- \9\9\9\9Inner = ex,\
- \9\9\9\9Tokens = tokenList,\
- \9\9\9}\
- \
- \9\9elseif tok:Is('Ident') then\
- \9\9\9local id = tok:Get(tokenList)\
- \9\9\9local var = scope:GetLocal(id.Data)\
- \9\9\9if not var then\
- \9\9\9\9var = scope:GetGlobal(id.Data)\
- \9\9\9\9if not var then\
- \9\9\9\9\9var = scope:CreateGlobal(id.Data)\
- \9\9\9\9else\
- \9\9\9\9\9var.References = var.References + 1\
- \9\9\9\9end\
- \9\9\9else\
- \9\9\9\9var.References = var.References + 1\
- \9\9\9end\
- \
- \9\9\9return {\
- \9\9\9\9AstType = 'VarExpr',\
- \9\9\9\9Name = id.Data,\
- \9\9\9\9Variable = var,\
- \9\9\9\9Tokens = tokenList,\
- \9\9\9}\
- \9\9else\
- \9\9\9GenerateError(\"primary expression expected\")\
- \9\9end\
- \9end\
- \
- \9--- Parse some table related expressions\
- \9-- @tparam Scope.Scope scope The current scope\
- \9-- @tparam boolean onlyDotColon Only allow '.' or ':' nodes\
- \9-- @treturn Node The resulting node\
- \9function ParseSuffixedExpr(scope, onlyDotColon)\
- \9\9--base primary expression\
- \9\9local prim = ParsePrimaryExpr(scope)\
- \
- \9\9while true do\
- \9\9\9local tokenList = {}\
- \
- \9\9\9if tok:IsSymbol('.') or tok:IsSymbol(':') then\
- \9\9\9\9local symb = tok:Get(tokenList).Data\
- \9\9\9\9if not tok:Is('Ident') then\
- \9\9\9\9\9GenerateError(\"<Ident> expected.\")\
- \9\9\9\9end\
- \9\9\9\9local id = tok:Get(tokenList)\
- \
- \9\9\9\9prim = {\
- \9\9\9\9\9AstType = 'MemberExpr',\
- \9\9\9\9\9Base = prim,\
- \9\9\9\9\9Indexer = symb,\
- \9\9\9\9\9Ident = id,\
- \9\9\9\9\9Tokens = tokenList,\
- \9\9\9\9}\
- \
- \9\9\9elseif not onlyDotColon and tok:ConsumeSymbol('[', tokenList) then\
- \9\9\9\9local ex = ParseExpr(scope)\
- \9\9\9\9if not tok:ConsumeSymbol(']', tokenList) then\
- \9\9\9\9\9GenerateError(\"`]` expected.\")\
- \9\9\9\9end\
- \
- \9\9\9\9prim = {\
- \9\9\9\9\9AstType = 'IndexExpr',\
- \9\9\9\9\9Base = prim,\
- \9\9\9\9\9Index = ex,\
- \9\9\9\9\9Tokens = tokenList,\
- \9\9\9\9}\
- \
- \9\9\9elseif not onlyDotColon and tok:ConsumeSymbol('(', tokenList) then\
- \9\9\9\9local args = {}\
- \9\9\9\9while not tok:ConsumeSymbol(')', tokenList) do\
- \9\9\9\9\9args[#args+1] = ParseExpr(scope)\
- \9\9\9\9\9if not tok:ConsumeSymbol(',', tokenList) then\
- \9\9\9\9\9\9if tok:ConsumeSymbol(')', tokenList) then\
- \9\9\9\9\9\9\9break\
- \9\9\9\9\9\9else\
- \9\9\9\9\9\9\9GenerateError(\"`)` Expected.\")\
- \9\9\9\9\9\9end\
- \9\9\9\9\9end\
- \9\9\9\9end\
- \
- \9\9\9\9prim = {\
- \9\9\9\9\9AstType = 'CallExpr',\
- \9\9\9\9\9Base = prim,\
- \9\9\9\9\9Arguments = args,\
- \9\9\9\9\9Tokens = tokenList,\
- \9\9\9\9}\
- \
- \9\9\9elseif not onlyDotColon and tok:Is('String') then\
- \9\9\9\9--string call\
- \9\9\9\9prim = {\
- \9\9\9\9\9AstType = 'StringCallExpr',\
- \9\9\9\9\9Base = prim,\
- \9\9\9\9\9Arguments = { tok:Get(tokenList) },\
- \9\9\9\9\9Tokens = tokenList,\
- \9\9\9\9}\
- \
- \9\9\9elseif not onlyDotColon and tok:IsSymbol('{') then\
- \9\9\9\9--table call\
- \9\9\9\9local ex = ParseSimpleExpr(scope)\
- \9\9\9\9-- FIX: ParseExpr(scope) parses the table AND and any following binary expressions.\
- \9\9\9\9-- We just want the table\
- \
- \9\9\9\9prim = {\
- \9\9\9\9\9AstType = 'TableCallExpr',\
- \9\9\9\9\9Base = prim,\
- \9\9\9\9\9Arguments = { ex },\
- \9\9\9\9\9Tokens = tokenList,\
- \9\9\9\9}\
- \
- \9\9\9else\
- \9\9\9\9break\
- \9\9\9end\
- \9\9end\
- \9\9return prim\
- \9end\
- \
- \9--- Parse a simple expression (strings, numbers, booleans, varargs)\
- \9-- @tparam Scope.Scope scope The current scope\
- \9-- @treturn Node The resulting node\
- \9function ParseSimpleExpr(scope)\
- \9\9local tokenList = {}\
- \
- \9\9if tok:Is('Number') then\
- \9\9\9return {\
- \9\9\9\9AstType = 'NumberExpr',\
- \9\9\9\9Value = tok:Get(tokenList),\
- \9\9\9\9Tokens = tokenList,\
- \9\9\9}\
- \
- \9\9elseif tok:Is('String') then\
- \9\9\9return {\
- \9\9\9\9AstType = 'StringExpr',\
- \9\9\9\9Value = tok:Get(tokenList),\
- \9\9\9\9Tokens = tokenList,\
- \9\9\9}\
- \
- \9\9elseif tok:ConsumeKeyword('nil', tokenList) then\
- \9\9\9return {\
- \9\9\9\9AstType = 'NilExpr',\
- \9\9\9\9Tokens = tokenList,\
- \9\9\9}\
- \
- \9\9elseif tok:IsKeyword('false') or tok:IsKeyword('true') then\
- \9\9\9return {\
- \9\9\9\9AstType = 'BooleanExpr',\
- \9\9\9\9Value = (tok:Get(tokenList).Data == 'true'),\
- \9\9\9\9Tokens = tokenList,\
- \9\9\9}\
- \
- \9\9elseif tok:ConsumeSymbol('...', tokenList) then\
- \9\9\9return {\
- \9\9\9\9AstType = 'DotsExpr',\
- \9\9\9\9Tokens = tokenList,\
- \9\9\9}\
- \
- \9\9elseif tok:ConsumeSymbol('{', tokenList) then\
- \9\9\9local entryList = {}\
- \9\9\9local v = {\
- \9\9\9\9AstType = 'ConstructorExpr',\
- \9\9\9\9EntryList = entryList,\
- \9\9\9\9Tokens = tokenList,\
- \9\9\9}\
- \
- \9\9\9while true do\
- \9\9\9\9if tok:IsSymbol('[', tokenList) then\
- \9\9\9\9\9--key\
- \9\9\9\9\9tok:Get(tokenList)\
- \9\9\9\9\9local key = ParseExpr(scope)\
- \9\9\9\9\9if not tok:ConsumeSymbol(']', tokenList) then\
- \9\9\9\9\9\9GenerateError(\"`]` Expected\")\
- \9\9\9\9\9end\
- \9\9\9\9\9if not tok:ConsumeSymbol('=', tokenList) then\
- \9\9\9\9\9\9GenerateError(\"`=` Expected\")\
- \9\9\9\9\9end\
- \9\9\9\9\9local value = ParseExpr(scope)\
- \9\9\9\9\9entryList[#entryList+1] = {\
- \9\9\9\9\9\9Type = 'Key',\
- \9\9\9\9\9\9Key = key,\
- \9\9\9\9\9\9Value = value,\
- \9\9\9\9\9}\
- \
- \9\9\9\9elseif tok:Is('Ident') then\
- \9\9\9\9\9--value or key\
- \9\9\9\9\9local lookahead = tok:Peek(1)\
- \9\9\9\9\9if lookahead.Type == 'Symbol' and lookahead.Data == '=' then\
- \9\9\9\9\9\9--we are a key\
- \9\9\9\9\9\9local key = tok:Get(tokenList)\
- \9\9\9\9\9\9if not tok:ConsumeSymbol('=', tokenList) then\
- \9\9\9\9\9\9\9GenerateError(\"`=` Expected\")\
- \9\9\9\9\9\9end\
- \9\9\9\9\9\9local value = ParseExpr(scope)\
- \9\9\9\9\9\9entryList[#entryList+1] = {\
- \9\9\9\9\9\9\9Type = 'KeyString',\
- \9\9\9\9\9\9\9Key = key.Data,\
- \9\9\9\9\9\9\9Value = value,\
- \9\9\9\9\9\9}\
- \
- \9\9\9\9\9else\
- \9\9\9\9\9\9--we are a value\
- \9\9\9\9\9\9local value = ParseExpr(scope)\
- \9\9\9\9\9\9entryList[#entryList+1] = {\
- \9\9\9\9\9\9\9Type = 'Value',\
- \9\9\9\9\9\9\9Value = value,\
- \9\9\9\9\9\9}\
- \
- \9\9\9\9\9end\
- \9\9\9\9elseif tok:ConsumeSymbol('}', tokenList) then\
- \9\9\9\9\9break\
- \
- \9\9\9\9else\
- \9\9\9\9\9--value\
- \9\9\9\9\9local value = ParseExpr(scope)\
- \9\9\9\9\9entryList[#entryList+1] = {\
- \9\9\9\9\9\9Type = 'Value',\
- \9\9\9\9\9\9Value = value,\
- \9\9\9\9\9}\
- \9\9\9\9end\
- \
- \9\9\9\9if tok:ConsumeSymbol(';', tokenList) or tok:ConsumeSymbol(',', tokenList) then\
- \9\9\9\9\9--all is good\
- \9\9\9\9elseif tok:ConsumeSymbol('}', tokenList) then\
- \9\9\9\9\9break\
- \9\9\9\9else\
- \9\9\9\9\9GenerateError(\"`}` or table entry Expected\")\
- \9\9\9\9end\
- \9\9\9end\
- \9\9\9return v\
- \
- \9\9elseif tok:ConsumeKeyword('function', tokenList) then\
- \9\9\9local func = ParseFunctionArgsAndBody(scope, tokenList)\
- \
- \9\9\9func.IsLocal = true\
- \9\9\9return func\
- \
- \9\9else\
- \9\9\9return ParseSuffixedExpr(scope)\
- \9\9end\
- \9end\
- \
- \9local unopprio = 8\
- \9local priority = {\
- \9\9['+'] = {6,6},\
- \9\9['-'] = {6,6},\
- \9\9['%'] = {7,7},\
- \9\9['/'] = {7,7},\
- \9\9['*'] = {7,7},\
- \9\9['^'] = {10,9},\
- \9\9['..'] = {5,4},\
- \9\9['=='] = {3,3},\
- \9\9['<'] = {3,3},\
- \9\9['<='] = {3,3},\
- \9\9['~='] = {3,3},\
- \9\9['>'] = {3,3},\
- \9\9['>='] = {3,3},\
- \9\9['and'] = {2,2},\
- \9\9['or'] = {1,1},\
- \9}\
- \
- \9--- Parse an expression\
- \9-- @tparam Skcope.Scope scope The current scope\
- \9-- @tparam int level Current level (Optional)\
- \9-- @treturn Node The resulting node\
- \9function ParseExpr(scope, level)\
- \9\9level = level or 0\
- \9\9--base item, possibly with unop prefix\
- \9\9local exp\
- \9\9if unops[tok:Peek().Data] then\
- \9\9\9local tokenList = {}\
- \9\9\9local op = tok:Get(tokenList).Data\
- \9\9\9exp = ParseExpr(scope, unopprio)\
- \
- \9\9\9local nodeEx = {\
- \9\9\9\9AstType = 'UnopExpr',\
- \9\9\9\9Rhs = exp,\
- \9\9\9\9Op = op,\
- \9\9\9\9OperatorPrecedence = unopprio,\
- \9\9\9\9Tokens = tokenList,\
- \9\9\9}\
- \
- \9\9\9exp = nodeEx\
- \9\9else\
- \9\9\9exp = ParseSimpleExpr(scope)\
- \9\9end\
- \
- \9\9--next items in chain\
- \9\9while true do\
- \9\9\9local prio = priority[tok:Peek().Data]\
- \9\9\9if prio and prio[1] > level then\
- \9\9\9\9local tokenList = {}\
- \9\9\9\9local op = tok:Get(tokenList).Data\
- \9\9\9\9local rhs = ParseExpr(scope, prio[2])\
- \
- \9\9\9\9local nodeEx = {\
- \9\9\9\9\9AstType = 'BinopExpr',\
- \9\9\9\9\9Lhs = exp,\
- \9\9\9\9\9Op = op,\
- \9\9\9\9\9OperatorPrecedence = prio[1],\
- \9\9\9\9\9Rhs = rhs,\
- \9\9\9\9\9Tokens = tokenList,\
- \9\9\9\9}\
- \
- \9\9\9\9exp = nodeEx\
- \9\9\9else\
- \9\9\9\9break\
- \9\9\9end\
- \9\9end\
- \
- \9\9return exp\
- \9end\
- \
- \9--- Parse a statement (if, for, while, etc...)\
- \9-- @tparam Scope.Scope scope The current scope\
- \9-- @treturn Node The resulting node\
- \9local function ParseStatement(scope)\
- \9\9local stat = nil\
- \9\9local tokenList = {}\
- \9\9if tok:ConsumeKeyword('if', tokenList) then\
- \9\9\9--setup\
- \9\9\9local clauses = {}\
- \9\9\9local nodeIfStat = {\
- \9\9\9\9AstType = 'IfStatement',\
- \9\9\9\9Clauses = clauses,\
- \9\9\9}\
- \9\9\9--clauses\
- \9\9\9repeat\
- \9\9\9\9local nodeCond = ParseExpr(scope)\
- \
- \9\9\9\9if not tok:ConsumeKeyword('then', tokenList) then\
- \9\9\9\9\9GenerateError(\"`then` expected.\")\
- \9\9\9\9end\
- \9\9\9\9local nodeBody = ParseStatementList(scope)\
- \9\9\9\9clauses[#clauses+1] = {\
- \9\9\9\9\9Condition = nodeCond,\
- \9\9\9\9\9Body = nodeBody,\
- \9\9\9\9}\
- \9\9\9until not tok:ConsumeKeyword('elseif', tokenList)\
- \
- \9\9\9--else clause\
- \9\9\9if tok:ConsumeKeyword('else', tokenList) then\
- \9\9\9\9local nodeBody = ParseStatementList(scope)\
- \9\9\9\9clauses[#clauses+1] = {\
- \9\9\9\9\9Body = nodeBody,\
- \9\9\9\9}\
- \9\9\9end\
- \
- \9\9\9--end\
- \9\9\9if not tok:ConsumeKeyword('end', tokenList) then\
- \9\9\9\9GenerateError(\"`end` expected.\")\
- \9\9\9end\
- \
- \9\9\9nodeIfStat.Tokens = tokenList\
- \9\9\9stat = nodeIfStat\
- \9\9elseif tok:ConsumeKeyword('while', tokenList) then\
- \9\9\9--condition\
- \9\9\9local nodeCond = ParseExpr(scope)\
- \
- \9\9\9--do\
- \9\9\9if not tok:ConsumeKeyword('do', tokenList) then\
- \9\9\9\9return GenerateError(\"`do` expected.\")\
- \9\9\9end\
- \
- \9\9\9--body\
- \9\9\9local nodeBody = ParseStatementList(scope)\
- \
- \9\9\9--end\
- \9\9\9if not tok:ConsumeKeyword('end', tokenList) then\
- \9\9\9\9GenerateError(\"`end` expected.\")\
- \9\9\9end\
- \
- \9\9\9--return\
- \9\9\9stat = {\
- \9\9\9\9AstType = 'WhileStatement',\
- \9\9\9\9Condition = nodeCond,\
- \9\9\9\9Body = nodeBody,\
- \9\9\9\9Tokens = tokenList,\
- \9\9\9}\
- \9\9elseif tok:ConsumeKeyword('do', tokenList) then\
- \9\9\9--do block\
- \9\9\9local nodeBlock = ParseStatementList(scope)\
- \9\9\9if not tok:ConsumeKeyword('end', tokenList) then\
- \9\9\9\9GenerateError(\"`end` expected.\")\
- \9\9\9end\
- \
- \9\9\9stat = {\
- \9\9\9\9AstType = 'DoStatement',\
- \9\9\9\9Body = nodeBlock,\
- \9\9\9\9Tokens = tokenList,\
- \9\9\9}\
- \9\9elseif tok:ConsumeKeyword('for', tokenList) then\
- \9\9\9--for block\
- \9\9\9if not tok:Is('Ident') then\
- \9\9\9\9GenerateError(\"<ident> expected.\")\
- \9\9\9end\
- \9\9\9local baseVarName = tok:Get(tokenList)\
- \9\9\9if tok:ConsumeSymbol('=', tokenList) then\
- \9\9\9\9--numeric for\
- \9\9\9\9local forScope = Scope(scope)\
- \9\9\9\9local forVar = forScope:CreateLocal(baseVarName.Data)\
- \
- \9\9\9\9local startEx = ParseExpr(scope)\
- \9\9\9\9if not tok:ConsumeSymbol(',', tokenList) then\
- \9\9\9\9\9GenerateError(\"`,` Expected\")\
- \9\9\9\9end\
- \9\9\9\9local endEx = ParseExpr(scope)\
- \9\9\9\9local stepEx\
- \9\9\9\9if tok:ConsumeSymbol(',', tokenList) then\
- \9\9\9\9\9stepEx = ParseExpr(scope)\
- \9\9\9\9end\
- \9\9\9\9if not tok:ConsumeKeyword('do', tokenList) then\
- \9\9\9\9\9GenerateError(\"`do` expected\")\
- \9\9\9\9end\
- \
- \9\9\9\9local body = ParseStatementList(forScope)\
- \9\9\9\9if not tok:ConsumeKeyword('end', tokenList) then\
- \9\9\9\9\9GenerateError(\"`end` expected\")\
- \9\9\9\9end\
- \
- \9\9\9\9stat = {\
- \9\9\9\9\9AstType = 'NumericForStatement',\
- \9\9\9\9\9Scope = forScope,\
- \9\9\9\9\9Variable = forVar,\
- \9\9\9\9\9Start = startEx,\
- \9\9\9\9\9End = endEx,\
- \9\9\9\9\9Step = stepEx,\
- \9\9\9\9\9Body = body,\
- \9\9\9\9\9Tokens = tokenList,\
- \9\9\9\9}\
- \9\9\9else\
- \9\9\9\9--generic for\
- \9\9\9\9local forScope = Scope(scope)\
- \
- \9\9\9\9local varList = { forScope:CreateLocal(baseVarName.Data) }\
- \9\9\9\9while tok:ConsumeSymbol(',', tokenList) do\
- \9\9\9\9\9if not tok:Is('Ident') then\
- \9\9\9\9\9\9GenerateError(\"for variable expected.\")\
- \9\9\9\9\9end\
- \9\9\9\9\9varList[#varList+1] = forScope:CreateLocal(tok:Get(tokenList).Data)\
- \9\9\9\9end\
- \9\9\9\9if not tok:ConsumeKeyword('in', tokenList) then\
- \9\9\9\9\9GenerateError(\"`in` expected.\")\
- \9\9\9\9end\
- \9\9\9\9local generators = {ParseExpr(scope)}\
- \9\9\9\9while tok:ConsumeSymbol(',', tokenList) do\
- \9\9\9\9\9generators[#generators+1] = ParseExpr(scope)\
- \9\9\9\9end\
- \
- \9\9\9\9if not tok:ConsumeKeyword('do', tokenList) then\
- \9\9\9\9\9GenerateError(\"`do` expected.\")\
- \9\9\9\9end\
- \
- \9\9\9\9local body = ParseStatementList(forScope)\
- \9\9\9\9if not tok:ConsumeKeyword('end', tokenList) then\
- \9\9\9\9\9GenerateError(\"`end` expected.\")\
- \9\9\9\9end\
- \
- \9\9\9\9stat = {\
- \9\9\9\9\9AstType = 'GenericForStatement',\
- \9\9\9\9\9Scope = forScope,\
- \9\9\9\9\9VariableList = varList,\
- \9\9\9\9\9Generators = generators,\
- \9\9\9\9\9Body = body,\
- \9\9\9\9\9Tokens = tokenList,\
- \9\9\9\9}\
- \9\9\9end\
- \9\9elseif tok:ConsumeKeyword('repeat', tokenList) then\
- \9\9\9local body = ParseStatementList(scope)\
- \
- \9\9\9if not tok:ConsumeKeyword('until', tokenList) then\
- \9\9\9\9GenerateError(\"`until` expected.\")\
- \9\9\9end\
- \
- \9\9\9cond = ParseExpr(body.Scope)\
- \
- \9\9\9stat = {\
- \9\9\9\9AstType = 'RepeatStatement',\
- \9\9\9\9Condition = cond,\
- \9\9\9\9Body = body,\
- \9\9\9\9Tokens = tokenList,\
- \9\9\9}\
- \9\9elseif tok:ConsumeKeyword('function', tokenList) then\
- \9\9\9if not tok:Is('Ident') then\
- \9\9\9\9GenerateError(\"Function name expected\")\
- \9\9\9end\
- \9\9\9local name = ParseSuffixedExpr(scope, true) --true => only dots and colons\
- \
- \9\9\9local func = ParseFunctionArgsAndBody(scope, tokenList)\
- \
- \9\9\9func.IsLocal = false\
- \9\9\9func.Name = name\
- \9\9\9stat = func\
- \9\9elseif tok:ConsumeKeyword('local', tokenList) then\
- \9\9\9if tok:Is('Ident') then\
- \9\9\9\9local varList = { tok:Get(tokenList).Data }\
- \9\9\9\9while tok:ConsumeSymbol(',', tokenList) do\
- \9\9\9\9\9if not tok:Is('Ident') then\
- \9\9\9\9\9\9GenerateError(\"local var name expected\")\
- \9\9\9\9\9end\
- \9\9\9\9\9varList[#varList+1] = tok:Get(tokenList).Data\
- \9\9\9\9end\
- \
- \9\9\9\9local initList = {}\
- \9\9\9\9if tok:ConsumeSymbol('=', tokenList) then\
- \9\9\9\9\9repeat\
- \9\9\9\9\9\9initList[#initList+1] = ParseExpr(scope)\
- \9\9\9\9\9until not tok:ConsumeSymbol(',', tokenList)\
- \9\9\9\9end\
- \
- \9\9\9\9--now patch var list\
- \9\9\9\9--we can't do this before getting the init list, because the init list does not\
- \9\9\9\9--have the locals themselves in scope.\
- \9\9\9\9for i, v in pairs(varList) do\
- \9\9\9\9\9varList[i] = scope:CreateLocal(v)\
- \9\9\9\9end\
- \
- \9\9\9\9stat = {\
- \9\9\9\9\9AstType = 'LocalStatement',\
- \9\9\9\9\9LocalList = varList,\
- \9\9\9\9\9InitList = initList,\
- \9\9\9\9\9Tokens = tokenList,\
- \9\9\9\9}\
- \
- \9\9\9elseif tok:ConsumeKeyword('function', tokenList) then\
- \9\9\9\9if not tok:Is('Ident') then\
- \9\9\9\9\9GenerateError(\"Function name expected\")\
- \9\9\9\9end\
- \9\9\9\9local name = tok:Get(tokenList).Data\
- \9\9\9\9local localVar = scope:CreateLocal(name)\
- \
- \9\9\9\9local func = ParseFunctionArgsAndBody(scope, tokenList)\
- \
- \9\9\9\9func.Name = localVar\
- \9\9\9\9func.IsLocal = true\
- \9\9\9\9stat = func\
- \
- \9\9\9else\
- \9\9\9\9GenerateError(\"local var or function def expected\")\
- \9\9\9end\
- \9\9elseif tok:ConsumeSymbol('::', tokenList) then\
- \9\9\9if not tok:Is('Ident') then\
- \9\9\9\9GenerateError('Label name expected')\
- \9\9\9end\
- \9\9\9local label = tok:Get(tokenList).Data\
- \9\9\9if not tok:ConsumeSymbol('::', tokenList) then\
- \9\9\9\9GenerateError(\"`::` expected\")\
- \9\9\9end\
- \9\9\9stat = {\
- \9\9\9\9AstType = 'LabelStatement',\
- \9\9\9\9Label = label,\
- \9\9\9\9Tokens = tokenList,\
- \9\9\9}\
- \9\9elseif tok:ConsumeKeyword('return', tokenList) then\
- \9\9\9local exList = {}\
- \9\9\9if not tok:IsKeyword('end') then\
- \9\9\9\9-- Use PCall as this may produce an error\
- \9\9\9\9local st, firstEx = pcall(function() return ParseExpr(scope) end)\
- \9\9\9\9if st then\
- \9\9\9\9\9exList[1] = firstEx\
- \9\9\9\9\9while tok:ConsumeSymbol(',', tokenList) do\
- \9\9\9\9\9\9exList[#exList+1] = ParseExpr(scope)\
- \9\9\9\9\9end\
- \9\9\9\9end\
- \9\9\9end\
- \9\9\9stat = {\
- \9\9\9\9AstType = 'ReturnStatement',\
- \9\9\9\9Arguments = exList,\
- \9\9\9\9Tokens = tokenList,\
- \9\9\9}\
- \9\9elseif tok:ConsumeKeyword('break', tokenList) then\
- \9\9\9stat = {\
- \9\9\9\9AstType = 'BreakStatement',\
- \9\9\9\9Tokens = tokenList,\
- \9\9\9}\
- \9\9elseif tok:ConsumeKeyword('goto', tokenList) then\
- \9\9\9if not tok:Is('Ident') then\
- \9\9\9\9GenerateError(\"Label expected\")\
- \9\9\9end\
- \9\9\9local label = tok:Get(tokenList).Data\
- \9\9\9stat = {\
- \9\9\9\9AstType = 'GotoStatement',\
- \9\9\9\9Label = label,\
- \9\9\9\9Tokens = tokenList,\
- \9\9\9}\
- \9\9else\
- \9\9\9--statementParseExpr\
- \9\9\9local suffixed = ParseSuffixedExpr(scope)\
- \
- \9\9\9--assignment or call?\
- \9\9\9if tok:IsSymbol(',') or tok:IsSymbol('=') then\
- \9\9\9\9--check that it was not parenthesized, making it not an lvalue\
- \9\9\9\9if (suffixed.ParenCount or 0) > 0 then\
- \9\9\9\9\9GenerateError(\"Can not assign to parenthesized expression, is not an lvalue\")\
- \9\9\9\9end\
- \
- \9\9\9\9--more processing needed\
- \9\9\9\9local lhs = { suffixed }\
- \9\9\9\9while tok:ConsumeSymbol(',', tokenList) do\
- \9\9\9\9\9lhs[#lhs+1] = ParseSuffixedExpr(scope)\
- \9\9\9\9end\
- \
- \9\9\9\9--equals\
- \9\9\9\9if not tok:ConsumeSymbol('=', tokenList) then\
- \9\9\9\9\9GenerateError(\"`=` Expected.\")\
- \9\9\9\9end\
- \
- \9\9\9\9--rhs\
- \9\9\9\9local rhs = {ParseExpr(scope)}\
- \9\9\9\9while tok:ConsumeSymbol(',', tokenList) do\
- \9\9\9\9\9rhs[#rhs+1] = ParseExpr(scope)\
- \9\9\9\9end\
- \
- \9\9\9\9--done\
- \9\9\9\9stat = {\
- \9\9\9\9\9AstType = 'AssignmentStatement',\
- \9\9\9\9\9Lhs = lhs,\
- \9\9\9\9\9Rhs = rhs,\
- \9\9\9\9\9Tokens = tokenList,\
- \9\9\9\9}\
- \
- \9\9\9elseif suffixed.AstType == 'CallExpr' or\
- \9\9\9\9 suffixed.AstType == 'TableCallExpr' or\
- \9\9\9\9 suffixed.AstType == 'StringCallExpr'\
- \9\9\9then\
- \9\9\9\9--it's a call statement\
- \9\9\9\9stat = {\
- \9\9\9\9\9AstType = 'CallStatement',\
- \9\9\9\9\9Expression = suffixed,\
- \9\9\9\9\9Tokens = tokenList,\
- \9\9\9\9}\
- \9\9\9else\
- \9\9\9\9GenerateError(\"Assignment Statement Expected\")\
- \9\9\9end\
- \9\9end\
- \
- \9\9if tok:IsSymbol(';') then\
- \9\9\9stat.Semicolon = tok:Get( stat.Tokens )\
- \9\9end\
- \9\9return stat\
- \9end\
- \
- \9--- Parse a a list of statements\
- \9-- @tparam Scope.Scope scope The current scope\
- \9-- @treturn Node The resulting node\
- \9function ParseStatementList(scope)\
- \9\9local body = {}\
- \9\9local nodeStatlist = {\
- \9\9\9Scope = Scope(scope),\
- \9\9\9AstType = 'Statlist',\
- \9\9\9Body = body,\
- \9\9\9Tokens = {},\
- \9\9}\
- \
- \9\9while not statListCloseKeywords[tok:Peek().Data] and not tok:IsEof() do\
- \9\9\9local nodeStatement = ParseStatement(nodeStatlist.Scope)\
- \9\9\9--stats[#stats+1] = nodeStatement\
- \9\9\9body[#body + 1] = nodeStatement\
- \9\9end\
- \
- \9\9if tok:IsEof() then\
- \9\9\9local nodeEof = {}\
- \9\9\9nodeEof.AstType = 'Eof'\
- \9\9\9nodeEof.Tokens = { tok:Get() }\
- \9\9\9body[#body + 1] = nodeEof\
- \9\9end\
- \
- \9\9--nodeStatlist.Body = stats\
- \9\9return nodeStatlist\
- \9end\
- \
- \9return ParseStatementList(Scope())\
- end\
- \
- --- @export\
- return { LexLua = LexLua, ParseLua = ParseLua }\
- end)\
- Rebuild=_W(function()\
- --- Rebuild source code from an AST\
- -- Does not preserve whitespace\
- -- @module lexer.Rebuild\
- \
- local lowerChars = Constants.LowerChars\
- local upperChars = Constants.UpperChars\
- local digits = Constants.Digits\
- local symbols = Constants.Symbols\
- \
- --- Join two statements together\
- -- @tparam string left The left statement\
- -- @tparam string right The right statement\
- -- @tparam string sep The string used to separate the characters\
- -- @treturn string The joined strings\
- local function JoinStatements(left, right, sep)\
- \9sep = sep or ' '\
- \9local leftEnd, rightStart = left:sub(-1,-1), right:sub(1,1)\
- \9if upperChars[leftEnd] or lowerChars[leftEnd] or leftEnd == '_' then\
- \9\9if not (rightStart == '_' or upperChars[rightStart] or lowerChars[rightStart] or digits[rightStart]) then\
- \9\9\9--rightStart is left symbol, can join without seperation\
- \9\9\9return left .. right\
- \9\9else\
- \9\9\9return left .. sep .. right\
- \9\9end\
- \9elseif digits[leftEnd] then\
- \9\9if rightStart == '(' then\
- \9\9\9--can join statements directly\
- \9\9\9return left .. right\
- \9\9elseif symbols[rightStart] then\
- \9\9\9return left .. right\
- \9\9else\
- \9\9\9return left .. sep .. right\
- \9\9end\
- \9elseif leftEnd == '' then\
- \9\9return left .. right\
- \9else\
- \9\9if rightStart == '(' then\
- \9\9\9--don't want to accidentally call last statement, can't join directly\
- \9\9\9return left .. sep .. right\
- \9\9else\
- \9\9\9return left .. right\
- \9\9end\
- \9end\
- end\
- \
- --- Returns the minified version of an AST. Operations which are performed:\
- -- - All comments and whitespace are ignored\
- -- - All local variables are renamed\
- -- @tparam Node ast The AST tree\
- -- @treturn string The minified string\
- -- @todo Ability to control minification level\
- local function Minify(ast)\
- \9local formatStatlist, formatExpr\
- \9local count = 0\
- \9local function joinStatements(left, right, set)\
- \9\9if count > 150 then\
- \9\9\9count = 0\
- \9\9\9return left .. \"\\n\" .. right\
- \9\9else\
- \9\9\9return JoinStatements(left, right, sep)\
- \9\9end\
- \9end\
- \
- \9formatExpr = function(expr, precedence)\
- \9\9local precedence = precedence or 0\
- \9\9local currentPrecedence = 0\
- \9\9local skipParens = false\
- \9\9local out = \"\"\
- \9\9if expr.AstType == 'VarExpr' then\
- \9\9\9if expr.Variable then\
- \9\9\9\9out = out..expr.Variable.Name\
- \9\9\9else\
- \9\9\9\9out = out..expr.Name\
- \9\9\9end\
- \
- \9\9elseif expr.AstType == 'NumberExpr' then\
- \9\9\9out = out..expr.Value.Data\
- \
- \9\9elseif expr.AstType == 'StringExpr' then\
- \9\9\9out = out..expr.Value.Data\
- \
- \9\9elseif expr.AstType == 'BooleanExpr' then\
- \9\9\9out = out..tostring(expr.Value)\
- \
- \9\9elseif expr.AstType == 'NilExpr' then\
- \9\9\9out = joinStatements(out, \"nil\")\
- \
- \9\9elseif expr.AstType == 'BinopExpr' then\
- \9\9\9currentPrecedence = expr.OperatorPrecedence\
- \9\9\9out = joinStatements(out, formatExpr(expr.Lhs, currentPrecedence))\
- \9\9\9out = joinStatements(out, expr.Op)\
- \9\9\9out = joinStatements(out, formatExpr(expr.Rhs))\
- \9\9\9if expr.Op == '^' or expr.Op == '..' then\
- \9\9\9\9currentPrecedence = currentPrecedence - 1\
- \9\9\9end\
- \
- \9\9\9if currentPrecedence < precedence then\
- \9\9\9\9skipParens = false\
- \9\9\9else\
- \9\9\9\9skipParens = true\
- \9\9\9end\
- \9\9elseif expr.AstType == 'UnopExpr' then\
- \9\9\9out = joinStatements(out, expr.Op)\
- \9\9\9out = joinStatements(out, formatExpr(expr.Rhs))\
- \
- \9\9elseif expr.AstType == 'DotsExpr' then\
- \9\9\9out = out..\"...\"\
- \
- \9\9elseif expr.AstType == 'CallExpr' then\
- \9\9\9out = out..formatExpr(expr.Base)\
- \9\9\9out = out..\"(\"\
- \9\9\9for i = 1, #expr.Arguments do\
- \9\9\9\9out = out..formatExpr(expr.Arguments[i])\
- \9\9\9\9if i ~= #expr.Arguments then\
- \9\9\9\9\9out = out..\",\"\
- \9\9\9\9end\
- \9\9\9end\
- \9\9\9out = out..\")\"\
- \
- \9\9elseif expr.AstType == 'TableCallExpr' then\
- \9\9\9out = out..formatExpr(expr.Base)\
- \9\9\9out = out..formatExpr(expr.Arguments[1])\
- \
- \9\9elseif expr.AstType == 'StringCallExpr' then\
- \9\9\9out = out..formatExpr(expr.Base)\
- \9\9\9out = out..expr.Arguments[1].Data\
- \
- \9\9elseif expr.AstType == 'IndexExpr' then\
- \9\9\9out = out..formatExpr(expr.Base)..\"[\"..formatExpr(expr.Index)..\"]\"\
- \
- \9\9elseif expr.AstType == 'MemberExpr' then\
- \9\9\9out = out..formatExpr(expr.Base)..expr.Indexer..expr.Ident.Data\
- \
- \9\9elseif expr.AstType == 'Function' then\
- \9\9\9expr.Scope:ObfuscateLocals()\
- \9\9\9out = out..\"function(\"\
- \9\9\9if #expr.Arguments > 0 then\
- \9\9\9\9for i = 1, #expr.Arguments do\
- \9\9\9\9\9out = out..expr.Arguments[i].Name\
- \9\9\9\9\9if i ~= #expr.Arguments then\
- \9\9\9\9\9\9out = out..\",\"\
- \9\9\9\9\9elseif expr.VarArg then\
- \9\9\9\9\9\9out = out..\",...\"\
- \9\9\9\9\9end\
- \9\9\9\9end\
- \9\9\9elseif expr.VarArg then\
- \9\9\9\9out = out..\"...\"\
- \9\9\9end\
- \9\9\9out = out..\")\"\
- \9\9\9out = joinStatements(out, formatStatlist(expr.Body))\
- \9\9\9out = joinStatements(out, \"end\")\
- \
- \9\9elseif expr.AstType == 'ConstructorExpr' then\
- \9\9\9out = out..\"{\"\
- \9\9\9for i = 1, #expr.EntryList do\
- \9\9\9\9local entry = expr.EntryList[i]\
- \9\9\9\9if entry.Type == 'Key' then\
- \9\9\9\9\9out = out..\"[\"..formatExpr(entry.Key)..\"]=\"..formatExpr(entry.Value)\
- \9\9\9\9elseif entry.Type == 'Value' then\
- \9\9\9\9\9out = out..formatExpr(entry.Value)\
- \9\9\9\9elseif entry.Type == 'KeyString' then\
- \9\9\9\9\9out = out..entry.Key..\"=\"..formatExpr(entry.Value)\
- \9\9\9\9end\
- \9\9\9\9if i ~= #expr.EntryList then\
- \9\9\9\9\9out = out..\",\"\
- \9\9\9\9end\
- \9\9\9end\
- \9\9\9out = out..\"}\"\
- \
- \9\9elseif expr.AstType == 'Parentheses' then\
- \9\9\9out = out..\"(\"..formatExpr(expr.Inner)..\")\"\
- \
- \9\9end\
- \9\9if not skipParens then\
- \9\9\9out = string.rep('(', expr.ParenCount or 0) .. out\
- \9\9\9out = out .. string.rep(')', expr.ParenCount or 0)\
- \9\9end\
- \9\9count = count + #out\
- \9\9return out\
- \9end\
- \
- \9local formatStatement = function(statement)\
- \9\9local out = ''\
- \9\9if statement.AstType == 'AssignmentStatement' then\
- \9\9\9for i = 1, #statement.Lhs do\
- \9\9\9\9out = out..formatExpr(statement.Lhs[i])\
- \9\9\9\9if i ~= #statement.Lhs then\
- \9\9\9\9\9out = out..\",\"\
- \9\9\9\9end\
- \9\9\9end\
- \9\9\9if #statement.Rhs > 0 then\
- \9\9\9\9out = out..\"=\"\
- \9\9\9\9for i = 1, #statement.Rhs do\
- \9\9\9\9\9out = out..formatExpr(statement.Rhs[i])\
- \9\9\9\9\9if i ~= #statement.Rhs then\
- \9\9\9\9\9\9out = out..\",\"\
- \9\9\9\9\9end\
- \9\9\9\9end\
- \9\9\9end\
- \
- \9\9elseif statement.AstType == 'CallStatement' then\
- \9\9\9out = formatExpr(statement.Expression)\
- \
- \9\9elseif statement.AstType == 'LocalStatement' then\
- \9\9\9out = out..\"local \"\
- \9\9\9for i = 1, #statement.LocalList do\
- \9\9\9\9out = out..statement.LocalList[i].Name\
- \9\9\9\9if i ~= #statement.LocalList then\
- \9\9\9\9\9out = out..\",\"\
- \9\9\9\9end\
- \9\9\9end\
- \9\9\9if #statement.InitList > 0 then\
- \9\9\9\9out = out..\"=\"\
- \9\9\9\9for i = 1, #statement.InitList do\
- \9\9\9\9\9out = out..formatExpr(statement.InitList[i])\
- \9\9\9\9\9if i ~= #statement.InitList then\
- \9\9\9\9\9\9out = out..\",\"\
- \9\9\9\9\9end\
- \9\9\9\9end\
- \9\9\9end\
- \
- \9\9elseif statement.AstType == 'IfStatement' then\
- \9\9\9out = joinStatements(\"if\", formatExpr(statement.Clauses[1].Condition))\
- \9\9\9out = joinStatements(out, \"then\")\
- \9\9\9out = joinStatements(out, formatStatlist(statement.Clauses[1].Body))\
- \9\9\9for i = 2, #statement.Clauses do\
- \9\9\9\9local st = statement.Clauses[i]\
- \9\9\9\9if st.Condition then\
- \9\9\9\9\9out = joinStatements(out, \"elseif\")\
- \9\9\9\9\9out = joinStatements(out, formatExpr(st.Condition))\
- \9\9\9\9\9out = joinStatements(out, \"then\")\
- \9\9\9\9else\
- \9\9\9\9\9out = joinStatements(out, \"else\")\
- \9\9\9\9end\
- \9\9\9\9out = joinStatements(out, formatStatlist(st.Body))\
- \9\9\9end\
- \9\9\9out = joinStatements(out, \"end\")\
- \
- \9\9elseif statement.AstType == 'WhileStatement' then\
- \9\9\9out = joinStatements(\"while\", formatExpr(statement.Condition))\
- \9\9\9out = joinStatements(out, \"do\")\
- \9\9\9out = joinStatements(out, formatStatlist(statement.Body))\
- \9\9\9out = joinStatements(out, \"end\")\
- \
- \9\9elseif statement.AstType == 'DoStatement' then\
- \9\9\9out = joinStatements(out, \"do\")\
- \9\9\9out = joinStatements(out, formatStatlist(statement.Body))\
- \9\9\9out = joinStatements(out, \"end\")\
- \
- \9\9elseif statement.AstType == 'ReturnStatement' then\
- \9\9\9out = \"return\"\
- \9\9\9for i = 1, #statement.Arguments do\
- \9\9\9\9out = joinStatements(out, formatExpr(statement.Arguments[i]))\
- \9\9\9\9if i ~= #statement.Arguments then\
- \9\9\9\9\9out = out..\",\"\
- \9\9\9\9end\
- \9\9\9end\
- \
- \9\9elseif statement.AstType == 'BreakStatement' then\
- \9\9\9out = \"break\"\
- \
- \9\9elseif statement.AstType == 'RepeatStatement' then\
- \9\9\9out = \"repeat\"\
- \9\9\9out = joinStatements(out, formatStatlist(statement.Body))\
- \9\9\9out = joinStatements(out, \"until\")\
- \9\9\9out = joinStatements(out, formatExpr(statement.Condition))\
- \
- \9\9elseif statement.AstType == 'Function' then\
- \9\9\9statement.Scope:ObfuscateLocals()\
- \9\9\9if statement.IsLocal then\
- \9\9\9\9out = \"local\"\
- \9\9\9end\
- \9\9\9out = joinStatements(out, \"function \")\
- \9\9\9if statement.IsLocal then\
- \9\9\9\9out = out..statement.Name.Name\
- \9\9\9else\
- \9\9\9\9out = out..formatExpr(statement.Name)\
- \9\9\9end\
- \9\9\9out = out..\"(\"\
- \9\9\9if #statement.Arguments > 0 then\
- \9\9\9\9for i = 1, #statement.Arguments do\
- \9\9\9\9\9out = out..statement.Arguments[i].Name\
- \9\9\9\9\9if i ~= #statement.Arguments then\
- \9\9\9\9\9\9out = out..\",\"\
- \9\9\9\9\9elseif statement.VarArg then\
- \9\9\9\9\9\9out = out..\",...\"\
- \9\9\9\9\9end\
- \9\9\9\9end\
- \9\9\9elseif statement.VarArg then\
- \9\9\9\9out = out..\"...\"\
- \9\9\9end\
- \9\9\9out = out..\")\"\
- \9\9\9out = joinStatements(out, formatStatlist(statement.Body))\
- \9\9\9out = joinStatements(out, \"end\")\
- \
- \9\9elseif statement.AstType == 'GenericForStatement' then\
- \9\9\9statement.Scope:ObfuscateLocals()\
- \9\9\9out = \"for \"\
- \9\9\9for i = 1, #statement.VariableList do\
- \9\9\9\9out = out..statement.VariableList[i].Name\
- \9\9\9\9if i ~= #statement.VariableList then\
- \9\9\9\9\9out = out..\",\"\
- \9\9\9\9end\
- \9\9\9end\
- \9\9\9out = out..\" in\"\
- \9\9\9for i = 1, #statement.Generators do\
- \9\9\9\9out = joinStatements(out, formatExpr(statement.Generators[i]))\
- \9\9\9\9if i ~= #statement.Generators then\
- \9\9\9\9\9out = joinStatements(out, ',')\
- \9\9\9\9end\
- \9\9\9end\
- \9\9\9out = joinStatements(out, \"do\")\
- \9\9\9out = joinStatements(out, formatStatlist(statement.Body))\
- \9\9\9out = joinStatements(out, \"end\")\
- \
- \9\9elseif statement.AstType == 'NumericForStatement' then\
- \9\9\9statement.Scope:ObfuscateLocals()\
- \9\9\9out = \"for \"\
- \9\9\9out = out..statement.Variable.Name..\"=\"\
- \9\9\9out = out..formatExpr(statement.Start)..\",\"..formatExpr(statement.End)\
- \9\9\9if statement.Step then\
- \9\9\9\9out = out..\",\"..formatExpr(statement.Step)\
- \9\9\9end\
- \9\9\9out = joinStatements(out, \"do\")\
- \9\9\9out = joinStatements(out, formatStatlist(statement.Body))\
- \9\9\9out = joinStatements(out, \"end\")\
- \9\9elseif statement.AstType == 'LabelStatement' then\
- \9\9\9out = \"::\" .. statement.Label .. \"::\"\
- \9\9elseif statement.AstType == 'GotoStatement' then\
- \9\9\9out = \"goto \" .. statement.Label\
- \9\9elseif statement.AstType == 'Comment' then\
- \9\9\9-- ignore\
- \9\9elseif statement.AstType == 'Eof' then\
- \9\9\9-- ignore\
- \9\9else\
- \9\9\9error(\"Unknown AST Type: \" .. statement.AstType)\
- \9\9end\
- \9\9count = count + #out\
- \9\9return out\
- \9end\
- \
- \9formatStatlist = function(statList)\
- \9\9local out = ''\
- \9\9statList.Scope:ObfuscateLocals()\
- \9\9for _, stat in pairs(statList.Body) do\
- \9\9\9out = joinStatements(out, formatStatement(stat), ';')\
- \9\9end\
- \9\9return out\
- \9end\
- \
- \9return formatStatlist(ast)\
- end\
- \
- local push, pop = os.queueEvent, coroutine.yield\
- \
- --- Minify a string\
- -- @tparam string input The input string\
- -- @treturn string The minifyied string\
- local function MinifyString(input)\
- \9local lex = Parse.LexLua(input)\
- \9push(\"sleep\") pop(\"sleep\") -- Minifying often takes too long\
- \
- \9lex = Parse.ParseLua(lex)\
- \9push(\"sleep\") pop(\"sleep\")\
- \
- \9return Minify(lex)\
- end\
- \
- --- Minify a file\
- -- @tparam string inputFile File to read from\
- -- @tparam string outputFile File to write to (Defaults to inputFile)\
- local function MinifyFile(inputFile, outputFile)\
- \9outputFile = outputFile or inputFile\
- \
- \9local input = fs.open(inputFile, \"r\")\
- \9local contents = input.readAll()\
- \9input.close()\
- \
- \9contents = MinifyString(contents)\
- \
- \9local result = fs.open(outputFile, \"w\")\
- \9result.write(contents)\
- \9result.close()\
- end\
- \
- --- @export\
- return {\
- \9JoinStatements = JoinStatements,\
- \9Minify = Minify,\
- \9MinifyString = MinifyString,\
- \9MinifyFile = MinifyFile,\
- }\
- end)";
- ["/run.lua"] = "\
- local files, main = ...\
- \
- if type( files ) ~= \"table\" or type( main ) ~= \"string\" then\
- \9if shell and shell.getRunningProgram() == \"Flare/run.lua\" then\
- \9\9return printError \"Use Flare/bin/debug to run a Flare project\"\
- \9else\
- \9\9return error( \"expected table files, string path, got \" .. type( files ) .. \", \" .. type( main ) )\
- \9end\
- end\
- \
- local libpath = \"Flare/lib;Flare/lib/elements\"\
- local env = setmetatable( {}, { __index = _ENV or getfenv() } )\
- local loaded, results = {}, {}\
- local class\
- \
- local function extends( new )\
- \9if not class.isClass( env[new] ) then\
- \9\9return error( new .. \" is not a class\" )\
- \9end\
- \9local last = class.last()\
- \9last:extends( env[new] )\
- \9return function( t )\
- \9\9last:mixin( t )\
- \9end\
- end\
- \
- local function loadf( content, name )\
- \9local f, err = load( content, name, nil, env )\
- \9if not f then\
- \9\9return error( err, 0 )\
- \9end\
- \9return f\
- end\
- \
- local function loadfile( path, name )\
- \9local h = fs.open( path, \"r\" )\
- \9local content = h.readAll() -- existence is already checked, so open() must work\
- \9h.close()\
- \
- \9return loadf( content, name )\
- end\
- \
- local function require( name )\
- \9if loaded[name] then\
- \9\9return results[name]\
- \9end\
- \9loaded[name] = true\
- \9if files[name] then\
- \9\9local ok, data = pcall( loadf( files[name], name ), name )\
- \9\9if not ok then\
- \9\9\9return error( data, 0 )\
- \9\9end\
- \9\9results[name] = data\
- \9\9return data\
- \9else\
- \9\9for path in libpath:gmatch \"[^;]+\" do\
- \9\9\9local file, fpath = path .. \"/\" .. name:gsub( \"%.\", \"/\" ), nil\
- \9\9\9if fs.exists( file .. \".lua\" ) and not fs.isDir( file .. \".lua\" ) then\
- \9\9\9\9fpath = file .. \".lua\"\
- \9\9\9elseif fs.exists( file .. \"/init.lua\" ) and not fs.isDir( file .. \"/init.lua\" ) then\
- \9\9\9\9fpath = file .. \"/init.lua\"\
- \9\9\9end\
- \9\9\9if fpath then\
- \9\9\9\9local ok, data = pcall( loadfile( fpath, name ), name )\
- \9\9\9\9if not ok then\
- \9\9\9\9\9return error( data, 0 )\
- \9\9\9\9end\
- \9\9\9\9results[name] = data\
- \9\9\9\9return data\
- \9\9\9end\
- \9\9end\
- \9\9return error( \"file not found: '\" .. name .. \"'\" )\
- \9end\
- end\
- \
- class = require \"class\"\
- \
- env.require = require\
- env.class = class\
- env.extends = extends\
- \
- local screen, view\
- local running = true\
- local held = {}\
- \
- require \"UIView\"\
- require \"Timer\"\
- require \"graphics.ScreenCanvas\"\
- \
- require \"Event.Event\"\
- require \"Event.MouseEvent\"\
- require \"Event.KeyboardEvent\"\
- require \"Event.TextEvent\"\
- require \"Timer\"\
- \
- local Timer = env.Timer\
- local Event = env.Event\
- local MouseEvent = env.MouseEvent\
- local KeyboardEvent = env.KeyboardEvent\
- local TextEvent = env.TextEvent\
- \
- local application = {\
- \9view = env.UIView( 0, 0, term.getSize() );\
- \9terminateable = true;\
- }\
- \
- function application:stop()\
- \9running = false\
- end\
- \
- application.view.canvas = application.view.canvas:cloneAs( env.ScreenCanvas )\
- screen = application.view.canvas\
- view = application.view\
- \
- local function getEventObject( ev )\
- \9if ev[1] == \"key\" then\
- \9\9held[keys.getName( ev[2] ) or ev[2]] = true\
- \9\9return KeyboardEvent( Event.KEYDOWN, ev[2], held, { isRepeat = ev[3] } )\
- \9elseif ev[1] == \"key_up\" then\
- \9\9held[keys.getName( ev[2] ) or ev[2]] = nil\
- \9\9return KeyboardEvent( Event.KEYUP, ev[2], held, {} )\
- \9elseif ev[1] == \"mouse_click\" then\
- \9\9return MouseEvent( Event.MOUSEDOWN, ev[3] - 1, ev[4] - 1, ev[2], true, {} )\
- \9elseif ev[1] == \"mouse_up\" then\
- \9\9return MouseEvent( Event.MOUSEUP, ev[3] - 1, ev[4] - 1, ev[2], true, {} )\
- \9elseif ev[1] == \"mouse_scroll\" then\
- \9\9return MouseEvent( Event.MOUSESCROLL, ev[3] - 1, ev[4] - 1, ev[2], true, {} )\
- \9elseif ev[1] == \"mouse_drag\" then\
- \9\9return MouseEvent( Event.MOUSEDRAG, ev[3] - 1, ev[4] - 1, ev[2], true, {} )\
- \9elseif ev[1] == \"char\" then\
- \9\9return TextEvent( Event.TEXT, ev[2], {} )\
- \9elseif ev[1] == \"paste\" then\
- \9\9return TextEvent( Event.PASTE, ev[2], {} )\
- \9else\
- \9\9return Event( ev[1], { unpack( ev, 2 ) } )\
- \9end\
- end\
- local function redraw()\
- \9if view.changed then\
- \9\9screen:clear()\
- \9\9view:draw()\
- \9\9screen:drawToScreen()\
- \9\9if screen.cursor then\
- \9\9\9local cursor = screen.cursor\
- \9\9\9term.setCursorPos( cursor.x + 1, cursor.y + 1 )\
- \9\9\9term.setTextColour( cursor.colour )\
- \9\9\9term.setCursorBlink( true )\
- \9\9else\
- \9\9\9term.setCursorBlink( false )\
- \9\9end\
- \9end\
- end\
- \
- env.application = application\
- \
- require \"init\"\
- \
- if type( application.load ) == \"function\" then\
- \9application:load( select( 3, ... ) )\
- end\
- \
- while running do\
- \9Timer.step()\
- \9redraw()\
- \9local event = { coroutine.yield() }\
- \9if event[1] == \"term_resize\" then\
- \9\9view.width, view.height = term.getSize()\
- \9elseif event[1] == \"terminate\" and application.terminateable then\
- \9\9running = false\
- \9elseif event[1] ~= \"timer\" or not Timer.update( event[1], event[2] ) then\
- \9\9view:handle( getEventObject( event ) )\
- \9end\
- \9view:update( Timer.getDelta() )\
- end\
- ";
- }
- fs.delete "Flare"
- for k, v in pairs( files ) do
- print( "Installing Flare file " .. k )
- local h = fs.open( "Flare" .. k, "w" )
- if h then
- h.write( v )
- h.close()
- end
- end
Add Comment
Please, Sign In to add comment