Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --[[type="executable_package", name="n"]]
- local files = {["sha256"]={content="-- Adaptation of the Secure Hashing Algorithm (SHA-244/256)\
- -- Found Here: http://lua-users.org/wiki/SecureHashAlgorithm\
- -- Using an adapted version of the bit library\
- -- Found Here: https://bitbucket.org/Boolsheet/bslf/src/1ee664885805/bit.lua\
- -- Compressed using Lua Minifier (https://mothereff.in/lua-minifier)\
- local a=2^32;local b=a-1;local function c(d)local mt={}local e=setmetatable({},mt)function mt:__index(f)local g=d(f)e[f]=g;return g end;return e end;local function h(e,i)local function j(k,l)local m,o=0,1;while k~=0 and l~=0 do local p,q=k%i,l%i;m=m+e[p][q]*o;k=(k-p)/i;l=(l-q)/i;o=o*i end;m=m+(k+l)*o;return m end;return j end;local function r(e)local s=h(e,2^1)local t=c(function(k)return c(function(l)return s(k,l)end)end)return h(t,2^e.n or 1)end;local u=r({[0]={[0]=0,[1]=1},[1]={[0]=1,[1]=0},n=4})local function v(k,l,w,...)local x=nil;if l then k=k%a;l=l%a;x=u(k,l)if w then x=v(x,w,...)end;return x elseif k then return k%a else return 0 end end;local function y(k,l,w,...)local x;if l then k=k%a;l=l%a;x=(k+l-u(k,l))/2;if w then x=bit32_band(x,w,...)end;return x elseif k then return k%a else return b end end;local function z(A)return(-1-A)%a end;local function B(k,C)if C<0 then return lshift(k,-C)end;return math.floor(k%2^32/2^C)end;local function D(A,C)if C>31 or C<-31 then return 0 end;return B(A%a,C)end;local function lshift(k,C)if C<0 then return D(k,-C)end;return k*2^C%2^32 end;local function E(A,C)A=A%a;C=C%32;local F=y(A,2^C-1)return D(A,C)+lshift(F,32-C)end;local f={0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2}local function G(H)return string.gsub(H,\".\",function(w)return string.format(\"%02x\",string.byte(w))end)end;local function I(J,n)local H=\"\"for K=1,n do local L=J%256;H=string.char(L)..H;J=(J-L)/256 end;return H end;local function M(H,K)local n=0;for K=K,K+3 do n=n*256+string.byte(H,K)end;return n end;local function N(O,len)local P=64-(len+9)%64;len=I(8*len,8)O=O..\"\\128\"..string.rep(\"\\0\",P)..len;assert(#O%64==0)return O end;local function Q(R)R[1]=0x6a09e667;R[2]=0xbb67ae85;R[3]=0x3c6ef372;R[4]=0xa54ff53a;R[5]=0x510e527f;R[6]=0x9b05688c;R[7]=0x1f83d9ab;R[8]=0x5be0cd19;return R end;local function S(O,K,R)local T={}for U=1,16 do T[U]=M(O,K+(U-1)*4)end;for U=17,64 do local g=T[U-15]local V=v(E(g,7),E(g,18),D(g,3))g=T[U-2]T[U]=T[U-16]+V+T[U-7]+v(E(g,17),E(g,19),D(g,10))end;local k,l,w,W,X,d,Y,Z=R[1],R[2],R[3],R[4],R[5],R[6],R[7],R[8]for K=1,64 do local V=v(E(k,2),E(k,13),E(k,22))local _=v(y(k,l),y(k,w),y(l,w))local a0=V+_;local a1=v(E(X,6),E(X,11),E(X,25))local a2=v(y(X,d),y(z(X),Y))local a3=Z+a1+a2+f[K]+T[K]Z,Y,d,X,W,w,l,k=Y,d,X,W+a3,w,l,k,a3+a0 end;R[1]=y(R[1]+k)R[2]=y(R[2]+l)R[3]=y(R[3]+w)R[4]=y(R[4]+W)R[5]=y(R[5]+X)R[6]=y(R[6]+d)R[7]=y(R[7]+Y)R[8]=y(R[8]+Z)end;local function a4(O)O=N(O,string.len(O))local R=Q({})for K=1,#O,64 do S(O,K,R)end;return G(I(R[1],4)..I(R[2],4)..I(R[3],4)..I(R[4],4)..I(R[5],4)..I(R[6],4)..I(R[7],4)..I(R[8],4))end;return a4", meta={
- type = "lib",
- }};["kernel"]={content="\
- require \"Session\"\
- \
- core.UI = NovaUI.UIHandler( )\
- \
- local lt, lk\
- \
- core.UIHost = Process \"UIManager\"\
- core.UIHost:newThread( function( ... )\
- local ev = { ... }\
- while true do\
- if ev[1] == \"key\" and ev[2] == 28 and lk == 29 and os.clock() - lt <= 0.3 and core.session then\
- core.session:menu( )\
- elseif ev[1] == \"update\" then\
- core.UI:update( ev[2] )\
- core.UI:draw( )\
- else\
- core.UI:event( ev )\
- end\
- if ev[1] == \"key\" then\
- lk = ev[2]\
- lt = os.clock( )\
- end\
- ev = { coroutine.yield( ) }\
- end\
- end )\
- function core.UIHost:onException( err )\
- core.log( \"UI error\", err )\
- self:restartThread( 1 )\
- end\
- \
- -- sessions\
- \
- Session()", meta={
- type = "lib",
- }};["DefaultAppInstance"]={content="\
- local function sandbox( args, app, instance )\
- local t = {\
- -- lua\
- _VERSION = _VERSION;\
- pairs = pairs;\
- ipairs = ipairs;\
- select = select;\
- unpack = unpack;\
- setfenv = setfenv;\
- getfenv = getfenv;\
- setmetatable = setmetatable;\
- getmetatable = getmetatable;\
- next = next;\
- rawset = rawset;\
- rawget = rawget;\
- rawequal = rawequal;\
- type = type;\
- tostring = tostring;\
- tonumber = tonumber;\
- pcall = pcall;\
- xpcall = xpcall;\
- loadstring = loadstring;\
- assert = assert;\
- error = error;\
- __inext = __inext;\
- math = math;\
- string = string;\
- table = table;\
- coroutine = coroutine;\
- \
- -- computercraft\
- sha256 = sha256;\
- encryption = encryption;\
- stringutils = stringutils;\
- keys = keys;\
- colours = colours;\
- colors = colors;\
- vector = vector;\
- bit = bit;\
- http = http;\
- textutils = textutils;\
- rednet = rednet;\
- os = os;\
- sleep = sleep;\
- }\
- t._G = t\
- t.ARGS = args\
- \
- if app.conf.runmode == \"app\" then\
- t.class = class;\
- t.NovaUI = NovaUI;\
- t.NovaFS = NovaFS;\
- t.NovaNet = NovaNet;\
- \
- t.Nova = {\
- name = core.name;\
- version = core.version;\
- platform = core.platform;\
- clipboard = clipboard;\
- name = core.name;\
- path = core.path;\
- author = core.author;\
- \
- user = { };\
- app = { };\
- }\
- \
- function t.Nova.getUpdateInfo( )\
- return core.getUpdateInfo( )\
- end\
- function t.Nova.compareVersions( )\
- return core.compareVersions( )\
- end\
- function t.Nova.update( )\
- return core.update( )\
- end\
- function t.Nova.shutdown( )\
- instance.app.session:logout( )\
- core.finish( )\
- os.shutdown( )\
- end\
- function t.Nova.restart( )\
- instance.app.session:logout( )\
- core.finish( )\
- os.reboot( )\
- end\
- function t.Nova.log( ... )\
- return core.log( app.name, ... )\
- end\
- \
- function t.Nova.time() return os.time() end\
- function t.Nova.clock() return os.clock() end\
- function t.Nova.irltime() return core.getirltime() end\
- function t.Nova.irldate() return core.getirldate() end\
- function t.Nova.day() return os.day() end\
- function t.Nova.date() return error \"not yet implemented\" end\
- \
- function t.Nova.sleep( n )\
- local t = os.clock()\
- while os.clock() - t < n do\
- coroutine.yield()\
- end\
- end\
- function t.Nova.pullEvent( )\
- local ev = { coroutine.yield( ) }\
- if ev[1] == \"terminate\" then\
- instance:close \"terminate\"\
- end\
- return unpack( ev )\
- end\
- function t.Nova.pullEventRaw( )\
- return coroutine.yield( )\
- end\
- function t.Nova.versionTable( )\
- return { major = core.version_major, minor = core.version_minor, patch = core.version_patch }\
- end\
- function t.Nova.getLabel( )\
- return os.getComputerLabel( )\
- end\
- function t.Nova.setLabel( label )\
- return os.setComputerLabel( label )\
- end\
- function t.Nova.getID( )\
- return os.getComputerID( )\
- end\
- \
- t.Nova.app.window = instance.window\
- function t.Nova.app.launch( name, args )\
- return not not core.session.appManager:launch( name, args )\
- end\
- function t.Nova.app.close( )\
- instance:close \"internal\"\
- end\
- function t.Nova.app.newThread( f )\
- return instance.process:newThread( f )\
- end\
- t.Nova.app.callback = setmetatable( { }, { __newindex = function( _, k, v )\
- if type( v ) ~= \"function\" then\
- return error \"expected function\"\
- end\
- instance.callbacks[k] = v\
- end } )\
- function t.Nova.app.setData( index, value )\
- return core.session.account:setData( app.name, index, value )\
- end\
- function t.Nova.app.getData( index )\
- return core.session.account:getData( app.name, index, value )\
- end\
- function t.Nova.app.isRegistered( path )\
- return core.session.appManager.app_paths[path] and core.session.appManager.app_paths[path].name or false\
- end\
- \
- end\
- \
- if type( app.conf.permissions ) == \"table\" then\
- if app.conf.permissions.listProcesses then\
- function t.Nova.listProcesses( )\
- return Process.list( )\
- end\
- end\
- end\
- \
- return t\
- end\
- \
- DefaultAppInstance.public \"process\"\
- DefaultAppInstance.public.process.write = false\
- \
- DefaultAppInstance.public \"window\"\
- DefaultAppInstance.public.window.write = false\
- \
- DefaultAppInstance.public \"environment\"\
- DefaultAppInstance.public.environment.write = false\
- \
- DefaultAppInstance.public \"callbacks\"\
- DefaultAppInstance.public.callbacks.write = false\
- \
- DefaultAppInstance.public \"running\"\
- DefaultAppInstance.public.running.write = false\
- \
- function DefaultAppInstance:DefaultAppInstance( session, app, args )\
- self.running = true\
- self.callbacks = { }\
- self.process = session.appManager:newProcess( app.name )\
- local position = session.account:getData( \"windowPosition\", app.name )\
- if position then\
- self.window = session.windowManager:newWindow( \"custom:\" .. position.w .. \"x\" .. position.h )\
- self.window.x, self.window.y = position.x, position.y\
- elseif app.conf.runmode == \"app\" then\
- if type( app.conf.width ) == \"number\" and type( app.conf.height ) == \"number\" then\
- self.window = session.windowManager:newWindow( \"custom:\" .. app.conf.width .. \"x\" .. app.conf.height )\
- else\
- self.window = session.windowManager:newWindow( type( app.conf.size ) == \"string\" and app.conf.size or \"default\" )\
- end\
- else\
- self.window = session.windowManager:newWindow \"full\"\
- end\
- \
- self.window.title = app.name\
- self.window.process = self.process\
- function self.window.onClose( )\
- local r = self.public:callback( \"canClose\", \"user\" )\
- if r or r == nil then\
- self.public:close \"user\"\
- end\
- end\
- function self.window.onMove( window, w, h )\
- self.public:callback( \"onMove\", w, h, window )\
- session.account:setData( \"windowPosition\", app.name, { x = window.x, y = window.y, w = w, h = h } )\
- end\
- function self.window.onResize( window, w, h )\
- self.public:callback( \"onResize\", w, h, window )\
- session.account:setData( \"windowPosition\", app.name, { x = window.x, y = window.y, w = w, h = h } )\
- end\
- \
- self.environment = sandbox( args, app, self.public )\
- self.process.environment = self.environment\
- \
- local f, err = loadstring( app.main, \"main.lua\" )\
- if f then\
- self.process:newThread( f ) \
- else\
- self.newProcess:newThread( function( )\
- error( err, 0 )\
- end )\
- end\
- self.process:newThread( function( )\
- while true do\
- local ev, window = coroutine.yield( )\
- if ev[1] == \"window_close\" and window == self.window then\
- self.public:close( )\
- end\
- end\
- end )\
- \
- local core = core\
- function self.process.onException( _, err )\
- self.process:newThread( function( )\
- core.log( err )\
- NovaUI.display.alert( self.window.display, err, true )\
- self.public:close( )\
- end )\
- end\
- \
- return self.public\
- end\
- \
- function DefaultAppInstance.public:close( reason )\
- if not self.running then return end\
- self.public:callback( \"onClose\", reason )\
- self.process:stop( )\
- self.window:close( )\
- self.running = false\
- end\
- \
- function DefaultAppInstance.public:callback( name, ... )\
- if type( self.callbacks[name] ) == \"function\" then\
- return self.callbacks[name]( ... )\
- end\
- return nil\
- end", meta={
- type = "class",
- }};["Process"]={content="\
- local processes = { }\
- \
- Process.public \"environment\"\
- function Process.public.environment:read( )\
- return self.env\
- end\
- function Process.public.environment:write( value )\
- if type( value ) ~= \"table\" then\
- error( \"expected table\", 3 )\
- end\
- self.env = value\
- end\
- \
- Process.public \"threadcount\"\
- function Process.public.threadcount:read( )\
- return #self.threads\
- end\
- \
- Process.public \"onException\" \"function\"\
- Process.public \"onFinish\" \"function\"\
- Process.public \"name\" \"string\"\
- \
- function Process:Process( name )\
- self.name = name\
- self.state = \"running\"\
- self.threads = { }\
- self.env = getfenv( )\
- self.threadID = 1\
- self.eventqueue = { }\
- \
- table.insert( processes, self.public )\
- \
- return self.public\
- end\
- \
- function Process.public:queueEvent( ... )\
- table.insert( self.eventqueue, { ... } )\
- end\
- \
- function Process.public:newThread( f )\
- local env = setmetatable( { process = self.public }, { __index = function( _, k )\
- return self.env[k]\
- end, __newindex = function( _, k, v )\
- self.env[k] = v\
- end } )\
- setfenv( f, env )\
- local t = {\
- f = f;\
- co = coroutine.create( f );\
- id = self.threadID;\
- }\
- self.threadID = self.threadID + 1\
- table.insert( self.threads, t )\
- return t.id\
- end\
- \
- function Process.public:pause( )\
- if self.state == \"running\" then\
- self.state = \"paused\"\
- return true\
- end\
- return false\
- end\
- \
- function Process.public:resume( )\
- if self.state == \"paused\" then\
- self.state = \"running\"\
- return true\
- end\
- return false\
- end\
- \
- function Process.public:restartThread( threadID )\
- for i = 1, #self.threads do\
- if self.threads[i].id == threadID then\
- self.threads[i].co = coroutine.create( self.threads[i].f )\
- end\
- end\
- self.state = self.state == \"paused\" and \"paused\" or \"running\"\
- end\
- \
- function Process.public:stopThread( threadID )\
- for i = 1, #self.threads do\
- if self.threads[i].id == threadID then\
- table.remove( self.threads, i )\
- return\
- end\
- end\
- end\
- \
- function Process.public:isThreadRunning( threadID )\
- for i = 1, #self.threads do\
- if self.threads[i].id == threadID then\
- return coroutine.status( self.threads[i].co ) ~= \"dead\"\
- end\
- end\
- return false\
- end\
- \
- function Process.public:stop( )\
- self.state = \"stopped\"\
- end\
- \
- function Process.public:isRunning( )\
- return self.state == \"running\"\
- end\
- \
- function Process.public:isPaused( )\
- return self.state == \"paused\"\
- end\
- \
- function Process.public:update( args )\
- if self.state == \"stopped\" then\
- return false, \"finished\"\
- end\
- if self.state ~= \"running\" then\
- return false, \"not running\"\
- end\
- local t = self.eventqueue[#self.eventqueue]\
- if t then\
- self.eventqueue[#self.eventqueue] = nil\
- self.public:update( t )\
- end\
- for i = #self.threads, 1, -1 do\
- local thread = self.threads[i]\
- local ok, data = coroutine.resume( thread.co, unpack( args ) )\
- if not ok then\
- if type( self.onException ) == \"function\" then\
- pcall( self.onException, self.public, data, thread.id )\
- else\
- self.state = \"stopped\"\
- core.log( \"Thread[\" .. thread.id .. \"] error in \" .. self.name, data )\
- end\
- if self.state == \"stopped\" then\
- return false, \"finished\"\
- end\
- end\
- if coroutine.status( thread.co ) == \"dead\" then -- give it a chance to restart\
- if type( self.onFinish ) == \"function\" then\
- pcall( self.onFinish, self.public, thread.id )\
- end\
- if coroutine.status( thread.co ) == \"dead\" then\
- for i = 1, #self.threads do\
- if self.threads[i] == thread then\
- table.remove( self.threads, i )\
- end\
- end\
- end\
- end\
- end\
- return true\
- end\
- \
- function Process.static.update( args )\
- for i = #processes, 1, -1 do\
- local ok, data = processes[i]:update( args )\
- if not ok and data == \"finished\" then\
- table.remove( processes, i )\
- end\
- end\
- end\
- \
- function Process.static.list( )\
- local t = { }\
- for i = 1, #processes do\
- local p = processes[i]\
- table.insert( t, { name = processes[i].name, threadcount = processes[i].threadcount, stop = function( )\
- p:stop( )\
- end } )\
- end\
- return t\
- end", meta={
- type = "class",
- }};["stringutils"]={content="\
- function gfind( str, pat, pos )\
- local t = { }\
- local params = { str:find( pat, pos ) }\
- while params[1] do\
- table.insert( t, params )\
- params = { str:find( pat, params[2] + 1 ) }\
- end\
- local i = 0\
- return function( )\
- i = i + 1\
- if t[i] then\
- return unpack( t[i] )\
- end\
- end\
- end\
- \
- function split( str, pat, pos )\
- local last = 1\
- local parts = { }\
- for s, f in gfind( str, pat, pos ) do\
- table.insert( parts, str:sub( last, s - 1 ) )\
- last = f + 1\
- end\
- table.insert( parts, str:sub( last ) )\
- return parts\
- end\
- \
- local function linewrap( str, w )\
- for i = 1, w do\
- if str:sub( i, i ) == \"\\n\" then\
- return str:sub( 1, i - 1 ), str:sub( i + 1 )\
- end\
- end\
- if #str < w then\
- return str, \"\"\
- end\
- for i = w + 1, 1, -1 do\
- if str:sub( i, i ):find \"%s\" then\
- return str:sub( 1, i - 1 ), str:sub( i + 1 )\
- end\
- end\
- return str:sub( 1, w ), str:sub( w + 1 )\
- end\
- \
- function wordwrap( str, w, h )\
- local s, f = linewrap( str, w )\
- local lines = { s }\
- while #f > 0 do\
- s, f = linewrap( f, w )\
- table.insert( lines, s )\
- end\
- while #lines > h do\
- table.remove( lines, #lines )\
- end\
- return lines\
- end", meta={
- type = "lib",
- }};["WindowManager"]={content="\
- WindowManager:extends( NovaUI.UIElement )\
- \
- WindowManager.public \"default_mode\" \"string\"\
- WindowManager.public \"desktop\" (NovaUI.UIElement)\
- \
- function WindowManager:WindowManager( )\
- self:UIElement( 1, 1, term.getSize( ) )\
- \
- self.windows = { }\
- self.addorder = { }\
- self.default_mode = \"custom:20x10\"\
- \
- self.desktop = self.public:newChild( NovaUI.UIFrame( 1, 1, self.w, self.h ) )\
- \
- return self.public\
- end\
- \
- function WindowManager.public:newWindow( mode )\
- local w, h\
- local static = false\
- if mode == \"alert\" then\
- w = self.w\
- h = 6\
- static = true\
- elseif mode == \"full\" then\
- w = self.w\
- h = self.h - 2\
- static = true\
- elseif mode == \"default\" then\
- return self.public:newWindow( self.default_mode )\
- elseif mode:sub( 1, 7 ) == \"custom:\" then\
- local ww, hh = mode:match \"custom:(%d+)x(%d+)\"\
- if ww and hh then\
- w, h = tonumber( ww ), tonumber( hh )\
- end\
- else\
- w = self.w\
- h = self.h - 2\
- end\
- local x, y = math.floor( ( self.w - w ) / 2 ) + 1, math.floor( ( self.h - h ) / 2 )\
- local window = Window( self.public, x, y, w, h )\
- table.insert( self.addorder, window )\
- table.insert( self.windows, window )\
- self.public:newChild( window.content )\
- if static then\
- window.minw = w\
- window.minh = h\
- window.maxw = w\
- window.maxh = h\
- end\
- return window\
- end\
- \
- function WindowManager.public:removeWindow( window )\
- for i = #self.windows, 1, -1 do\
- if self.windows[i] == window then\
- table.remove( self.windows, i )\
- end\
- end\
- for i = #self.addorder, 1, -1 do\
- if self.addorder[i] == window then\
- table.remove( self.addorder, i )\
- end\
- end\
- end\
- \
- function WindowManager.public:focusOn( window )\
- for i = 1, #self.windows do\
- if self.windows[i] == window then\
- self.public:removeChild( window.content )\
- table.remove( self.windows, i )\
- end\
- end\
- self.public:newChild( window.content )\
- table.insert( self.windows, window )\
- end\
- \
- function WindowManager.public:listWindows( )\
- local t = { }\
- for i = 1, #self.addorder do\
- table.insert( t, self.addorder[i] )\
- end\
- return t\
- end", meta={
- type = "class",
- }};["ProcessAppInstance"]={content="\
- function ProcessAppInstance( session, app, args )\
- \
- self.process = session.appManager:newProcess( app.name )\
- \
- \
- end", meta={
- type = "class",
- }};["appttf"]={content="\
- local pack = {\
- Run={\
- [\"main.lua\"]=\"\\\
- if not Nova then\\\
- error( \\\"Cannot run outside Nova\\\", 0 )\\\
- end\\\
- \\\
- local window = Nova.app.window\\\
- local display = window.display\\\
- \\\
- window.minw = 20\\\
- window.minh = 8\\\
- \\\
- local buffer = display:newChild( NovaUI.UIBuffer( 1, 1, display.w, display.h ) )\\\
- \\\
- function Nova.app.callback.onResize( w, h )\\\
- buffer:resize( w, h )\\\
- buffer:passEvent \\\"term_resize\\\"\\\
- end\\\
- \\\
- if type( ARGS[1] ) ~= \\\"string\\\" then\\\
- ARGS[1] = \\\"C:/rom/programs/shell\\\"\\\
- end\\\
- \\\
- local ARGS = ARGS\\\
- local Nova = Nova\\\
- \\\
- local data, err = NovaFS.readfile( ARGS[1] )\\\
- if data then\\\
- local f, err = loadstring( data.content, NovaFS.getName( ARGS[1], true ) )\\\
- if f then\\\
- buffer:setTask( function( )\\\
- local ok, err = pcall( f, unpack( ARGS, 2 ) )\\\
- if not ok then\\\
- printError( err )\\\
- end\\\
- print \\\"Click anywhere to close...\\\"\\\
- parallel.waitForAny( function( )\\\
- while coroutine.yield() ~= \\\"key\\\" do end\\\
- end, function( )\\\
- while coroutine.yield() ~= \\\"mouse_click\\\" do end\\\
- end )\\\
- Nova.app.close( )\\\
- end )\\\
- else\\\
- buffer:setTask( function( )\\\
- error( err, 0 )\\\
- end )\\\
- end\\\
- else\\\
- buffer:setTask( function( )\\\
- error( err, 0 )\\\
- end )\\\
- end\\\
- \\\
- buffer:passEvent( )\\\
- buffer:getHandler( ):setFocus( buffer )\\\
- \\\
- while true do\\\
- local event = { coroutine.yield( ) }\\\
- if event[1] ~= \\\"mouse_click\\\"\\\
- and event[1] ~= \\\"mouse_drag\\\"\\\
- and event[1] ~= \\\"mouse_scroll\\\"\\\
- and event[1] ~= \\\"key\\\"\\\
- and event[1] ~= \\\"char\\\"\\\
- and event[1] ~= \\\"update\\\" then\\\
- buffer:passEvent( unpack( event ) )\\\
- end\\\
- end\",\
- [\"appconfig.txt\"]=\"runmode = \\\"app\\\";\\\
- size = \\\"custom:20x8\\\";\\\
- handles = {\\\
- \\\"unknown\\\";\\\
- \\\"lua\\\";\\\
- };\",\
- }\
- ,\
- Files={\
- [\"main.lua\"]=\"\\\
- if not Nova then\\\
- error( \\\"Cannot run outside Nova\\\", 0 )\\\
- end\\\
- \\\
- local icons = { }\\\
- icons.app = NovaUI.Image( 5, 3 ):loadstr [[BF BF BF BF BF \\\
- 3F 3Ea3Ep3Ep3F \\\
- 9F 9F 9F 9F 9F ]]\\\
- icons.archive = NovaUI.Image( 5, 3 ):loadstr [[4F 4F 4F 0F 0F \\\
- 1F 1F 1F 1F 1F \\\
- 1F 1F 1F 1F 1F ]]\\\
- icons.class = NovaUI.Image( 5, 3 ):loadstr [[01c01l01a01s01s\\\
- 00 08-08-08-00 \\\
- 00 08-08-08-00 ]]\\\
- icons.design_file = NovaUI.Image( 5, 3 ):loadstr [[ F BF BF F F \\\
- F BF F BF F \\\
- F BF BF F F ]]\\\
- icons.design_project = NovaUI.Image( 5, 3 ):loadstr [[ F BF BF F F \\\
- F BF 3 BF F \\\
- 3PB3rB3j 3c 3t]]\\\
- icons.folder = NovaUI.Image( 5, 3 ):loadstr [[10 10 10 0F 0F \\\
- 40 40 40 4F 4F \\\
- 40 40 40 40 40 ]]\\\
- icons.lib = NovaUI.Image( 5, 3 ):loadstr [[0F 09l09i09b0F \\\
- 00 08-08-08-00 \\\
- 00 08-08-08-00 ]]\\\
- icons.lua = NovaUI.Image( 5, 3 ):loadstr [[0F 03l03u03a0F \\\
- 00 08-08-08-00 \\\
- 00 08-08-08-00 ]]\\\
- icons.nova_image = NovaUI.Image( 5, 3 ):loadstr [[3Bi3Bm3B 3B 4B \\\
- 3B 3B 3B 3B 3B \\\
- DB DB DB DB DB ]]\\\
- icons.shortcut = NovaUI.Image( 5, 3 ):loadstr [[08s08h08o08r08t\\\
- 03 08c08u08t08 \\\
- 08-08-08-08-08-]]\\\
- icons.text = NovaUI.Image( 5, 3 ):loadstr [[0F 0Ft0Fx0Ft08 \\\
- 0F 08-08-08-08 \\\
- 0F 08-08-08-08 ]]\\\
- icons.unknown = NovaUI.Image( 5, 3 ):loadstr [[0F 08-08-08-0F \\\
- 0F 08-08-08-0F \\\
- 0F 08-08-08-0F ]]\\\
- \\\
- local window = Nova.app.window\\\
- local display = window.display\\\
- \\\
- if ARGS.width or ARGS.height then\\\
- window:resize( ARGS.width or display.w, ARGS.height or display.h )\\\
- end\\\
- \\\
- window.minw = 12\\\
- window.minh = 6\\\
- \\\
- local fpath, mount\\\
- local loadfiles, openpath, refresh, renaming\\\
- local showFolderOptions, showFileOptions, showRightClickOptions\\\
- local history, historyindex = { }, 0\\\
- local loading, loadstage, loaddir, loader, ltime = false, 0, 1, nil, 0\\\
- \\\
- local header, sidebar, filecontent, keyhandler\\\
- local backbutton, forwardbutton, upbutton, pathinput\\\
- local headerb, sidebarb, filecontentb, filelist, scrollbar\\\
- local resizeDisplay, resizer\\\
- \\\
- local favourites = Nova.app.getData \\\"favourites\\\" or { }\\\
- local sortorder = Nova.app.getData \\\"sortorder\\\" or { mode = \\\"name\\\", direction = \\\"ascending\\\" }\\\
- local viewhidden = Nova.app.getData \\\"viewhidden\\\" or false\\\
- local defaults = Nova.app.getData \\\"defaults\\\" or { }\\\
- for k, v in pairs( defaults ) do\\\
- NovaFS.setDefaultHandler( k, v )\\\
- end\\\
- \\\
- local function charweight( char )\\\
- return string.byte( char:lower( ) )\\\
- end\\\
- local function compstring( s1, s2 ) -- s1 > s2\\\
- for i = 1, #s1 do\\\
- if #s2 < i then\\\
- return true\\\
- end\\\
- local c1, c2 = s1:sub( i, i ), s2:sub( i, i )\\\
- local w1, w2 = charweight( c1 ), charweight( c2 )\\\
- if w1 > w2 then\\\
- return true\\\
- elseif w1 < w2 then\\\
- return false\\\
- end\\\
- end\\\
- return false\\\
- end\\\
- \\\
- local function formatSize( s )\\\
- if s < 1024 then\\\
- return s .. \\\" bytes\\\"\\\
- end\\\
- s = s / 1024\\\
- if s < 1024 then\\\
- return math.floor( s ) .. \\\"KB\\\"\\\
- end\\\
- s = s / 1024\\\
- if s < 1024 then\\\
- return math.floor( s ) .. \\\"MB\\\"\\\
- end\\\
- end\\\
- \\\
- local activedropdown\\\
- local function dropdown( x, y, options )\\\
- local frame = display:newChild( NovaUI.UIFrame( 1, 1, display.w, display.h ) )\\\
- local close = frame:newChild( NovaUI.UIButton( 1, 1, frame.w, frame.h, \\\"\\\" ) )\\\
- close.bc = 0\\\
- close.align = false\\\
- function close:onClick( )\\\
- activedropdown = nil\\\
- frame:remove( )\\\
- end\\\
- local f = frame:newChild( NovaUI.UIFrame( x, y, 15, 10 ) )\\\
- activedropdown = frame\\\
- NovaUI.display.menu( f, options )\\\
- if f.x + f.w > display.w then\\\
- f.x = display.w - f.w + 1\\\
- end\\\
- if f.y + f.h > display.h then\\\
- f.y = display.h - f.h + 1\\\
- end\\\
- if f.y < 1 then\\\
- f.h = f.h + f.y - 1\\\
- f.y = 1\\\
- end\\\
- end\\\
- \\\
- function Nova.app.callback.onClose( )\\\
- if mount then\\\
- NovaFS.unmount( mount )\\\
- end\\\
- end\\\
- \\\
- function Nova.app.callback.onResize( w, h )\\\
- resizeDisplay( w, h )\\\
- refresh( )\\\
- end\\\
- \\\
- function resizeDisplay( w, h )\\\
- header.w = w\\\
- if w < 35 then\\\
- sidebar.active = false\\\
- sidebarb.active = false\\\
- filecontent.x = 1\\\
- filecontent.w = w\\\
- else\\\
- sidebar.active = true\\\
- sidebarb.active = true\\\
- filecontent.x = 16\\\
- filecontent.w = w - 15\\\
- end\\\
- if w > 18 then\\\
- pathinput.x = 14\\\
- forwardbutton.active = true\\\
- upbutton.x = 10\\\
- upbutton.active = true\\\
- elseif w > 14 then\\\
- pathinput.x = 10\\\
- forwardbutton.active = false\\\
- upbutton.x = 6\\\
- upbutton.active = true\\\
- elseif w > 10 then\\\
- pathinput.x = 6\\\
- forwardbutton.active = false\\\
- upbutton.active = false\\\
- end\\\
- pathinput.w = w - pathinput.x\\\
- sidebar.h = h - 3\\\
- filecontent.h = h - 3\\\
- headerb.w, headerb.h = header.w, header.h\\\
- sidebarb.x, sidebarb.h = sidebar.w + 1, sidebar.h\\\
- filecontentb.w, filecontentb.h = filecontent.w, filecontent.h\\\
- filelist.w = filecontent.w - 4\\\
- filelist.h = filecontent.h - 2\\\
- scrollbar.x = filecontent.w - 1\\\
- scrollbar.h = filecontent.h - 2\\\
- loader.x = filecontent.x\\\
- loader.y = display.h\\\
- resizer.x, resizer.y = w, h\\\
- end\\\
- \\\
- header = display:newChild( NovaUI.UIFrame( 1, 1, display.w, 3 ) )\\\
- sidebar = display:newChild( NovaUI.UIFrame( 1, 4, 14, display.h - 3 ) )\\\
- filecontent = display:newChild( NovaUI.UIFrame( 16, 4, display.w - 15, display.h - 3 ) )\\\
- keyhandler = display:newChild( NovaUI.UIKeyHandler( ) )\\\
- resizer = display:newChild( NovaUI.UIButton( display.w, display.h, 1, 1, \\\"@\\\" ) )\\\
- local xx, yy\\\
- function resizer:onClick( rx, ry, button )\\\
- if button == 1 then xx, yy = rx, ry end\\\
- end\\\
- function resizer:onDrag( rx, ry, cx, cy, button )\\\
- if button == 1 and xx then\\\
- window:resize( display.w + rx - 1, display.h + ry - 1 )\\\
- end\\\
- end\\\
- loader = display:newChild( NovaUI.UIText( 16, display.h, 5, 1, function( )\\\
- if loading then\\\
- if os.clock() - ltime >= 0.2 then\\\
- loadstage = loadstage + loaddir\\\
- if loadstage == 1 then loaddir = 1\\\
- elseif loadstage == 5 then loaddir = -1\\\
- end\\\
- ltime = os.clock( )\\\
- end\\\
- return string.rep( \\\".\\\", loadstage - 1 ) .. \\\"o\\\" .. string.rep( \\\".\\\", math.max( 5 - loadstage - 1, 0 ) )\\\
- end\\\
- return \\\"\\\"\\\
- end ) )\\\
- \\\
- headerb = header:newChild( NovaUI.UIText( 1, 1, header.w, header.h, \\\"\\\" ) )\\\
- headerb.bc = colours.grey\\\
- sidebarb = display:newChild( NovaUI.UIText( sidebar.w + 1, 4, 1, sidebar.h, \\\"\\\" ) )\\\
- sidebarb.bc = colours.lightGrey\\\
- filecontentb = filecontent:newChild( NovaUI.UIButton( 1, 1, filecontent.w, filecontent.h, \\\"\\\" ) )\\\
- filecontentb.bc = colours.white\\\
- filelist = filecontent:newChild( NovaUI.UIFrame( 2, 2, filecontent.w - 4, filecontent.h - 2 ) )\\\
- scrollbar = filecontent:newChild( NovaUI.UIScrollBar( filecontent.w - 1, 2, 1, filecontent.h, filelist ) )\\\
- \\\
- backbutton = header:newChild( NovaUI.UIButton( 2, 2, 3, 1, \\\"<\\\" ) )\\\
- backbutton.bc = colours.lightGrey\\\
- backbutton.tc = colours.black\\\
- forwardbutton = header:newChild( NovaUI.UIButton( 6, 2, 3, 1, \\\">\\\" ) )\\\
- forwardbutton.bc = colours.lightGrey\\\
- forwardbutton.tc = colours.black\\\
- upbutton = header:newChild( NovaUI.UIButton( 10, 2, 3, 1, \\\"^\\\" ) )\\\
- upbutton.bc = colours.lightGrey\\\
- upbutton.tc = colours.black\\\
- pathinput = header:newChild( NovaUI.UIInput( 14, 2, display.w - 14, 1 ) )\\\
- pathinput.bc = colours.lightGrey\\\
- pathinput.fbc = colours.white\\\
- \\\
- function backbutton:onClick( )\\\
- if history[historyindex - 1] then\\\
- historyindex = historyindex - 1\\\
- loadfiles( history[historyindex] )\\\
- pathinput.text = history[historyindex]\\\
- fpath = history[historyindex]\\\
- end\\\
- end\\\
- function forwardbutton:onClick( )\\\
- if history[historyindex + 1] then\\\
- historyindex = historyindex + 1\\\
- loadfiles( history[historyindex] )\\\
- pathinput.text = history[historyindex]\\\
- fpath = history[historyindex]\\\
- end\\\
- end\\\
- \\\
- function upbutton:onClick( )\\\
- local up = ( fpath:match \\\"(.+/)\\\" or fpath .. \\\" \\\" ):sub( 1, -2 )\\\
- if up == fpath and fpath:find \\\":.\\\" then\\\
- up = ( fpath:match \\\"(.+:.)\\\" or fpath .. \\\" \\\" ):sub( 1, -2 )\\\
- end\\\
- if up ~= fpath then\\\
- openpath( up )\\\
- end\\\
- end\\\
- \\\
- function pathinput:onEnter( )\\\
- openpath( self.text )\\\
- end\\\
- \\\
- function filecontentb:onClick( rx, ry, button )\\\
- if button == 2 then\\\
- showRightClickOptions( filecontent.x + rx - 1, filecontent.y + ry - 1 )\\\
- end\\\
- end\\\
- \\\
- function keyhandler:onKey( key, lastkey )\\\
- if key == keys.backspace and not lastkey then\\\
- if history[historyindex - 1] then\\\
- historyindex = historyindex - 1\\\
- loadfiles( history[historyindex] )\\\
- pathinput.text = history[historyindex]\\\
- fpath = history[historyindex]\\\
- end\\\
- end\\\
- end\\\
- \\\
- local lt\\\
- function loadfiles( path, openAs )\\\
- if lt then process:stopThread( lt ) loading = false end\\\
- lt = Nova.app.newThread( function( )\\\
- local l = loading\\\
- loading = true\\\
- path = path or fpath\\\
- \\\
- if mount and path:sub( 1, #mount ) ~= mount then\\\
- NovaFS.unmount( mount )\\\
- mount = nil\\\
- end\\\
- \\\
- local _dirs, _files = { }, { }\\\
- local files = NovaFS.listFiles( path, function( files )\\\
- for i = 1, #files do\\\
- if files[i]:sub( 1, 1 ) ~= \\\".\\\" or viewhidden then\\\
- if NovaFS.isDirectory( NovaFS.merge( path, files[i] ) ) then\\\
- table.insert( _dirs, { name = files[i], size = NovaFS.getSize( NovaFS.merge( path, files[i] ) ) } )\\\
- else\\\
- table.insert( _files, { name = files[i], size = NovaFS.getSize( NovaFS.merge( path, files[i] ) ) } )\\\
- end\\\
- end\\\
- coroutine.yield( )\\\
- end\\\
- end )\\\
- \\\
- if not files then\\\
- loading = l\\\
- if not openAs then\\\
- openAs = NovaFS.getType( path )\\\
- end\\\
- if openAs == \\\"shortcut\\\" then\\\
- local ok, data = NovaFS.getFileMetaValue( path, \\\"destination\\\" )\\\
- if ok then\\\
- openpath( data )\\\
- else\\\
- Nova.app.newThread( function( )\\\
- NovaUI.display.alert( display, \\\"Malformed shorcut\\\", true )\\\
- Nova.app.close( )\\\
- end )\\\
- end\\\
- return\\\
- elseif openAs == \\\"archive\\\" then\\\
- local archive = NovaFS.Archive( path )\\\
- local pass\\\
- if NovaFS.isProtected( path ) then\\\
- local r = NovaUI.display.response( display, \\\"Please enter archive password\\\" )\\\
- if r then\\\
- pass = r\\\
- else\\\
- return\\\
- end\\\
- end\\\
- archive:loadFile( path, pass )\\\
- mount = NovaFS.getName( path, true )\\\
- NovaFS.mount( archive:createDrive( ), mount )\\\
- openpath( mount .. \\\":\\\" )\\\
- return\\\
- end\\\
- if NovaFS.exists( path ) then\\\
- local handlers = NovaFS.getHandlers( openAs )\\\
- if handlers[#handlers] then\\\
- Nova.app.close( )\\\
- Nova.app.launch( handlers[#handlers], { path, openAs } )\\\
- else\\\
- Nova.app.newThread( function( )\\\
- NovaUI.display.alert( display, \\\"Unknown file type.\\\", true )\\\
- Nova.app.close( )\\\
- end )\\\
- end\\\
- else\\\
- Nova.app.newThread( function( )\\\
- NovaUI.display.alert( display, \\\"File doesn't exist\\\", true )\\\
- Nova.app.close( )\\\
- end )\\\
- end\\\
- return\\\
- end\\\
- \\\
- if #files > 0 then\\\
- coroutine.yield( )\\\
- end\\\
- \\\
- table.sort( _dirs, function( i1, i2 )\\\
- if sortorder.mode == \\\"name\\\" then\\\
- return not compstring( i1.name, i2.name )\\\
- elseif sortorder.mode == \\\"size\\\" then\\\
- return i1.size < i2.size\\\
- end\\\
- end )\\\
- coroutine.yield( )\\\
- table.sort( _files, function( i1, i2 )\\\
- if sortorder.mode == \\\"name\\\" then\\\
- return not compstring( i1.name, i2.name )\\\
- elseif sortorder.mode == \\\"size\\\" then\\\
- return i1.size < i2.size\\\
- end\\\
- end )\\\
- coroutine.yield( )\\\
- for i = 1, #_dirs do\\\
- _dirs[i].path = NovaFS.merge( path, _dirs[i].name )\\\
- end\\\
- for i = 1, #_files do\\\
- _files[i].path = NovaFS.merge( path, _files[i].name )\\\
- end\\\
- if sortorder.direction == \\\"descending\\\" then\\\
- for i = 1, math.ceil( #_dirs / 2 ) do\\\
- _dirs[i], _dirs[#_dirs-i + 1] = _dirs[#_dirs-i + 1], _dirs[i]\\\
- end\\\
- coroutine.yield( )\\\
- for i = 1, math.ceil( #_files / 2 ) do\\\
- _files[i], _files[#_files-i + 1] = _files[#_files-i + 1], _files[i]\\\
- end\\\
- coroutine.yield( )\\\
- end\\\
- \\\
- local y = 1\\\
- \\\
- local function loadDirs( )\\\
- for i = 1, #_dirs do\\\
- local frame = filelist:newChild( NovaUI.UIFrame( 1, y, filelist.w, 3 ) )\\\
- local app_registered = not viewhidden and Nova.app.isRegistered( _dirs[i].path:gsub( \\\"^C:/?\\\", \\\"\\\", 1 ) )\\\
- if app_registered then\\\
- local icon = frame:newChild( NovaUI.UIImage( 1, 1, 5, 3, icons.app ) )\\\
- else\\\
- local icon = frame:newChild( NovaUI.UIImage( 1, 1, 5, 3, icons.folder ) )\\\
- end\\\
- local name = frame:newChild( NovaUI.UIText( 7, 1, frame.w - 6, 1, NovaFS.getName( _dirs[i].path ) ) )\\\
- local size = frame:newChild( NovaUI.UIText( 7, 2, frame.w - 7, 1, formatSize( _dirs[i].size ) ) )\\\
- size.tc = colours.lightGrey\\\
- local desc = frame:newChild( NovaUI.UIText( 7, 3, frame.w - 7, 1, app_registered and \\\"Nova app\\\" or \\\"Folder\\\" ) )\\\
- desc.tc = colours.lightGrey\\\
- \\\
- local overlay = frame:newChild( NovaUI.UIButton( 1, 1, frame.w, frame.h, \\\"\\\" ) )\\\
- overlay.bc = 0\\\
- overlay.align = false\\\
- function overlay:onClick( x, y, button )\\\
- if button == 1 or button == \\\"enter\\\" then\\\
- if app_registered then\\\
- Nova.app.launch( app_registered, { } )\\\
- else\\\
- openpath( _dirs[i].path )\\\
- end\\\
- else\\\
- showFolderOptions( filelist.x + frame.x + x - 2, filelist.y + frame.y + y - 2 + filelist.cy, _dirs[i].path, frame )\\\
- end\\\
- end\\\
- y = y + 4\\\
- end\\\
- end\\\
- \\\
- local cy = filelist.cy\\\
- filelist:clearChildren( )\\\
- \\\
- if #files == 0 then\\\
- local nofiles = filelist:newChild( NovaUI.UIText( 0, 1, 9, 1, \\\"No files!\\\" ) )\\\
- nofiles:centreX( )\\\
- end\\\
- \\\
- if sortorder.direction == \\\"ascending\\\" then\\\
- loadDirs( )\\\
- coroutine.yield( )\\\
- end\\\
- for i = 1, #_files do\\\
- local filetype = NovaFS.getType( _files[i].path )\\\
- \\\
- local app_registered = not viewhidden and Nova.app.isRegistered( _files[i].path:gsub( \\\"^C:/?\\\", \\\"\\\", 1 ) )\\\
- local frame = filelist:newChild( NovaUI.UIFrame( 1, y, filelist.w, 3 ) )\\\
- if app_registered then\\\
- local icon = frame:newChild( NovaUI.UIImage( 1, 1, 5, 3, icons.app ) )\\\
- else\\\
- local icon = frame:newChild( NovaUI.UIImage( 1, 1, 5, 3, icons[filetype] or icons.unknown ) )\\\
- end\\\
- local name = frame:newChild( NovaUI.UIText( 7, 1, frame.w - 6, 1, NovaFS.getName( _files[i].path, true ) ) )\\\
- local size = frame:newChild( NovaUI.UIText( 7, 2, frame.w - 7, 1, formatSize( _files[i].size ) ) )\\\
- size.tc = colours.lightGrey\\\
- local desc = frame:newChild( NovaUI.UIText( 7, 3, frame.w - 7, 1, app_registered and \\\"Nova app\\\" or NovaFS.getTypeDescription( filetype ) or filetype ) )\\\
- desc.tc = colours.lightGrey\\\
- \\\
- local overlay = frame:newChild( NovaUI.UIButton( 1, 1, frame.w, frame.h, \\\"\\\" ) )\\\
- overlay.bc = 0\\\
- overlay.align = false\\\
- function overlay:onClick( x, y, button )\\\
- if button == 1 or button == \\\"enter\\\" then\\\
- if app_registered then\\\
- Nova.app.launch( app_registered, { } )\\\
- else\\\
- if filetype == \\\"shortcut\\\" then\\\
- local ok, data = NovaFS.getFileMetaValue( _files[i].path, \\\"destination\\\" )\\\
- if ok then\\\
- if NovaFS.isDirectory( data ) then\\\
- openpath( data )\\\
- else\\\
- Nova.app.launch( \\\"Files\\\", { data, width = display.w, height = display.h } )\\\
- end\\\
- else\\\
- Nova.app.newThread( function( )\\\
- NovaUI.display.alert( display, \\\"Malformed shorcut\\\", true )\\\
- Nova.app.close( )\\\
- end )\\\
- end\\\
- return\\\
- elseif filetype == \\\"archive\\\" then\\\
- Nova.app.newThread( function( )\\\
- local l = loading\\\
- loading = true\\\
- local archive = NovaFS.Archive( _files[i].path )\\\
- local pass\\\
- if NovaFS.isProtected( _files[i].path ) then\\\
- local r = NovaUI.display.response( display, \\\"Please enter archive password\\\" )\\\
- if r then\\\
- pass = r\\\
- else\\\
- return\\\
- end\\\
- end\\\
- archive:loadFile( _files[i].path, pass )\\\
- mount = NovaFS.getName( _files[i].path, true )\\\
- NovaFS.mount( archive:createDrive( ), mount )\\\
- openpath( mount .. \\\":\\\" )\\\
- loading = l\\\
- end )\\\
- return\\\
- else\\\
- Nova.app.launch( \\\"Files\\\", { _files[i].path, filetype, width = display.w, height = display.h } )\\\
- end\\\
- end\\\
- else\\\
- showFileOptions( filelist.x + frame.x + x - 2, filelist.y + frame.y + y - 2 + filelist.cy, _files[i].path, frame )\\\
- end\\\
- end\\\
- \\\
- y = y + 4\\\
- end\\\
- coroutine.yield( )\\\
- if sortorder.direction == \\\"descending\\\" then\\\
- loadDirs( )\\\
- coroutine.yield( )\\\
- end\\\
- \\\
- filelist.cy = cy\\\
- \\\
- loading = l\\\
- end )\\\
- end\\\
- \\\
- function refresh( )\\\
- Nova.app.newThread( function( )\\\
- loadfiles( fpath )\\\
- end )\\\
- end\\\
- \\\
- function openpath( path, openAs )\\\
- if not viewhidden and Nova.app.isRegistered( path:gsub( \\\"^C:/?\\\", \\\"\\\", 1 ) ) then\\\
- Nova.app.launch( Nova.app.isRegistered( path:gsub( \\\"^C:/?\\\", \\\"\\\", 1 ) ), { } )\\\
- return\\\
- end\\\
- filelist.cy = 0\\\
- path = NovaFS.merge( path )\\\
- historyindex = historyindex + 1\\\
- while history[historyindex] do\\\
- table.remove( history, historyindex )\\\
- end\\\
- table.insert( history, path )\\\
- pathinput.text = path\\\
- fpath = path\\\
- loadfiles( fpath, openAs )\\\
- end\\\
- \\\
- local sidebarcontent\\\
- \\\
- Nova.app.newThread( function( )\\\
- while true do\\\
- local cy = sidebarcontent and sidebarcontent.cy or 0\\\
- local t = {\\\
- \\\"space\\\";\\\
- { type = \\\"label\\\", name = \\\"Drives\\\" };\\\
- }\\\
- local drives = NovaFS.listmounted( )\\\
- for i = 1, #drives do\\\
- t[#t + 1] = {\\\
- type = \\\"button\\\";\\\
- name = drives[i] .. \\\":\\\";\\\
- onClick = function( self, _, button )\\\
- if button == 1 then\\\
- openpath( drives[i] .. \\\":\\\" )\\\
- else\\\
- local x, y = self:positionIn( display )\\\
- dropdown( x, y, {\\\
- spacing = false;\\\
- shadow = colours.grey;\\\
- height = 5;\\\
- \\\"space\\\";\\\
- { type = \\\"button\\\", name = \\\"open\\\", onClick = function( _, frame )\\\
- if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
- openpath( drives[i] .. \\\":\\\" )\\\
- end };\\\
- { type = \\\"button\\\", name = \\\"unmount\\\", onClick = function( _, frame )\\\
- if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
- if drives[i] ~= \\\"C\\\" then\\\
- NovaFS.unmount( drives[i] )\\\
- else\\\
- NovaUI.display.alert( display, \\\"Cannot unmount this drive.\\\" )\\\
- end\\\
- end };\\\
- } )\\\
- end\\\
- end;\\\
- }\\\
- end\\\
- table.insert( t, \\\"space\\\" )\\\
- table.insert( t, { type = \\\"label\\\", name = \\\"Favourites\\\" } )\\\
- for k, v in pairs( favourites ) do\\\
- table.insert( t, { type = \\\"button\\\", name = NovaFS.getName( k, true ), onClick = function( self, frame, button )\\\
- if button == 1 then\\\
- openPath( k )\\\
- else\\\
- local x, y = self:positionIn( display )\\\
- dropdown( x, y, {\\\
- spacing = false;\\\
- shadow = colours.grey;\\\
- height = 5;\\\
- \\\"space\\\";\\\
- { type = \\\"button\\\", name = \\\"open\\\", onClick = function( _, frame )\\\
- if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
- openPath( k )\\\
- end };\\\
- { type = \\\"button\\\", name = \\\"remove\\\", onClick = function( _, frame )\\\
- if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
- favourites[k] = nil;\\\
- Nova.app.setData( \\\"favourites\\\", favourites )\\\
- end };\\\
- } )\\\
- end\\\
- end } )\\\
- end\\\
- sidebarcontent = NovaUI.display.menu( sidebar, t )\\\
- sidebarcontent.cy = cy\\\
- local t = os.clock( )\\\
- while os.clock( ) - t < 0.5 do\\\
- coroutine.yield( )\\\
- end\\\
- end\\\
- end )\\\
- \\\
- if ARGS[1] then\\\
- openpath( ARGS[1], ARGS[2] )\\\
- else\\\
- openpath \\\"C:/\\\"\\\
- end\\\
- \\\
- resizeDisplay( display.w, display.h )\\\
- \\\
- function showRightClickOptions( x, y )\\\
- dropdown( x, y, {\\\
- spacing = false;\\\
- shadow = colours.grey;\\\
- height = 11;\\\
- { type = \\\"button\\\", name = \\\"new file\\\", onClick = function( )\\\
- if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
- local p = NovaFS.merge( fpath, \\\"New File\\\" )\\\
- if NovaFS.exists( p ) then\\\
- local i = 1\\\
- while NovaFS.exists( p .. \\\" (\\\" .. i .. \\\")\\\" ) do\\\
- i = i + 1\\\
- end\\\
- p = p .. \\\" (\\\" .. i .. \\\")\\\"\\\
- end\\\
- NovaFS.newFile( p )\\\
- refresh( )\\\
- end };\\\
- { type = \\\"button\\\", name = \\\"new folder\\\", onClick = function( )\\\
- if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
- local p = NovaFS.merge( fpath, \\\"New Folder\\\" )\\\
- if NovaFS.exists( p ) then\\\
- local i = 1\\\
- while NovaFS.exists( p .. \\\" (\\\" .. i .. \\\")\\\" ) do\\\
- i = i + 1\\\
- end\\\
- p = p .. \\\" (\\\" .. i .. \\\")\\\"\\\
- end\\\
- NovaFS.newDirectory( p )\\\
- refresh( )\\\
- end };\\\
- \\\"rule\\\";\\\
- { type = \\\"button\\\", name = \\\"paste\\\", onClick = function( )\\\
- if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
- NovaFS.paste( fpath )\\\
- refresh( )\\\
- end };\\\
- \\\"rule\\\";\\\
- { type = \\\"button\\\", name = \\\"refresh\\\", onClick = function( )\\\
- if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
- refresh( )\\\
- end };\\\
- \\\"rule\\\";\\\
- { type = \\\"button\\\", name = viewhidden and \\\"hide hidden\\\" or \\\"show hidden\\\", onClick = function( )\\\
- if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
- viewhidden = not viewhidden\\\
- Nova.app.setData( \\\"viewhidden\\\", viewhidden )\\\
- refresh( )\\\
- end };\\\
- { type = \\\"menu\\\", name = \\\"sort by\\\", options = {\\\
- spacing = false;\\\
- shadow = colours.grey;\\\
- width = 7;\\\
- height = 3;\\\
- { type = \\\"button\\\", name = \\\"name\\\", onClick = function( )\\\
- if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
- sortorder.mode = \\\"name\\\"\\\
- Nova.app.setData( \\\"sortorder\\\", sortorder )\\\
- refresh( )\\\
- end };\\\
- { type = \\\"button\\\", name = \\\"size\\\", onClick = function( )\\\
- if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
- sortorder.mode = \\\"size\\\"\\\
- Nova.app.setData( \\\"sortorder\\\", sortorder )\\\
- refresh( )\\\
- end };\\\
- } };\\\
- { type = \\\"menu\\\", name = \\\"sort\\\", options = {\\\
- spacing = false;\\\
- shadow = colours.grey;\\\
- width = 13;\\\
- height = 3;\\\
- { type = \\\"button\\\", name = \\\"ascending\\\", onClick = function( )\\\
- if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
- sortorder.direction = \\\"ascending\\\"\\\
- Nova.app.setData( \\\"sortorder\\\", sortorder )\\\
- refresh( )\\\
- end };\\\
- { type = \\\"button\\\", name = \\\"descending\\\", onClick = function( )\\\
- if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
- sortorder.direction = \\\"descending\\\"\\\
- Nova.app.setData( \\\"sortorder\\\", sortorder )\\\
- refresh( )\\\
- end };\\\
- } };\\\
- } )\\\
- end\\\
- \\\
- function showFileOptions( x, y, path, frame )\\\
- local archive = NovaFS.getType( path ) == \\\"archive\\\"\\\
- local _type = NovaFS.getType( path )\\\
- dropdown( x, y, {\\\
- spacing = false;\\\
- shadow = colours.grey;\\\
- height = 14;\\\
- { type = \\\"button\\\", name = \\\"open\\\", onClick = function( )\\\
- if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
- Nova.app.launch( \\\"Files\\\", { path, _type, width = display.w, height = display.h } )\\\
- end };\\\
- { type = \\\"button\\\", name = archive and \\\"extract\\\" or \\\"open with\\\", onClick = function( )\\\
- if archive then\\\
- Nova.app.newThread( function( )\\\
- if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
- local p = path:gsub( \\\".nac$\\\", \\\"\\\", 1 )\\\
- local archive = NovaFS.Archive( )\\\
- local pass\\\
- if NovaFS.isProtected( path ) then\\\
- pass = NovaUI.display.response( display, \\\"Please enter password\\\" )\\\
- if not pass then\\\
- NovaUI.display.response( display, \\\"Archive is password protected\\\" )\\\
- return\\\
- end\\\
- end\\\
- archive:loadFile( path, pass )\\\
- local i = 1\\\
- while NovaFS.exists( p ) do\\\
- p = path:gsub( \\\".nac$\\\", \\\"\\\", 1 ) .. \\\" (\\\" .. i .. \\\")\\\"\\\
- i = i + 1\\\
- end\\\
- archive:saveDirectory( p )\\\
- refresh( )\\\
- end )\\\
- else\\\
- if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
- local frame = display:newChild( NovaUI.UIFrame( 1, 1, display.w, display.h ) )\\\
- local close = frame:newChild( NovaUI.UIButton( 1, 1, frame.w, frame.h, \\\"\\\" ) )\\\
- close.bc = 0\\\
- close.tc = 0\\\
- function close:onClick( )\\\
- frame:remove( )\\\
- end\\\
- local content = frame:newChild( NovaUI.UIFrame( 0, 0, 25, 12 ) )\\\
- content:centre( )\\\
- content:newChild( NovaUI.UIText( 2, 2, 24, 11, \\\"\\\" ) ).bc = colours.grey\\\
- content:newChild( NovaUI.UIText( 1, 1, 24, 11, \\\"\\\" ) ).bc = colours.white\\\
- content:newChild( NovaUI.UIText( 0, 2, 12, 1, \\\"Open with...\\\" ) ):centreX( )\\\
- content:newChild( NovaUI.UIText( 2, 3, 22, 1, \\\"----------------------\\\" ) ).tc = colours.lightGrey\\\
- content:newChild( NovaUI.UIText( 2, 9, 22, 1, \\\"----------------------\\\" ) ).tc = colours.lightGrey\\\
- local defaultbrackets = content:newChild( NovaUI.UIButton( 2, 10, 3, 1, \\\"[ ]\\\" ) )\\\
- defaultbrackets.tc = colours.lightGrey\\\
- local defaultlabel = content:newChild( NovaUI.UIButton( 5, 10, 8, 1, \\\" Default\\\" ) )\\\
- defaultlabel.tc = colours.grey\\\
- local default = content:newChild( NovaUI.UIButton( 3, 10, 1, 1, \\\"@\\\" ) )\\\
- function defaultbrackets.onClick( )\\\
- default.text = default.text == \\\"@\\\" and \\\" \\\" or \\\"@\\\"\\\
- end\\\
- defaultlabel.onClick = defaultbrackets.onClick\\\
- default.onClick = defaultbrackets.onClick\\\
- local ok = content:newChild( NovaUI.UIButton( 20, 10, 4, 1, \\\"ok\\\" ) )\\\
- ok.tc = colours.grey\\\
- local applist = content:newChild( NovaUI.UIFrame( 2, 4, 22, 5 ) )\\\
- local handlers = NovaFS.getHandlers( _type )\\\
- if #handlers == 0 then\\\
- handlers = NovaFS.getAllHandlers( )\\\
- end\\\
- if #handlers + 1 > applist.h then\\\
- applist.w = applist.w - 1\\\
- content:newChild( NovaUI.UIScrollBar( 0, 0, 1, 0, applist ) ):align( \\\"right\\\", applist )\\\
- end\\\
- local current\\\
- for i = 1, #handlers do\\\
- local button = applist:newChild( NovaUI.UIButton( 1, i, applist.w, 1, handlers[i] ) )\\\
- button.tc = colours.grey\\\
- button.align = false\\\
- function button:onClick( )\\\
- if current == button then\\\
- button.bc = colours.white ok.bc = colours.white current = nil return\\\
- end\\\
- ok.bc = colours.lightBlue\\\
- if current then current.bc = colours.white end\\\
- button.bc = colours.lightBlue\\\
- current = button\\\
- end\\\
- end\\\
- local showall = applist:newChild( NovaUI.UIButton( 1, #handlers + 1, applist.w, 1, \\\"Show all\\\" ) )\\\
- showall.align = false\\\
- function showall:onClick( )\\\
- current = nil\\\
- applist:clearChildren( )\\\
- local handlers = NovaFS.getAllHandlers( )\\\
- for i = 1, #handlers do\\\
- local button = applist:newChild( NovaUI.UIButton( 1, i, applist.w, 1, handlers[i] ) )\\\
- button.tc = colours.grey\\\
- button.align = false\\\
- function button:onClick( )\\\
- if current == button then\\\
- button.bc = colours.white ok.bc = colours.lightGrey current = nil return\\\
- end\\\
- ok.bc = colours.lightBlue\\\
- if current then current.bc = colours.white end\\\
- button.bc = colours.lightBlue\\\
- current = button\\\
- end\\\
- end\\\
- end\\\
- function ok:onClick( )\\\
- if not current then return end\\\
- local app = current.text\\\
- if default.text == \\\"@\\\" then\\\
- if defaults[_type] ~= app then\\\
- defaults[_type] = app\\\
- Nova.app.setData( \\\"defaults\\\", defaults )\\\
- end\\\
- NovaFS.setDefaultHandler( _type, app )\\\
- end\\\
- frame:remove( )\\\
- Nova.app.launch( app, { path, _type } )\\\
- end\\\
- end\\\
- end };\\\
- \\\"rule\\\";\\\
- { type = \\\"button\\\", name = \\\"copy\\\", onClick = function( )\\\
- if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
- NovaFS.copy( path )\\\
- end };\\\
- { type = \\\"button\\\", name = \\\"cut\\\", onClick = function( )\\\
- if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
- NovaFS.cut( path )\\\
- refresh( )\\\
- end };\\\
- { type = \\\"button\\\", name = \\\"delete\\\", onClick = function( )\\\
- if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
- NovaFS.delete( path )\\\
- refresh( )\\\
- end };\\\
- { type = \\\"button\\\", name = \\\"rename\\\", onClick = function( )\\\
- if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
- renaming = true\\\
- local input = filelist:newChild( NovaUI.UIInput( frame.x + 6, frame.y, frame.w - 7, 1 ) )\\\
- input.bc = 0\\\
- input.fbc = 0\\\
- input.text = NovaFS.getName( path )\\\
- input:focusOn( )\\\
- input:select( 1, #NovaFS.getName( path, true ) )\\\
- \\\
- function input:onEnter( )\\\
- local p = NovaFS.merge( filepath, self.text )\\\
- if p == path then return end\\\
- if NovaFS.exists( p ) then\\\
- NovaUI.display.alert( display, \\\"File already exists\\\" )\\\
- else\\\
- pcall( NovaFS.move, path, p )\\\
- refresh( )\\\
- end\\\
- end\\\
- function input:whenUnFocussed( )\\\
- input:remove( )\\\
- renaming = false\\\
- end\\\
- end };\\\
- \\\"rule\\\";\\\
- { type = \\\"button\\\", name = \\\"shortcut\\\", onClick = function( )\\\
- if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
- local fd = FileData \\\"\\\"\\\
- fd.meta.destination = path\\\
- fd.meta.type = \\\"shortcut\\\"\\\
- local p = NovaFS.merge( NovaFS.getDirectory( path ), NovaFS.getName( path, true ) ) .. \\\" - Shortcut\\\"\\\
- if NovaFS.exists( p ) then\\\
- local i = 1\\\
- while NovaFS.exists( p .. \\\" (\\\" .. i .. \\\")\\\" ) do\\\
- i = i + 1\\\
- end\\\
- p = p .. \\\" (\\\" .. i .. \\\")\\\"\\\
- end\\\
- NovaFS.writefile( p, fd )\\\
- refresh( )\\\
- end };\\\
- { type = \\\"button\\\", name = \\\"properties\\\" };\\\
- \\\"rule\\\";\\\
- { type = \\\"button\\\", name = NovaFS.isProtected( path ) and \\\"unprotect\\\" or \\\"protect\\\", onClick = function( )\\\
- if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
- Nova.app.newThread( function( )\\\
- local r\\\
- local p = NovaFS.isProtected( path )\\\
- if p then\\\
- r = NovaUI.display.response( display, \\\"Password to decrypt file with.\\\" )\\\
- else\\\
- r = NovaUI.display.response( display, \\\"Password to encrypt file with.\\\" )\\\
- end\\\
- loading = true\\\
- if r then\\\
- local data, err = NovaFS.readfile( path, p and r )\\\
- if not p then\\\
- data.password = r\\\
- end\\\
- data.meta.password = not p and true or nil\\\
- NovaFS.writefile( path, data )\\\
- refresh( )\\\
- end\\\
- loading = false\\\
- end )\\\
- end };\\\
- { type = \\\"button\\\", name = \\\"favourite\\\", onClick = function( )\\\
- if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
- if not favourites[path] then\\\
- favourites[path] = true\\\
- Nova.app.setData( \\\"favourites\\\", favourites )\\\
- end\\\
- end };\\\
- } )\\\
- end\\\
- \\\
- function showFolderOptions( x, y, path, frame )\\\
- dropdown( x, y, {\\\
- spacing = false;\\\
- shadow = colours.grey;\\\
- height = 14;\\\
- { type = \\\"button\\\", name = \\\"open\\\", onClick = function( )\\\
- if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
- openpath( path )\\\
- end };\\\
- { type = \\\"button\\\", name = \\\"open in new\\\", onClick = function( )\\\
- if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
- Nova.app.launch( \\\"Files\\\", { path, width = display.w, height = display.h } )\\\
- end };\\\
- \\\"rule\\\";\\\
- { type = \\\"button\\\", name = \\\"copy\\\", onClick = function( )\\\
- if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
- NovaFS.copy( path )\\\
- end };\\\
- { type = \\\"button\\\", name = \\\"cut\\\", onClick = function( )\\\
- if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
- NovaFS.cut( path )\\\
- refresh( )\\\
- end };\\\
- { type = \\\"button\\\", name = \\\"delete\\\", onClick = function( )\\\
- if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
- NovaFS.delete( path )\\\
- refresh( )\\\
- end };\\\
- { type = \\\"button\\\", name = \\\"rename\\\", onClick = function( )\\\
- if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
- renaming = true\\\
- local input = filelist:newChild( NovaUI.UIInput( frame.x + 6, frame.y, frame.w - 7, 1 ) )\\\
- input.bc = 0\\\
- input.fbc = 0\\\
- input.text = NovaFS.getName( path )\\\
- input:focusOn( )\\\
- input:select( 1, #NovaFS.getName( path, true ) )\\\
- \\\
- function input:onEnter( )\\\
- local p = NovaFS.merge( filepath, self.text )\\\
- if p == path then return end\\\
- if NovaFS.exists( p ) then\\\
- NovaUI.display.alert( display, \\\"File already exists\\\" )\\\
- else\\\
- pcall( NovaFS.move, path, p )\\\
- refresh( )\\\
- end\\\
- end\\\
- function input:whenUnFocussed( ) \\\
- input:remove( )\\\
- renaming = false\\\
- end\\\
- end };\\\
- \\\"rule\\\";\\\
- { type = \\\"button\\\", name = \\\"shortcut\\\", onClick = function( )\\\
- if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
- local fd = FileData \\\"\\\"\\\
- fd.meta.destination = path\\\
- fd.meta.type = \\\"shortcut\\\"\\\
- local p = NovaFS.merge( NovaFS.getDirectory( path ), NovaFS.getName( path, true ) ) .. \\\" - Shortcut\\\"\\\
- if NovaFS.exists( p ) then\\\
- local i = 1\\\
- while NovaFS.exists( p .. \\\" (\\\" .. i .. \\\")\\\" ) do\\\
- i = i + 1\\\
- end\\\
- p = p .. \\\" (\\\" .. i .. \\\")\\\"\\\
- end\\\
- NovaFS.writefile( p, fd )\\\
- refresh( )\\\
- end };\\\
- { type = \\\"button\\\", name = \\\"properties\\\" };\\\
- \\\"rule\\\";\\\
- { type = \\\"button\\\", name = \\\"archive\\\", onClick = function( )\\\
- if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
- local archive = NovaFS.Archive( )\\\
- local l = loading\\\
- loading = true\\\
- archive:loadDirectory( path )\\\
- local p = path .. \\\".nac\\\"\\\
- local i = 1\\\
- while NovaFS.exists( p ) do\\\
- p = path .. \\\" (\\\" .. i .. \\\").nac\\\"\\\
- i = i + 1\\\
- end\\\
- archive:saveFile( p )\\\
- refresh( )\\\
- loading = l\\\
- end };\\\
- { type = \\\"button\\\", name = \\\"favourite\\\", onClick = function( )\\\
- if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
- if not favourites[path] then\\\
- favourites[path] = true\\\
- Nova.app.setData( \\\"favourites\\\", favourites )\\\
- end\\\
- end };\\\
- } )\\\
- end\",\
- [\"appconfig.txt\"]=\"runmode = \\\"app\\\";\\\
- size = \\\"full\\\";\",\
- }\
- ,\
- Tasks={\
- [\"main.lua\"]=\"\\\
- if not Nova then\\\
- error( \\\"Cannot run outside Nova\\\", 0 )\\\
- end\\\
- \\\
- local window = Nova.app.window\\\
- local display = window.display\\\
- \\\
- window.minw = 10\\\
- window.minh = 4\\\
- \\\
- local background = display:newChild( NovaUI.UIText( 1, 1, display.w - 1, display.h - 1, \\\"\\\" ) )\\\
- local list = display:newChild( NovaUI.UIFrame( 1, 1, display.w - 1, display.h - 1 ) )\\\
- local scroll = display:newChild( NovaUI.UIScrollBar( display.w, 1, 1, display.h - 1, list ) )\\\
- local scroll2 = display:newChild( NovaUI.UIScrollBar( 1, display.h, display.w, 1, list, \\\"horizontal\\\" ) )\\\
- \\\
- function Nova.app.callback.onResize( w, h )\\\
- background.w = w - 1\\\
- background.h = h - 1\\\
- list.w = w - 1\\\
- list.h = h - 1\\\
- scroll.x = w\\\
- scroll.h = h - 1\\\
- scroll2.w = w\\\
- scroll2.y = h\\\
- end\\\
- \\\
- Nova.app.newThread( function( )\\\
- while true do\\\
- local processes = Nova.listProcesses( )\\\
- local max = 4\\\
- for i = 1, #processes do\\\
- local title = list:newChild( NovaUI.UIText( 1, i, #processes[i].name, 1, processes[i].name ) )\\\
- max = math.max( #processes[i].name + 1, max )\\\
- end\\\
- local rm = max + 1\\\
- max = max + 3\\\
- for i = 1, #processes do\\\
- local c = tostring( processes[i].threadcount )\\\
- local count = list:newChild( NovaUI.UIText( rm, i, #c, 1, c ) )\\\
- count.tc = colours.lightGrey\\\
- max = math.max( rm + #c, max )\\\
- end\\\
- for i = 1, #processes do\\\
- local close = list:newChild( NovaUI.UIButton( max + 1, i, 4, 1, \\\"stop\\\" ) )\\\
- close.tc = colours.red\\\
- function close:onClick( )\\\
- processes[i].stop( )\\\
- end\\\
- end\\\
- coroutine.yield( )\\\
- list:clearChildren( )\\\
- end\\\
- end )\",\
- [\"appconfig.txt\"]=\"runmode = \\\"app\\\";\\\
- width = 23;\\\
- height = 10;\\\
- permissions = {\\\
- listProcesses = true;\\\
- }\",\
- }\
- ,\
- Edit={\
- [\"main.lua\"]=\"\\\
- local window, display = { }, { }\\\
- local ARGS = ARGS\\\
- local running = true\\\
- local threads = { }\\\
- \\\
- local function newThread( f, wait )\\\
- if Nova then\\\
- local tID = Nova.app.newThread( f )\\\
- if wait then\\\
- while process:isThreadRunning( tID ) do\\\
- coroutine.yield( )\\\
- end\\\
- end\\\
- else\\\
- local co = coroutine.create( f )\\\
- table.insert( threads, co )\\\
- if wait then\\\
- while coroutine.status( co ) ~= \\\"dead\\\" do\\\
- coroutine.yield( )\\\
- end\\\
- end\\\
- end\\\
- end\\\
- \\\
- if not NovaUI then os.loadAPI \\\"NovaUI\\\" end\\\
- if not NovaFS then os.loadAPI \\\"NovaFS\\\" end\\\
- \\\
- if Nova then\\\
- window = Nova.app.window\\\
- display = window.display\\\
- window.minw = 25\\\
- window.minh = 10\\\
- else\\\
- display = NovaUI.UIHandler( )\\\
- ARGS = { ... }\\\
- NovaUI.buffer.reset( )\\\
- end\\\
- \\\
- local function copytable( t )\\\
- local t2 = { }\\\
- for k, v in pairs( t ) do t2[k] = v end\\\
- return t2\\\
- end\\\
- \\\
- local syntax = {\\\
- lua = {\\\
- blocks = {\\\
- { start = \\\"--[[\\\", finish = \\\"]]\\\", tc = colours.green };\\\
- { start = \\\"--\\\", finish = \\\"\\\\n\\\", tc = colours.green };\\\
- };\\\
- words = {\\\
- [\\\"while\\\"] = { tc = colours.blue };\\\
- [\\\"do\\\"] = { tc = colours.blue };\\\
- [\\\"if\\\"] = { tc = colours.blue };\\\
- [\\\"elseif\\\"] = { tc = colours.blue };\\\
- [\\\"else\\\"] = { tc = colours.blue };\\\
- [\\\"then\\\"] = { tc = colours.blue };\\\
- [\\\"for\\\"] = { tc = colours.blue };\\\
- [\\\"in\\\"] = { tc = colours.blue };\\\
- [\\\"end\\\"] = { tc = colours.blue };\\\
- [\\\"local\\\"] = { tc = colours.blue };\\\
- [\\\"not\\\"] = { tc = colours.blue };\\\
- [\\\"and\\\"] = { tc = colours.blue };\\\
- [\\\"or\\\"] = { tc = colours.blue };\\\
- [\\\"function\\\"] = { tc = colours.blue };\\\
- [\\\"return\\\"] = { tc = colours.blue };\\\
- [\\\"for\\\"] = { tc = colours.blue };\\\
- [\\\"break\\\"] = { tc = colours.blue };\\\
- \\\
- [\\\"true\\\"] = { tc = colours.blue };\\\
- [\\\"false\\\"] = { tc = colours.blue };\\\
- [\\\"nil\\\"] = { tc = colours.blue };\\\
- [\\\"self\\\"] = { tc = colours.blue };\\\
- \\\
- [\\\"_VERSION\\\"] = { tc = colours.purple };\\\
- [\\\"pairs\\\"] = { tc = colours.cyan };\\\
- [\\\"ipairs\\\"] = { tc = colours.cyan };\\\
- [\\\"select\\\"] = { tc = colours.cyan };\\\
- [\\\"unpack\\\"] = { tc = colours.cyan };\\\
- [\\\"setfenv\\\"] = { tc = colours.cyan };\\\
- [\\\"getfenv\\\"] = { tc = colours.cyan };\\\
- [\\\"setmetatable\\\"] = { tc = colours.cyan };\\\
- [\\\"getmetatable\\\"] = { tc = colours.cyan };\\\
- [\\\"next\\\"] = { tc = colours.cyan };\\\
- [\\\"rawset\\\"] = { tc = colours.cyan };\\\
- [\\\"rawget\\\"] = { tc = colours.cyan };\\\
- [\\\"rawequal\\\"] = { tc = colours.cyan };\\\
- [\\\"type\\\"] = { tc = colours.cyan };\\\
- [\\\"tostring\\\"] = { tc = colours.cyan };\\\
- [\\\"tonumber\\\"] = { tc = colours.cyan };\\\
- [\\\"pcall\\\"] = { tc = colours.cyan };\\\
- [\\\"xpcall\\\"] = { tc = colours.cyan };\\\
- [\\\"loadstring\\\"] = { tc = colours.cyan };\\\
- [\\\"assert\\\"] = { tc = colours.cyan };\\\
- [\\\"error\\\"] = { tc = colours.cyan };\\\
- [\\\"__inext\\\"] = { tc = colours.cyan };\\\
- [\\\"math\\\"] = { tc = colours.purple };\\\
- [\\\"string\\\"] = { tc = colours.purple };\\\
- [\\\"table\\\"] = { tc = colours.purple };\\\
- [\\\"coroutine\\\"] = { tc = colours.purple };\\\
- };\\\
- default = { bc = colours.white, tc = colours.grey };\\\
- linen = { bc = colours.grey, tc = colours.lightGrey };\\\
- string = { tc = colours.red, escape = { tc = colours.brown } };\\\
- tab = { tc = colours.lightGrey, char = \\\":\\\" };\\\
- symbols = {\\\
- [\\\"=\\\"] = { tc = colours.brown };\\\
- [\\\"+\\\"] = { tc = colours.brown };\\\
- [\\\"*\\\"] = { tc = colours.brown };\\\
- [\\\"/\\\"] = { tc = colours.brown };\\\
- [\\\"^\\\"] = { tc = colours.brown };\\\
- [\\\"%\\\"] = { tc = colours.brown };\\\
- [\\\">\\\"] = { tc = colours.brown };\\\
- [\\\"<\\\"] = { tc = colours.brown };\\\
- [\\\"#\\\"] = { tc = colours.brown };\\\
- [\\\"~\\\"] = { tc = colours.brown };\\\
- };\\\
- selection = { bc = colours.blue, tc = colours.white };\\\
- };\\\
- default = {\\\
- blocks = { };\\\
- words = { a = { colours.orange } };\\\
- default = { bc = colours.white, tc = colours.grey };\\\
- linen = { bc = colours.grey, tc = colours.lightGrey };\\\
- string = { };\\\
- symbols = { };\\\
- selection = { bc = colours.blue, tc = colours.white };\\\
- };\\\
- }\\\
- \\\
- local taskbar = display:newChild( NovaUI.UIFrame( 1, 1, display.w, 1 ) )\\\
- local tb = taskbar:newChild( NovaUI.UIButton( 1, 1, taskbar.w, 1, \\\"\\\" ) )\\\
- tb.bc = colours.grey\\\
- \\\
- local codefield = display:newChild( NovaUI.UICode( 1, 2, display.w - 1, display.h - 2, syntax.lua ) )\\\
- codefield:focusOn( )\\\
- \\\
- local scrollv = display:newChild( NovaUI.UIScrollBar( display.w, 2, 1, display.h - 2, codefield, \\\"vertical\\\" ) )\\\
- local scrollh = display:newChild( NovaUI.UIScrollBar( 1, display.h, display.w - 1, 1, codefield, \\\"horizontal\\\" ) )\\\
- local resizer = display:newChild( NovaUI.UIButton( display.w, display.h, 1, 1, \\\"@\\\" ) )\\\
- resizer.bc = colours.lightGrey\\\
- resizer.tc = colours.white\\\
- local xx, yy\\\
- function resizer:onClick( rx, ry, button )\\\
- if button == 1 then xx, yy = rx, ry end\\\
- end\\\
- function resizer:onDrag( rx, ry, cx, cy, button )\\\
- if button == 1 and xx then\\\
- window:resize( display.w + rx - 1, display.h + ry - 1 )\\\
- end\\\
- end\\\
- \\\
- local save = {\\\
- path = false;\\\
- meta = false;\\\
- changed = false;\\\
- }\\\
- local activedropdown, activemenu\\\
- local history, historyindex = { \\\"\\\" }, 1\\\
- local menus = { }\\\
- local asterix\\\
- \\\
- local function final( )\\\
- while history[historyindex + 1] do\\\
- table.remove( history, historyindex + 1 )\\\
- end\\\
- end\\\
- local function contentChanged( )\\\
- if save.changed then return end\\\
- save.changed = true\\\
- if Nova then\\\
- window.title = \\\"Edit* - \\\" .. ( save.path or \\\"unknown\\\" )\\\
- else\\\
- for k, v in pairs( menus ) do\\\
- v.x = v.x + 2\\\
- end\\\
- asterix = taskbar:newChild( NovaUI.UIText( 1, 1, 1, 1, \\\"*\\\" ) )\\\
- asterix.bc = 0\\\
- asterix.tc = 1\\\
- end\\\
- end\\\
- local function contentSaved( )\\\
- if not save.changed then return end\\\
- if asterix then\\\
- asterix:remove( )\\\
- end\\\
- save.changed = false\\\
- if Nova then\\\
- window.title = \\\"Edit - \\\" .. ( save.path or \\\"unknown\\\" )\\\
- else\\\
- for k, v in pairs( menus ) do\\\
- v.x = v.x - 2\\\
- end\\\
- end\\\
- end\\\
- \\\
- local function copy( )\\\
- if codefield.selected then\\\
- local text = codefield:getSelection( )\\\
- Nova.clipboard.set( \\\"plaintext\\\", text )\\\
- end\\\
- end\\\
- local function cut( )\\\
- if codefield.selected then\\\
- local text = codefield:getSelection( )\\\
- Nova.clipboard.set( \\\"plaintext\\\", text )\\\
- codefield:setSelection \\\"\\\"\\\
- final( )\\\
- table.insert( history, codefield:getCode( ) )\\\
- historyindex = #history\\\
- contentChanged( )\\\
- end\\\
- end\\\
- local function paste( )\\\
- local mode, data = Nova.clipboard.get( )\\\
- if mode ~= \\\"plaintext\\\" then\\\
- return\\\
- end\\\
- if codefield.selected then\\\
- codefield:setSelection( data )\\\
- else\\\
- codefield:write( data )\\\
- end\\\
- final( )\\\
- table.insert( history, codefield:getCode( ) )\\\
- historyindex = #history\\\
- contentChanged( )\\\
- end\\\
- \\\
- function codefield:onChange( mode )\\\
- if mode == \\\" \\\" or mode == \\\"\\\\n\\\" or mode == \\\" \\\" or mode == \\\"paste\\\" or mode == \\\"cut\\\" or mode == \\\"backspace\\\" or mode == \\\"delete\\\" then\\\
- final( )\\\
- table.insert( history, self:getCode( ) )\\\
- else\\\
- final( )\\\
- history[math.max( #history, 1 )] = self:getCode( )\\\
- end\\\
- historyindex = #history\\\
- contentChanged( )\\\
- end\\\
- \\\
- local function undo( )\\\
- if history[historyindex-1] then\\\
- historyindex = historyindex - 1\\\
- codefield:setCode( history[historyindex] )\\\
- contentChanged( )\\\
- end\\\
- end\\\
- local function redo( )\\\
- if history[historyindex+1] then\\\
- historyindex = historyindex + 1\\\
- codefield:setCode( history[historyindex] )\\\
- contentChanged( )\\\
- end\\\
- end\\\
- \\\
- local function savefile( wait )\\\
- newThread( function( )\\\
- if not save.path then\\\
- save.path = NovaUI.display.response( display, \\\"Where would you like to save?\\\" )\\\
- end\\\
- if not save.path then\\\
- return\\\
- end\\\
- if NovaFS then\\\
- local fd = NovaFS.FileData( codefield:getCode( ) )\\\
- fd.meta = save.meta or { }\\\
- NovaFS.writefile( save.path, fd )\\\
- else\\\
- local h = fs.open( save.path, \\\"w\\\" )\\\
- if h then\\\
- h.write( codefield:getCode( ) )\\\
- h.close( )\\\
- else\\\
- NovaUI.display.alert( display, \\\"Could not save file!\\\" )\\\
- end\\\
- end\\\
- contentSaved( )\\\
- end, wait )\\\
- end\\\
- \\\
- local function saved( )\\\
- if save.changed then\\\
- local result = NovaUI.display.confirm( display, \\\"You have unsaved changes, would you like to save?\\\" )\\\
- if result then\\\
- savefile( true )\\\
- end\\\
- if result == nil then\\\
- return false\\\
- end\\\
- end\\\
- return true\\\
- end\\\
- \\\
- local function openfile( path, mode )\\\
- if not mode and NovaFS then\\\
- mode = NovaFS.getType( path )\\\
- elseif not mode then\\\
- mode = \\\"lua\\\"\\\
- end\\\
- save.path = path\\\
- local content, meta\\\
- if NovaFS then\\\
- local filedata, err = NovaFS.readfile( path )\\\
- if filedata then\\\
- content = filedata.content\\\
- meta = filedata.meta\\\
- else\\\
- NovaUI.display.alert( display, err )\\\
- return\\\
- end\\\
- else\\\
- local h = fs.open( path, \\\"r\\\" )\\\
- if h then\\\
- content = h.readAll( )\\\
- meta = { }\\\
- h.close( )\\\
- else\\\
- NovaUI.display.alert( display, \\\"could not open file\\\" )\\\
- return\\\
- end\\\
- end\\\
- if mode == \\\"lua\\\" or mode == \\\"lib\\\" then\\\
- codefield.syntax = syntax.lua\\\
- elseif mode == \\\"class\\\" then\\\
- local s = copytable( syntax.lua )\\\
- local name = path:gsub( \\\"^.+/\\\", \\\"\\\" ) -- remove directories\\\
- if name:sub( 2 ):find \\\"%.\\\" then\\\
- name = name:sub( 1, 1 ) .. name:sub( 2 ):gsub( \\\"%.%w+$\\\", \\\"\\\" ) -- remove extension\\\
- end\\\
- s.words[name] = { tc = colours.cyan }\\\
- s.words[\\\"extends\\\"] = { tc = colours.lightGrey }\\\
- s.words[\\\"public\\\"] = { tc = colours.lightGrey }\\\
- codefield.syntax = s\\\
- else\\\
- codefield.syntax = syntax.default\\\
- end\\\
- save.meta = meta\\\
- codefield:setCode( content )\\\
- contentSaved( )\\\
- history = { content }\\\
- historyindex = 1\\\
- if Nova then\\\
- window.title = \\\"Edit - \\\" .. path\\\
- end\\\
- end\\\
- \\\
- if Nova then\\\
- function Nova.app.callback.onResize( w, h )\\\
- taskbar.w = w\\\
- tb.w = w\\\
- codefield.w, codefield.h = w - 1, h - 2\\\
- scrollv.x = w\\\
- scrollv.h = h - 2\\\
- scrollh.y = h\\\
- scrollh.w = w - 1\\\
- resizer.x, resizer.y = w, h\\\
- end\\\
- function Nova.app.callback.canClose( reason )\\\
- if ( reason == \\\"user\\\" or reason == \\\"terminate\\\" ) and save.changed then\\\
- newThread( function( )\\\
- if saved( ) then\\\
- Nova.app.close( )\\\
- end\\\
- end )\\\
- return false\\\
- end\\\
- return true\\\
- end\\\
- end\\\
- \\\
- menus.file = taskbar:newChild( NovaUI.UIButton( 1, 1, 4, 1, \\\"File\\\" ) )\\\
- menus.edit = taskbar:newChild( NovaUI.UIButton( 7, 1, 4, 1, \\\"Edit\\\" ) )\\\
- menus.syntax = taskbar:newChild( NovaUI.UIButton( 13, 1, 6, 1, \\\"Syntax\\\" ) )\\\
- menus.debug = taskbar:newChild( NovaUI.UIButton( 21, 1, 5, 1, \\\"Debug\\\" ) )\\\
- local menuoptions = { }\\\
- menuoptions.file = {\\\
- width = 10;\\\
- height = 8;\\\
- spacing = false;\\\
- shadow = colours.grey;\\\
- { type = \\\"button\\\", name = \\\"New\\\", onClick = function( )\\\
- activedropdown:remove( )\\\
- if activemenu then activemenu.tc = colours.cyan activemenu = nil end\\\
- newThread( function( )\\\
- if saved( ) then\\\
- codefield:setCode \\\"\\\"\\\
- history = { \\\"\\\", \\\"\\\" }\\\
- historyindex = 1\\\
- contentChanged( )\\\
- end\\\
- end )\\\
- end };\\\
- { type = \\\"button\\\", name = \\\"Open\\\", onClick = function( )\\\
- activedropdown:remove( )\\\
- if activemenu then activemenu.tc = colours.cyan activemenu = nil end\\\
- newThread( function( )\\\
- if saved( ) then\\\
- local path = NovaUI.display.response( display, \\\"Path to open from\\\" )\\\
- if path then\\\
- openfile( path )\\\
- end\\\
- end\\\
- end )\\\
- end };\\\
- \\\"rule\\\";\\\
- { type = \\\"button\\\", name = \\\"Save\\\", onClick = function( )\\\
- activedropdown:remove( )\\\
- if activemenu then activemenu.tc = colours.cyan activemenu = nil end\\\
- savefile( )\\\
- end };\\\
- { type = \\\"button\\\", name = \\\"Save As\\\", onClick = function( )\\\
- activedropdown:remove( )\\\
- newThread( function( )\\\
- local path = NovaUI.display.response( display, \\\"Path to save as\\\" )\\\
- if path then\\\
- savepath = path\\\
- savefile( )\\\
- end\\\
- end )\\\
- end };\\\
- \\\"rule\\\";\\\
- { type = \\\"button\\\", name = \\\"Exit\\\", onClick = function( )\\\
- activedropdown:remove( )\\\
- if activemenu then activemenu.tc = colours.cyan activemenu = nil end\\\
- newThread( function( )\\\
- if saved( ) then\\\
- if Nova then\\\
- Nova.app.close( )\\\
- else\\\
- running = false\\\
- end\\\
- end\\\
- end )\\\
- end };\\\
- }\\\
- menuoptions.edit = {\\\
- width = 9;\\\
- height = 8;\\\
- spacing = false;\\\
- shadow = colours.grey;\\\
- { type = \\\"button\\\", name = \\\"Undo\\\", onClick = function( )\\\
- undo( )\\\
- activedropdown:remove( )\\\
- if activemenu then activemenu.tc = colours.cyan activemenu = nil end\\\
- end };\\\
- { type = \\\"button\\\", name = \\\"Redo\\\", onClick = function( )\\\
- redo( )\\\
- activedropdown:remove( )\\\
- if activemenu then activemenu.tc = colours.cyan activemenu = nil end\\\
- end };\\\
- \\\"rule\\\";\\\
- { type = \\\"button\\\", name = \\\"Copy\\\", onClick = function( )\\\
- copy( )\\\
- activedropdown:remove( )\\\
- if activemenu then activemenu.tc = colours.cyan activemenu = nil end\\\
- end };\\\
- { type = \\\"button\\\", name = \\\"Cut\\\", onClick = function( )\\\
- cut( )\\\
- activedropdown:remove( )\\\
- if activemenu then activemenu.tc = colours.cyan activemenu = nil end\\\
- end };\\\
- \\\"rule\\\";\\\
- { type = \\\"button\\\", name = \\\"Paste\\\", onClick = function( )\\\
- paste( )\\\
- activedropdown:remove( )\\\
- if activemenu then activemenu.tc = colours.cyan activemenu = nil end\\\
- end };\\\
- }\\\
- menuoptions.syntax = {\\\
- width = 12;\\\
- height = 3;\\\
- spacing = false;\\\
- shadow = colours.grey;\\\
- { type = \\\"button\\\", name = \\\"Plain text\\\", onClick = function( )\\\
- codefield.syntax = syntax.default\\\
- codefield:updateCharacters( )\\\
- activedropdown:remove( )\\\
- if activemenu then activemenu.tc = colours.cyan activemenu = nil end\\\
- end };\\\
- { type = \\\"button\\\", name = \\\"Lua\\\", onClick = function( )\\\
- codefield.syntax = syntax.lua\\\
- codefield:updateCharacters( )\\\
- activedropdown:remove( )\\\
- if activemenu then activemenu.tc = colours.cyan activemenu = nil end\\\
- end };\\\
- }\\\
- menuoptions.debug = {\\\
- width = 18;\\\
- height = 5;\\\
- spacing = false;\\\
- shadow = colours.grey;\\\
- { type = \\\"button\\\", name = \\\"Run\\\", onClick = function( )\\\
- activedropdown:remove( )\\\
- if activemenu then activemenu.tc = colours.cyan activemenu = nil end\\\
- newThread( function( )\\\
- local f, err = loadstring( codefield:getCode( ), \\\"program\\\" )\\\
- if f then\\\
- local frame = display:newChild( NovaUI.UIFrame( 1, 1, display.w, display.h ) )\\\
- local title = frame:newChild( NovaUI.UIText( 1, 1, display.w - 5, 1, \\\"Running...\\\" ) ) title.bc = colours.black title.tc = colours.white\\\
- local close = frame:newChild( NovaUI.UIButton( display.w - 4, 1, 5, 1, \\\"close\\\" ) ) close.bc = colours.black close.tc = colours.red\\\
- function close:onClick( ) frame:remove( ) end\\\
- local canvas = frame:newChild( NovaUI.UIBuffer( 1, 2, display.w, display.h - 1 ) ) canvas:setTask( f ) canvas:passEvent( )\\\
- else\\\
- NovaUI.display.help( display, \\\"Syntax error\\\", err:gsub( \\\"main:433: \\\", \\\"\\\" ) )\\\
- end\\\
- end )\\\
- end };\\\
- { type = \\\"button\\\", name = \\\"Run with params\\\", onClick = function( )\\\
- activedropdown:remove( )\\\
- if activemenu then activemenu.tc = colours.cyan activemenu = nil end\\\
- newThread( function( )\\\
- local r = NovaUI.display.response( display, \\\"Please enter params, separated by a space.\\\" )\\\
- local p = { }\\\
- if r then\\\
- local last = 1\\\
- for i = 1, #r do\\\
- if r:sub( i, i ) == \\\" \\\" then\\\
- p[#p+1] = r:sub( last, i - 1 )\\\
- last = i + 1\\\
- end\\\
- end\\\
- p[#p+1] = r:sub( last )\\\
- end\\\
- local f, err = loadstring( codefield:getCode( ), \\\"program\\\" )\\\
- if f then\\\
- local frame = display:newChild( NovaUI.UIFrame( 1, 1, display.w, display.h ) )\\\
- local title = frame:newChild( NovaUI.UIText( 1, 1, display.w - 5, 1, \\\"Running...\\\" ) ) title.bc = colours.black title.tc = colours.white\\\
- local close = frame:newChild( NovaUI.UIButton( display.w - 4, 1, 5, 1, \\\"close\\\" ) ) close.bc = colours.black close.tc = colours.red\\\
- function close:onClick( ) frame:remove( ) end\\\
- local canvas = frame:newChild( NovaUI.UIBuffer( 1, 2, display.w, display.h - 1 ) ) canvas:setTask( f ) canvas:passEvent( unpack( p ) )\\\
- else\\\
- NovaUI.display.help( display, \\\"Syntax error\\\", err:gsub( \\\"main:461: \\\", \\\"\\\" ) )\\\
- end\\\
- end )\\\
- end };\\\
- \\\"rule\\\";\\\
- { type = \\\"button\\\", name = \\\"Check syntax\\\", onClick = function( )\\\
- activedropdown:remove( )\\\
- if activemenu then activemenu.tc = colours.cyan activemenu = nil end\\\
- local f, err = loadstring( codefield:getCode( ), \\\"program\\\" )\\\
- if f then\\\
- NovaUI.display.alert( display, \\\"Everything looks good!\\\" )\\\
- else\\\
- NovaUI.display.help( display, \\\"Syntax error\\\", err:gsub( \\\"main:477: \\\", \\\"\\\" ) )\\\
- end\\\
- end };\\\
- }\\\
- \\\
- for k, v in pairs( menus ) do\\\
- v.bc = 0\\\
- v.tc = colours.cyan\\\
- function v:onClick( )\\\
- if activemenu == v then\\\
- activedropdown:remove( )\\\
- activemenu.tc = colours.cyan\\\
- activemenu = nil\\\
- return\\\
- end\\\
- activemenu = v\\\
- v.tc = colours.lightBlue\\\
- local frame = display:newChild( NovaUI.UIFrame( 1, 1, display.w, display.h ) )\\\
- activedropdown = frame\\\
- local close = frame:newChild( NovaUI.UIButton( 1, 1, display.w, display.h, \\\"\\\" ) )\\\
- close.bc = 0\\\
- close.align = false\\\
- function close:onClick( x, y, button )\\\
- v.tc = colours.cyan\\\
- frame:remove( )\\\
- if y == 1 then\\\
- if Nova then\\\
- os.queueEvent( \\\"mouse_click\\\", button, window.x + x - 1, window.y + 1 )\\\
- else\\\
- os.queueEvent( \\\"mouse_click\\\", button, x, 1 )\\\
- end\\\
- else\\\
- activemenu = nil\\\
- end\\\
- end\\\
- local miniframe = frame:newChild( NovaUI.UIFrame( v.x, v.y + 1, 0, 0 ) )\\\
- NovaUI.display.menu( miniframe, menuoptions[k] )\\\
- if miniframe.x + miniframe.w > frame.w then\\\
- miniframe.x = frame.w - miniframe.w + 1\\\
- end\\\
- end\\\
- end\\\
- \\\
- local keyhandler = display:newChild( NovaUI.UIKeyHandler( ) )\\\
- \\\
- function keyhandler:onKey( key, lastkey )\\\
- if lastkey == 29 then\\\
- if key == 31 then -- ctrl-s\\\
- savefile( )\\\
- elseif key == 24 then -- ctrl-o\\\
- newThread( function( )\\\
- if saved( ) then\\\
- local path = NovaUI.display.response( display, \\\"Path to open from\\\" )\\\
- if path then\\\
- openfile( path )\\\
- end\\\
- end\\\
- end )\\\
- elseif key == keys.z then\\\
- undo( )\\\
- elseif key == keys.y then\\\
- redo( )\\\
- end\\\
- end\\\
- end\\\
- function codefield:onCtrlKey( key )\\\
- if key == 31 then -- ctrl-s\\\
- savefile( )\\\
- elseif key == 24 then -- ctrl-o\\\
- newThread( function( )\\\
- if saved( ) then\\\
- local path = NovaUI.display.response( display, \\\"Path to open from\\\" )\\\
- if path then\\\
- openfile( path )\\\
- end\\\
- end\\\
- end )\\\
- elseif key == keys.z then\\\
- undo( )\\\
- elseif key == keys.y then\\\
- redo( )\\\
- end\\\
- end\\\
- \\\
- if ARGS[1] then\\\
- openfile( ARGS[1], ARGS[2] )\\\
- end\\\
- \\\
- if not Nova then\\\
- local function update( event, dt )\\\
- if event[1] ~= \\\"update\\\" then\\\
- display:event( event )\\\
- for i = #threads, 1, -1 do\\\
- local ok, err = coroutine.resume( threads[i] )\\\
- if not ok or coroutine.status( threads[i] ) == \\\"dead\\\" then\\\
- table.remove( threads, i )\\\
- end\\\
- end\\\
- else\\\
- display:update( event[2] )\\\
- display:draw( )\\\
- NovaUI.buffer.drawChanges( )\\\
- NovaUI.buffer.clear( )\\\
- end\\\
- end\\\
- \\\
- local ok, err = pcall( function( )\\\
- local time = os.clock( )\\\
- local timer = os.startTimer( 0 )\\\
- os.queueEvent \\\"start\\\"\\\
- while running do\\\
- local ev = { coroutine.yield( ) }\\\
- local dt = os.clock( ) - time\\\
- time = os.clock( )\\\
- if ev[1] == \\\"timer\\\" and ev[2] == timer then\\\
- update( { \\\"update\\\" }, dt )\\\
- timer = os.startTimer( 0.05 )\\\
- else\\\
- update( ev, dt )\\\
- end\\\
- end\\\
- end )\\\
- if not ok then\\\
- print( err )\\\
- end\\\
- term.setBackgroundColour( colours.black )\\\
- term.scroll( 1 )\\\
- term.setCursorPos( 1, ({ term.getSize( ) })[2] )\\\
- term.setTextColour( colours.blue )\\\
- print \\\"Thank you for using Nova Edit\\\"\\\
- end\",\
- [\"appconfig.txt\"]=\"runmode = \\\"app\\\";\\\
- size = \\\"full\\\";\\\
- handles = {\\\
- \\\"text\\\";\\\
- \\\"lua\\\";\\\
- \\\"unknown\\\";\\\
- \\\"lib\\\";\\\
- \\\"class\\\";\\\
- };\",\
- }\
- ,\
- }\
- \
- function ttf( table, dir )\
- if not fs.isDir( dir ) then\
- fs.makeDir( dir )\
- end\
- for k, v in pairs( table ) do\
- if type( v ) == \"table\" then\
- ttf( v, dir..\"/\"..k )\
- elseif type( v ) == \"string\" then\
- local f = fs.open( dir..\"/\"..k, \"w\" )\
- f.write( v )\
- f.close( )\
- end\
- end\
- end\
- return function( path )\
- ttf( pack, path )\
- end", meta={
- type = "lib",
- }};["main"]={content="\
- require \"clipboard\"\
- require \"core\"\
- require \"encryption\"\
- require \"login\"\
- require \"sha256\"\
- require \"stringutils\"\
- \
- local w, h = term.getSize( )\
- \
- if _G.NovaRunning then\
- return\
- end\
- _G.NovaRunning = true\
- \
- if not term.isColour( ) then\
- error( \"Nova requires an advanced computer\", 0 )\
- end\
- \
- term.setBackgroundColour( colours.black )\
- term.clear( )\
- sleep( 0.1 )\
- term.setBackgroundColour( colours.grey )\
- term.clear( )\
- sleep( 0.1 )\
- term.setBackgroundColour( colours.lightGrey )\
- term.clear( )\
- sleep( 0.1 )\
- \
- local kernelLoader = Process \"kernelLoader\"\
- kernelLoader:newThread( function( )\
- require \"kernel\"\
- while not NovaFS.updateIndex( ) do end\
- kernelLoader:stop( )\
- end )\
- \
- NovaNet.com.updateModems( )\
- \
- local function update( event, dt )\
- if event[1] == \"update\" then\
- Process.update( { \"update\", dt } )\
- NovaUI.buffer.drawChanges( )\
- NovaUI.buffer.clear( )\
- NovaFS.updateIndex( )\
- else\
- NovaNet.com.listen( event )\
- NovaNet.com.clear( )\
- Process.update( { unpack( event ) }, true )\
- NovaNet.clearBuffers( )\
- end\
- NovaFS.clearCache( )\
- end\
- \
- ok, err = pcall( function( )\
- local time = os.clock( )\
- local timer = os.startTimer( 0 )\
- os.queueEvent \"start\"\
- while core.running and not core.error_message do\
- local ev = { coroutine.yield( ) }\
- local dt = os.clock( ) - time\
- time = os.clock( )\
- if ev[1] == \"timer\" and ev[2] == timer then\
- update( { \"update\" }, dt )\
- timer = os.startTimer( 0.05 )\
- else\
- update( ev, dt )\
- end\
- end\
- end )\
- \
- core.finish( )\
- \
- if not ok or core.error_message then\
- core.log( \"fatal error\", core.error_message or err )\
- local w, h = term.getSize( )\
- term.setBackgroundColour( colours.blue )\
- term.clear( )\
- term.setTextColour( colours.white )\
- term.setCursorPos( 1, 1 )\
- term.write \"Nova has encountered a fatal error.\"\
- term.setCursorPos( 1, 2 )\
- print \"Please report this message to the creator\\n\"\
- term.setTextColour( colours.white )\
- print( core.error_message or err )\
- parallel.waitForAny( function( )\
- os.pullEvent \"key\"\
- end, function( )\
- os.pullEvent \"mouse_click\"\
- end )\
- end", meta={}};["AppManager"]={content="\
- AppManager.public \"app_paths\"\
- AppManager.public.app_paths.write = false\
- \
- function AppManager:AppManager( session )\
- self.apps = { }\
- self.app_paths = { }\
- self.instances = { }\
- self.processes = { }\
- \
- self.session = session\
- \
- local t = fs.list( core.path .. \"/apps\" )\
- for i = 1, #t do\
- t[i] = core.path .. \"/apps/\" .. t[i]\
- end\
- for _, p in pairs( fs.list( session.account.userpath .. \"apps\" ) ) do\
- t[#t+1] = session.account.userpath .. \"apps/\" .. p\
- end\
- \
- for i = 1, #t do\
- self.public:register( t[i] )\
- end\
- \
- session.process:newThread( function( )\
- while true do\
- for i = #self.instances, 1, -1 do\
- if not self.instances[i].process:isRunning( ) and not self.instances[i].process:isPaused( ) then\
- self.instances[i]:close \"process stopped\"\
- end\
- if not self.instances[i].running then\
- table.remove( self.instances, i )\
- end\
- end\
- for i = #self.processes, 1, -1 do\
- if not self.processes[i]:isRunning( ) and not self.processes[i]:isPaused( ) then\
- table.remove( self.processes, i )\
- end\
- end\
- coroutine.yield( )\
- end\
- end )\
- \
- return self.public\
- end\
- \
- function AppManager.public:register( path )\
- local app, err = App( self.session, path )\
- if app then\
- self.apps[app.name] = app\
- self.app_paths[path] = app\
- else\
- self.session.process:newThread( function( )\
- local window = self.session.windowManager:newWindow \"alert\"\
- window.title = NovaFS.getName( path, true )\
- NovaUI.display.alert( window.display, err, true )\
- window:close( )\
- end )\
- end\
- end\
- \
- function AppManager.public:launch( name, args )\
- if self.apps[name] then\
- local instance = self.apps[name]:launch( args )\
- table.insert( self.instances, instance )\
- return instance\
- end\
- end\
- \
- function AppManager.public:newProcess( name )\
- local process = Process( name )\
- table.insert( self.processes, process )\
- return process\
- end\
- \
- function AppManager.public:stop( reason )\
- for i = 1, #self.instances do\
- self.instances[i]:close( reason )\
- end\
- for i = 1, #self.processes do\
- self.processes[i]:stop( )\
- end\
- end", meta={
- type = "class",
- }};["Sandbox"]={content="function self.environment.Nova.app.register( path )\
- local a, err = instance.session:registerApp( path )\
- if not a then\
- return false, err\
- end\
- return true\
- end\
- function self.environment.Nova.app.listNames( )\
- local t = { }\
- for k, v in pairs( instance.session.apps ) do\
- table.insert( t, k )\
- end\
- return t\
- end\
- function self.environment.Nova.app.getInstallPath( name )\
- if instance.session.apps[name] then\
- return instance.session.apps[name].installpath\
- end\
- return false\
- end\
- function self.environment.Nova.app.getIcon( name )\
- if instance.session.apps[name] then\
- return instance.session.apps[name].icon\
- end\
- return false\
- end\
- function self.environment.Nova.app.require( file, ... )\
- file = file:gsub( \"%.\", \"/\" )\
- local content = instance.app:readFile( tostring( file ) .. \".lua\" )\
- if content then\
- local f, err = loadstring( content, filesystem.getName( file ) )\
- if f then\
- local t = instance:newThread( Thread( f ) )\
- return t\
- else\
- instance:showError( err:gsub( \"Sandbox.lua:103: \", \"\" ) )\
- end\
- else\
- instance:showError \"no such file\"\
- end\
- end\
- \
- function self.environment.Nova.user.getName( )\
- return instance.app.session.account.username\
- end\
- function self.environment.Nova.user.rename( name )\
- return instance.app.session.account:rename( name )\
- end\
- function self.environment.Nova.user.changePassword( pass )\
- return instance.app.session.account:changePassword( pass )\
- end\
- function self.environment.Nova.user.logout( )\
- Thread( function( )\
- instance.app.session:logout( )\
- core.session = Session( )\
- end )\
- end\
- function self.environment.Nova.user.delete( )\
- Thread( function( )\
- fs.delete( instance.app.session.account.userpath )\
- instance.app.session:logout( )\
- core.session = Session( )\
- end )\
- end\
- function self.environment.Nova.user.create( name, pass )\
- return Account.create( name, pass )\
- end\
- function self.environment.Nova.user.switch( name, pass )\
- Thread( function( )\
- instance.app.session:logout( )\
- core.session = Session( name, pass )\
- end )\
- end", meta={}};["core"]={content="\
- require \"appttf\"\
- require \"libttf\"\
- \
- name = \"Nova Horizon\"\
- version_major = 1\
- version_minor = 3\
- version_patch = 0\
- version = (\"%d.%d.%02d\"):format( version_major, version_minor, version_patch )\
- author = \"Benedict Allen\"\
- \
- platform = \"computer\"\
- local w, h = term.getSize( )\
- if turtle then\
- platform = \"turtle\"\
- elseif pocket or w < 39 then\
- platform = \"pocket\"\
- end\
- \
- running = true\
- path = \"Nova\"\
- \
- session = false\
- UI = false\
- \
- if not fs.isDir( path ) then\
- fs.makeDir( path )\
- end\
- if not fs.isDir( path .. \"/apps\" ) then\
- fs.makeDir( path .. \"/apps\" )\
- appttf( path .. \"/apps\" )\
- end\
- if not fs.isDir( path .. \"/user\" ) then\
- fs.makeDir( path .. \"/user\" )\
- end\
- if not fs.isDir( path .. \"/lib\" ) then\
- fs.makeDir( path .. \"/lib\" )\
- libttf( path .. \"/lib\" )\
- end\
- \
- start_time = os.clock( )\
- \
- error_message = false\
- \
- function error( message )\
- core.error_message = message\
- end\
- \
- local file = path .. \"/log.txt\"\
- local h = fs.open( file, \"w\" )\
- if h then\
- h.close( )\
- end\
- \
- function log( pre, ... )\
- local data\
- if ... then\
- local t = { ... }\
- data = tostring( t[1] )\
- for i = 2, #t do\
- data = data .. \", \" .. tostring( t[i] )\
- end\
- end\
- local h = fs.open( file, \"a\" )\
- if h then\
- local time = \"[\" .. os.clock( ) - start_time .. \"] \"\
- if data then\
- h.write( time .. tostring( pre ) .. \": \" .. textutils.serialize( data ) .. \"\\n\" )\
- else\
- h.write( time .. tostring( pre ) .. \"\\n\" )\
- end\
- h.close( )\
- return true\
- end\
- return false\
- end\
- \
- function getRunTime( )\
- return os.clock( ) - start_time\
- end\
- \
- function finish( )\
- if core.session then\
- core.session:logout( )\
- end\
- core.log \"stopping\"\
- _G.NovaRunning = false\
- end\
- \
- function getUpdateInfo( )\
- local t = {\
- name = \"Nova Horizon\";\
- version_major = version_major;\
- version_minor = version_minor;\
- version_patch = version_patch;\
- }\
- pcall( function( )\
- local h = http.get \"http://pastebin.com/raw.php?i=K8MKteNu\"\
- if h then\
- t = textutils.unserialize( h.readAll( ) ) or t\
- h.close( )\
- end\
- end )\
- return t\
- end\
- \
- function compareVersions( t )\
- t = t or getUpdateInfo( )\
- if t.version_major > version_major then\
- return true\
- elseif t.version_major == version_major then\
- if t.version_minor > version_minor then\
- return true\
- elseif t.version_minor == version_minor then\
- return t.version_patch > version_patch\
- end\
- end\
- return false\
- end\
- \
- function update( updateInfo )\
- updateInfo = updateInfo or getUpdateInfo( )\
- local h = http.get( \"http://pastebin.com/raw.php?i=\" .. updateInfo.link )\
- if h then\
- if type( updateInfo.pre_update ) == \"function\" then\
- setfenv( updateInfo.pre_update, getfenv( ) )\
- updateInfo.pre_update( )\
- end\
- local content = h.readAll( )\
- h.close( )\
- filesystem.delete( \"C:/\" .. path .. \"/apps\" )\
- filesystem.delete \"C:/startup\"\
- filesystem.writefile( \"startup\", FileData( content ) )\
- if type( updateInfo.post_update ) == \"function\" then\
- setfenv( updateInfo.post_update, getfenv( ) )\
- updateInfo.post_update( )\
- end\
- return true\
- end\
- return false\
- end\
- \
- -- Credits to lbphacker for code below\
- local a=math.floor;local b=24*60*60;local c=365*b;local d=c+b;local e=4*c+b;local f=4;local g=1970;local h={-1,30,58,89,119,150,180,211,242,272,303,333,364}local i={}for j=1,2 do i[j]=h[j]end;for j=3,13 do i[j]=h[j]+1 end;local function gmtime(k)local m,n,o,p,q,r,s,t;local u=h;t=k;m=a(t/e)t=t-m*e;m=m*4+g;if t>=c then m=m+1;t=t-c;if t>=c then m=m+1;t=t-c;if t>=d then m=m+1;t=t-d else u=i end end end;n=a(t/b)t=t-n*b;local o=1;while u[o]<n do o=o+1 end;o=o-1;local p=n-u[o]q=(a(k/b)+f)%7;r=a(t/3600)t=t-r*3600;s=a(t/60)t=t-s*60;return m,n+1,o,p,q,r,s,t end\
- --time since last update\
- local firstepoch, firstclock, tslu\
- function getirltime(gmtoffset)\
- if not firstepoch or os.clock( ) - tslu > 300 then\
- pcall( function( )\
- local httpResponseHandle = http.get \"http://lbphacker.hu/cctime.php\"\
- if not httpResponseHandle then\
- return false\
- end\
- firstepoch = tonumber(httpResponseHandle.readAll())\
- if not firstepoch then\
- return false\
- end\
- firstclock = os.clock()\
- httpResponseHandle.close()\
- tslu = os.clock( )\
- end )\
- end\
- if firstepoch then\
- local y, j, m, d, w, h, n, s = gmtime(firstepoch + math.floor(os.clock() - firstclock) + (gmtoffset or 0) * 3600)\
- return {h = h, m = n, s = s}\
- end\
- return { h = math.floor( os.time( ) / 60 ), m = os.time( ) % 60, s = 0 }\
- end\
- \
- local months = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }\
- \
- function getirldate(gmtoffset)\
- if not firstepoch or os.clock( ) - tslu > 300 then\
- pcall( function( )\
- local httpResponseHandle = http.get \"http://lbphacker.hu/cctime.php\"\
- if not httpResponseHandle then\
- return false\
- end\
- firstepoch = tonumber(httpResponseHandle.readAll())\
- if not firstepoch then\
- return false\
- end\
- firstclock = os.clock()\
- httpResponseHandle.close()\
- tslu = os.clock( )\
- end )\
- end\
- if firstepoch then\
- local y, j, m, d, w, h, n, s = gmtime(firstepoch + math.floor(os.clock() - firstclock) + (gmtoffset or 0) * 3600)\
- return {y = y, j = j, m = m, d = d, w = w}\
- end\
- local day = os.day( )\
- local month = 12\
- local year = math.floor( day / 365 )\
- day = day - year * 365\
- for i = 1, #months do\
- if day < months[i] then\
- month = i\
- break\
- else\
- day = day - months[i]\
- end\
- end\
- return { y = year, j = 0, m = month, d = day, w = 0 }\
- end", meta={
- type = "lib",
- }};["ServiceAppInstance"]={content="--@meta[type]:\"class\"", meta={}};["Account"]={content="\
- Account.public \"username\"\
- Account.public.username.write = false\
- Account.public \"password\"\
- Account.public.password.write = false\
- Account.public \"shaPassword\"\
- Account.public.shaPassword.write = false\
- Account.public \"userpath\"\
- Account.public.userpath.write = false\
- \
- function Account:Account( name, pass )\
- if not fs.isDir( core.path .. \"/user/\" .. name ) then\
- return false, \"no such user\"\
- end\
- \
- -- try to login\
- local userpath = core.path .. \"/user/\" .. name .. \"/\"\
- local h = fs.open( userpath .. \"passcheck.txt\", \"r\" )\
- if h then\
- local passcheck = h.readAll( )\
- h.close( )\
- if encryption.decrypt( passcheck, sha256( pass ) ) ~= name then\
- return false, \"incorrect password\"\
- end\
- else\
- return false, \"corrupt user\"\
- end\
- \
- self.username = name\
- self.password = pass\
- self.shaPassword = sha256( pass )\
- self.userpath = userpath\
- \
- return self.public\
- end\
- \
- function Account.public:rename( name )\
- if fs.exists( core.path .. \"/user/\" .. name ) then\
- return false, \"user exists\"\
- end\
- local h = fs.open( self.userpath .. \"passcheck.txt\", \"w\" )\
- if h then\
- h.write( encryption.encrypt( name, self.shaPassword ) )\
- h.close( )\
- if not pcall( function( )\
- fs.move( self.userpath, core.path .. \"/user/\" .. name )\
- end ) then\
- local h = fs.open( self.userpath .. \"passcheck.txt\", \"w\" )\
- if h then\
- h.write( encryption.encrypt( self.username, self.shaPassword ) )\
- h.close( )\
- end\
- return false\
- end\
- self.username = name\
- self.userpath = core.path .. \"/user/\" .. name .. \"/\"\
- return true\
- end\
- return false\
- end\
- \
- function Account.public:changePassword( pass )\
- local h = fs.open( self.userpath .. \"passcheck.txt\", \"w\" )\
- if h then\
- self.password = pass\
- self.shaPassword = sha256( pass )\
- h.write( encryption.encrypt( self.username, self.shaPassword ) )\
- h.close( )\
- end\
- return false\
- end\
- \
- function Account.public:setData( index, entry, value )\
- local data\
- local h = fs.open( self.userpath .. \"data/\" .. index, \"r\" )\
- if h then\
- data = textutils.unserialize( h.readAll( ) ) or { }\
- h.close( )\
- else\
- data = { }\
- end\
- data[entry] = value\
- local h = fs.open( self.userpath .. \"data/\" .. index, \"w\" )\
- if h then\
- h.write( textutils.serialize( data ) )\
- h.close( )\
- return true\
- end\
- return false\
- end\
- \
- function Account.public:getData( index, entry )\
- local data\
- local h = fs.open( self.userpath .. \"data/\" .. index, \"r\" )\
- if h then\
- data = textutils.unserialize( h.readAll( ) ) or { }\
- h.close( )\
- return data[entry]\
- end\
- end\
- \
- function Account.public:hasAccess( request ) -- things like root filesystem access, settings, etc\
- return true\
- end\
- \
- function Account.static.create( name, pass )\
- if fs.exists( core.path .. \"/user/\" .. name ) then\
- return false, \"account already exists\"\
- end\
- fs.makeDir( core.path .. \"/user/\" .. name )\
- local h = fs.open( core.path .. \"/user/\" .. name .. \"/passcheck.txt\", \"w\" )\
- if h then\
- h.write( encryption.encrypt( name, sha256( pass ) ) )\
- h.close( )\
- else\
- fs.delete( core.path .. \"/user/\" .. name )\
- end\
- fs.makeDir( core.path .. \"/user/\" .. name .. \"/data\" )\
- fs.makeDir( core.path .. \"/user/\" .. name .. \"/apps\" )\
- fs.makeDir( core.path .. \"/user/\" .. name .. \"/files\" )\
- fs.makeDir( core.path .. \"/user/\" .. name .. \"/files/screenshots\" )\
- end", meta={
- type = "class",
- }};["clipboard"]={content="if NovaClipboard then\
- set, get = NovaClipboard.set, NovaClipboard.get\
- else\
- local a=false;function set(b,c)a={mode=b,data=c}end;function get()if a then return a.mode,a.data end end\
- export(\"NovaClipboard\",{set=set,get=get})\
- end", meta={
- type = "lib",
- }};["App"]={content="\
- App.public \"name\"\
- App.public \"readfile\"\
- App.public \"conf\"\
- App.public \"path\"\
- App.public \"main\"\
- \
- App.public.name.write = false\
- App.public.readfile.write = false\
- App.public.conf.write = false\
- App.public.path.write = false\
- App.public.main.write = false\
- \
- function App:App( session, path )\
- local readfile\
- if NovaFS.isDirectory( path ) then\
- function readfile( p )\
- local data = NovaFS.readfile( path .. \"/\" .. p )\
- if data then\
- return data.content\
- end\
- end\
- elseif NovaFS.exists( path ) and NovaFS.getType( path ) == \"archive\" then\
- return false, \"cannot load apps from archives just now...\"\
- -- read from an archive\
- elseif NovaFS.exists( path ) then\
- function readfile( p )\
- if p == \"main.lua\" then\
- local data = NovaFS.readfile( path )\
- if data then\
- return data.content\
- end\
- else\
- return false\
- end\
- end\
- else\
- return false, \"app not found\"\
- end\
- \
- self.session = session\
- self.path = path\
- self.name = NovaFS.getName( path, true )\
- self.conf = { }\
- self.readfile = readfile\
- \
- local main = readfile \"main.lua\"\
- if main then\
- self.main = main\
- else\
- return false, \"couldn't load main file\"\
- end\
- \
- local conf = readfile \"appconfig.txt\"\
- if conf and type( textutils.unserialize( \"{\" .. conf .. \"}\" ) == \"table\" ) then\
- self.conf = textutils.unserialize( \"{\" .. conf .. \"}\" ) or { }\
- end\
- \
- local icon = readfile \"icon.nim\"\
- if icon then\
- self.icon = NovaUI.Image( 7, 3 )\
- self.icon:loadstr( icon )\
- end\
- \
- if type( self.conf.name ) == \"string\" then\
- self.name = self.conf.name\
- end\
- \
- if type( self.conf.handles ) == \"table\" then\
- for i = 1, #self.conf.handles do\
- NovaFS.addHandler( self.conf.handles[i], self.name )\
- end\
- end\
- if type( self.conf.filetypes ) == \"table\" then\
- for k, v in pairs( self.conf.filetypes ) do\
- NovaFS.addTypeDescription( k, v )\
- end\
- end\
- if type( self.conf.extensions ) == \"table\" then\
- for k, v in pairs( self.conf.extensions ) do\
- NovaFS.addExtension( k, v )\
- end\
- end\
- \
- return self.public\
- end\
- \
- function App.public:launch( args )\
- args = type( args ) == \"table\" and args or { }\
- if self.conf.runmode == \"service\" then\
- return ServiceAppInstance( self.session, self.public, args )\
- elseif self.conf.runmode == \"process\" then\
- return ProcessAppInstance( self.session, self.public, args )\
- else\
- return DefaultAppInstance( self.session, self.public, args )\
- end\
- end", meta={
- type = "class",
- }};["login"]={content="\
- local function login( )\
- if #( fs.list( core.path .. \"/user\" ) or {} ) == 0 then\
- local w, h = term.getSize( )\
- \
- local finished = false\
- \
- local frame = core.UI:newChild( NovaUI.UIFrame( 2, 3, w - 2, h - 3 ) )\
- local title = frame:newChild( NovaUI.UIText( 1, 1, core.UI.w - 2, 2, \"Please set up a user account.\" ) )\
- title.tc = colours.grey\
- title:centreX( )\
- local ut = frame:newChild( NovaUI.UIText( 0, 6, 8, 1, \"username\" ) )\
- ut.tc = colours.lightGrey\
- ut:centreX( )\
- local username = frame:newChild( NovaUI.UIInput( 0, 7, math.floor( w * 2 / 3 ), 1 ) )\
- username.tc = 1\
- username:centreX( )\
- username:focusOn( )\
- local pt = frame:newChild( NovaUI.UIText( 0, 9, 8, 1, \"password\" ) )\
- pt.tc = colours.lightGrey\
- pt:centreX( )\
- local password = frame:newChild( NovaUI.UIInput( 0, 10, math.floor( w * 2 / 3 ), 1, \"*\" ) )\
- password.tc = 1\
- password:centreX( )\
- \
- local continue = frame:newChild( NovaUI.UIButton( 0, frame.h - 2, 20, 3, \"continue\" ) )\
- continue:centreX( )\
- continue.tc = 1\
- function continue:onClick( ) finished = true end\
- continue.tabIndex = true\
- function continue:whenFocussed( ) self.bc = colours.lightBlue end\
- function continue:whenUnFocussed( ) self.bc = colours.lightGrey end\
- \
- function username:onEnter( ) password:focusOn( ) end\
- function password:onEnter( ) finished = true end\
- \
- while not finished do\
- coroutine.yield( )\
- end\
- frame:remove( )\
- \
- Account.create( username.text, password.text )\
- core.log( \"created user\", username.text )\
- \
- return Account( username.text, password.text )\
- end\
- \
- local loginProcess = Process \"loginProcess\"\
- \
- local waiting = true\
- local account\
- \
- local frame = core.UI:newChild( NovaUI.UIFrame( 1, 1, term.getSize( ) ) )\
- local input = { }\
- local title = { }\
- \
- title = frame:newChild( NovaUI.UIText( 1, 1, frame.w, 3, \"\\n Login\" ) )\
- title.bc = colours.grey\
- title.tc = colours.white\
- \
- local userlist = frame:newChild( NovaUI.UIMenu( 2, 8, frame.w - 2, 4, \"horizontal\" ) )\
- userlist.padding = 1\
- local scrollbar = frame:newChild( NovaUI.UIScrollBar( 2, 13, frame.w - 2, 1, userlist, \"horizontal\" ) )\
- scrollbar.bc = colours.lightGrey\
- scrollbar.tc = colours.grey\
- \
- local password = { }\
- \
- local cols = { colours.blue, colours.red, colours.green, colours.yellow }\
- local n = 1\
- \
- local users = fs.list( core.path .. \"/user\" )\
- for i = 1, #users do\
- local f = userlist:newChild( NovaUI.UIFrame( 0, 0, 10, 4 ) )\
- f:newChild( NovaUI.UIButton( 2, 1, f.w - 2, f.h - 1, \"\" ) ).bc = cols[n]\
- f:newChild( NovaUI.UIText( 1, f.h, #users[i], 1, users[i] ) ):centreX( )\
- c = f:newChild( NovaUI.UIButton( 1, 1, f.w, f.h, \"\" ) )\
- c.bc = 0\
- c.align = false\
- function c:onClick( x )\
- if password.input then\
- password.input:remove( )\
- password.title:remove( )\
- end\
- password.title = frame:newChild( NovaUI.UIText( 0, 15, 8, 1, \"password\" ) )\
- password.title:centreX( )\
- password.input = frame:newChild( NovaUI.UIInput( 0, 16, math.floor( term.getSize( ) / 2 ), 1 ) )\
- password.input.mask = \"*\"\
- password.input:centreX( )\
- password.input:focusOn( )\
- function password.input:whenUnFocussed( )\
- password.input:remove( )\
- password.title:remove( )\
- password = { }\
- end\
- function password.input:onEnter( )\
- local err\
- account, err = Account( users[i], password.input.text )\
- if account then\
- waiting = false\
- else\
- loginProcess:newThread( function( )\
- local alert = frame:newChild( NovaUI.UIText( 0, ({term.getSize()})[2] - 1, #err, 1, err ) )\
- alert.bc = 0\
- alert.tc = colours.red\
- alert:centreX( )\
- sleep( 1 )\
- alert:remove( )\
- end )\
- end\
- end\
- if type( x ) == \"string\" then\
- password.input.text = x\
- password.input:onEnter( )\
- end\
- end\
- \
- if users[i] == name then\
- c:onClick( pass )\
- end\
- \
- n = n + 1\
- if n == 5 then\
- n = 1\
- end\
- \
- if #users == 1 then\
- c:onClick( )\
- end\
- end\
- if #users * 11 <= userlist.w then\
- userlist.w = #users * 11 - 1\
- userlist:centreX( )\
- scrollbar:remove( )\
- end\
- \
- loginProcess:newThread( function( )\
- while true do\
- local ev = { coroutine.yield( ) }\
- if password.input then\
- if ev[1] == \"mag_swipe\" then\
- password.input.text = ev[2]\
- password.input:onEnter( )\
- end\
- end\
- end\
- end )\
- \
- while waiting do\
- coroutine.yield( )\
- end\
- frame:remove( )\
- loginProcess:stop( )\
- return account\
- end\
- \
- return login", meta={
- type = "lib",
- }};["Session"]={content="\
- Session.public \"account\"\
- Session.public.account.write = false\
- Session.public \"process\"\
- Session.public.process.write = false\
- Session.public \"UI\"\
- Session.public.UI.write = false\
- Session.public \"windowManager\"\
- Session.public.windowManager.write = false\
- Session.public \"appManager\"\
- Session.public.appManager.write = false\
- \
- function Session:Session( )\
- if core.session then\
- core.session:logout( )\
- end\
- \
- -- login\
- self.account = login( )\
- core.log( \"starting session for \" .. self.account.username )\
- \
- -- mount user drive\
- NovaFS.mount( NovaFS.Drive.redirect( self.account.userpath .. \"/files\" ), \"user\" )\
- \
- -- multitasking host\
- self.process = Process \"SessionManager\"\
- \
- -- create UI\
- self.UI = core.UI:newChild( NovaUI.UIFrame( 1, 1, term.getSize( ) ) )\
- self.windowManager = self.UI:newChild( WindowManager( self.public ) )\
- \
- -- create AppManager\
- self.appManager = AppManager( self.public )\
- \
- -- update OS\
- local updateInfo = core.getUpdateInfo( )\
- if core.compareVersions( updateInfo ) then\
- local window = self.windowManager:newWindow \"alert\"\
- window.title = \"Update\"\
- local response = NovaUI.display.confirm( window.display, \"Update found, would you like to update?\" )\
- window:close( )\
- if response then\
- if core.update( ) then\
- if NovaUI.display.confirm( self.UI, \"Updated, would you like to restart?\" ) then\
- core.finish( )\
- os.reboot( )\
- end\
- else\
- NovaUI.display.alert( self.UI, \"Failed to download update\" )\
- end\
- end\
- end\
- \
- self:desktop( )\
- \
- core.session = self.public\
- return self.public\
- end\
- \
- function Session.public:logout( )\
- NovaFS.unmount \"user\"\
- self.UI:remove( )\
- self.appManager:stop( )\
- core.session = nil\
- end\
- \
- function Session:desktop( )\
- local desktop = self.windowManager.desktop\
- \
- local background = desktop:newChild( NovaUI.UIFrame( 1, 1, desktop.w, desktop.h - 1 ) )\
- self.taskbar = desktop:newChild( NovaUI.UIFrame( 1, desktop.h, desktop.w, 1 ) )\
- \
- local open = false\
- \
- self.process:newThread( function( )\
- while true do\
- self.taskbar:newChild( NovaUI.UIText( 1, 1, self.taskbar.w, 1, \"\" ) ).bc = colours.grey\
- local menu = self.taskbar:newChild( NovaUI.UIButton( 1, 1, 6, 1, \" Nova \" ) )\
- menu.bc = open and colours.cyan or colours.grey\
- menu.tc = colours.white\
- function menu.onClick( )\
- self.public:menu( )\
- end\
- \
- local windows = self.windowManager:listWindows( )\
- local ww = 0\
- for i = 1, #windows do\
- ww = ww + math.min( #windows[i].title:gsub( \" .+\", \"\" ), 15 ) + 1\
- end\
- local cutoff = 0\
- while ww > self.taskbar.w - 6 do\
- ww = ww - #windows - 1\
- cutoff = cutoff + 1\
- end\
- local x = 7\
- for i = 1, #windows do\
- local w = windows[i]\
- local display = windows[i].title:gsub( \" .+\", \"\" ):sub( 1, 15 )\
- display = display:sub( 1, #display - cutoff )\
- local b = self.taskbar:newChild( NovaUI.UIButton( x, 1, #display, 1, display ) )\
- b.bc = colours.lightGrey\
- b.tc = colours.grey\
- x = x + #display + 1\
- function b.onClick( )\
- w.content.active = true\
- self.windowManager:focusOn( w )\
- end\
- end\
- coroutine.yield( )\
- self.taskbar:clearChildren( )\
- end\
- end )\
- end\
- \
- function Session.public:menu( )\
- local function updateContent( content, name, close )\
- content:clearChildren( )\
- local files = NovaFS.findName( name )\
- \
- local categories = {\
- { name = \"Apps\", onClick = function( item )\
- self.appManager:launch( item.name, { } )\
- end };\
- { name = \"Pictures\", onClick = function( item )\
- self.appManager:launch( \"Files\", { item.path } )\
- end };\
- { name = \"Lua Files\", onClick = function( item )\
- self.appManager:launch( \"Files\", { item.path } )\
- end };\
- { name = \"Folders\", onClick = function( item )\
- self.appManager:launch( \"Files\", { item.path } )\
- end };\
- { name = \"Files\", onClick = function( item )\
- self.appManager:launch( \"Files\", { item.path } )\
- end };\
- { name = \"System Files\", onClick = function( item )\
- self.appManager:launch( \"Files\", { item.path } )\
- end };\
- }\
- \
- for i = 1, #files do\
- if self.appManager.app_paths[NovaFS.merge( files[i].path )] then\
- table.insert( categories[1], self.appManager.app_paths[files[i].path] )\
- elseif files[i].path:find( \"^/?\" .. core.path .. \"/\" ) and not files[i].path:find( \"^/?\" .. core.path .. \"/user/\" .. self.account.username .. \"/\" ) then\
- table.insert( categories[6], files[i] )\
- elseif files[i].type == \"directory\" then\
- table.insert( categories[4], files[i] )\
- elseif files[i].filetype == \"nova_image\" then\
- table.insert( categories[2], files[i] )\
- elseif files[i].filetype == \"lua\" or files[i].filetype == \"lib\" or files[i].filetype == \"class\" then\
- table.insert( categories[3], files[i] )\
- else\
- table.insert( categories[5], files[i] )\
- end\
- end\
- \
- local f = false\
- local t = { }\
- for i = 1, #categories do\
- local c = categories[i]\
- if #c > 0 then\
- f = true\
- table.insert( t, \"space\" )\
- table.insert( t, { type = \"label\", name = c.name } )\
- for i = 1, #c do\
- table.insert( t, {\
- type = \"button\";\
- name = c[i].name;\
- onClick = function( )\
- close( )\
- c.onClick( c[i] )\
- end;\
- } )\
- end\
- while categories[i+1] do\
- if #categories[i+1] > 0 then\
- table.insert( t, \"space\" )\
- table.insert( t, \"rule\" )\
- break\
- end\
- i = i + 1\
- end\
- end\
- end\
- if not f then\
- table.insert( t, \"space\" )\
- table.insert( t, { type = \"label\", name = \"Nothing found!\" } )\
- end\
- \
- NovaUI.display.menu( content, t )\
- \
- for i = 1, #categories do\
- if #categories[i] > 0 then\
- return function( )\
- categories[i].onClick( categories[i][1] )\
- end\
- end\
- end\
- return function( )\
- Nova.display.alert( display, \"No search results\" )\
- end\
- end\
- \
- open = true\
- local close = self.UI:newChild( NovaUI.UIButton( 1, 1, self.UI.w, self.UI.h, \"\" ) )\
- close.bc = 0\
- close.tc = 0\
- local dropdown = self.UI:newChild( NovaUI.UIFrame( 1, self.UI.h - 15, 26, 15 ) )\
- function close:onClick( )\
- open = false\
- close:remove( )\
- dropdown:remove( )\
- end\
- dropdown:newChild( NovaUI.UIText( 1, 1, 26, 15, \"\" ) ).bc = colours.cyan\
- \
- local content = dropdown:newChild( NovaUI.UIFrame( 2, 2, dropdown.w - 2, dropdown.h - 4 ) )\
- local enter = updateContent( content, \"*\", function( )\
- close:onClick( )\
- end )\
- \
- local search = dropdown:newChild( NovaUI.UIInput( 2, dropdown.h - 1, dropdown.w - 2, 1 ) )\
- search.bc = colours.white\
- search.fbc = colours.white\
- \
- function search.onEnter( )\
- close:onClick( )\
- enter( )\
- end\
- function search:onChange( )\
- enter = updateContent( content, self.text, function( )\
- close:onClick( )\
- end )\
- end\
- \
- search:focusOn( )\
- end", meta={
- type = "class",
- }};["encryption"]={content="\
- local function sum( n, ... ) -- number n, number ...additions\
- local t = { ... }\
- for i = 1, #t do\
- n = n + t[i]\
- end\
- return n\
- \
- -- number sum\
- end\
- \
- local function loop( n, lim ) -- number n, number limit\
- while n > lim do\
- n = n - lim\
- end\
- while n < 1 do\
- n = n + lim\
- end\
- return n\
- -- number limited\
- end\
- \
- local function shift( str, count )\
- local str = { str:byte( 1, #str ) }\
- for i = 1, #str do\
- str[i] = loop( str[i] + count, 255 )\
- end\
- for i = 1, #str do\
- str[i] = string.char( str[i] )\
- end\
- return table.concat( str, \"\" )\
- end\
- \
- local function tohex( b ) -- string[4] bits\
- local n = 0\
- for i = 1, 4 do\
- n = n * 2\
- n = n + tonumber( b:sub( i, i ) )\
- end\
- if n >= 10 then\
- local hexes = { \"A\", \"B\", \"C\", \"D\", \"E\", \"F\" }\
- return hexes[n - 9]\
- end\
- return tostring( n )\
- \
- -- string[1] hex\
- end\
- \
- local function fromhex( h ) -- string[1] hex\
- local n = tonumber( h )\
- if not n then\
- local hexes = { [\"A\"] = 10, [\"B\"] = 11, [\"C\"] = 12, [\"D\"] = 13, [\"E\"] = 14, [\"F\"] = 15 }\
- n = hexes[h]\
- end\
- local str = \"\"\
- for i = 1, 4 do\
- str = str .. n % 2\
- n = math.floor( n / 2 )\
- end\
- return str:reverse( )\
- \
- -- string[4] bits\
- end\
- \
- local function xor( n1, n2 )\
- if n1 > 255 or n2 > 255 or n1 < 0 or n2 < 0 then\
- return error \"expected numbers between 0 and 255\"\
- end\
- local bit1, bit2 = { }, { }\
- for i = 1, 8 do\
- bit1[9-i] = n1 % 2 == 1\
- n1 = math.floor( n1 / 2 )\
- end\
- for i = 1, 8 do\
- bit2[9-i] = n2 % 2 == 1\
- n2 = math.floor( n2 / 2 )\
- end\
- local bits = { }\
- for i = 1, 8 do\
- bits[i] = ( bit1[i] and not bit2[i] ) or ( not bit1[i] and bit2[i] )\
- end\
- local n = 0\
- for i = 1, 8 do\
- n = n * 2\
- n = n + ( bits[i] and 1 or 0 )\
- end\
- return n\
- end\
- \
- local function nand( n1, n2 )\
- if n1 > 255 or n2 > 255 or n1 < 0 or n2 < 0 then\
- return error \"expected numbers between 0 and 255\"\
- end\
- local bit1, bit2 = { }, { }\
- for i = 1, 8 do\
- bit1[9-i] = n1 % 2 == 1\
- n1 = math.floor( n1 / 2 )\
- end\
- for i = 1, 8 do\
- bit2[9-i] = n2 % 2 == 1\
- n2 = math.floor( n2 / 2 )\
- end\
- local bits = { }\
- for i = 1, 8 do\
- bits[i] = not bit1[i] == bit2[i]\
- end\
- local n = 0\
- for i = 1, 8 do\
- n = n * 2\
- n = n + ( bits[i] and 1 or 0 )\
- end\
- return n\
- end\
- \
- local function tobits( n )\
- local str = \"\"\
- for i = 1, 8 do\
- str = str .. n % 2\
- n = math.floor( n / 2 )\
- end\
- return str:reverse( )\
- end\
- \
- local function frombits( b )\
- local n = 0\
- for i = 1, 8 do\
- n = n * 2\
- n = n + tonumber( b:sub( i, i ) )\
- end\
- return n\
- end\
- \
- local t = os.clock( )\
- local function start( )\
- t = os.clock( )\
- end\
- local function yield( )\
- if os.clock( ) - t > .1 then\
- coroutine.yield( )\
- start( )\
- end\
- end\
- \
- function encrypt( str, key ) -- string text, string key\
- local enc = \"\"\
- start( )\
- for i = 1, #str do\
- math.randomseed( sum( key:byte( 1, #key ) ) )\
- key = shift( key, math.random( 1, 100 ) )\
- local ki = loop( i, #key )\
- local a = str:sub( i, i ):byte( )\
- local b = key:sub( ki, ki ):byte( )\
- enc = enc .. tobits( xor( a, b ) )\
- yield( )\
- end\
- local enc2 = \"\"\
- for i = 1, #enc / 4 do\
- enc2 = enc2 .. tohex( enc:sub( i * 4 - 3, i * 4 ) )\
- yield( )\
- end\
- return enc2\
- \
- -- string cipher\
- end\
- \
- function decrypt( str, key ) -- string cipher, string key\
- start( )\
- local dec2 = \"\"\
- for i = 1, #str do\
- dec2 = dec2 .. fromhex( str:sub( i, i ) )\
- yield( )\
- end\
- str = dec2\
- local dec = \"\"\
- local keys = { }\
- for i = 1, #str / 8 do\
- math.randomseed( sum( key:byte( 1, #key ) ) )\
- keys[i] = shift( key, math.random( 1, 100 ) )\
- key = keys[i]\
- yield( )\
- end\
- for i = 1, #str / 8 do\
- local ki = loop( i, #key )\
- local a = frombits( str:sub( ( i - 1 ) * 8 + 1, i * 8 ) )\
- local b = string.byte( keys[i]:sub( ki, ki ) )\
- dec = dec .. string.char( nand( a, b ) )\
- yield( )\
- end\
- return dec\
- \
- -- string text\
- end", meta={
- type = "lib",
- }};["loader"]={content="\
- require \"core\"\
- \
- local function loadExternalAPI( name )\
- local path = core.path .. \"/lib/\" .. name .. \".lua\"\
- local f, err = loadfile( path )\
- if not f then\
- error( err, 0 )\
- end\
- local env = setmetatable( { }, { __index = getfenv( ) } )\
- setfenv( f, env )\
- f( )\
- local t = { }\
- for k, v in pairs( env ) do\
- t[k] = v\
- end\
- shared[name] = t\
- if env.NovaClipboard then\
- shared.NovaClipboard = env.NovaClipboard\
- end\
- end\
- \
- loadExternalAPI \"NovaFS\"\
- loadExternalAPI \"NovaNet\"\
- loadExternalAPI \"NovaUI\"", meta={
- type = "pre-class",
- }};["Window"]={content="\
- require \"Process\"\
- \
- Window.title = \"Blank window\"\
- Window.public \"title\" \"string\"\
- \
- Window.public \"overlay\" (NovaUI.UIElement)\
- Window.public \"active\" \"boolean\"\
- \
- Window.public \"process\" (Process)\
- \
- Window.public \"x\"\
- Window.public \"y\"\
- \
- function Window.public.x:write( value )\
- self.content.x = tonumber( value ) or self.content.x\
- end\
- function Window.public.x:read( )\
- return self.content.x\
- end\
- function Window.public.y:write( value )\
- self.content.y = tonumber( value ) or self.content.y\
- end\
- function Window.public.y:read( )\
- return self.content.y\
- end\
- \
- Window.public \"minw\" \"number\"\
- Window.public \"minh\" \"number\"\
- Window.public \"maxw\" \"number\"\
- Window.public \"maxh\" \"number\"\
- \
- Window.public \"content\"\
- Window.public.content.write = false\
- Window.public \"display\"\
- Window.public.display.write = false\
- \
- Window.public \"onClose\" \"function\"\
- Window.public \"onResize\" \"function\"\
- Window.public \"onMove\" \"function\"\
- \
- function Window:Window( windowmanager, x, y, w, h )\
- self.active = true\
- self.manager = windowmanager\
- \
- self.minw = 8\
- self.minh = 1\
- self.maxw = windowmanager.w\
- self.maxh = windowmanager.h - 1\
- \
- self.width = w\
- self.height = h\
- self.content = NovaUI.UIFrame( x, math.max( y, 1 ), w, h + 1 )\
- self.background = self.content:newChild( NovaUI.UIText( 1, 1, w, h + 1, \"\" ) )\
- \
- self.titlebc = colours.grey\
- self.titletc = colours.white\
- self.buttontc = colours.cyan\
- \
- self.borderactive = false\
- self.border = self.content:newChild( NovaUI.UIText( 1, 1, 0, 0, \"\" ) )\
- self.titlebar = self.content:newChild( NovaUI.UIFrame( 1, 1, w, 1 ) )\
- self.display = self.content:newChild( NovaUI.UIFrame( 1, 2, w, h - 1 ) )\
- \
- self.titledisplay = self.titlebar:newChild( NovaUI.UIButton( 1, 1, w - 4, 1, function( )\
- return self.title\
- end ) )\
- self.titledisplay.align = false\
- self.borderbutton = self.titlebar:newChild( NovaUI.UIButton( w - 3, 1, 1, 1, \"b\" ) )\
- self.minimisebutton = self.titlebar:newChild( NovaUI.UIButton( w - 2, 1, 1, 1, \"_\" ) )\
- self.maximisebutton = self.titlebar:newChild( NovaUI.UIButton( w - 1, 1, 1, 1, \"+\" ) )\
- self.closebutton = self.titlebar:newChild( NovaUI.UIButton( w, 1, 1, 1, \"x\" ) )\
- \
- local xx, yy, _cx, _cy\
- function self.titledisplay.onClick( _, rx, ry, button )\
- if button == 1 then\
- _cx, _cy = 0, 0\
- xx, yy = rx, ry\
- self.manager:focusOn( self.public )\
- end\
- end\
- function self.titledisplay.onDrag( _, rx, ry, cx, cy, button )\
- if button == 1 and xx then\
- if self.content.x + rx - _cx - 1 == 1 then\
- self.content.x = 1\
- self.content.y = 1\
- self.public:resize( math.floor( self.manager.w / 2 ), self.manager.h - 2 )\
- xx = nil\
- elseif self.content.x + rx - _cx - 1 == self.manager.w then\
- self.content.y = 1\
- self.public:resize( math.floor( self.manager.w / 2 ), self.manager.h - 2 )\
- self.content.x = self.manager.w - self.content.w + 1\
- xx = nil\
- else\
- self.content.x = self.content.x + rx - xx\
- self.content.y = self.content.y + ry - yy\
- xx, yy = rx, ry\
- end\
- _cx = _cx + cx\
- _cy = _cy + cy\
- self.manager:focusOn( self.public )\
- if self.onMove then\
- self.onMove( self.public, self.width, self.height )\
- end\
- end\
- end\
- \
- function self.borderbutton.onClick( )\
- self.manager:focusOn( self.public )\
- if self.borderactive then\
- self.public:hideBorder( )\
- else\
- self.public:showBorder( )\
- end\
- end\
- function self.minimisebutton.onClick( )\
- self.public:minimise( )\
- end\
- function self.maximisebutton.onClick( )\
- self.public:maximise( )\
- end\
- function self.closebutton.onClick( )\
- if self.onClose then\
- self.onClose( self.public )\
- else\
- self.public:close( )\
- end\
- end\
- \
- self:updateColours( )\
- \
- self.public:resize( w, h )\
- \
- return self.public\
- end\
- \
- function Window:updateColours( )\
- self.titledisplay.bc = self.titlebc\
- self.titledisplay.tc = self.titletc\
- self.borderbutton.bc = self.titlebc\
- self.minimisebutton.bc = self.titlebc\
- self.maximisebutton.bc = self.titlebc\
- self.closebutton.bc = self.titlebc\
- self.borderbutton.tc = self.buttontc\
- self.minimisebutton.tc = self.buttontc\
- self.maximisebutton.tc = self.buttontc\
- self.closebutton.tc = self.buttontc\
- if self.borderactive then\
- self.border.bc = self.titlebc\
- self.resizer.bc = self.titlebc\
- self.resizer.tc = self.buttontc\
- end\
- end\
- \
- function Window.public:resize( w, h )\
- w = math.min( math.max( w, self.minw ), self.maxw )\
- h = math.min( math.max( h, self.minh ), self.maxh )\
- self.width = w\
- self.height = h\
- if self.borderactive then\
- self.border.w = w + 2\
- self.border.h = h + 2\
- self.content.w = w + 2\
- self.content.h = h + 2\
- self.titlebar.w = w\
- self.resizer.x = w + 2\
- self.resizer.y = h + 2\
- self.background.w = w + 2\
- self.background.h = h + 2\
- else\
- self.content.w = w\
- self.content.h = h + 1\
- self.background.w = w\
- self.background.h = h + 1\
- self.titlebar.w = w\
- end\
- self.titledisplay.w = self.titlebar.w - 4\
- self.borderbutton.x = self.titlebar.w - 3\
- self.minimisebutton.x = self.titlebar.w - 2\
- self.maximisebutton.x = self.titlebar.w - 1\
- self.closebutton.x = self.titlebar.w\
- self.display.w = w\
- self.display.h = h\
- if self.onResize then\
- self.onResize( self.public, w, h )\
- elseif self.process then\
- self.process:queueEvent( \"window_resize\", w, h )\
- end\
- end\
- \
- function Window.public:minimise( )\
- self.content.active = false\
- end\
- \
- function Window.public:maximise( )\
- if self.manager.w > self.maxw or self.manager.h - 2 > self.maxh then\
- return\
- end\
- if self.borderactive then\
- self.public:hideBorder( )\
- end\
- self.content.x = 1\
- self.content.y = 1\
- self.public:resize( self.manager.w, self.manager.h - 2 )\
- self.manager:focusOn( self.public )\
- end\
- \
- function Window.public:close( )\
- if self.process then\
- self.process:queueEvent( \"window_close\", self.public )\
- end\
- self.content:remove( )\
- self.manager:removeWindow( self.public )\
- end\
- \
- function Window.public:showBorder( )\
- self.borderactive = true\
- self.border.active = true\
- self.border.bc = self.titlebc\
- self.display.x = 2\
- self.titlebar.x = 2\
- self.content.x = self.content.x - 1\
- self.resizer = self.content:newChild( NovaUI.UIButton( self.width + 2, self.height + 1, 1, 1, \"@\" ) )\
- self.resizer.bc = self.titlebc\
- self.resizer.tc = self.buttontc\
- function self.resizer.onDrag( _, rx, ry, _, _, button )\
- if button == 1 then\
- self.public:resize( self.width + rx - 1, self.height + ry - 1 )\
- end\
- end\
- self.public:resize( self.width, self.height )\
- end\
- \
- function Window.public:hideBorder( )\
- self.border.active = false\
- self.resizer:remove( )\
- self.borderactive = false\
- self.display.x = 1\
- self.titlebar.x = 1\
- self.content.x = self.content.x + 1\
- self.public:resize( self.width, self.height )\
- end", meta={
- type = "class",
- }};["libttf"]={content="\
- local pack = {\
- [\"NovaFS.lua\"]=\"--[[type=\\\"executable_package\\\", name=\\\"NovaFS\\\"]]\\\
- local files = {[\\\"filesystem\\\"]={content=\\\"\\\\\\\
- -- drives\\\\\\\
- \\\\\\\
- local drives = { }\\\\\\\
- drives.C = Drive.redirect \\\\\\\"\\\\\\\"\\\\\\\
- \\\\\\\
- local function formatDriveName( name )\\\\\\\
- if type( name ) == \\\\\\\"string\\\\\\\" then\\\\\\\
- return name\\\\\\\
- end\\\\\\\
- return false\\\\\\\
- end\\\\\\\
- \\\\\\\
- local function getDrive( path )\\\\\\\
- if not path:find \\\\\\\":\\\\\\\" then\\\\\\\
- return drives.C, \\\\\\\"no such drive C\\\\\\\"\\\\\\\
- end\\\\\\\
- local drive = formatDriveName( path:gsub( \\\\\\\":.*\\\\\\\", \\\\\\\"\\\\\\\" ) )\\\\\\\
- if drives[drive] then\\\\\\\
- return drives[drive]\\\\\\\
- end\\\\\\\
- return false, \\\\\\\"no such drive \\\\\\\" .. drive\\\\\\\
- end\\\\\\\
- \\\\\\\
- function mount( drive, name )\\\\\\\
- name = formatDriveName( name )\\\\\\\
- if not name then\\\\\\\
- return false, \\\\\\\"expected string name\\\\\\\"\\\\\\\
- end\\\\\\\
- if drives[name] then\\\\\\\
- return false, \\\\\\\"drive already mounted\\\\\\\"\\\\\\\
- end\\\\\\\
- if not class.typeOf( drive, Drive ) then\\\\\\\
- return false, \\\\\\\"expected Drive object\\\\\\\"\\\\\\\
- end\\\\\\\
- drives[name] = drive\\\\\\\
- return true\\\\\\\
- end\\\\\\\
- \\\\\\\
- function unmount( name )\\\\\\\
- drives[formatDriveName( name )] = nil\\\\\\\
- end\\\\\\\
- \\\\\\\
- function getmounted( name )\\\\\\\
- return drives[formatDriveName( name )]\\\\\\\
- end\\\\\\\
- \\\\\\\
- function listmounted( )\\\\\\\
- local t = { }\\\\\\\
- for name in pairs( drives ) do\\\\\\\
- t[#t+1] = name\\\\\\\
- end\\\\\\\
- return t\\\\\\\
- end\\\\\\\
- \\\\\\\
- -- types\\\\\\\
- \\\\\\\
- local extensions = {\\\\\\\
- txt = \\\\\\\"text\\\\\\\";\\\\\\\
- lua = \\\\\\\"lua\\\\\\\";\\\\\\\
- nim = \\\\\\\"nova_image\\\\\\\";\\\\\\\
- nac = \\\\\\\"archive\\\\\\\";\\\\\\\
- }\\\\\\\
- local types = {\\\\\\\
- text = \\\\\\\"Plain text file\\\\\\\";\\\\\\\
- lua = \\\\\\\"Lua script file\\\\\\\";\\\\\\\
- unknown = \\\\\\\"Unknown file type\\\\\\\";\\\\\\\
- archive = \\\\\\\"Nova archive\\\\\\\";\\\\\\\
- class = \\\\\\\"Class file\\\\\\\";\\\\\\\
- folder = \\\\\\\"Folder\\\\\\\";\\\\\\\
- lib = \\\\\\\"Lua library\\\\\\\";\\\\\\\
- nova_image = \\\\\\\"Nova image file\\\\\\\";\\\\\\\
- shortcut = \\\\\\\"Shortcut\\\\\\\";\\\\\\\
- -- design_file\\\\\\\
- -- design_project\\\\\\\
- }\\\\\\\
- local handlers = { }\\\\\\\
- \\\\\\\
- function addExtension( ext, type )\\\\\\\
- extensions[ext] = type\\\\\\\
- end\\\\\\\
- \\\\\\\
- function addTypeDescription( type, description )\\\\\\\
- types[type] = description\\\\\\\
- end\\\\\\\
- \\\\\\\
- function getTypeDescription( type )\\\\\\\
- return types[type]\\\\\\\
- end\\\\\\\
- \\\\\\\
- function addHandler( type, handler )\\\\\\\
- handlers[type] = handlers[type] or { }\\\\\\\
- for i = 1, #handlers[type] do\\\\\\\
- if handlers[type][i] == handler then\\\\\\\
- return\\\\\\\
- end\\\\\\\
- end\\\\\\\
- table.insert( handlers[type], handler )\\\\\\\
- end\\\\\\\
- \\\\\\\
- function setDefaultHandler( type, handler )\\\\\\\
- handlers[type] = handlers[type] or { }\\\\\\\
- for i = 1, #handlers[type] do\\\\\\\
- if handlers[type][i] == handler then\\\\\\\
- table.remove( handlers[type], i )\\\\\\\
- break\\\\\\\
- end\\\\\\\
- end\\\\\\\
- table.insert( handlers[type], handler )\\\\\\\
- end\\\\\\\
- \\\\\\\
- function getHandlers( type )\\\\\\\
- return { unpack( handlers[type] or { } ) }\\\\\\\
- end\\\\\\\
- \\\\\\\
- function getAllHandlers( )\\\\\\\
- local t = { }\\\\\\\
- for k, v in pairs( handlers ) do\\\\\\\
- for i = 1, #v do\\\\\\\
- t[v[i]] = true\\\\\\\
- end\\\\\\\
- end\\\\\\\
- local t2 = { }\\\\\\\
- for k, v in pairs( t ) do\\\\\\\
- t2[#t2+1] = k\\\\\\\
- end\\\\\\\
- return t2\\\\\\\
- end\\\\\\\
- \\\\\\\
- -- caching\\\\\\\
- \\\\\\\
- local cache = { }\\\\\\\
- local function filecacheset( path, value )\\\\\\\
- path = path:lower( )\\\\\\\
- cache[path] = value\\\\\\\
- end\\\\\\\
- local function filecacheget( path )\\\\\\\
- path = path:lower( )\\\\\\\
- return cache[path]\\\\\\\
- end\\\\\\\
- \\\\\\\
- function clearCache( )\\\\\\\
- cache = { }\\\\\\\
- end\\\\\\\
- \\\\\\\
- -- read/write stuff (cache stuff!)\\\\\\\
- \\\\\\\
- local function read( path )\\\\\\\
- if type( filecacheget( path ) ) == \\\\\\\"table\\\\\\\" then return false, \\\\\\\"could not read folder\\\\\\\" end\\\\\\\
- local drive, err = getDrive( path )\\\\\\\
- if not drive then\\\\\\\
- return false, err\\\\\\\
- end\\\\\\\
- local content = filecacheget( path )\\\\\\\
- if content then\\\\\\\
- return content\\\\\\\
- end\\\\\\\
- local h = drive.open( path:gsub( \\\\\\\".-:\\\\\\\", \\\\\\\"\\\\\\\", 1 ), \\\\\\\"r\\\\\\\" )\\\\\\\
- if h then\\\\\\\
- local content = h.readAll( )\\\\\\\
- h.close( )\\\\\\\
- return content\\\\\\\
- else\\\\\\\
- return false, \\\\\\\"could not open file\\\\\\\"\\\\\\\
- end\\\\\\\
- end\\\\\\\
- local function write( path, content )\\\\\\\
- if type( filecacheget( path ) ) == \\\\\\\"table\\\\\\\" then return false, \\\\\\\"could not write to folder\\\\\\\" end\\\\\\\
- local drive, err = getDrive( path )\\\\\\\
- if not drive then\\\\\\\
- return false, err\\\\\\\
- end\\\\\\\
- local h = drive.open( path:gsub( \\\\\\\".-:\\\\\\\", \\\\\\\"\\\\\\\", 1 ), \\\\\\\"w\\\\\\\" )\\\\\\\
- if h then\\\\\\\
- h.write( content )\\\\\\\
- h.close( )\\\\\\\
- filecacheset( path, content )\\\\\\\
- return true\\\\\\\
- else\\\\\\\
- return false, \\\\\\\"could not open file\\\\\\\"\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- -- name operations\\\\\\\
- \\\\\\\
- function getDirectory( path )\\\\\\\
- if not path:find \\\\\\\"/\\\\\\\" then return \\\\\\\"\\\\\\\" end\\\\\\\
- return path:gsub( \\\\\\\"/.-$\\\\\\\", \\\\\\\"\\\\\\\" ) -- remove filename\\\\\\\
- end\\\\\\\
- \\\\\\\
- function getPath( path )\\\\\\\
- if not path:find \\\\\\\"/\\\\\\\" then return \\\\\\\"\\\\\\\" end\\\\\\\
- return path:gsub( \\\\\\\"/.-$\\\\\\\", \\\\\\\"\\\\\\\" ) -- remove filename\\\\\\\
- end\\\\\\\
- \\\\\\\
- function getName( path, trimExt )\\\\\\\
- path = path:gsub( \\\\\\\"^.+/\\\\\\\", \\\\\\\"\\\\\\\" ) -- remove directories\\\\\\\
- if trimExt and path:sub( 2 ):find \\\\\\\"%.\\\\\\\" then\\\\\\\
- return path:sub( 1, 1 ) .. path:sub( 2 ):gsub( \\\\\\\"%.%w+$\\\\\\\", \\\\\\\"\\\\\\\" ) -- remove extension\\\\\\\
- end\\\\\\\
- return path\\\\\\\
- end\\\\\\\
- \\\\\\\
- function getExtension( path )\\\\\\\
- path = path:gsub( \\\\\\\"^.+/\\\\\\\", \\\\\\\"\\\\\\\" )\\\\\\\
- if not path:sub( 2 ):find \\\\\\\"%.\\\\\\\" then\\\\\\\
- return \\\\\\\"\\\\\\\"\\\\\\\
- end\\\\\\\
- return path:gsub( \\\\\\\".+%.\\\\\\\", \\\\\\\"\\\\\\\" ) -- remove directories, then stuff before the .\\\\\\\
- end\\\\\\\
- \\\\\\\
- function merge( path, ... )\\\\\\\
- local paths = { ... }\\\\\\\
- for i = 1, #paths do\\\\\\\
- path = path .. \\\\\\\"/\\\\\\\" .. tostring( paths[i] )\\\\\\\
- end\\\\\\\
- return path:gsub( \\\\\\\"//+\\\\\\\", \\\\\\\"/\\\\\\\" ):gsub( \\\\\\\"^/\\\\\\\", \\\\\\\"\\\\\\\" ):gsub( \\\\\\\"/$\\\\\\\", \\\\\\\"\\\\\\\" )\\\\\\\
- end\\\\\\\
- \\\\\\\
- -- file methods\\\\\\\
- \\\\\\\
- function readfile( path, password )\\\\\\\
- local content, err = read( path )\\\\\\\
- if content then\\\\\\\
- return FileData( content, password )\\\\\\\
- end\\\\\\\
- return false, err\\\\\\\
- end\\\\\\\
- \\\\\\\
- function writefile( path, data, password )\\\\\\\
- if password then\\\\\\\
- data.password = password\\\\\\\
- end\\\\\\\
- return write( path, data:compile( ) )\\\\\\\
- end\\\\\\\
- \\\\\\\
- function appendfile( path, new, password, readpass )\\\\\\\
- local data, err = readfile( path, readpass or password )\\\\\\\
- if data then\\\\\\\
- data.content = data.content .. new\\\\\\\
- if password then\\\\\\\
- data.password = password\\\\\\\
- end\\\\\\\
- return write( path, password, data:compile( ) )\\\\\\\
- end\\\\\\\
- return false, err\\\\\\\
- end\\\\\\\
- \\\\\\\
- function getType( path )\\\\\\\
- if type( filecacheget( path ) ) ~= \\\\\\\"string\\\\\\\" and ( type( filecacheget( path ) ) == \\\\\\\"table\\\\\\\" or isDirectory( path ) ) then\\\\\\\
- filecacheset( path, { } )\\\\\\\
- return \\\\\\\"folder\\\\\\\"\\\\\\\
- end\\\\\\\
- local content, err = read( path )\\\\\\\
- if not content then\\\\\\\
- return \\\\\\\"unknown\\\\\\\"\\\\\\\
- end\\\\\\\
- local data = FileData( content )\\\\\\\
- if data.meta.type then\\\\\\\
- return data.meta.type\\\\\\\
- end\\\\\\\
- local ext = getExtension( path )\\\\\\\
- if #ext == 0 then return \\\\\\\"unknown\\\\\\\" end\\\\\\\
- return extensions[ext] or \\\\\\\".\\\\\\\" .. ext\\\\\\\
- end\\\\\\\
- \\\\\\\
- function getFileMetaValue( path, index )\\\\\\\
- if type( filecacheget( path ) ) == \\\\\\\"table\\\\\\\" or ( type( filecacheget( path ) ) ~= \\\\\\\"string\\\\\\\" and isDirectory( path ) ) then\\\\\\\
- return false, \\\\\\\"folders can't have metadata\\\\\\\"\\\\\\\
- end\\\\\\\
- local content, err = read( path )\\\\\\\
- if not content then\\\\\\\
- return false, err\\\\\\\
- end\\\\\\\
- return true, FileData( content ).meta[index]\\\\\\\
- end\\\\\\\
- \\\\\\\
- function isProtected( path )\\\\\\\
- if type( filecacheget( path ) ) == \\\\\\\"table\\\\\\\" then return false end\\\\\\\
- local content, err = read( path )\\\\\\\
- if content then\\\\\\\
- local fd = FileData( content )\\\\\\\
- return fd.meta.password\\\\\\\
- end\\\\\\\
- return false, err\\\\\\\
- end\\\\\\\
- \\\\\\\
- function newFile( path, content, password )\\\\\\\
- if filecacheget( path ) then return false, \\\\\\\"file exists\\\\\\\" end\\\\\\\
- if exists( path ) then return false, \\\\\\\"file exists\\\\\\\" end\\\\\\\
- local fd = FileData( content )\\\\\\\
- if password then\\\\\\\
- fd.password = password\\\\\\\
- end\\\\\\\
- write( path, fd:compile( ) )\\\\\\\
- return true\\\\\\\
- end\\\\\\\
- \\\\\\\
- -- directory methods\\\\\\\
- \\\\\\\
- function newDirectory( path )\\\\\\\
- if type( filecacheget( path ) ) == \\\\\\\"string\\\\\\\" then return false, \\\\\\\"file exists\\\\\\\" end\\\\\\\
- if filecacheget( path ) then return true end\\\\\\\
- local drive, err = getDrive( path )\\\\\\\
- if not drive then\\\\\\\
- return false, err\\\\\\\
- end\\\\\\\
- if drive.exists( path:gsub( \\\\\\\"^.-:\\\\\\\", \\\\\\\"\\\\\\\", 1 ) ) then\\\\\\\
- if drive.isDir( path:gsub( \\\\\\\"^.-:\\\\\\\", \\\\\\\"\\\\\\\", 1 ) ) then\\\\\\\
- filecacheset( path, { } )\\\\\\\
- else\\\\\\\
- return false, \\\\\\\"file exists\\\\\\\"\\\\\\\
- end\\\\\\\
- else\\\\\\\
- drive.makeDir( path:gsub( \\\\\\\"^.-:\\\\\\\", \\\\\\\"\\\\\\\", 1 ) )\\\\\\\
- filecacheset( path, { } )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function listFiles( path, sorter )\\\\\\\
- if type( filecacheget( path ) ) == \\\\\\\"string\\\\\\\" then return false, \\\\\\\"not a directory\\\\\\\" end\\\\\\\
- local drive, err = getDrive( path )\\\\\\\
- if not drive then\\\\\\\
- return false, err\\\\\\\
- end\\\\\\\
- if type( filecacheget( path ) ) == \\\\\\\"table\\\\\\\" or drive.isDir( path:gsub( \\\\\\\"^.-:\\\\\\\", \\\\\\\"\\\\\\\", 1 ) ) then\\\\\\\
- local files = drive.list( path:gsub( \\\\\\\"^.-:\\\\\\\", \\\\\\\"\\\\\\\", 1 ) )\\\\\\\
- if files and type( sorter ) == \\\\\\\"function\\\\\\\" then\\\\\\\
- sorter( files )\\\\\\\
- end\\\\\\\
- return files\\\\\\\
- end\\\\\\\
- return false, \\\\\\\"not a directory\\\\\\\"\\\\\\\
- end\\\\\\\
- \\\\\\\
- function emptyDirectory( path, filter )\\\\\\\
- \\\\\\\
- end\\\\\\\
- \\\\\\\
- -- existance checking\\\\\\\
- \\\\\\\
- function isFile( path ) -- bool isFile\\\\\\\
- if type( filecacheget( path ) ) == \\\\\\\"string\\\\\\\" then return true end\\\\\\\
- if type( filecacheget( path ) ) == \\\\\\\"table\\\\\\\" then return false end\\\\\\\
- local drive, err = getDrive( path )\\\\\\\
- if not drive then\\\\\\\
- return false\\\\\\\
- end\\\\\\\
- return drive.exists( path:gsub( \\\\\\\"^.-:\\\\\\\", \\\\\\\"\\\\\\\", 1 ) ) and not drive.isDir( path:gsub( \\\\\\\"^.-:\\\\\\\", \\\\\\\"\\\\\\\", 1 ) )\\\\\\\
- end\\\\\\\
- \\\\\\\
- function isDirectory( path )\\\\\\\
- if type( filecacheget( path ) ) == \\\\\\\"string\\\\\\\" then return false end\\\\\\\
- if type( filecacheget( path ) ) == \\\\\\\"table\\\\\\\" then return true end\\\\\\\
- local drive, err = getDrive( path )\\\\\\\
- if not drive then\\\\\\\
- return false\\\\\\\
- end\\\\\\\
- local isdir = drive.isDir( path:gsub( \\\\\\\"^.-:\\\\\\\", \\\\\\\"\\\\\\\", 1 ) )\\\\\\\
- if isdir then filecacheset( path, { } ) end\\\\\\\
- return isdir\\\\\\\
- end\\\\\\\
- \\\\\\\
- function exists( path )\\\\\\\
- if filecacheget( path ) then return true end\\\\\\\
- local drive, err = getDrive( path )\\\\\\\
- if not drive then\\\\\\\
- return false\\\\\\\
- end\\\\\\\
- return drive.exists( path:gsub( \\\\\\\"^.-:\\\\\\\", \\\\\\\"\\\\\\\", 1 ) )\\\\\\\
- end\\\\\\\
- \\\\\\\
- -- space checking\\\\\\\
- \\\\\\\
- function getSize( path )\\\\\\\
- local drive, err = getDrive( path )\\\\\\\
- if not drive then\\\\\\\
- return 0\\\\\\\
- end\\\\\\\
- return drive.getSize( path:gsub( \\\\\\\"^.-:\\\\\\\", \\\\\\\"\\\\\\\", 1 ) )\\\\\\\
- end\\\\\\\
- \\\\\\\
- function getSpace( path )\\\\\\\
- local drive, err = getDrive( path )\\\\\\\
- if not drive then\\\\\\\
- return 0\\\\\\\
- end\\\\\\\
- return drive.getFreeSpace( path:gsub( \\\\\\\"^.-:\\\\\\\", \\\\\\\"\\\\\\\", 1 ) )\\\\\\\
- end\\\\\\\
- \\\\\\\
- function isReadOnly( path )\\\\\\\
- local drive, err = getDrive( path )\\\\\\\
- if not drive then\\\\\\\
- return false\\\\\\\
- end\\\\\\\
- return drive.isReadOnly( path:gsub( \\\\\\\"^.-:\\\\\\\", \\\\\\\"\\\\\\\", 1 ) )\\\\\\\
- end\\\\\\\
- \\\\\\\
- -- mixed methods\\\\\\\
- \\\\\\\
- function delete( path )\\\\\\\
- local drive, err = getDrive( path )\\\\\\\
- if not drive then\\\\\\\
- return false, err\\\\\\\
- end\\\\\\\
- filecacheset( path, nil )\\\\\\\
- return drive.delete( path:gsub( \\\\\\\"^.-:\\\\\\\", \\\\\\\"\\\\\\\", 1 ) )\\\\\\\
- end\\\\\\\
- \\\\\\\
- function copy( path )\\\\\\\
- local drive, err = getDrive( path )\\\\\\\
- if not drive then\\\\\\\
- return false, err\\\\\\\
- end\\\\\\\
- if isFile( path ) then\\\\\\\
- local data, err = readfile( path )\\\\\\\
- if data then\\\\\\\
- clipboard.set( \\\\\\\"file\\\\\\\", { name = getName( path ), file = data } )\\\\\\\
- else\\\\\\\
- return false, err\\\\\\\
- end\\\\\\\
- elseif isDirectory( path ) then\\\\\\\
- local a = Archive( )\\\\\\\
- a:loadDirectory( path )\\\\\\\
- clipboard.set( \\\\\\\"file\\\\\\\", { name = getName( path ), file = a } )\\\\\\\
- else\\\\\\\
- return false, \\\\\\\"no such file\\\\\\\"\\\\\\\
- end\\\\\\\
- return true\\\\\\\
- end\\\\\\\
- \\\\\\\
- function cut( path )\\\\\\\
- local drive, err = getDrive( path )\\\\\\\
- if not drive then\\\\\\\
- return false, err\\\\\\\
- end\\\\\\\
- if isFile( path ) then\\\\\\\
- local data, err = readfile( path )\\\\\\\
- if data then\\\\\\\
- clipboard.set( \\\\\\\"file\\\\\\\", { name = getName( path ), file = data } )\\\\\\\
- else\\\\\\\
- return false, err\\\\\\\
- end\\\\\\\
- elseif isDirectory( path ) then\\\\\\\
- local a = Archive( )\\\\\\\
- a:loadDirectory( path )\\\\\\\
- clipboard.set( \\\\\\\"file\\\\\\\", { name = getName( path ), file = a } )\\\\\\\
- else\\\\\\\
- return false, \\\\\\\"no such file\\\\\\\"\\\\\\\
- end\\\\\\\
- delete( path )\\\\\\\
- return true\\\\\\\
- end\\\\\\\
- \\\\\\\
- function paste( up_path )\\\\\\\
- up_path = type( up_path ) == \\\\\\\"string\\\\\\\" and up_path or \\\\\\\"\\\\\\\"\\\\\\\
- local mode, data = clipboard.get( )\\\\\\\
- if mode == \\\\\\\"file\\\\\\\" then\\\\\\\
- local n = up_path .. \\\\\\\"/\\\\\\\" .. data.name\\\\\\\
- if filesystem.exists( n ) then\\\\\\\
- local name = up_path .. \\\\\\\"/\\\\\\\" .. getName( data.name, true ) .. \\\\\\\" - Copy (\\\\\\\"\\\\\\\
- local ext = getExtension( data.name )\\\\\\\
- if #ext > 0 then\\\\\\\
- ext = \\\\\\\".\\\\\\\" .. ext\\\\\\\
- end\\\\\\\
- local i = 1\\\\\\\
- while filesystem.exists( name .. i .. \\\\\\\")\\\\\\\" .. ext ) do\\\\\\\
- i = i + 1\\\\\\\
- end\\\\\\\
- n = name .. i .. \\\\\\\").\\\\\\\" .. ext\\\\\\\
- end\\\\\\\
- if data.file:type( ) == \\\\\\\"Archive\\\\\\\" then\\\\\\\
- data.file:saveDirectory( n )\\\\\\\
- else\\\\\\\
- write( n, data.file:compile( ) )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function rename( path, name )\\\\\\\
- local drive, err = getDrive( path )\\\\\\\
- if not drive then\\\\\\\
- return false, err\\\\\\\
- end\\\\\\\
- filecacheset( path, nil )\\\\\\\
- local path = path:gsub( \\\\\\\"^.-:\\\\\\\", \\\\\\\"\\\\\\\", 1 )\\\\\\\
- return drive.move( path, path:gsub( getName( path ) .. \\\\\\\"$\\\\\\\", name, 1 ) )\\\\\\\
- end\\\\\\\
- \\\\\\\
- function move( path, path2 )\\\\\\\
- local drive, err = getDrive( path )\\\\\\\
- if not drive then\\\\\\\
- return false, err\\\\\\\
- end\\\\\\\
- filecacheset( path, nil )\\\\\\\
- local path = path:gsub( \\\\\\\"^.-:\\\\\\\", \\\\\\\"\\\\\\\", 1 )\\\\\\\
- local path2 = path2:gsub( \\\\\\\"^.-:\\\\\\\", \\\\\\\"\\\\\\\", 1 )\\\\\\\
- return drive.move( path, path2 )\\\\\\\
- end\\\\\\\
- \\\\\\\
- local index = { }\\\\\\\
- local function updateInternal( )\\\\\\\
- while true do\\\\\\\
- local nindex = { }\\\\\\\
- local function listDir( p )\\\\\\\
- local files = listFiles( p )\\\\\\\
- for i = 1, #files do\\\\\\\
- local path = merge( p, files[i] )\\\\\\\
- nindex[path] = { name = files[i] }\\\\\\\
- if isDirectory( \\\\\\\"C:/\\\\\\\" .. path ) then\\\\\\\
- nindex[path].type = \\\\\\\"directory\\\\\\\"\\\\\\\
- listDir( path, nindex )\\\\\\\
- else\\\\\\\
- nindex[path].type = \\\\\\\"file\\\\\\\"\\\\\\\
- nindex[path].filetype = getType( \\\\\\\"C:/\\\\\\\" .. path )\\\\\\\
- end\\\\\\\
- coroutine.yield( )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- listDir \\\\\\\"\\\\\\\"\\\\\\\
- index = nindex\\\\\\\
- coroutine.yield \\\\\\\"full\\\\\\\"\\\\\\\
- end\\\\\\\
- end\\\\\\\
- local co = coroutine.create( updateInternal )\\\\\\\
- function updateIndex( )\\\\\\\
- local ok, data = coroutine.resume( co )\\\\\\\
- if not ok then\\\\\\\
- error( data, 0 )\\\\\\\
- end\\\\\\\
- return data == \\\\\\\"full\\\\\\\"\\\\\\\
- end\\\\\\\
- \\\\\\\
- function findType( _type )\\\\\\\
- local t = { }\\\\\\\
- for k, v in pairs( index ) do\\\\\\\
- if v.type == \\\\\\\"file\\\\\\\" and v.filetype == _type then\\\\\\\
- table.insert( t, {\\\\\\\
- path = k;\\\\\\\
- name = v.name;\\\\\\\
- type = v.type;\\\\\\\
- filetype = v.filetype;\\\\\\\
- } )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- return t\\\\\\\
- end\\\\\\\
- \\\\\\\
- function findName( name )\\\\\\\
- name = name:lower( ):gsub( \\\\\\\"%*\\\\\\\", \\\\\\\".*\\\\\\\" )\\\\\\\
- local t = { }\\\\\\\
- for k, v in pairs( index ) do\\\\\\\
- if k:lower( ):find( name ) then\\\\\\\
- table.insert( t, {\\\\\\\
- path = k;\\\\\\\
- name = v.name;\\\\\\\
- type = v.type;\\\\\\\
- filetype = v.filetype;\\\\\\\
- } )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- return t\\\\\\\
- end\\\", meta={\\\
- type = \\\"lib\\\",\\\
- }};[\\\"clipboard\\\"]={content=\\\"if NovaClipboard then\\\\\\\
- set, get = NovaClipboard.set, NovaClipboard.get\\\\\\\
- else\\\\\\\
- local a=false;function set(b,c)a={mode=b,data=c}end;function get()if a then return a.mode,a.data end end\\\\\\\
- export(\\\\\\\"NovaClipboard\\\\\\\",{set=set,get=get})\\\\\\\
- end\\\", meta={\\\
- type = \\\"lib\\\",\\\
- }};[\\\"Archive\\\"]={content=\\\"\\\\\\\
- require \\\\\\\"Drive\\\\\\\"\\\\\\\
- \\\\\\\
- local rfs = fs\\\\\\\
- \\\\\\\
- function Archive:Archive( savepath )\\\\\\\
- self.files = { }\\\\\\\
- self.savepath = savepath\\\\\\\
- \\\\\\\
- return self.public\\\\\\\
- end\\\\\\\
- \\\\\\\
- function Archive.public:loadDirectory( path )\\\\\\\
- if not filesystem.isDirectory( path ) then\\\\\\\
- return false, \\\\\\\"not a directory\\\\\\\"\\\\\\\
- end\\\\\\\
- local function loadDirectory( path, t )\\\\\\\
- local files = filesystem.listFiles( path )\\\\\\\
- for i = 1, #files do\\\\\\\
- if filesystem.isDirectory( path .. \\\\\\\"/\\\\\\\" .. files[i] ) then\\\\\\\
- t[files[i]] = { }\\\\\\\
- loadDirectory( path .. \\\\\\\"/\\\\\\\" .. files[i], t[files[i]] )\\\\\\\
- else\\\\\\\
- local filedata, err = filesystem.readfile( path .. \\\\\\\"/\\\\\\\" .. files[i] )\\\\\\\
- if filedata then\\\\\\\
- t[files[i]] = filedata:compile( )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- end\\\\\\\
- end\\\\\\\
- self.files = { }\\\\\\\
- loadDirectory( path, self.files )\\\\\\\
- return self.public\\\\\\\
- end\\\\\\\
- \\\\\\\
- function Archive.public:loadFile( path, password )\\\\\\\
- if not filesystem.isFile( path ) then\\\\\\\
- return false, \\\\\\\"not a file\\\\\\\"\\\\\\\
- end\\\\\\\
- local filedata, err = filesystem.readfile( path, password )\\\\\\\
- if not filedata then\\\\\\\
- return false, err\\\\\\\
- end\\\\\\\
- if filedata.meta.password then\\\\\\\
- return false, \\\\\\\"password protected\\\\\\\"\\\\\\\
- end\\\\\\\
- local files = textutils.unserialize( filedata.content )\\\\\\\
- if type( files ) == \\\\\\\"table\\\\\\\" then\\\\\\\
- self.files = files\\\\\\\
- return true\\\\\\\
- end\\\\\\\
- return false, \\\\\\\"corrupted archive\\\\\\\"\\\\\\\
- end\\\\\\\
- \\\\\\\
- function Archive.public:saveDirectory( path )\\\\\\\
- if filesystem.isFile( path ) then\\\\\\\
- return false, \\\\\\\"path exists\\\\\\\"\\\\\\\
- elseif not filesystem.exists( path ) then\\\\\\\
- filesystem.newDirectory( path )\\\\\\\
- end\\\\\\\
- local function saveFile( file, p )\\\\\\\
- if type( file ) == \\\\\\\"table\\\\\\\" then\\\\\\\
- for k, v in pairs( file ) do\\\\\\\
- saveFile( v, p .. \\\\\\\"/\\\\\\\" .. k )\\\\\\\
- end\\\\\\\
- else\\\\\\\
- filesystem.writefile( path .. p, FileData( file ) )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- saveFile( self.files, \\\\\\\"\\\\\\\" )\\\\\\\
- return true\\\\\\\
- end\\\\\\\
- \\\\\\\
- function Archive.public:saveFile( path, password )\\\\\\\
- if filesystem.exists( path ) then\\\\\\\
- return false, \\\\\\\"file exists\\\\\\\"\\\\\\\
- end\\\\\\\
- local content = \\\\\\\"--@meta[type]:\\\\\\\\\\\\\\\"archive\\\\\\\\\\\\\\\"\\\\\\\\n\\\\\\\" .. textutils.serialize( self.files )\\\\\\\
- local filedata = FileData( content )\\\\\\\
- if password then\\\\\\\
- filedata.password = password\\\\\\\
- end\\\\\\\
- return filesystem.writefile( path, filedata )\\\\\\\
- end\\\\\\\
- \\\\\\\
- function Archive.public:createDrive( )\\\\\\\
- \\\\\\\
- local fs = { }\\\\\\\
- local function splitPath( path )\\\\\\\
- local parts = { }\\\\\\\
- for part in path:gmatch \\\\\\\"([^/]+)\\\\\\\" do\\\\\\\
- table.insert( parts, part )\\\\\\\
- end\\\\\\\
- return parts\\\\\\\
- end\\\\\\\
- local function getDir( path )\\\\\\\
- if type( path ) ~= \\\\\\\"string\\\\\\\" then\\\\\\\
- return false, \\\\\\\"string expected\\\\\\\"\\\\\\\
- end\\\\\\\
- if path == \\\\\\\"\\\\\\\" or path == \\\\\\\"/\\\\\\\" then\\\\\\\
- return true, { [\\\\\\\"\\\\\\\"] = self.files }, \\\\\\\"\\\\\\\"\\\\\\\
- end\\\\\\\
- local parts = splitPath( path )\\\\\\\
- local f = self.files\\\\\\\
- for i = 1, #parts - 1 do\\\\\\\
- if type( f[parts[i]] ) == \\\\\\\"nil\\\\\\\" then\\\\\\\
- f[parts[i]] = { }\\\\\\\
- end\\\\\\\
- if type( f[parts[i]] ) == \\\\\\\"table\\\\\\\" then\\\\\\\
- f = f[parts[i]]\\\\\\\
- else\\\\\\\
- return false, \\\\\\\"invalid path\\\\\\\"\\\\\\\
- end\\\\\\\
- end\\\\\\\
- return true, f, parts[#parts]\\\\\\\
- end\\\\\\\
- \\\\\\\
- function fs.open( path, mode )\\\\\\\
- local ok, data, file = getDir( path )\\\\\\\
- if not ok then\\\\\\\
- return false, data\\\\\\\
- end\\\\\\\
- if type( data[file] ) == \\\\\\\"table\\\\\\\" then\\\\\\\
- return false, \\\\\\\"cannot open directory\\\\\\\"\\\\\\\
- elseif mode == \\\\\\\"r\\\\\\\" and data[file] == nil then\\\\\\\
- return false, \\\\\\\"file doesn't exist\\\\\\\"\\\\\\\
- end\\\\\\\
- local handle = { }\\\\\\\
- local open = true\\\\\\\
- function handle.close( )\\\\\\\
- open = false\\\\\\\
- end\\\\\\\
- \\\\\\\
- if mode == \\\\\\\"w\\\\\\\" then\\\\\\\
- data[file] = \\\\\\\"\\\\\\\"\\\\\\\
- elseif mode == \\\\\\\"a\\\\\\\" then\\\\\\\
- data[file] = data[file] or \\\\\\\"\\\\\\\"\\\\\\\
- elseif mode ~= \\\\\\\"r\\\\\\\" then\\\\\\\
- return false, \\\\\\\"unsupported mode\\\\\\\"\\\\\\\
- end\\\\\\\
- local filedata = FileData( data[file] )\\\\\\\
- \\\\\\\
- if mode == \\\\\\\"w\\\\\\\" or mode == \\\\\\\"a\\\\\\\" then\\\\\\\
- function handle.write( str )\\\\\\\
- if not open then return end\\\\\\\
- filedata.content = filedata.content .. tostring( str )\\\\\\\
- end\\\\\\\
- function handle.writeLine( )\\\\\\\
- if not open then return end\\\\\\\
- filedata.content = filedata.content .. tostring( str ) .. \\\\\\\"\\\\\\\\n\\\\\\\"\\\\\\\
- end\\\\\\\
- function handle.flush( )\\\\\\\
- if not open then return end\\\\\\\
- data[file] = filedata:compile( )\\\\\\\
- if self.savepath then\\\\\\\
- filesystem.delete( self.savepath )\\\\\\\
- self.public:saveFile( self.savepath )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- function handle.close( )\\\\\\\
- if not open then return end\\\\\\\
- handle.flush( )\\\\\\\
- open = false\\\\\\\
- end\\\\\\\
- elseif mode == \\\\\\\"r\\\\\\\" then\\\\\\\
- local lcontent = filedata.content\\\\\\\
- function handle.readLine( )\\\\\\\
- if not open then return end\\\\\\\
- local line = lcontent:match \\\\\\\"(.-)\\\\\\\\n\\\\\\\"\\\\\\\
- if line then\\\\\\\
- lcontent = lcontent:gsub( \\\\\\\".-\\\\\\\\n\\\\\\\", \\\\\\\"\\\\\\\" )\\\\\\\
- else\\\\\\\
- if #lcontent > 0 then\\\\\\\
- line = lcontent\\\\\\\
- lcontent = \\\\\\\"\\\\\\\"\\\\\\\
- else\\\\\\\
- line = nil\\\\\\\
- end\\\\\\\
- end\\\\\\\
- return line\\\\\\\
- end\\\\\\\
- function handle.readAll( )\\\\\\\
- if not open then return end\\\\\\\
- return filedata.content\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- return handle\\\\\\\
- end\\\\\\\
- \\\\\\\
- function fs.list( path )\\\\\\\
- local ok, data, file = getDir( path )\\\\\\\
- if not ok then\\\\\\\
- return false, data\\\\\\\
- end\\\\\\\
- if type( data[file] ) ~= \\\\\\\"table\\\\\\\" then\\\\\\\
- return false, \\\\\\\"not a directory\\\\\\\"\\\\\\\
- end\\\\\\\
- local t = { }\\\\\\\
- for k, v in pairs( data[file] ) do\\\\\\\
- t[#t+1] = k\\\\\\\
- end\\\\\\\
- return t\\\\\\\
- end\\\\\\\
- function fs.exists( p )\\\\\\\
- local ok, data, file = getDir( p )\\\\\\\
- if not ok then\\\\\\\
- return false, data\\\\\\\
- end\\\\\\\
- return data[file] ~= nil\\\\\\\
- end\\\\\\\
- function fs.isDir( p )\\\\\\\
- local ok, data, file = getDir( p )\\\\\\\
- if not ok then\\\\\\\
- return false, data\\\\\\\
- end\\\\\\\
- return type( data[file] ) == \\\\\\\"table\\\\\\\"\\\\\\\
- end\\\\\\\
- function fs.isReadOnly( p )\\\\\\\
- return false\\\\\\\
- end\\\\\\\
- function fs.getDrive( )\\\\\\\
- return \\\\\\\"archive\\\\\\\"\\\\\\\
- end\\\\\\\
- function fs.getSize( path )\\\\\\\
- local ok, data, file = getDir( path )\\\\\\\
- if not ok then\\\\\\\
- return false, data\\\\\\\
- end\\\\\\\
- if not data[file] then\\\\\\\
- return false, \\\\\\\"file doesn't exist\\\\\\\"\\\\\\\
- end\\\\\\\
- if type( data[file] ) == \\\\\\\"table\\\\\\\" then\\\\\\\
- local n = 0\\\\\\\
- for k, v in pairs( data[file] ) do\\\\\\\
- n = n + fs.getSize( path .. \\\\\\\"/\\\\\\\" .. k )\\\\\\\
- end\\\\\\\
- return n\\\\\\\
- end\\\\\\\
- return #data[file] + #path -- each character is a byte right? 2^8 (a byte) = 256, ascii is 256 characters\\\\\\\
- end\\\\\\\
- function fs.getFreeSpace( )\\\\\\\
- return math.huge\\\\\\\
- end\\\\\\\
- function fs.makeDir( path )\\\\\\\
- local ok, data, file = getDir( path )\\\\\\\
- if not ok then\\\\\\\
- return false, data\\\\\\\
- end\\\\\\\
- if type( data[file] ) == \\\\\\\"string\\\\\\\" then\\\\\\\
- return false, \\\\\\\"path is a file\\\\\\\"\\\\\\\
- end\\\\\\\
- data[file] = data[file] or { }\\\\\\\
- if self.savepath then\\\\\\\
- filesystem.delete( self.savepath )\\\\\\\
- self.public:saveFile( self.savepath )\\\\\\\
- end\\\\\\\
- return true\\\\\\\
- end\\\\\\\
- function fs.move( path1, path2 )\\\\\\\
- local ok, data, file = getDir( path )\\\\\\\
- if not ok then\\\\\\\
- return false, data\\\\\\\
- end\\\\\\\
- local ok, data2, file2 = getDir( path )\\\\\\\
- if not ok then\\\\\\\
- return false, data2\\\\\\\
- end\\\\\\\
- if data[file] then\\\\\\\
- data2[file2] = data[file]\\\\\\\
- data[file] = nil\\\\\\\
- end\\\\\\\
- if self.savepath then\\\\\\\
- filesystem.delete( self.savepath )\\\\\\\
- self.public:saveFile( self.savepath )\\\\\\\
- end\\\\\\\
- return true\\\\\\\
- end\\\\\\\
- function fs.copy( path1, path2 )\\\\\\\
- local ok, data, file = getDir( path )\\\\\\\
- if not ok then\\\\\\\
- return false, data\\\\\\\
- end\\\\\\\
- local ok, data2, file2 = getDir( path )\\\\\\\
- if not ok then\\\\\\\
- return false, data2\\\\\\\
- end\\\\\\\
- if data[file] then\\\\\\\
- data2[file2] = data[file]\\\\\\\
- end\\\\\\\
- if self.savepath then\\\\\\\
- filesystem.delete( self.savepath )\\\\\\\
- self.public:saveFile( self.savepath )\\\\\\\
- end\\\\\\\
- return true\\\\\\\
- end\\\\\\\
- function fs.delete( path )\\\\\\\
- local ok, data, file = getDir( path )\\\\\\\
- if not ok then\\\\\\\
- return false, data\\\\\\\
- end\\\\\\\
- data[file] = nil\\\\\\\
- if self.savepath then\\\\\\\
- filesystem.delete( self.savepath )\\\\\\\
- self.public:saveFile( self.savepath )\\\\\\\
- end\\\\\\\
- return true\\\\\\\
- end\\\\\\\
- function fs.find( path )\\\\\\\
- error( \\\\\\\"not supported\\\\\\\", 2 )\\\\\\\
- end\\\\\\\
- fs.combine = rfs.combine\\\\\\\
- fs.getDir = rfs.getDir\\\\\\\
- fs.getName = rfs.getName\\\\\\\
- \\\\\\\
- return Drive( fs )\\\\\\\
- end\\\", meta={\\\
- type = \\\"class\\\",\\\
- }};[\\\"main\\\"]={content=\\\"\\\\\\\
- require \\\\\\\"filesystem\\\\\\\"\\\\\\\
- \\\\\\\
- for k, v in pairs( filesystem ) do\\\\\\\
- export( k, v )\\\\\\\
- end\\\\\\\
- \\\\\\\
- export \\\\\\\"Archive\\\\\\\"\\\\\\\
- export \\\\\\\"FileData\\\\\\\"\\\\\\\
- export \\\\\\\"Drive\\\\\\\"\\\", meta={}};[\\\"Drive\\\"]={content=\\\"\\\\\\\
- function Drive:Drive( methods ) -- table methods\\\\\\\
- if type( methods ) ~= \\\\\\\"table\\\\\\\" then\\\\\\\
- error( \\\\\\\"expected table methods\\\\\\\", 2 )\\\\\\\
- end\\\\\\\
- for k, v in pairs( fs ) do\\\\\\\
- if not methods[k] then\\\\\\\
- error( \\\\\\\"expected \\\\\\\" .. k .. \\\\\\\" method in table\\\\\\\", 2 )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- self.methods = methods\\\\\\\
- \\\\\\\
- return self.public\\\\\\\
- end\\\\\\\
- \\\\\\\
- function Drive.meta:index( key )\\\\\\\
- return self.methods[key]\\\\\\\
- end\\\\\\\
- \\\\\\\
- for k, v in pairs( fs ) do\\\\\\\
- Drive.public( k )\\\\\\\
- Drive.public[k].write = false\\\\\\\
- Drive.public[k].read = function( self )\\\\\\\
- return self.methods[k]\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- local function combine( p1, p2 )\\\\\\\
- if type( p2 ) ~= \\\\\\\"string\\\\\\\" then\\\\\\\
- return error \\\\\\\"expected string\\\\\\\"\\\\\\\
- end\\\\\\\
- return fs.combine( p1, p2 )\\\\\\\
- end\\\\\\\
- \\\\\\\
- function Drive.static.redirect( path )\\\\\\\
- \\\\\\\
- local mfs = setmetatable( { }, { __index = fs } )\\\\\\\
- mfs.open = function( p, mode )\\\\\\\
- return fs.open( combine( path, p ), mode )\\\\\\\
- end\\\\\\\
- mfs.list = function( p )\\\\\\\
- return fs.list( combine( path, p ) )\\\\\\\
- end\\\\\\\
- mfs.exists = function( p )\\\\\\\
- return fs.exists( combine( path, p ) )\\\\\\\
- end\\\\\\\
- mfs.isDir = function( p )\\\\\\\
- return fs.isDir( combine( path, p ) )\\\\\\\
- end\\\\\\\
- mfs.isReadOnly = function( p )\\\\\\\
- return fs.isReadOnly( combine( path, p ) )\\\\\\\
- end\\\\\\\
- mfs.getDrive = function( p )\\\\\\\
- return fs.getDrive( combine( path, p ) )\\\\\\\
- end\\\\\\\
- mfs.getSize = function( p )\\\\\\\
- if mfs.isDir( p ) then\\\\\\\
- local files = mfs.list( p )\\\\\\\
- local n = 0\\\\\\\
- for i = 1, #files do\\\\\\\
- n = n + mfs.getSize( p .. \\\\\\\"/\\\\\\\" .. files[i] )\\\\\\\
- end\\\\\\\
- return n\\\\\\\
- else\\\\\\\
- return fs.getSize( combine( path, p ) )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- mfs.getFreeSpace = function( p )\\\\\\\
- return fs.getFreeSpace( combine( path, p ) )\\\\\\\
- end\\\\\\\
- mfs.makeDir = function( p )\\\\\\\
- return fs.makeDir( combine( path, p ) )\\\\\\\
- end\\\\\\\
- mfs.move = function( p1, p2 )\\\\\\\
- return fs.move( combine( path, p1 ), combine( path, p2 ) )\\\\\\\
- end\\\\\\\
- mfs.copy = function( p1, p2 )\\\\\\\
- return fs.copy( combine( path, p1 ), combine( path, p2 ) )\\\\\\\
- end\\\\\\\
- mfs.delete = function( p )\\\\\\\
- return fs.delete( combine( path, p ) )\\\\\\\
- end\\\\\\\
- mfs.find = function( p )\\\\\\\
- return fs.find( combine( path, p ) )\\\\\\\
- end\\\\\\\
- \\\\\\\
- return Drive( mfs )\\\\\\\
- \\\\\\\
- end\\\", meta={\\\
- type = \\\"class\\\",\\\
- }};[\\\"FileData\\\"]={content=\\\"\\\\\\\
- FileData.public \\\\\\\"content\\\\\\\" \\\\\\\"string\\\\\\\"\\\\\\\
- FileData.public \\\\\\\"meta\\\\\\\"\\\\\\\
- FileData.public \\\\\\\"password\\\\\\\" \\\\\\\"string\\\\\\\"\\\\\\\
- \\\\\\\
- local function parseMeta( content )\\\\\\\
- local meta = { }\\\\\\\
- while content:sub( 1, 7 ) == \\\\\\\"--@meta\\\\\\\" do\\\\\\\
- local name = content:match \\\\\\\"%-%-@meta%[(%w+)%]\\\\\\\"\\\\\\\
- if name then\\\\\\\
- local value = content:match \\\\\\\"%-%-@meta%[%w+%]:(.-)\\\\\\\\n\\\\\\\"\\\\\\\
- if value then\\\\\\\
- content = content:gsub( \\\\\\\"%-%-@meta%[%w+%]:(.-)\\\\\\\\n\\\\\\\", \\\\\\\"\\\\\\\", 1 )\\\\\\\
- local f, err = loadstring( \\\\\\\"return \\\\\\\" .. value )\\\\\\\
- if f then\\\\\\\
- local ok, data = pcall( f )\\\\\\\
- if ok then\\\\\\\
- meta[name] = data\\\\\\\
- end\\\\\\\
- end\\\\\\\
- else\\\\\\\
- break\\\\\\\
- end\\\\\\\
- else\\\\\\\
- break\\\\\\\
- end\\\\\\\
- end\\\\\\\
- return meta, content\\\\\\\
- end\\\\\\\
- \\\\\\\
- function FileData.public.meta:write( value )\\\\\\\
- if type( value ) == \\\\\\\"table\\\\\\\" then\\\\\\\
- self.metadata = value\\\\\\\
- else\\\\\\\
- error( \\\\\\\"expected table meta\\\\\\\", 3 )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- function FileData.public.meta:read( )\\\\\\\
- return self.metadata\\\\\\\
- end\\\\\\\
- \\\\\\\
- function FileData:FileData( content, password ) -- string content\\\\\\\
- \\\\\\\
- self.metadata, self.content = parseMeta( content )\\\\\\\
- \\\\\\\
- if self.metadata.protected then\\\\\\\
- if password then\\\\\\\
- self.content = encryption.decrypt( self.content, password )\\\\\\\
- end\\\\\\\
- self.metadata.password = nil\\\\\\\
- end\\\\\\\
- \\\\\\\
- self.password = false\\\\\\\
- \\\\\\\
- return self.public\\\\\\\
- end\\\\\\\
- \\\\\\\
- function FileData.public:compile( )\\\\\\\
- local content = self.content\\\\\\\
- if self.password then\\\\\\\
- content = encryption.encrypt( content, self.password )\\\\\\\
- self.metadata.protected = true\\\\\\\
- end\\\\\\\
- local metadata = \\\\\\\"\\\\\\\"\\\\\\\
- for k, v in pairs( self.metadata ) do\\\\\\\
- metadata = metadata .. \\\\\\\"--@meta[\\\\\\\" .. k ..\\\\\\\"]:\\\\\\\" .. textutils.serialize( v ) .. \\\\\\\"\\\\\\\\n\\\\\\\"\\\\\\\
- end\\\\\\\
- return metadata .. \\\\\\\"\\\\\\\" .. content\\\\\\\
- end\\\", meta={\\\
- type = \\\"class\\\",\\\
- }};}\\\
- --/end of files\\\
- local autorun = {\\\"Archive\\\";\\\"Drive\\\";\\\"FileData\\\";\\\"main\\\"}\\\
- --@meta[type]:\\\"class\\\"\\\
- \\\
- -- Swift Class Library\\\
- -- Made by awsumben13\\\
- -- Feel free to use this in any of your projects but keep this at the top and give credit where necessary\\\
- \\\
- --[[\\\
- This class library offers:\\\
- Inheritance \\\"Class:extends( other class )\\\"\\\
- Initialiser methods \\\"function ClassName:ClassName( )\\\"\\\
- Public / private variables with customisable read/write access \\\"Class.public.x.write = false\\\"\\\
- Static variables ( accessed from the class object ) \\\"Class.static.x = 5\\\"\\\
- Custom metamethods including __index and __newindex \\\"Class.meta.index = { }\\\"\\\
- ]]\\\
- \\\
- local class = { }\\\
- \\\
- function class.new( name )\\\
- \\\
- local object = { }\\\
- local public = { }\\\
- \\\
- object.public = { }\\\
- object.private = { }\\\
- object.static = { }\\\
- object.name = name\\\
- object.extends = false\\\
- object.class = public\\\
- \\\
- public.name = name\\\
- \\\
- local customindex = false\\\
- local customnewindex = false\\\
- local objectmeta = { }\\\
- \\\
- function public:new( ... )\\\
- \\\
- local ob = { }\\\
- local pb = { }\\\
- \\\
- ob.class = public\\\
- ob.public = pb\\\
- \\\
- setmetatable( ob, {\\\
- __index = object.private;\\\
- } )\\\
- \\\
- function pb:type( full )\\\
- return ob.class:type( full )\\\
- end\\\
- \\\
- function pb:typeOf( class )\\\
- return ob.class:typeOf( class )\\\
- end\\\
- \\\
- local obmeta = { }\\\
- obmeta.__index = function( _, k )\\\
- if object.public[k] then\\\
- if object.public[k].read then\\\
- local val\\\
- if customindex then\\\
- if type( customindex ) == \\\"function\\\" then\\\
- return customindex( ob, k )\\\
- else\\\
- return customindex[k]\\\
- end\\\
- elseif type( object.public[k].read ) == \\\"function\\\" then\\\
- val = object.public[k].read( ob )\\\
- elseif object.public[k].value ~= nil then\\\
- val = object.public[k].value\\\
- else\\\
- val = ob[k]\\\
- end\\\
- if type( val ) == \\\"function\\\" then\\\
- return function( self, ... )\\\
- if self == pb then\\\
- return val( ob, ... )\\\
- end\\\
- return val( self, ... )\\\
- end\\\
- end\\\
- return val\\\
- else\\\
- error( \\\"variable has no read access\\\", 2 )\\\
- end\\\
- elseif customindex then\\\
- if type( customindex ) == \\\"function\\\" then\\\
- return customindex( ob, k )\\\
- else\\\
- return customindex[k]\\\
- end\\\
- else\\\
- error( \\\"no such variable \\\\\\\"\\\" .. tostring( k ) .. \\\"\\\\\\\"\\\", 2 )\\\
- end\\\
- end;\\\
- obmeta.__newindex = function( _, k, v )\\\
- if object.public[k] then\\\
- if object.public[k].write then\\\
- if customnewindex then\\\
- if type( customnewindex ) == \\\"function\\\" then\\\
- return customnewindex( ob, k, v )\\\
- else\\\
- customnewindex[k] = v\\\
- end\\\
- elseif type( object.public[k].write ) == \\\"function\\\" then\\\
- object.public[k].write( ob, v )\\\
- else\\\
- ob[k] = v\\\
- end\\\
- else\\\
- error( \\\"variable has no write access\\\", 2 )\\\
- end\\\
- else\\\
- error( \\\"no such variable \\\\\\\"\\\" .. tostring( k ) .. \\\"\\\\\\\"\\\", 2 )\\\
- end\\\
- end;\\\
- obmeta.__tostring = function( )\\\
- return object.name\\\
- end;\\\
- obmeta.__metatable = \\\"SwiftClassObject\\\";\\\
- \\\
- for k, v in pairs( objectmeta ) do\\\
- obmeta[\\\"__\\\" .. tostring( k )] = v\\\
- end\\\
- \\\
- setmetatable( pb, obmeta )\\\
- \\\
- local c = object\\\
- while true do\\\
- if type( c.private[c.name] ) == \\\"function\\\" then\\\
- return c.private[c.name]( ob, ... )\\\
- end\\\
- if c.extends then\\\
- c = c.extends\\\
- else\\\
- break\\\
- end\\\
- end\\\
- \\\
- return pb\\\
- end\\\
- \\\
- function public:type( full )\\\
- local str = \\\"\\\"\\\
- if full then\\\
- local c = object.extends\\\
- while c do\\\
- str = c.name .. \\\".\\\" .. str\\\
- c = c.extends\\\
- end\\\
- end\\\
- return str .. object.name\\\
- end\\\
- \\\
- function public:typeOf( other )\\\
- if type( other ) == \\\"table\\\" then\\\
- if getmetatable( other ) == \\\"SwiftClass\\\" then\\\
- local ob = object\\\
- while ob do\\\
- if ob.class == other then\\\
- return true\\\
- end\\\
- ob = ob.extends\\\
- end\\\
- end\\\
- end\\\
- return false\\\
- end\\\
- \\\
- function public:extends( ob )\\\
- ob:extend( object )\\\
- end\\\
- \\\
- function public:extend( ob )\\\
- setmetatable( ob.static, { __index = object.static } )\\\
- setmetatable( ob.public, { __index = object.public } )\\\
- setmetatable( ob.private, { __index = object.private } )\\\
- ob.extends = object\\\
- end\\\
- \\\
- local meta = { }\\\
- meta.__index = function( _, k )\\\
- if k == \\\"static\\\" then\\\
- return setmetatable( { }, {\\\
- __newindex = function( _, k, v )\\\
- object.static[k] = v\\\
- end;\\\
- __metatable = { };\\\
- } )\\\
- elseif k == \\\"public\\\" then\\\
- return setmetatable( { }, {\\\
- __newindex = function( _, k, v )\\\
- object.public[k] = {\\\
- read = true;\\\
- write = false;\\\
- value = v;\\\
- }\\\
- end;\\\
- __call = function( _, k )\\\
- object.public[k] = {\\\
- read = true;\\\
- write = true;\\\
- value = nil;\\\
- }\\\
- return function( _type )\\\
- local types = { _type }\\\
- object.public[k].write = function( ob, value )\\\
- for i = 1, #types do\\\
- if class.typeOf( value, types[i] ) then\\\
- ob[k] = value\\\
- return\\\
- end\\\
- end\\\
- if class.type( types[1] ) == \\\"Class\\\" then\\\
- error( \\\"expected \\\" .. types[1]:type( ) .. \\\" \\\" .. k, 3 )\\\
- else\\\
- error( \\\"expected \\\" .. types[1] .. \\\" \\\" .. k, 3 )\\\
- end\\\
- end\\\
- return function( _type )\\\
- table.insert( types, _type )\\\
- end\\\
- end\\\
- end;\\\
- __index = function( _, k )\\\
- if object.public[k] then\\\
- return setmetatable( { }, {\\\
- __newindex = function( _, name, v )\\\
- if name == \\\"read\\\" then\\\
- if type( v ) == \\\"boolean\\\" or type( v ) == \\\"function\\\" then\\\
- object.public[k].read = v\\\
- else\\\
- error( \\\"invalid modifier value\\\", 2 )\\\
- end\\\
- elseif name == \\\"write\\\" then\\\
- if type( v ) == \\\"boolean\\\" or type( v ) == \\\"function\\\" then\\\
- object.public[k].write = v\\\
- else\\\
- error( \\\"invalid modifier value\\\", 2 )\\\
- end\\\
- else\\\
- error( \\\"invalid modifier name\\\", 2 )\\\
- end\\\
- end;\\\
- __metatable = { };\\\
- } )\\\
- else\\\
- error( \\\"public index \\\" .. tostring( k ) .. \\\" not found\\\", 2 )\\\
- end\\\
- end;\\\
- __metatable = { };\\\
- } )\\\
- elseif k == \\\"meta\\\" then\\\
- return setmetatable( { }, {\\\
- __index = function( _, k )\\\
- if k == \\\"index\\\" then\\\
- return customindex\\\
- elseif k == \\\"newindex\\\" then\\\
- return customnewindex\\\
- else\\\
- return objectmeta[k]\\\
- end\\\
- end;\\\
- __newindex = function( _, k, v )\\\
- if k == \\\"metatable\\\" then\\\
- error( \\\"cannot change this metamethod\\\", 2 )\\\
- elseif k == \\\"index\\\" then\\\
- if type( v ) == \\\"function\\\" or type( v ) == \\\"table\\\" or v == nil then\\\
- customindex = v\\\
- else\\\
- error( \\\"cannot use type \\\" .. type( v ) .. \\\" for index metamethod\\\", 2 )\\\
- end\\\
- elseif k == \\\"newindex\\\" then\\\
- if type( v ) == \\\"function\\\" or type( v ) == \\\"table\\\" or v == nil then\\\
- customnewindex = v\\\
- else\\\
- error( \\\"cannot use type \\\" .. type( v ) .. \\\" for newindex metamethod\\\", 2 )\\\
- end\\\
- else\\\
- objectmeta[k] = v\\\
- end\\\
- end;\\\
- __metatable = { };\\\
- } )\\\
- else\\\
- return object.static[k]\\\
- end\\\
- end\\\
- meta.__newindex = function( _, k, v )\\\
- object.private[k] = v\\\
- end;\\\
- meta.__call = function( self, ... )\\\
- return self:new( ... )\\\
- end;\\\
- meta.__totring = function( )\\\
- return \\\"Class\\\"\\\
- end;\\\
- meta.__metatable = \\\"SwiftClass\\\"\\\
- \\\
- setmetatable( public, meta )\\\
- \\\
- return public\\\
- end\\\
- \\\
- function class.public( name )\\\
- local object = class.new( name )\\\
- getfenv( )[name] = object\\\
- end\\\
- \\\
- function class.type( object )\\\
- if type( object ) == \\\"table\\\" then\\\
- if getmetatable( object ) == \\\"SwiftClass\\\" then\\\
- return \\\"Class\\\"\\\
- end\\\
- if getmetatable( object ) == \\\"SwiftClassObject\\\" then\\\
- return object:type( )\\\
- end\\\
- end\\\
- return type( object )\\\
- end\\\
- \\\
- function class.typeOf( object, ... )\\\
- local types = { ... }\\\
- for i = 1, #types do\\\
- if type( object ) == \\\"table\\\" and getmetatable( object ) == \\\"SwiftClassObject\\\" then\\\
- if object:typeOf( types[i] ) then\\\
- return types[i]\\\
- end\\\
- elseif type( object ) == \\\"table\\\" and getmetatable( object ) == \\\"SwiftClass\\\" then\\\
- if types[i] == \\\"Class\\\" then\\\
- return \\\"Class\\\"\\\
- end\\\
- else\\\
- if type( object ) == types[i] then\\\
- return types[i]\\\
- end\\\
- end\\\
- end\\\
- return false\\\
- end\\\
- \\\
- setmetatable( class, { __call = function( _, ... ) return class.new( ... ) end } )\\\
- local args = { ... }\\\
- local global = getfenv( )\\\
- local custom = setmetatable( { class = class, ARGS = args }, { __index = global } )\\\
- function custom.export( variable, value )\\\
- if custom[variable] ~= nil or value then\\\
- global[variable] = custom[variable] == nil and value or custom[variable]\\\
- end\\\
- end\\\
- local required = { }\\\
- local function require( file, ... )\\\
- if not required[file] and files[file] then\\\
- required[file] = true\\\
- if files[file].meta.type == \\\"class\\\" then\\\
- custom[file] = class( file )\\\
- end\\\
- local fenv = setmetatable( { shared = custom }, { __index = custom } )\\\
- local f, err = loadstring( files[file].content, file .. \\\".lua\\\" )\\\
- if f then\\\
- setfenv( f, fenv )\\\
- local ok, data = pcall( f )\\\
- if ok then\\\
- if files[file].meta.type == \\\"lib\\\" then\\\
- if data ~= nil then\\\
- custom[file] = data\\\
- return data\\\
- else\\\
- local t = { }\\\
- for k, v in pairs( fenv ) do\\\
- t[k] = v\\\
- end\\\
- custom[file] = t\\\
- return t\\\
- end\\\
- end\\\
- return data\\\
- else\\\
- error( data, 0 )\\\
- end\\\
- else\\\
- error( err, 0 )\\\
- end\\\
- end\\\
- end\\\
- custom.require = require\\\
- \\\
- for i = 1, #autorun do\\\
- require( autorun[i] )\\\
- end\",\
- [\"NovaUI.lua\"]=\"--[[type=\\\"executable_package\\\", name=\\\"NovaUI\\\"]]\\\
- local files = {[\\\"shader\\\"]={content=\\\"\\\\\\\
- local maps = {}\\\\\\\
- maps.darken = {\\\\\\\
- [colours.white] = colours.lightGrey, [colours.orange] = colours.red, [colours.magenta] = colours.purple, [colours.lightBlue] = colours.cyan;\\\\\\\
- [colours.yellow] = colours.orange, [colours.lime] = colours.green, [colours.pink] = colours.magenta, [colours.grey] = colours.black;\\\\\\\
- [colours.lightGrey] = colours.grey, [colours.cyan] = colours.blue, [colours.purple] = colours.black, [colours.blue] = colours.black;\\\\\\\
- [colours.brown] = colours.black, [colours.green] = colours.black, [colours.red] = colours.brown, [colours.black] = colours.black;\\\\\\\
- }\\\\\\\
- maps.lighten = {\\\\\\\
- [colours.white] = colours.white, [colours.orange] = colours.yellow, [colours.magenta] = colours.pink, [colours.lightBlue] = colours.white;\\\\\\\
- [colours.yellow] = colours.white, [colours.lime] = colours.white, [colours.pink] = colours.white, [colours.grey] = colours.lightGrey;\\\\\\\
- [colours.lightGrey] = colours.white, [colours.cyan] = colours.lightBlue, [colours.purple] = colours.magenta, [colours.blue] = colours.cyan;\\\\\\\
- [colours.brown] = colours.red, [colours.green] = colours.lime, [colours.red] = colours.orange, [colours.black] = colours.grey;\\\\\\\
- }\\\\\\\
- maps.greyscale = {\\\\\\\
- [colours.white] = 1, [colours.orange] = 256, [colours.magenta] = 256, [colours.lightBlue] = 256;\\\\\\\
- [colours.yellow] = 1, [colours.lime] = 256, [colours.pink] = 1, [colours.grey] = 256;\\\\\\\
- [colours.lightGrey] = 256, [colours.cyan] = 128, [colours.purple] = 128, [colours.blue] = 32768;\\\\\\\
- [colours.brown] = 32768, [colours.green] = 128, [colours.red] = 128, [colours.black] = 32768;\\\\\\\
- }\\\\\\\
- maps.sepia = {\\\\\\\
- [colours.white] = 1, [colours.orange] = 2, [colours.magenta] = 2, [colours.lightBlue] = 2;\\\\\\\
- [colours.yellow] = 1, [colours.lime] = 2, [colours.pink] = 1, [colours.grey] = 2;\\\\\\\
- [colours.lightGrey] = 2, [colours.cyan] = 16, [colours.purple] = 16, [colours.blue] = 4096;\\\\\\\
- [colours.brown] = 4096, [colours.green] = 16, [colours.red] = 16, [colours.black] = 4096;\\\\\\\
- }\\\\\\\
- \\\\\\\
- function darken( col )\\\\\\\
- return maps.darken[col] or 0\\\\\\\
- end\\\\\\\
- \\\\\\\
- function lighten( col )\\\\\\\
- return maps.lighten[col] or 0\\\\\\\
- end\\\\\\\
- \\\\\\\
- function greyscale( col )\\\\\\\
- return maps.greyscale[col] or 0\\\\\\\
- end\\\\\\\
- \\\\\\\
- function sepia( col )\\\\\\\
- return maps.sepia[col] or 0\\\\\\\
- end\\\", meta={\\\
- type = \\\"lib\\\",\\\
- }};[\\\"UIButton\\\"]={content=\\\"\\\\\\\
- require \\\\\\\"UIElement\\\\\\\"\\\\\\\
- \\\\\\\
- UIButton:extends( UIElement )\\\\\\\
- \\\\\\\
- UIButton.text = \\\\\\\"\\\\\\\"\\\\\\\
- UIButton.public \\\\\\\"text\\\\\\\"\\\\\\\
- \\\\\\\
- UIButton.public \\\\\\\"bc\\\\\\\" \\\\\\\"number\\\\\\\" \\\\\\\"function\\\\\\\"\\\\\\\
- UIButton.public \\\\\\\"tc\\\\\\\" \\\\\\\"number\\\\\\\" \\\\\\\"function\\\\\\\"\\\\\\\
- \\\\\\\
- UIButton.onClick = false\\\\\\\
- UIButton.public \\\\\\\"onClick\\\\\\\" \\\\\\\"function\\\\\\\"\\\\\\\
- UIButton.onDrag = false\\\\\\\
- UIButton.public \\\\\\\"onDrag\\\\\\\" \\\\\\\"function\\\\\\\"\\\\\\\
- \\\\\\\
- UIButton.align = true\\\\\\\
- UIButton.public \\\\\\\"align\\\\\\\" \\\\\\\"boolean\\\\\\\"\\\\\\\
- \\\\\\\
- UIButton.handlesEnter = true\\\\\\\
- \\\\\\\
- function UIButton:UIButton( x, y, w, h, text )\\\\\\\
- self:UIElement( x, y, w, h )\\\\\\\
- self.text = text\\\\\\\
- self.bc = colours.white\\\\\\\
- self.tc = colours.blue\\\\\\\
- return self.public\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIButton.public:draw( x, y )\\\\\\\
- local layer = stencil.addLayer( x, y, self.w, self.h )\\\\\\\
- local text = tostring( self.text )\\\\\\\
- if type( self.text ) == \\\\\\\"function\\\\\\\" then\\\\\\\
- text = self.text( self.public )\\\\\\\
- end\\\\\\\
- if self.align then\\\\\\\
- stencil.text( x, y, self.w, self.h, self.bc, self.tc, text, function( lines )\\\\\\\
- while #lines < self.h do\\\\\\\
- table.insert( lines, 1, string.rep( \\\\\\\" \\\\\\\", self.w ) )\\\\\\\
- table.insert( lines, string.rep( \\\\\\\" \\\\\\\", self.w ) )\\\\\\\
- end\\\\\\\
- if #lines > self.h then\\\\\\\
- table.remove( lines, 1 )\\\\\\\
- end\\\\\\\
- for i = 1, #lines do\\\\\\\
- lines[i] = string.rep( \\\\\\\" \\\\\\\", math.floor( ( self.w - #lines[i] ) / 2 ) ) .. lines[i]\\\\\\\
- end\\\\\\\
- end )\\\\\\\
- else\\\\\\\
- stencil.text( x, y, self.w, self.h, self.bc, self.tc, text )\\\\\\\
- end\\\\\\\
- local c = self.public:getChildren( )\\\\\\\
- for i, child in ipairs( c ) do\\\\\\\
- child:draw( x + child.x - 1 + self.cx, y + child.y - 1 + self.cy )\\\\\\\
- end\\\\\\\
- stencil.closeLayer( layer )\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIButton.public:onMouseClick( rx, ry, button )\\\\\\\
- if self.last and self.last.x == rx and self.last.y == ry and os.clock( ) - self.last.time <= 0.5 then\\\\\\\
- if self.onDoubleClick then\\\\\\\
- self.onDoubleClick( self.public, rx, ry, button )\\\\\\\
- end\\\\\\\
- else\\\\\\\
- if self.onClick then\\\\\\\
- self.onClick( self.public, rx, ry, button )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- self.last = {\\\\\\\
- x = rx;\\\\\\\
- y = ry;\\\\\\\
- time = os.clock( );\\\\\\\
- }\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIButton.public:onMouseDrag( x, y, cx, cy, button )\\\\\\\
- if self.onDrag then\\\\\\\
- self.onDrag( self.public, x, y, cx, cy, button )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIButton.public:onKeyPress( key )\\\\\\\
- if key == keys.enter and self.onClick then\\\\\\\
- self.onClick( self.public, 1, 1, \\\\\\\"enter\\\\\\\" )\\\\\\\
- end\\\\\\\
- end\\\", meta={\\\
- type = \\\"class\\\",\\\
- }};[\\\"UIImage\\\"]={content=\\\"\\\\\\\
- require \\\\\\\"Image\\\\\\\"\\\\\\\
- require \\\\\\\"UIElement\\\\\\\"\\\\\\\
- \\\\\\\
- UIImage.public \\\\\\\"image\\\\\\\" (Image)\\\\\\\
- UIImage.public \\\\\\\"onClick\\\\\\\" \\\\\\\"function\\\\\\\"\\\\\\\
- UIImage.public \\\\\\\"onDrag\\\\\\\" \\\\\\\"function\\\\\\\"\\\\\\\
- UIImage.public \\\\\\\"onScroll\\\\\\\" \\\\\\\"function\\\\\\\"\\\\\\\
- \\\\\\\
- UIImage:extends( UIElement )\\\\\\\
- \\\\\\\
- function UIImage:UIImage( x, y, w, h, image ) -- Image object\\\\\\\
- self:UIElement( x, y, w, h )\\\\\\\
- self.image = image\\\\\\\
- return self.public\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIImage.public:draw( x, y )\\\\\\\
- local layer = stencil.addLayer( x, y, self.w, self.h )\\\\\\\
- if not self.image then return end\\\\\\\
- for xx = 1, self.w do\\\\\\\
- for yy = 1, self.h do\\\\\\\
- local bc, tc, char = self.image:getPixel( xx, yy )\\\\\\\
- if bc then\\\\\\\
- if bc ~= 0 or char ~= \\\\\\\" \\\\\\\" then\\\\\\\
- stencil.pixel( x + xx - 1, y + yy - 1, bc, tc, char )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- end\\\\\\\
- end\\\\\\\
- local c = self.public:getChildren( )\\\\\\\
- for i, child in ipairs( c ) do\\\\\\\
- child:draw( x + child.x - 1 + self.cx, y + child.y - 1 + self.cy )\\\\\\\
- end\\\\\\\
- stencil.closeLayer( layer )\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIImage.public:onMouseClick( rx, ry, button )\\\\\\\
- if self.onClick then\\\\\\\
- self.onClick( self.public, rx, ry, button )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIImage.public:onMouseDrag( rx, ry, cx, cy, button )\\\\\\\
- if self.onDrag then\\\\\\\
- self.onDrag( self.public, rx, ry, cx, cy, button )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIImage.public:onMouseScroll( rx, ry, dir )\\\\\\\
- if self.onScroll then\\\\\\\
- self.onScroll( self.public, rx, ry, dir )\\\\\\\
- end\\\\\\\
- end\\\", meta={\\\
- type = \\\"class\\\",\\\
- }};[\\\"display\\\"]={content=\\\"\\\\\\\
- local function bluebox( frame, height )\\\\\\\
- local w, h = frame.w, frame.h\\\\\\\
- local f = frame:newChild( UIFrame( 1, math.ceil( h / 2 - height / 2 ) + 1, w, height ) )\\\\\\\
- f:newChild( UIText( 1, 1, f.w, f.h, \\\\\\\"\\\\\\\" ) ).bc = colours.blue\\\\\\\
- return f\\\\\\\
- end\\\\\\\
- \\\\\\\
- function alert( frame, message, wait )\\\\\\\
- local active = true\\\\\\\
- local close = frame:newChild( UIButton( 1, 1, frame.w, frame.h, \\\\\\\"\\\\\\\" ) )\\\\\\\
- close.bc = 0\\\\\\\
- close.align = false\\\\\\\
- local f = bluebox( frame, 6 )\\\\\\\
- function close:onClick( )\\\\\\\
- close:remove( )\\\\\\\
- f:remove( )\\\\\\\
- active = false\\\\\\\
- end\\\\\\\
- local title = f:newChild( UIText( 0, 2, math.min( #message, f.w - 2 ), 2, message ) )\\\\\\\
- title.bc = 0\\\\\\\
- title.tc = colours.lightBlue\\\\\\\
- title:centreX( )\\\\\\\
- local ok = f:newChild( UIButton( 0, 5, 2, 1, \\\\\\\"ok\\\\\\\" ) )\\\\\\\
- ok.bc = 0\\\\\\\
- ok.tc = colours.white\\\\\\\
- ok:centreX( )\\\\\\\
- function ok:onClick( )\\\\\\\
- close:remove( )\\\\\\\
- f:remove( )\\\\\\\
- active = false\\\\\\\
- end\\\\\\\
- if wait then\\\\\\\
- while active do\\\\\\\
- coroutine.yield( )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function response( frame, title, mask )\\\\\\\
- local active = true\\\\\\\
- local r = false\\\\\\\
- local close = frame:newChild( UIButton( 1, 1, frame.w, frame.h, \\\\\\\"\\\\\\\" ) )\\\\\\\
- close.bc = 0\\\\\\\
- close.align = false\\\\\\\
- local f = bluebox( frame, 6 )\\\\\\\
- function close:onClick( )\\\\\\\
- close:remove( )\\\\\\\
- f:remove( )\\\\\\\
- active = false\\\\\\\
- end\\\\\\\
- local title = f:newChild( UIText( 0, 2, math.min( #title, f.w - 2 ), 2, title ) )\\\\\\\
- title.bc = 0\\\\\\\
- title.tc = colours.lightBlue\\\\\\\
- title:centreX( )\\\\\\\
- local input = f:newChild( UIInput( 2, 5, frame.w - 2, 1, mask ) )\\\\\\\
- input.bc = 0\\\\\\\
- input.fbc = 0\\\\\\\
- input.tc = colours.white\\\\\\\
- input:focusOn( )\\\\\\\
- function input:onEnter( )\\\\\\\
- close:remove( )\\\\\\\
- f:remove( )\\\\\\\
- active = false\\\\\\\
- r = self.text\\\\\\\
- end\\\\\\\
- while active do\\\\\\\
- coroutine.yield( )\\\\\\\
- end\\\\\\\
- return r\\\\\\\
- end\\\\\\\
- \\\\\\\
- function confirm( frame, title )\\\\\\\
- local result = \\\\\\\"none\\\\\\\"\\\\\\\
- local close = frame:newChild( UIButton( 1, 1, frame.w, frame.h, \\\\\\\"\\\\\\\" ) )\\\\\\\
- close.bc = 0\\\\\\\
- close.align = false\\\\\\\
- local f = bluebox( frame, 6 )\\\\\\\
- function close:onClick( )\\\\\\\
- close:remove( )\\\\\\\
- f:remove( )\\\\\\\
- result = nil\\\\\\\
- end\\\\\\\
- local t = f:newChild( UIText( 2, 2, math.min( #title, f.w - 2 ), 2, title ) )\\\\\\\
- t.bc = colours.blue\\\\\\\
- t.tc = colours.lightBlue\\\\\\\
- t:centreX( )\\\\\\\
- local yes = f:newChild( UIButton( 2, 5, math.floor( ( f.w - 2 ) / 2 ), 1, \\\\\\\"yes\\\\\\\" ) )\\\\\\\
- yes.bc = colours.blue\\\\\\\
- yes.tc = colours.white\\\\\\\
- function yes:onClick( )\\\\\\\
- f:remove( )\\\\\\\
- close:remove( )\\\\\\\
- result = true\\\\\\\
- end\\\\\\\
- local no = f:newChild( UIButton( f.w - yes.w, 5, math.floor( ( f.w - 2 ) / 2 ), 1, \\\\\\\"no\\\\\\\" ) )\\\\\\\
- no.bc = colours.blue\\\\\\\
- no.tc = colours.white\\\\\\\
- function no:onClick( )\\\\\\\
- f:remove( )\\\\\\\
- close:remove( )\\\\\\\
- result = false\\\\\\\
- end\\\\\\\
- while result == \\\\\\\"none\\\\\\\" do\\\\\\\
- coroutine.yield( )\\\\\\\
- end\\\\\\\
- return result\\\\\\\
- end\\\\\\\
- \\\\\\\
- function sframe( frame, x, y, w, h ) -- scrolling frame (auto scrollbar)\\\\\\\
- \\\\\\\
- end\\\\\\\
- \\\\\\\
- function help( frame, title, content )\\\\\\\
- local close = frame:newChild( UIButton( 1, 1, frame.w, frame.h, \\\\\\\"\\\\\\\" ) )\\\\\\\
- close.bc = 0\\\\\\\
- close.align = false\\\\\\\
- local f = bluebox( frame, 14 )\\\\\\\
- local t = f:newChild( UIText( 2, 2, #title, 1, title ) )\\\\\\\
- t.bc = colours.blue\\\\\\\
- t.tc = colours.white\\\\\\\
- t:centreX( )\\\\\\\
- f:newChild( UIText( 3, 5, f.w - 3, f.h - 7, \\\\\\\"\\\\\\\" ) ).bc = colours.grey\\\\\\\
- local c = f:newChild( UIText( 2, 4, f.w - 3, f.h - 7, content ) )\\\\\\\
- c.tc = colours.grey\\\\\\\
- local ok = f:newChild( UIButton( 2, f.h - 1, f.w - 2, 1, \\\\\\\"Got it!\\\\\\\" ) )\\\\\\\
- ok.bc = colours.blue\\\\\\\
- ok.tc = colours.white\\\\\\\
- function ok:onClick( )\\\\\\\
- close:remove( )\\\\\\\
- f:remove( )\\\\\\\
- end\\\\\\\
- function close:onClick( )\\\\\\\
- close:remove( )\\\\\\\
- f:remove( )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function menu( frame, options )\\\\\\\
- frame:clearChildren( )\\\\\\\
- if options.width then\\\\\\\
- frame.w = options.width\\\\\\\
- end\\\\\\\
- if options.height then\\\\\\\
- frame.h = options.height\\\\\\\
- end\\\\\\\
- if options.shadow then\\\\\\\
- frame.w = frame.w - 1\\\\\\\
- frame.h = frame.h - 1\\\\\\\
- frame:newChild( UIText( 2, 2, frame.w, frame.h, \\\\\\\"\\\\\\\" ) ).bc = options.shadow\\\\\\\
- end\\\\\\\
- local sframe = frame\\\\\\\
- local y = 1\\\\\\\
- local bc = options.colour or 1\\\\\\\
- frame:newChild( UIButton( 1, 1, frame.w, frame.h, \\\\\\\"\\\\\\\" ) ).bc = bc\\\\\\\
- if #options > frame.h then\\\\\\\
- sframe = frame:newChild( UIFrame( 1, 1, frame.w - 1, frame.h ) )\\\\\\\
- local scrollbar = frame:newChild( UIScrollBar( frame.w, 1, 1, frame.h, sframe ) )\\\\\\\
- end\\\\\\\
- for i = 1, #options do\\\\\\\
- if options[i] == \\\\\\\"rule\\\\\\\" then\\\\\\\
- local r = sframe:newChild( UIText( 2, y, sframe.w - 2, 1, (\\\\\\\"-\\\\\\\"):rep( math.max( sframe.w - 2, 0 ) ) ) )\\\\\\\
- r.tc = colours.lightGrey\\\\\\\
- r.bc = 0\\\\\\\
- elseif options[i] ~= \\\\\\\"space\\\\\\\" then\\\\\\\
- if options[i].type == \\\\\\\"menu\\\\\\\" then\\\\\\\
- local arrow = sframe:newChild( UIText( sframe.w - 1, y, 1, 1, \\\\\\\">\\\\\\\" ) )\\\\\\\
- arrow.bc = 0\\\\\\\
- arrow.tc = colours.blue\\\\\\\
- local button = sframe:newChild( UIButton( options.spacing == false and 2 or 3, y, sframe.w - ( options.spacing == false and 2 or 3 ), 1, options[i].name ) )\\\\\\\
- button.bc = 0\\\\\\\
- button.tc = colours.grey\\\\\\\
- button.align = false\\\\\\\
- local cx, cy = frame.x + frame.w, y - 1\\\\\\\
- function button:onClick( )\\\\\\\
- if frame.parent then\\\\\\\
- local close = frame:newChild( UIButton( 1, 1, frame.w, frame.h, \\\\\\\"\\\\\\\" ) )\\\\\\\
- close.bc, close.align = 0, false\\\\\\\
- local f = UIFrame( cx, cy + frame.y + sframe.cy, frame.w, frame.h )\\\\\\\
- local w, h = frame.parent.w, frame.parent.h\\\\\\\
- menu( f, options[i].options )\\\\\\\
- if f.x + f.w > w + 1 then\\\\\\\
- f.x = w - f.w + 1\\\\\\\
- end\\\\\\\
- if f.y + f.h > h + 1 then\\\\\\\
- f.y = h - f.h + 1\\\\\\\
- end\\\\\\\
- frame.parent:newChild( f )\\\\\\\
- function close:onClick( )\\\\\\\
- f:remove( )\\\\\\\
- close:remove( )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- end\\\\\\\
- elseif options[i].type == \\\\\\\"button\\\\\\\" then\\\\\\\
- local button = sframe:newChild( UIButton( options.spacing == false and 2 or 4, y, sframe.w - ( options.spacing == false and 2 or 4 ), 1, options[i].name ) )\\\\\\\
- button.bc = 0\\\\\\\
- button.tc = options[i].blue and colours.blue or colours.grey\\\\\\\
- button.align = false\\\\\\\
- function button:onClick( x, y, b )\\\\\\\
- if type( options[i].onClick ) == \\\\\\\"function\\\\\\\" then\\\\\\\
- options[i].onClick( button, frame, b )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- elseif options[i].type == \\\\\\\"display\\\\\\\" then\\\\\\\
- local text = sframe:newChild( UIText( options.spacing == false and 2 or 4, y, sframe.w - ( options.spacing == false and 2 or 4 ), 1, function( )\\\\\\\
- if type( options[i].getDisplay ) == \\\\\\\"function\\\\\\\" then\\\\\\\
- return options[i].name .. tostring( options[i].getDisplay( ) )\\\\\\\
- end\\\\\\\
- return options[i].name\\\\\\\
- end ) )\\\\\\\
- text.bc = 0\\\\\\\
- text.tc = colours.grey\\\\\\\
- elseif options[i].type == \\\\\\\"input\\\\\\\" then\\\\\\\
- if options[i].label then\\\\\\\
- local label = sframe:newChild( UIText( options.spacing == false and 2 or 5, y, sframe.w - ( options.spacing == false and 2 or 5 ), 1, options[i].label ) )\\\\\\\
- label.bc = 0\\\\\\\
- label.tc = colours.lightGrey\\\\\\\
- y = y + 1\\\\\\\
- end\\\\\\\
- local input = sframe:newChild( UIInput( options.spacing == false and 2 or 4, y, sframe.w - ( options.spacing == false and 2 or 4 ), 1, options[i].mask ) )\\\\\\\
- input.bc = colours.lightGrey\\\\\\\
- input.fbc = colours.lightGrey\\\\\\\
- function input:onEnter( )\\\\\\\
- if type( options[i].onEnter ) == \\\\\\\"function\\\\\\\" then\\\\\\\
- options[i].onEnter( input )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- function input:whenUnFocussed( )\\\\\\\
- self.text = \\\\\\\"\\\\\\\"\\\\\\\
- end\\\\\\\
- elseif options[i].type == \\\\\\\"label\\\\\\\" then\\\\\\\
- local text = sframe:newChild( UIText( 2, y, sframe.w - 2, 1, options[i].name ) )\\\\\\\
- text.bc = 0\\\\\\\
- text.tc = colours.lightGrey\\\\\\\
- end\\\\\\\
- end\\\\\\\
- y = y + 1\\\\\\\
- end\\\\\\\
- if options.shadow then\\\\\\\
- frame.w = frame.w + 1\\\\\\\
- frame.h = frame.h + 1\\\\\\\
- end\\\\\\\
- return sframe\\\\\\\
- end\\\", meta={\\\
- type = \\\"lib\\\",\\\
- }};[\\\"UIElement\\\"]={content=\\\"\\\\\\\
- require \\\\\\\"UIHandler\\\\\\\"\\\\\\\
- \\\\\\\
- UIElement.public \\\\\\\"x\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
- UIElement.public \\\\\\\"y\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
- UIElement.public \\\\\\\"w\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
- UIElement.public \\\\\\\"h\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
- UIElement.public \\\\\\\"cx\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
- UIElement.public \\\\\\\"cy\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
- \\\\\\\
- UIElement.public \\\\\\\"parent\\\\\\\"\\\\\\\
- function UIElement.public.parent:write( value )\\\\\\\
- if value == false or class.typeOf( value, UIElement ) or class.typeOf( value, UIHandler ) then\\\\\\\
- self.parent = value\\\\\\\
- else\\\\\\\
- error( \\\\\\\"expected UIElement parent\\\\\\\", 3 )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- UIElement.handlesMouse = true\\\\\\\
- UIElement.handlesScroll = false\\\\\\\
- UIElement.handlesTab = false\\\\\\\
- UIElement.tabIndex = false\\\\\\\
- \\\\\\\
- UIElement.active = true\\\\\\\
- UIElement.public \\\\\\\"active\\\\\\\" \\\\\\\"boolean\\\\\\\"\\\\\\\
- \\\\\\\
- UIElement.public \\\\\\\"handlesMouse\\\\\\\"\\\\\\\
- UIElement.public.handlesMouse.write = false\\\\\\\
- UIElement.public \\\\\\\"handlesKeys\\\\\\\"\\\\\\\
- UIElement.public.handlesKeys.write = false\\\\\\\
- UIElement.public \\\\\\\"handlesScroll\\\\\\\"\\\\\\\
- UIElement.public.handlesScroll.write = false\\\\\\\
- UIElement.public \\\\\\\"handlesTab\\\\\\\"\\\\\\\
- UIElement.public.handlesTab.write = false\\\\\\\
- UIElement.public \\\\\\\"handlesEnter\\\\\\\"\\\\\\\
- UIElement.public.handlesEnter.write = false\\\\\\\
- UIElement.public \\\\\\\"tabIndex\\\\\\\" \\\\\\\"boolean\\\\\\\"\\\\\\\
- \\\\\\\
- UIElement.isScrollTarget = false\\\\\\\
- UIElement.public \\\\\\\"isScrollTarget\\\\\\\"\\\\\\\
- UIElement.public.isScrollTarget.write = false\\\\\\\
- UIElement.scrollDirection = \\\\\\\"none\\\\\\\"\\\\\\\
- UIElement.public \\\\\\\"scrollDirection\\\\\\\"\\\\\\\
- UIElement.public.scrollDirection.write = false\\\\\\\
- \\\\\\\
- UIElement.public \\\\\\\"whenFocussed\\\\\\\" \\\\\\\"function\\\\\\\"\\\\\\\
- UIElement.public \\\\\\\"whenUnFocussed\\\\\\\" \\\\\\\"function\\\\\\\"\\\\\\\
- \\\\\\\
- function UIElement:UIElement( x, y, w, h )\\\\\\\
- self.x = x\\\\\\\
- self.y = y\\\\\\\
- self.w = w\\\\\\\
- self.h = h\\\\\\\
- self.cx = 0 -- children offset\\\\\\\
- self.cy = 0\\\\\\\
- self.children = { }\\\\\\\
- return self.public\\\\\\\
- end\\\\\\\
- \\\\\\\
- -- children\\\\\\\
- \\\\\\\
- function UIElement.public:newChild( child )\\\\\\\
- if class.typeOf( child, UIElement ) then\\\\\\\
- table.insert( self.children, child )\\\\\\\
- child.parent = self.public\\\\\\\
- return child\\\\\\\
- else\\\\\\\
- error( \\\\\\\"expected UIElement child\\\\\\\", 2 )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIElement.public:removeChild( child )\\\\\\\
- if class.typeOf( child, UIElement ) then\\\\\\\
- for i = 1, #self.children do\\\\\\\
- if self.children[i] == child then\\\\\\\
- table.remove( self.children, i )\\\\\\\
- child.parent = false\\\\\\\
- return true\\\\\\\
- end\\\\\\\
- end\\\\\\\
- return false\\\\\\\
- else\\\\\\\
- error( \\\\\\\"expected UIElement child\\\\\\\", 3 )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIElement.public:getChildren( )\\\\\\\
- local t = { }\\\\\\\
- for i = 1, #self.children do\\\\\\\
- if self.children[i].active then\\\\\\\
- t[#t+1] = self.children[i]\\\\\\\
- end\\\\\\\
- end\\\\\\\
- return t\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIElement.public:clearChildren( )\\\\\\\
- for i = #self.children, 1, -1 do\\\\\\\
- self.children[i]:remove( )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- local function merge( t1, t2 )\\\\\\\
- local t = { }\\\\\\\
- for i, v in ipairs( t1 ) do\\\\\\\
- table.insert( t, v )\\\\\\\
- end\\\\\\\
- for i, v in ipairs( t2 ) do\\\\\\\
- table.insert( t, v )\\\\\\\
- end\\\\\\\
- return t\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIElement.public:getTabIndexes( )\\\\\\\
- local t = { }\\\\\\\
- if self.tabIndex then\\\\\\\
- table.insert( t, self.public )\\\\\\\
- end\\\\\\\
- for i = 1, #self.children do\\\\\\\
- local t2 = self.children[i]:getTabIndexes( )\\\\\\\
- if #t2 > 0 then\\\\\\\
- t = merge( t, t2 )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- return t\\\\\\\
- end\\\\\\\
- \\\\\\\
- -- parent dependant\\\\\\\
- \\\\\\\
- function UIElement.public:positionIn( parent )\\\\\\\
- local x, y = self.x, self.y\\\\\\\
- local p = self.parent\\\\\\\
- while p and p ~= parent and p:type( ) ~= \\\\\\\"UIHandler\\\\\\\" do\\\\\\\
- x = x + p.x\\\\\\\
- y = y + p.y\\\\\\\
- p = p.parent\\\\\\\
- end\\\\\\\
- return x, y\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIElement.public:remove( )\\\\\\\
- if self.parent then\\\\\\\
- return self.parent:removeChild( self.public )\\\\\\\
- end\\\\\\\
- return false\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIElement.public:getHandler( )\\\\\\\
- if self.parent then\\\\\\\
- local h = self.parent\\\\\\\
- while h:type( ) ~= \\\\\\\"UIHandler\\\\\\\" do\\\\\\\
- h = h.parent\\\\\\\
- if not h then return false end\\\\\\\
- end\\\\\\\
- return h\\\\\\\
- end\\\\\\\
- return false\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIElement.public:centreX( )\\\\\\\
- if self.parent then\\\\\\\
- self.x = math.ceil( self.parent.w / 2 - self.w / 2 ) + 1\\\\\\\
- end\\\\\\\
- return self.public\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIElement.public:centreY( )\\\\\\\
- if self.parent then\\\\\\\
- self.y = math.ceil( self.parent.h / 2 - self.h / 2 ) + 1\\\\\\\
- end\\\\\\\
- return self.public\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIElement.public:centre( )\\\\\\\
- self.public:centreX( )\\\\\\\
- self.public:centreY( )\\\\\\\
- end\\\\\\\
- \\\\\\\
- -- sibling dependant\\\\\\\
- \\\\\\\
- function UIElement.public:align( mode, sibling, spacing )\\\\\\\
- if mode == \\\\\\\"above\\\\\\\" then\\\\\\\
- self.x = sibling.x\\\\\\\
- self.w = sibling.w\\\\\\\
- self.y = sibling.y - self.h - ( spacing or 0 )\\\\\\\
- elseif mode == \\\\\\\"below\\\\\\\" then\\\\\\\
- self.x = sibling.x\\\\\\\
- self.w = sibling.w\\\\\\\
- self.y = sibling.y + sibling.h + ( spacing or 0 )\\\\\\\
- elseif mode == \\\\\\\"left\\\\\\\" then\\\\\\\
- self.y = sibling.y\\\\\\\
- self.h = sibling.h\\\\\\\
- self.x = sibling.x - self.w - ( spacing or 0 )\\\\\\\
- elseif mode == \\\\\\\"right\\\\\\\" then\\\\\\\
- self.y = sibling.y\\\\\\\
- self.h = sibling.h\\\\\\\
- self.x = sibling.x + sibling.w + ( spacing or 0 )\\\\\\\
- end\\\\\\\
- return self.public\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIElement.public:alignTo( mode, sibling, spacing )\\\\\\\
- if mode == \\\\\\\"above\\\\\\\" then\\\\\\\
- self.x = sibling.x\\\\\\\
- self.w = sibling.w\\\\\\\
- self.y = sibling.y - self.h - ( spacing or 0 )\\\\\\\
- elseif mode == \\\\\\\"below\\\\\\\" then\\\\\\\
- self.x = sibling.x\\\\\\\
- self.w = sibling.w\\\\\\\
- self.y = sibling.y + sibling.h + ( spacing or 0 )\\\\\\\
- elseif mode == \\\\\\\"left\\\\\\\" then\\\\\\\
- self.y = sibling.y\\\\\\\
- self.h = sibling.h\\\\\\\
- self.x = sibling.x - self.w - ( spacing or 0 )\\\\\\\
- elseif mode == \\\\\\\"right\\\\\\\" then\\\\\\\
- self.y = sibling.y\\\\\\\
- self.h = sibling.h\\\\\\\
- self.x = sibling.x + sibling.w + ( spacing or 0 )\\\\\\\
- end\\\\\\\
- return self.public\\\\\\\
- end\\\\\\\
- \\\\\\\
- -- callbacks\\\\\\\
- \\\\\\\
- function UIElement.public:update( dt )\\\\\\\
- local c = self.public:getChildren( )\\\\\\\
- for i, child in ipairs( c ) do\\\\\\\
- child:update( dt )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIElement.public:draw( x, y )\\\\\\\
- local layer = stencil.addLayer( x, y, self.w, self.h )\\\\\\\
- local c = self.public:getChildren( )\\\\\\\
- for i, child in ipairs( c ) do\\\\\\\
- child:draw( x + child.x - 1 + self.cx, y + child.y - 1 + self.cy )\\\\\\\
- end\\\\\\\
- stencil.closeLayer( layer )\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIElement.public:onMouseClick( rx, ry, button )\\\\\\\
- \\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIElement.public:onMouseDrag( rx, ry, cx, cy, button )\\\\\\\
- \\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIElement.public:onMouseScroll( rx, ry, dir )\\\\\\\
- \\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIElement.public:onKeyPress( key, lastkey )\\\\\\\
- \\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIElement.public:onTextInput( text, lastkey )\\\\\\\
- \\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIElement.public:onFocus( )\\\\\\\
- if type( self.whenFocussed ) == \\\\\\\"function\\\\\\\" then\\\\\\\
- self.whenFocussed( self.public )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIElement.public:onUnFocus( )\\\\\\\
- if type( self.whenUnFocussed ) == \\\\\\\"function\\\\\\\" then\\\\\\\
- self.whenUnFocussed( self.public )\\\\\\\
- end\\\\\\\
- end\\\", meta={\\\
- type = \\\"class\\\",\\\
- }};[\\\"UIHandler\\\"]={content=\\\"\\\\\\\
- require \\\\\\\"stencil\\\\\\\"\\\\\\\
- \\\\\\\
- UIHandler.public.w, UIHandler.public.h = term.getSize( )\\\\\\\
- \\\\\\\
- UIHandler.public \\\\\\\"focus\\\\\\\"\\\\\\\
- UIHandler.public.focus.write = false\\\\\\\
- \\\\\\\
- function UIHandler:UIHandler( )\\\\\\\
- self.children = { }\\\\\\\
- self.focus = false\\\\\\\
- self.lastx = 0\\\\\\\
- self.lasty = 0\\\\\\\
- self.lastt = false\\\\\\\
- self.lastkey = nil\\\\\\\
- self.lastkeytime = false\\\\\\\
- return self.public\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIHandler.public:getChildren( )\\\\\\\
- local t = { }\\\\\\\
- for i = 1, #self.children do\\\\\\\
- if self.children[i].active then\\\\\\\
- t[i] = self.children[i]\\\\\\\
- end\\\\\\\
- end\\\\\\\
- return t\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIHandler.public:draw( )\\\\\\\
- local c = self.public:getChildren( )\\\\\\\
- for i, child in ipairs( c ) do\\\\\\\
- child:draw( child.x, child.y )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIHandler.public:update( dt )\\\\\\\
- local c = self.public:getChildren( )\\\\\\\
- for i, child in ipairs( c ) do\\\\\\\
- child:update( dt )\\\\\\\
- end\\\\\\\
- if self.lastkeytime then\\\\\\\
- if os.clock( ) - self.lastkeytime > 0.3 then\\\\\\\
- self.lastkey = nil\\\\\\\
- self.lastkeytime = false\\\\\\\
- end\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- local function findMouseTarget( children, parent, handler, x, y )\\\\\\\
- x = x - parent.cx\\\\\\\
- y = y - parent.cy\\\\\\\
- for i = #children, 1, -1 do\\\\\\\
- local child = children[i]\\\\\\\
- if child.active and x >= child.x and y >= child.y and x < child.x + child.w and y < child.y + child.h then\\\\\\\
- local ok, c, data = findMouseTarget( child:getChildren( ), child, handler, x - child.x + 1, y - child.y + 1 )\\\\\\\
- if ok then\\\\\\\
- return true, c, data\\\\\\\
- end\\\\\\\
- local ok, data = handler( child, parent, x, y )\\\\\\\
- if ok then\\\\\\\
- return true, child, data\\\\\\\
- end\\\\\\\
- end\\\\\\\
- end\\\\\\\
- return false\\\\\\\
- end\\\\\\\
- \\\\\\\
- local function screenCoords( child )\\\\\\\
- local x, y = child.x, child.y\\\\\\\
- while child.parent and child.parent:type( ) ~= \\\\\\\"UIHandler\\\\\\\" do -- everything but a UIHandler will have a parent\\\\\\\
- child = child.parent\\\\\\\
- x = x + child.x - 1 + child.cx\\\\\\\
- y = y + child.y - 1 + child.cy\\\\\\\
- end\\\\\\\
- return x, y\\\\\\\
- end\\\\\\\
- \\\\\\\
- local function merge( t1, t2 )\\\\\\\
- local t = { }\\\\\\\
- for i, v in ipairs( t1 ) do\\\\\\\
- table.insert( t, v )\\\\\\\
- end\\\\\\\
- for i, v in ipairs( t2 ) do\\\\\\\
- table.insert( t, v )\\\\\\\
- end\\\\\\\
- return t\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIHandler.public:event( event )\\\\\\\
- if event[1] == \\\\\\\"mouse_click\\\\\\\" then\\\\\\\
- self.lastx = event[3]\\\\\\\
- self.lasty = event[4]\\\\\\\
- local found, child, data = findMouseTarget( self.children, {cx=0, cy=0}, function( child, parent, x, y )\\\\\\\
- if child.handlesMouse then\\\\\\\
- return true, { rx = x - child.x + 1, ry = y - child.y + 1 }\\\\\\\
- end\\\\\\\
- end, event[3], event[4] )\\\\\\\
- if found then\\\\\\\
- if child ~= self.focus and self.focus then\\\\\\\
- self.focus:onUnFocus( )\\\\\\\
- end\\\\\\\
- if child ~= self.focus then\\\\\\\
- child:onFocus( )\\\\\\\
- end\\\\\\\
- self.focus = child\\\\\\\
- child:onMouseClick( data.rx, data.ry, event[2] )\\\\\\\
- local sx, sy = screenCoords( child )\\\\\\\
- self.lastt = { child = child, sx = sx, sy = sy, x = child.x, y = child.y }\\\\\\\
- else\\\\\\\
- self.lastt = false\\\\\\\
- if self.focus then\\\\\\\
- self.focus:onUnFocus( )\\\\\\\
- end\\\\\\\
- self.focus = false\\\\\\\
- end\\\\\\\
- elseif event[1] == \\\\\\\"mouse_scroll\\\\\\\" then\\\\\\\
- local found, child, data = findMouseTarget( self.children, {cx=0, cy=0}, function( child, parent, x, y )\\\\\\\
- if child.handlesScroll then\\\\\\\
- return true, { rx = x - child.x + 1, ry = y - child.y + 1 }\\\\\\\
- end\\\\\\\
- end, event[3], event[4] )\\\\\\\
- if found then\\\\\\\
- child:onMouseScroll( data.rx, data.ry, event[2] )\\\\\\\
- end\\\\\\\
- elseif event[1] == \\\\\\\"mouse_drag\\\\\\\" then\\\\\\\
- if self.lastt then\\\\\\\
- local cx, cy = event[3] - self.lastx, event[4] - self.lasty\\\\\\\
- local sx, sy = self.lastt.sx + ( self.lastt.child.x - self.lastt.x ), self.lastt.sy + ( self.lastt.child.y - self.lastt.y )\\\\\\\
- self.lastt.child:onMouseDrag( event[3] - sx + 1, event[4] - sy + 1, cx, cy, event[2] )\\\\\\\
- self.lastx = event[3]\\\\\\\
- self.lasty = event[4]\\\\\\\
- end\\\\\\\
- elseif event[1] == \\\\\\\"key\\\\\\\" then\\\\\\\
- if event[2] == keys.tab then\\\\\\\
- \\\\\\\
- if self.focus and self.focus.handlesTab then\\\\\\\
- self.focus:onKeyPress( keys.tab, self.lastkey )\\\\\\\
- else\\\\\\\
- local tabs = { }\\\\\\\
- for i = 1, #self.children do\\\\\\\
- local t2 = self.children[i]:getTabIndexes( )\\\\\\\
- if #t2 > 0 then\\\\\\\
- tabs = merge( tabs, t2 )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- local of = self.focus\\\\\\\
- local found = false\\\\\\\
- for i = 1, #tabs do\\\\\\\
- if tabs[i] == self.focus then\\\\\\\
- if self.lastkey == keys.leftShift or self.lastkey == keys.rightShift then\\\\\\\
- if tabs[i-1] then\\\\\\\
- self.focus = tabs[i-1]\\\\\\\
- else\\\\\\\
- self.focus = tabs[#tabs]\\\\\\\
- end\\\\\\\
- else\\\\\\\
- if tabs[i+1] then\\\\\\\
- self.focus = tabs[i+1]\\\\\\\
- else\\\\\\\
- self.focus = tabs[1]\\\\\\\
- end\\\\\\\
- end\\\\\\\
- found = true\\\\\\\
- break\\\\\\\
- end\\\\\\\
- end\\\\\\\
- if not found then\\\\\\\
- self.focus = tabs[1]\\\\\\\
- end\\\\\\\
- if of ~= self.focus then\\\\\\\
- if of then\\\\\\\
- of:onUnFocus( )\\\\\\\
- end\\\\\\\
- if self.focus then\\\\\\\
- self.focus:onFocus( )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- end\\\\\\\
- else\\\\\\\
- if self.focus and ( self.focus.handlesKeys or ( self.focus.handlesEnter and event[2] == keys.enter ) ) then\\\\\\\
- self.focus:onKeyPress( event[2], self.lastkey )\\\\\\\
- else\\\\\\\
- local function find( c )\\\\\\\
- for i = #c, 1, -1 do\\\\\\\
- local ok, obj = find( c[i]:getChildren( ) )\\\\\\\
- if ok then\\\\\\\
- return true, obj\\\\\\\
- end\\\\\\\
- if c[i].active and c[i].handlesKeys then\\\\\\\
- c[i]:onKeyPress( event[2], self.lastkey )\\\\\\\
- return true, c[i]\\\\\\\
- end\\\\\\\
- end\\\\\\\
- end\\\\\\\
- find( self.children )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- elseif event[1] == \\\\\\\"char\\\\\\\" then\\\\\\\
- if self.focus and self.focus.handlesKeys then\\\\\\\
- self.focus:onTextInput( event[2], self.lastkey )\\\\\\\
- else\\\\\\\
- local function find( c )\\\\\\\
- for i = #c, 1, -1 do\\\\\\\
- local ok, obj = find( c[i]:getChildren( ) )\\\\\\\
- if ok then\\\\\\\
- return true, obj\\\\\\\
- end\\\\\\\
- if c[i].active and c[i].handlesKeys then\\\\\\\
- c[i]:onTextInput( event[2], self.lastkey )\\\\\\\
- return true, c[i]\\\\\\\
- end\\\\\\\
- end\\\\\\\
- end\\\\\\\
- find( self.children )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- if event[1] == \\\\\\\"key\\\\\\\" or ( event[1] == \\\\\\\"char\\\\\\\" and self.lastkey ) then\\\\\\\
- if event[1] == \\\\\\\"key\\\\\\\" then\\\\\\\
- self.lastkey = event[2]\\\\\\\
- end\\\\\\\
- self.lastkeytime = os.clock( )\\\\\\\
- else\\\\\\\
- self.lastkey = nil\\\\\\\
- self.lastkeytime = false\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIHandler.public:newChild( child )\\\\\\\
- if class.typeOf( child, UIElement ) then\\\\\\\
- table.insert( self.children, child )\\\\\\\
- child.parent = self.public\\\\\\\
- return child\\\\\\\
- else\\\\\\\
- error( \\\\\\\"expected UIElement child\\\\\\\", 2 )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIHandler.public:removeChild( child )\\\\\\\
- local removed = false\\\\\\\
- for i = #self.children, 1, -1 do\\\\\\\
- if self.children[i] == child then\\\\\\\
- table.remove( self.children, i )\\\\\\\
- removed = true\\\\\\\
- end\\\\\\\
- end\\\\\\\
- return removed\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIHandler.public:unFocus( )\\\\\\\
- if self.focus then\\\\\\\
- self.focus:onUnFocus( )\\\\\\\
- self.focus = false\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIHandler.public:setFocus( child )\\\\\\\
- if class.typeOf( child, UIElement ) then\\\\\\\
- if self.focus ~= child then\\\\\\\
- if self.focus then\\\\\\\
- self.focus:onUnFocus( )\\\\\\\
- end\\\\\\\
- child:onFocus( )\\\\\\\
- self.focus = child\\\\\\\
- end\\\\\\\
- else\\\\\\\
- error( \\\\\\\"expected UIElement child\\\\\\\", 3 )\\\\\\\
- end\\\\\\\
- end\\\", meta={\\\
- type = \\\"class\\\",\\\
- }};[\\\"main\\\"]={content=\\\"\\\\\\\
- require \\\\\\\"buffer\\\\\\\"\\\\\\\
- require \\\\\\\"clipboard\\\\\\\"\\\\\\\
- require \\\\\\\"display\\\\\\\"\\\\\\\
- require \\\\\\\"shader\\\\\\\"\\\\\\\
- require \\\\\\\"stencil\\\\\\\"\\\\\\\
- \\\\\\\
- export \\\\\\\"buffer\\\\\\\"\\\\\\\
- export \\\\\\\"clipboard\\\\\\\"\\\\\\\
- export \\\\\\\"display\\\\\\\"\\\\\\\
- export \\\\\\\"shader\\\\\\\"\\\\\\\
- export \\\\\\\"stencil\\\\\\\"\\\\\\\
- \\\\\\\
- export \\\\\\\"Image\\\\\\\"\\\\\\\
- export \\\\\\\"Thread\\\\\\\"\\\\\\\
- export \\\\\\\"UIBuffer\\\\\\\"\\\\\\\
- export \\\\\\\"UIButton\\\\\\\"\\\\\\\
- export \\\\\\\"UICode\\\\\\\"\\\\\\\
- export \\\\\\\"UIElement\\\\\\\"\\\\\\\
- export \\\\\\\"UIFrame\\\\\\\"\\\\\\\
- export \\\\\\\"UIHandler\\\\\\\"\\\\\\\
- export \\\\\\\"UIImage\\\\\\\"\\\\\\\
- export \\\\\\\"UIInput\\\\\\\"\\\\\\\
- export \\\\\\\"UIKeyHandler\\\\\\\"\\\\\\\
- export \\\\\\\"UIMenu\\\\\\\"\\\\\\\
- export \\\\\\\"UIScrollBar\\\\\\\"\\\\\\\
- export \\\\\\\"UIText\\\\\\\"\\\\\\\
- export \\\\\\\"UITextbox\\\\\\\"\\\", meta={}};[\\\"UIScrollBar\\\"]={content=\\\"\\\\\\\
- require \\\\\\\"UIElement\\\\\\\"\\\\\\\
- \\\\\\\
- UIScrollBar:extends( UIElement )\\\\\\\
- \\\\\\\
- UIScrollBar.public \\\\\\\"bc\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
- UIScrollBar.public \\\\\\\"tc\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
- \\\\\\\
- UIScrollBar.handlesScroll = true\\\\\\\
- \\\\\\\
- function UIScrollBar:UIScrollBar( x, y, w, h, target, direction )\\\\\\\
- self:UIElement( x, y, w, h )\\\\\\\
- \\\\\\\
- self.bc = colours.lightGrey\\\\\\\
- self.tc = colours.grey\\\\\\\
- self.direction = direction or \\\\\\\"vertical\\\\\\\"\\\\\\\
- \\\\\\\
- if target then\\\\\\\
- if class.typeOf( target, UIElement ) then\\\\\\\
- if target.isScrollTarget then\\\\\\\
- self.target = target\\\\\\\
- else\\\\\\\
- error( \\\\\\\"cannot set \\\\\\\" .. target:type( ) .. \\\\\\\" as scroll target\\\\\\\", 3 )\\\\\\\
- end\\\\\\\
- else\\\\\\\
- error( \\\\\\\"expected UIElement target\\\\\\\", 3 )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- return self.public\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIScrollBar.public:setScrollTarget( target )\\\\\\\
- if class.typeOf( target, UIElement ) then\\\\\\\
- if target.isScrollTarget then\\\\\\\
- self.target = target\\\\\\\
- else\\\\\\\
- error( \\\\\\\"cannot set \\\\\\\" .. target:type( ) .. \\\\\\\" as scroll target\\\\\\\", 2 )\\\\\\\
- end\\\\\\\
- else\\\\\\\
- error( \\\\\\\"expected UIElement target\\\\\\\", 2 )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- --[[\\\\\\\
- barsize/traysize = framesize/contentsize\\\\\\\
- barposition = traysize * contentscroll / contentsize\\\\\\\
- ]]\\\\\\\
- \\\\\\\
- function UIScrollBar:getBar( )\\\\\\\
- if not self.target then\\\\\\\
- return 0, self.direction == \\\\\\\"vertical\\\\\\\" and self.h or self.w\\\\\\\
- end\\\\\\\
- local traysize\\\\\\\
- if self.direction == \\\\\\\"vertical\\\\\\\" then\\\\\\\
- traysize = self.h\\\\\\\
- else\\\\\\\
- traysize = self.w\\\\\\\
- end\\\\\\\
- local framesize, contentsize, contentscroll\\\\\\\
- if self.direction == \\\\\\\"vertical\\\\\\\" then\\\\\\\
- framesize = self.target:getDisplaySizeV( )\\\\\\\
- contentsize = self.target:getContentSizeV( )\\\\\\\
- contentscroll = self.target:getContentScrollV( )\\\\\\\
- else\\\\\\\
- framesize = self.target:getDisplaySizeH( )\\\\\\\
- contentsize = self.target:getContentSizeH( )\\\\\\\
- contentscroll = self.target:getContentScrollH( )\\\\\\\
- end\\\\\\\
- \\\\\\\
- local barsize = math.max( math.floor( traysize * framesize / contentsize ), 1 )\\\\\\\
- \\\\\\\
- local barpos = traysize * contentscroll / contentsize\\\\\\\
- return math.ceil( barpos ), barsize\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIScrollBar.public:draw( x, y )\\\\\\\
- local layer = stencil.addLayer( x, y, self.w, self.h )\\\\\\\
- if self.target then\\\\\\\
- local pos, size = self:getBar( )\\\\\\\
- stencil.rectangle( x, y, self.w, self.h, self.tc, 1, \\\\\\\" \\\\\\\" )\\\\\\\
- if self.direction == \\\\\\\"vertical\\\\\\\" then\\\\\\\
- stencil.rectangle( x, y + pos, self.w, size, self.bc, 1, \\\\\\\" \\\\\\\" )\\\\\\\
- else\\\\\\\
- stencil.rectangle( x + pos, y, size, self.h, self.bc, 1, \\\\\\\" \\\\\\\" )\\\\\\\
- end\\\\\\\
- else\\\\\\\
- stencil.rectangle( x, y, self.w, self.h, self.bc, 1, \\\\\\\" \\\\\\\" )\\\\\\\
- end\\\\\\\
- local c = self.public:getChildren( )\\\\\\\
- for i, child in ipairs( c ) do\\\\\\\
- child:draw( x + child.x - 1 + self.cx, y + child.y - 1 + self.cy )\\\\\\\
- end\\\\\\\
- stencil.closeLayer( layer )\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIScrollBar.public:onMouseClick( rx, ry, button )\\\\\\\
- if not self.target then return end\\\\\\\
- local pos, size = self:getBar( )\\\\\\\
- local traysize\\\\\\\
- if self.direction == \\\\\\\"vertical\\\\\\\" then\\\\\\\
- self.downoffset = ry - pos\\\\\\\
- if ry > pos and ry < pos + size - 1 then return end\\\\\\\
- pos = ry - 1\\\\\\\
- traysize = self.h\\\\\\\
- else\\\\\\\
- self.downoffset = rx - pos\\\\\\\
- if rx > pos and rx < pos + size - 1 then return end\\\\\\\
- pos = rx - 1\\\\\\\
- traysize = self.w\\\\\\\
- end\\\\\\\
- pos = math.max( math.min( pos, traysize - size), 0 )\\\\\\\
- if self.direction == \\\\\\\"vertical\\\\\\\" then\\\\\\\
- self.target:setContentScrollV( math.floor( pos / traysize * self.target:getContentSizeV( ) ) )\\\\\\\
- else\\\\\\\
- self.target:setContentScrollH( math.floor( pos / traysize * self.target:getContentSizeH( ) ) )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIScrollBar.public:onMouseDrag( rx, ry, cx, cy, button )\\\\\\\
- if not self.target then return end\\\\\\\
- local pos, size = self:getBar( )\\\\\\\
- local traysize\\\\\\\
- if self.direction == \\\\\\\"vertical\\\\\\\" then\\\\\\\
- pos = ry - self.downoffset\\\\\\\
- traysize = self.h\\\\\\\
- else\\\\\\\
- pos = rx - self.downoffset\\\\\\\
- traysize = self.w\\\\\\\
- end\\\\\\\
- pos = math.max( math.min( pos, traysize - size), 0 )\\\\\\\
- if self.direction == \\\\\\\"vertical\\\\\\\" then\\\\\\\
- self.target:setContentScrollV( math.floor( pos / traysize * self.target:getContentSizeV( ) ) )\\\\\\\
- else\\\\\\\
- self.target:setContentScrollH( math.floor( pos / traysize * self.target:getContentSizeH( ) ) )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIScrollBar.public:onMouseScroll( rx, ry, dir )\\\\\\\
- if not self.target then return end\\\\\\\
- local pos, size = self:getBar( )\\\\\\\
- local traysize\\\\\\\
- if self.direction == \\\\\\\"vertical\\\\\\\" then\\\\\\\
- traysize = self.h\\\\\\\
- else\\\\\\\
- traysize = self.w\\\\\\\
- end\\\\\\\
- pos = math.max( math.min( pos + dir, traysize - size), 0 )\\\\\\\
- if self.direction == \\\\\\\"vertical\\\\\\\" then\\\\\\\
- self.target:setContentScrollV( math.floor( pos / traysize * self.target:getContentSizeV( ) ) )\\\\\\\
- else\\\\\\\
- self.target:setContentScrollH( math.floor( pos / traysize * self.target:getContentSizeH( ) ) )\\\\\\\
- end\\\\\\\
- end\\\", meta={\\\
- type = \\\"class\\\",\\\
- }};[\\\"UIBuffer\\\"]={content=\\\"\\\\\\\
- local colour = term.isColour( )\\\\\\\
- \\\\\\\
- require \\\\\\\"UIElement\\\\\\\"\\\\\\\
- require \\\\\\\"Image\\\\\\\"\\\\\\\
- UIBuffer:extends( UIElement )\\\\\\\
- \\\\\\\
- UIBuffer.public \\\\\\\"cx\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
- UIBuffer.public \\\\\\\"cy\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
- UIBuffer.public \\\\\\\"cb\\\\\\\" \\\\\\\"boolean\\\\\\\"\\\\\\\
- UIBuffer.public \\\\\\\"bc\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
- UIBuffer.public \\\\\\\"tc\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
- UIBuffer.public \\\\\\\"image\\\\\\\"\\\\\\\
- UIBuffer.public.image.write = false\\\\\\\
- \\\\\\\
- UIBuffer.handlesKeys = true\\\\\\\
- UIBuffer.handlesScroll = true\\\\\\\
- UIBuffer.handlesTab = true\\\\\\\
- UIBuffer.handlesEnter = true\\\\\\\
- \\\\\\\
- local function termObject( canvas )\\\\\\\
- local t = { }\\\\\\\
- function t.write( str )\\\\\\\
- if type( str ) ~= \\\\\\\"string\\\\\\\" and type( str ) ~= \\\\\\\"number\\\\\\\" then return error \\\\\\\"expected string\\\\\\\" end\\\\\\\
- str = tostring( str )\\\\\\\
- for x = 1, #str do\\\\\\\
- canvas.image:pixel( canvas.cx, canvas.cy, canvas.bc, canvas.tc, str:sub( x, x ) )\\\\\\\
- canvas.cx = canvas.cx + 1\\\\\\\
- end\\\\\\\
- end\\\\\\\
- function t.clearLine( )\\\\\\\
- canvas.image:foreach( function( x, y, bc, tc, char )\\\\\\\
- if y == canvas.cy then\\\\\\\
- return canvas.bc, canvas.tc, \\\\\\\" \\\\\\\"\\\\\\\
- end\\\\\\\
- end )\\\\\\\
- end\\\\\\\
- function t.clear( )\\\\\\\
- canvas.image:foreach( function( x, y, bc, tc, char )\\\\\\\
- return canvas.bc, canvas.tc, \\\\\\\" \\\\\\\"\\\\\\\
- end )\\\\\\\
- end\\\\\\\
- function t.setCursorPos( x, y )\\\\\\\
- if type( x ) ~= \\\\\\\"number\\\\\\\" or type( y ) ~= \\\\\\\"number\\\\\\\" then\\\\\\\
- return error \\\\\\\"expected number, number\\\\\\\"\\\\\\\
- end\\\\\\\
- canvas.cx = x\\\\\\\
- canvas.cy = y\\\\\\\
- end\\\\\\\
- function t.getCursorPos( )\\\\\\\
- return canvas.cx, canvas.cy\\\\\\\
- end\\\\\\\
- function t.setBackgroundColour( col )\\\\\\\
- if type( col ) ~= \\\\\\\"number\\\\\\\" then\\\\\\\
- return error \\\\\\\"expected number\\\\\\\"\\\\\\\
- end\\\\\\\
- canvas.bc = col\\\\\\\
- end\\\\\\\
- function t.setTextColour( col )\\\\\\\
- if type( col ) ~= \\\\\\\"number\\\\\\\" then\\\\\\\
- return error \\\\\\\"expected number\\\\\\\"\\\\\\\
- end\\\\\\\
- canvas.tc = col\\\\\\\
- end\\\\\\\
- function t.setBackgroundColor( col )\\\\\\\
- if type( col ) ~= \\\\\\\"number\\\\\\\" then\\\\\\\
- return error \\\\\\\"expected number\\\\\\\"\\\\\\\
- end\\\\\\\
- canvas.bc = col\\\\\\\
- end\\\\\\\
- function t.setTextColor( col )\\\\\\\
- if type( col ) ~= \\\\\\\"number\\\\\\\" then\\\\\\\
- return error \\\\\\\"expected number\\\\\\\"\\\\\\\
- end\\\\\\\
- canvas.tc = col\\\\\\\
- end\\\\\\\
- function t.scroll( dir )\\\\\\\
- canvas.image:foreach( function( x, y, bc, tc, char )\\\\\\\
- if canvas.image:getPixel( x, y + dir ) then\\\\\\\
- return canvas.image:getPixel( x, y + dir )\\\\\\\
- else\\\\\\\
- return canvas.bc, canvas.tc, \\\\\\\" \\\\\\\"\\\\\\\
- end\\\\\\\
- end )\\\\\\\
- end\\\\\\\
- function t.setCursorBlink( state )\\\\\\\
- canvas.cb = not not state\\\\\\\
- end\\\\\\\
- function t.isColour( )\\\\\\\
- return colour\\\\\\\
- end\\\\\\\
- function t.isColor( )\\\\\\\
- return colour\\\\\\\
- end\\\\\\\
- function t.getSize( )\\\\\\\
- return canvas.w, canvas.h\\\\\\\
- end\\\\\\\
- \\\\\\\
- return t\\\\\\\
- end\\\\\\\
- \\\\\\\
- local function luaEnvironment( canvas )\\\\\\\
- local env = { }\\\\\\\
- env.fs = fs\\\\\\\
- env.term = term\\\\\\\
- env._VERSION = _VERSION\\\\\\\
- env.pairs = pairs\\\\\\\
- env.ipairs = ipairs\\\\\\\
- env.select = select\\\\\\\
- env.unpack = unpack\\\\\\\
- env.setfenv = setfenv\\\\\\\
- env.getfenv = getfenv\\\\\\\
- env.setmetatable = setmetatable\\\\\\\
- env.getmetatable = getmetatable\\\\\\\
- env.next = next\\\\\\\
- env.rawset = rawset\\\\\\\
- env.rawget = rawget\\\\\\\
- env.rawequal = rawequal\\\\\\\
- env.type = type\\\\\\\
- env.tostring = tostring\\\\\\\
- env.tonumber = tonumber\\\\\\\
- env.pcall = pcall\\\\\\\
- env.xpcall = xpcall\\\\\\\
- env.loadstring = loadstring\\\\\\\
- env.assert = assert\\\\\\\
- env.error = error\\\\\\\
- env.sleep = sleep\\\\\\\
- env.__inext = __inext\\\\\\\
- env.math = math\\\\\\\
- env.string = string\\\\\\\
- env.table = table\\\\\\\
- env.coroutine = coroutine\\\\\\\
- env.keys = keys\\\\\\\
- env.colours = colours\\\\\\\
- env.colors = colors\\\\\\\
- env.vector = vector\\\\\\\
- env.bit = bit\\\\\\\
- env.http = http\\\\\\\
- env.write = write\\\\\\\
- env.print = print\\\\\\\
- env.printError = printError\\\\\\\
- env.read = read\\\\\\\
- env.rednet = rednet\\\\\\\
- local tAPIsLoading = { }\\\\\\\
- env.os = setmetatable( {\\\\\\\
- pullEventRaw = function( sFilter )\\\\\\\
- while true do\\\\\\\
- local event = { coroutine.yield( ) }\\\\\\\
- if not sFilter or sFilter == event[1] then\\\\\\\
- return unpack( event )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- end;\\\\\\\
- pullEvent = function( sFilter )\\\\\\\
- while true do\\\\\\\
- local event = { coroutine.yield( ) }\\\\\\\
- if event[1] == \\\\\\\"terminate\\\\\\\" then\\\\\\\
- error( \\\\\\\"Terminated\\\\\\\", 0 )\\\\\\\
- end\\\\\\\
- if not sFilter or sFilter == event[1] then\\\\\\\
- return unpack( event )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- end;\\\\\\\
- run = function( _tEnv, _sPath, ... )\\\\\\\
- local tArgs = { ... }\\\\\\\
- local fnFile, err = env.loadfile( _sPath )\\\\\\\
- if fnFile then\\\\\\\
- local tEnv = _tEnv\\\\\\\
- --setmetatable( tEnv, { __index = function(t,k) return _G[k] end } )\\\\\\\
- setmetatable( tEnv, { __index = env } )\\\\\\\
- setfenv( fnFile, tEnv )\\\\\\\
- local ok, err = pcall( function()\\\\\\\
- fnFile( unpack( tArgs ) )\\\\\\\
- end )\\\\\\\
- if not ok then\\\\\\\
- if err and err ~= \\\\\\\"\\\\\\\" then\\\\\\\
- printError( err )\\\\\\\
- end\\\\\\\
- return false\\\\\\\
- end\\\\\\\
- return true\\\\\\\
- end\\\\\\\
- if err and err ~= \\\\\\\"\\\\\\\" then\\\\\\\
- printError( err )\\\\\\\
- end\\\\\\\
- return false\\\\\\\
- end;\\\\\\\
- loadAPI = function( _sPath )\\\\\\\
- local sName = fs.getName( _sPath )\\\\\\\
- if tAPIsLoading[sName] == true then\\\\\\\
- printError( \\\\\\\"API \\\\\\\"..sName..\\\\\\\" is already being loaded\\\\\\\" )\\\\\\\
- return false\\\\\\\
- end\\\\\\\
- tAPIsLoading[sName] = true\\\\\\\
- \\\\\\\
- local tEnv = {}\\\\\\\
- setmetatable( tEnv, { __index = env } )\\\\\\\
- local fnAPI, err = loadfile( _sPath )\\\\\\\
- if fnAPI then\\\\\\\
- setfenv( fnAPI, tEnv )\\\\\\\
- fnAPI()\\\\\\\
- else\\\\\\\
- printError( err )\\\\\\\
- tAPIsLoading[sName] = nil\\\\\\\
- return false\\\\\\\
- end\\\\\\\
- \\\\\\\
- local tAPI = {}\\\\\\\
- for k,v in pairs( tEnv ) do\\\\\\\
- tAPI[k] = v\\\\\\\
- end\\\\\\\
- \\\\\\\
- env[sName] = tAPI \\\\\\\
- tAPIsLoading[sName] = nil\\\\\\\
- return true\\\\\\\
- end;\\\\\\\
- unloadAPI = function( _sName )\\\\\\\
- if _sName ~= \\\\\\\"_G\\\\\\\" and type(env[_sName]) == \\\\\\\"table\\\\\\\" then\\\\\\\
- env[_sName] = nil\\\\\\\
- end\\\\\\\
- end;\\\\\\\
- }, { __index = os } );\\\\\\\
- env.help = help\\\\\\\
- env.io = io\\\\\\\
- env.parallel = parallel\\\\\\\
- \\\\\\\
- env.loadfile = function( _sFile )\\\\\\\
- local file = env.fs.open( _sFile, \\\\\\\"r\\\\\\\" )\\\\\\\
- if file then\\\\\\\
- local func, err = loadstring( file.readAll(), env.fs.getName( _sFile ) )\\\\\\\
- file.close()\\\\\\\
- return func, err\\\\\\\
- end\\\\\\\
- return nil, \\\\\\\"File not found\\\\\\\"\\\\\\\
- end\\\\\\\
- \\\\\\\
- env.dofile = function( _sFile )\\\\\\\
- local fnFile, e = env.loadfile( _sFile )\\\\\\\
- if fnFile then\\\\\\\
- setfenv( fnFile, env )\\\\\\\
- return fnFile()\\\\\\\
- else\\\\\\\
- error( e, 2 )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- env.shell = shell\\\\\\\
- env.multishell = multishell\\\\\\\
- env.redstone = redstone -- change in future, use device system\\\\\\\
- env.rs = rs -- change in future, use device system\\\\\\\
- env.gps = gps -- change in future, use Nova gps system\\\\\\\
- env.peripheral = peripheral -- change in future, use device system\\\\\\\
- env.disk = disk -- change in future, use filesystem\\\\\\\
- env.window = window -- oh crap...\\\\\\\
- env.textutils = textutils\\\\\\\
- env.paintutils = paintutils\\\\\\\
- env.term = term\\\\\\\
- \\\\\\\
- env._G = env\\\\\\\
- \\\\\\\
- return env\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIBuffer:UIBuffer( x, y, w, h )\\\\\\\
- self:UIElement( x, y, w, h )\\\\\\\
- self.image = Image( w, h )\\\\\\\
- self.image:foreach( function( x, y, bc, tc, char )\\\\\\\
- return colours.black, colours.white, \\\\\\\" \\\\\\\"\\\\\\\
- end )\\\\\\\
- self.term = termObject( self.public )\\\\\\\
- self.running = false\\\\\\\
- self.co = false\\\\\\\
- self.cx = 1\\\\\\\
- self.cy = 1\\\\\\\
- self.bc = colours.black\\\\\\\
- self.tc = colours.white\\\\\\\
- self.cb = false\\\\\\\
- self.environment = luaEnvironment( )\\\\\\\
- return self.public\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIBuffer.public:resize( w, h )\\\\\\\
- self.image:resize( w, h, self.bc, self.tc, \\\\\\\" \\\\\\\" )\\\\\\\
- self.w = w\\\\\\\
- self.h = h\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIBuffer.public:setTask( func )\\\\\\\
- setfenv( func, self.environment )\\\\\\\
- self.co = coroutine.create( func )\\\\\\\
- self.running = true\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIBuffer.public:passEvent( ... )\\\\\\\
- if not self.running then return end\\\\\\\
- local prev = term.redirect( self.term )\\\\\\\
- local ok, err = coroutine.resume( self.co, ... )\\\\\\\
- term.redirect( prev )\\\\\\\
- if not ok then\\\\\\\
- self.running = false\\\\\\\
- local prev = term.redirect( self.term )\\\\\\\
- self.environment.printError( err )\\\\\\\
- term.redirect( prev )\\\\\\\
- end\\\\\\\
- if coroutine.status( self.co ) == \\\\\\\"dead\\\\\\\" then\\\\\\\
- self.running = false\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIBuffer.public:draw( x, y )\\\\\\\
- local layer = stencil.addLayer( x, y, self.w, self.h )\\\\\\\
- self.image:resize( self.w, self.h )\\\\\\\
- self.image:foreach( function( px, py, bc, tc, char )\\\\\\\
- stencil.pixel( x + px - 1, y + py - 1, bc, tc, char )\\\\\\\
- return bc, tc, char\\\\\\\
- end )\\\\\\\
- if self.cb then\\\\\\\
- stencil.setCursorBlink( x + self.cx - 1, y + self.cy - 1, self.tc )\\\\\\\
- end\\\\\\\
- local c = self.public:getChildren( )\\\\\\\
- for i, child in ipairs( c ) do\\\\\\\
- child:draw( x + child.x - 1 + self.cx, y + child.y - 1 + self.cy )\\\\\\\
- end\\\\\\\
- stencil.closeLayer( layer )\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIBuffer.public:onMouseClick( x, y, button )\\\\\\\
- self.public:passEvent( \\\\\\\"mouse_click\\\\\\\", button, x, y )\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIBuffer.public:onMouseDrag( x, y, cx, cy, button )\\\\\\\
- self.public:passEvent( \\\\\\\"mouse_drag\\\\\\\", button, x, y )\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIBuffer.public:onMouseScroll( x, y, dir )\\\\\\\
- self.public:passEvent( \\\\\\\"mouse_scroll\\\\\\\", dir, x, y )\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIBuffer.public:onKeyPress( key, lastkey )\\\\\\\
- self.public:passEvent( \\\\\\\"key\\\\\\\", key )\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIBuffer.public:onTextInput( text, lastkey )\\\\\\\
- self.public:passEvent( \\\\\\\"char\\\\\\\", text )\\\\\\\
- end\\\", meta={\\\
- type = \\\"class\\\",\\\
- }};[\\\"parser\\\"]={content=\\\"\\\\\\\
- local function split( str, pat )\\\\\\\
- local sopened = false\\\\\\\
- local opened = false\\\\\\\
- local parts = { }\\\\\\\
- local last = 1\\\\\\\
- for i = 1,#str do\\\\\\\
- if str:sub( i, i ) == \\\\\\\"\\\\\\\\\\\\\\\"\\\\\\\" then\\\\\\\
- local count = 0\\\\\\\
- for ii = i - 1, 1, -1 do\\\\\\\
- if str:sub( ii, ii ) ~= \\\\\\\"\\\\\\\\\\\\\\\\\\\\\\\" then\\\\\\\
- break\\\\\\\
- end\\\\\\\
- count = count + 1\\\\\\\
- end\\\\\\\
- if math.floor( count / 2 ) == count / 2 then -- an even number of \\\\\\\\s therefore a string opener\\\\\\\
- if sopened and sopened == str:sub( i, i ) then\\\\\\\
- sopened = false\\\\\\\
- elseif not sopened then\\\\\\\
- sopened = str:sub( i, i )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- elseif not sopened then\\\\\\\
- if str:sub( i, i + #pat - 1 ) == pat then\\\\\\\
- table.insert( parts, str:sub( last, i - 1 ) )\\\\\\\
- last = i + #pat\\\\\\\
- end\\\\\\\
- end\\\\\\\
- end\\\\\\\
- table.insert( parts, str:sub( last ) )\\\\\\\
- return parts\\\\\\\
- end\\\\\\\
- \\\\\\\
- local function findTags( str )\\\\\\\
- local sopened = false\\\\\\\
- local opened = false\\\\\\\
- local tags = { }\\\\\\\
- for i = 1,#str do\\\\\\\
- if str:sub( i, i ) == \\\\\\\"\\\\\\\\\\\\\\\"\\\\\\\" then\\\\\\\
- local count = 0\\\\\\\
- for ii = i - 1, 1, -1 do\\\\\\\
- if str:sub( ii, ii ) ~= \\\\\\\"\\\\\\\\\\\\\\\\\\\\\\\" then\\\\\\\
- break\\\\\\\
- end\\\\\\\
- count = count + 1\\\\\\\
- end\\\\\\\
- if math.floor( count / 2 ) == count / 2 then -- an even number of \\\\\\\\s therefore a string opener\\\\\\\
- if sopened and sopened == str:sub( i, i ) then\\\\\\\
- sopened = false\\\\\\\
- elseif not sopened then\\\\\\\
- sopened = str:sub( i, i )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- elseif not sopened then\\\\\\\
- if str:sub( i, i ) == \\\\\\\"<\\\\\\\" then\\\\\\\
- opened = i\\\\\\\
- elseif str:sub( i, i ) == \\\\\\\">\\\\\\\" then\\\\\\\
- table.insert( tags, { start = opened, finish = i, content = str:sub( opened + 1, i - 1 ) } )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- end\\\\\\\
- return tags\\\\\\\
- end\\\\\\\
- \\\\\\\
- local function readTags( tags )\\\\\\\
- for i = 1,#tags do\\\\\\\
- local c = tags[i].content\\\\\\\
- tags[i].content = nil\\\\\\\
- if c:sub( 1, 1 ) == \\\\\\\"/\\\\\\\" then\\\\\\\
- tags[i].type = \\\\\\\"close\\\\\\\"\\\\\\\
- tags[i].name = c:sub( 2 )\\\\\\\
- else\\\\\\\
- local parts = split( c, \\\\\\\" \\\\\\\" )\\\\\\\
- tags[i].type = \\\\\\\"open\\\\\\\"\\\\\\\
- tags[i].name = parts[1]\\\\\\\
- tags[i].attributes = { }\\\\\\\
- for ii = 2,#parts do\\\\\\\
- local s, f, name, value = parts[ii]:find( \\\\\\\"([%w_]-)[:=](.+)\\\\\\\" )\\\\\\\
- if s then\\\\\\\
- tags[i].attributes[name] = value\\\\\\\
- end\\\\\\\
- end\\\\\\\
- end\\\\\\\
- end\\\\\\\
- return tags\\\\\\\
- end\\\\\\\
- \\\\\\\
- local function loadAttributes( at )\\\\\\\
- local vals = { }\\\\\\\
- for k, v in pairs( at ) do\\\\\\\
- if v:sub( 1, 1 ) == \\\\\\\"\\\\\\\\\\\\\\\"\\\\\\\" and v:sub( #v ) == \\\\\\\"\\\\\\\\\\\\\\\"\\\\\\\" then\\\\\\\
- local str = loadstring( \\\\\\\"return \\\\\\\" .. v, \\\\\\\"string\\\\\\\" )\\\\\\\
- if str then\\\\\\\
- vals[k] = str( )\\\\\\\
- end\\\\\\\
- else\\\\\\\
- if v == \\\\\\\"true\\\\\\\" or v == \\\\\\\"false\\\\\\\" then\\\\\\\
- vals[k] = v == \\\\\\\"true\\\\\\\"\\\\\\\
- elseif tonumber( v ) then\\\\\\\
- vals[k] = tonumber( v )\\\\\\\
- else\\\\\\\
- vals[k] = v\\\\\\\
- end\\\\\\\
- end\\\\\\\
- end\\\\\\\
- return vals\\\\\\\
- end\\\\\\\
- \\\\\\\
- local _tags = {\\\\\\\
- [\\\\\\\"script\\\\\\\"] = {\\\\\\\
- content = true;\\\\\\\
- children = false;\\\\\\\
- whenloaded = function( parent, attributes, content, scripts, ids, errors )\\\\\\\
- local name = attributes.name or \\\\\\\"script\\\\\\\"\\\\\\\
- local f, err = loadstring( content, name )\\\\\\\
- if f then\\\\\\\
- table.insert( scripts, { run = true, script = f } )\\\\\\\
- else\\\\\\\
- table.insert( errors, err )\\\\\\\
- end\\\\\\\
- end;\\\\\\\
- };\\\\\\\
- [\\\\\\\"style\\\\\\\"] = {\\\\\\\
- content = true;\\\\\\\
- children = false;\\\\\\\
- whenloaded = function( parent, attributes, content, scripts, ids )\\\\\\\
- local s = Style( content )\\\\\\\
- s:save( attributes.name or \\\\\\\"default\\\\\\\" )\\\\\\\
- end;\\\\\\\
- };\\\\\\\
- }\\\\\\\
- \\\\\\\
- function registerTag( name, content, children, whenloaded, parents, childrenallowed )\\\\\\\
- _tags[name] = { content = content, children = children, whenloaded = whenloaded, parents = parents, childrenallowed = childrenallowed }\\\\\\\
- end\\\\\\\
- \\\\\\\
- local loadString\\\\\\\
- \\\\\\\
- local function groupTags( tags, str, parenttype )\\\\\\\
- local t = { }\\\\\\\
- local i = 1\\\\\\\
- while i <= #tags do\\\\\\\
- local name = tags[i].name\\\\\\\
- if _tags[name] then\\\\\\\
- if tags[i].type == \\\\\\\"open\\\\\\\" then\\\\\\\
- if _tags[name].parents then\\\\\\\
- local ok = false\\\\\\\
- for i = 1,#_tags[name].parents do\\\\\\\
- if _tags[name].parents[i] == parenttype then\\\\\\\
- ok = true\\\\\\\
- break\\\\\\\
- end\\\\\\\
- end\\\\\\\
- if not ok then\\\\\\\
- error( \\\\\\\"cannot have \\\\\\\" .. parenttype .. \\\\\\\" as a parent to \\\\\\\" .. name, 0 )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- local tag = {\\\\\\\
- name = name;\\\\\\\
- attributes = loadAttributes( tags[i].attributes );\\\\\\\
- whenloaded = _tags[name].whenloaded;\\\\\\\
- }\\\\\\\
- if _tags[name].content then\\\\\\\
- local layer = 1\\\\\\\
- local found = false\\\\\\\
- for c = i + 1, #tags do\\\\\\\
- if tags[c].name == name then\\\\\\\
- if tags[c].type == \\\\\\\"open\\\\\\\" then\\\\\\\
- layer = layer + 1\\\\\\\
- else\\\\\\\
- layer = layer - 1\\\\\\\
- if layer == 0 then\\\\\\\
- found = c\\\\\\\
- break\\\\\\\
- end\\\\\\\
- end\\\\\\\
- end\\\\\\\
- end\\\\\\\
- if found then\\\\\\\
- tag.content = str:sub( tags[i].finish + 1, tags[found].start - 1 )\\\\\\\
- i = found\\\\\\\
- else\\\\\\\
- error( \\\\\\\"ending tag expected for \\\\\\\" .. name, 0 )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- if tag.content and _tags[name].children then\\\\\\\
- tag.children = loadString( tag.content, tag.name )\\\\\\\
- tag.content = nil\\\\\\\
- if _tags[name].childrenallowed then\\\\\\\
- for i = 1,#tag.children do\\\\\\\
- local ok = false\\\\\\\
- for ii = 1,#_tags[name].childrenallowed do\\\\\\\
- if _tags[name].childrenallowed[ii] == tag.children[i].name then\\\\\\\
- ok = true\\\\\\\
- break\\\\\\\
- end\\\\\\\
- end\\\\\\\
- if not ok then\\\\\\\
- error( \\\\\\\"not allowed \\\\\\\" .. tag.children[i].name .. \\\\\\\" inside a \\\\\\\" .. tag.name, 0 )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- end\\\\\\\
- end\\\\\\\
- table.insert( t, tag )\\\\\\\
- i = i + 1\\\\\\\
- else\\\\\\\
- error( \\\\\\\"expected opening tag before closing tag for \\\\\\\" .. name, 0 )\\\\\\\
- end\\\\\\\
- else\\\\\\\
- error( \\\\\\\"unknown tag: \\\\\\\" .. name, 0 )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- return t\\\\\\\
- end\\\\\\\
- \\\\\\\
- function loadString( str, parent )\\\\\\\
- return groupTags( readTags( findTags( str ) ), str, parent or \\\\\\\"frame\\\\\\\" )\\\\\\\
- end\\\\\\\
- \\\\\\\
- local loadElements\\\\\\\
- function loadElements( children, parent, scripts, ids, errors )\\\\\\\
- if not scripts then\\\\\\\
- scripts = { }\\\\\\\
- ids = { }\\\\\\\
- errors = { }\\\\\\\
- end\\\\\\\
- for i = 1,#children do\\\\\\\
- local child = children[i].whenloaded( parent, children[i].attributes, children[i].content, scripts, ids, errors )\\\\\\\
- if children[i].children then\\\\\\\
- loadElements( children[i].children, child, scripts, ids, errors )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- return parent, scripts, ids, errors\\\\\\\
- end\\\\\\\
- \\\\\\\
- function load( str, frame )\\\\\\\
- local tags = loadString( str )\\\\\\\
- return loadElements( tags, frame )\\\\\\\
- end\\\", meta={\\\
- type = \\\"lib\\\",\\\
- }};[\\\"UICode\\\"]={content=\\\"\\\\\\\
- require \\\\\\\"UIElement\\\\\\\"\\\\\\\
- \\\\\\\
- UICode:extends( UIElement )\\\\\\\
- \\\\\\\
- UICode.handlesKeys = true\\\\\\\
- UICode.handlesScroll = true\\\\\\\
- UICode.isScrollTarget = true\\\\\\\
- UICode.handlesTab = true\\\\\\\
- UICode.scrollDirection = \\\\\\\"vertical\\\\\\\"\\\\\\\
- \\\\\\\
- UICode.public \\\\\\\"showLines\\\\\\\" \\\\\\\"boolean\\\\\\\"\\\\\\\
- UICode.public \\\\\\\"syntax\\\\\\\" \\\\\\\"table\\\\\\\"\\\\\\\
- \\\\\\\
- UICode.public \\\\\\\"onChange\\\\\\\" \\\\\\\"function\\\\\\\"\\\\\\\
- UICode.public \\\\\\\"onCtrlKey\\\\\\\" \\\\\\\"function\\\\\\\"\\\\\\\
- \\\\\\\
- UICode.public \\\\\\\"selected\\\\\\\"\\\\\\\
- UICode.public.selected.write = false\\\\\\\
- function UICode.public.selected:read( )\\\\\\\
- return not not self.selection\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UICode:UICode( x, y, w, h, syntax )\\\\\\\
- self:UIElement( x, y, w, h )\\\\\\\
- \\\\\\\
- self.showLines = true\\\\\\\
- self.lineWidth = 4\\\\\\\
- self.linechars = { }\\\\\\\
- \\\\\\\
- self.selection = false\\\\\\\
- \\\\\\\
- self.scrollx = 0\\\\\\\
- self.scrolly = 0\\\\\\\
- self.cursorx = 1\\\\\\\
- self.cursory = 1\\\\\\\
- \\\\\\\
- self.lastclick = false\\\\\\\
- \\\\\\\
- self.syntax = syntax or {\\\\\\\
- default = { bc = colours.white, tc = colours.black };\\\\\\\
- words = { };\\\\\\\
- blocks = { };\\\\\\\
- linen = { bc = colours.grey, tc = colours.white };\\\\\\\
- string = { tc = colours.lightGrey };\\\\\\\
- symbols = { };\\\\\\\
- selection = { bc = colours.blue, tc = colours.white };\\\\\\\
- }\\\\\\\
- \\\\\\\
- self.lines = { \\\\\\\"\\\\\\\" }\\\\\\\
- self.characters = { }\\\\\\\
- \\\\\\\
- return self.public\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UICode.public:updateCharacters( )\\\\\\\
- self.linechars = { [1] = 1 }\\\\\\\
- local str = table.concat( self.lines, \\\\\\\"\\\\\\\\n\\\\\\\" )\\\\\\\
- local characters = { }\\\\\\\
- local line = 1\\\\\\\
- local pos = 1\\\\\\\
- local done = true\\\\\\\
- local function nextC( )\\\\\\\
- local char = str:sub( pos, pos )\\\\\\\
- pos = pos + 1\\\\\\\
- done = false\\\\\\\
- return char\\\\\\\
- end\\\\\\\
- local function setC( colour, char, d )\\\\\\\
- if done and not d then return end\\\\\\\
- if char == \\\\\\\"\\\\\\\\n\\\\\\\" then\\\\\\\
- done = true\\\\\\\
- line = line + 1\\\\\\\
- -- self.linechars[line] = pos\\\\\\\
- return\\\\\\\
- end\\\\\\\
- if #char == 0 then return end\\\\\\\
- done = true\\\\\\\
- table.insert( characters, { colour = colour, char = char, line = line } )\\\\\\\
- end\\\\\\\
- while pos <= #str do\\\\\\\
- local c = nextC( )\\\\\\\
- if c == \\\\\\\"\\\\\\\\\\\\\\\"\\\\\\\" then\\\\\\\
- setC( self.syntax.string, c )\\\\\\\
- local escape = false\\\\\\\
- for i = pos, #str do\\\\\\\
- c = nextC( )\\\\\\\
- if escape then\\\\\\\
- setC( self.syntax.string.escape or self.syntax.string, c )\\\\\\\
- escape = false\\\\\\\
- else\\\\\\\
- if c == \\\\\\\"\\\\\\\\\\\\\\\\\\\\\\\" then\\\\\\\
- setC( self.syntax.string.escape or self.syntax.string, c )\\\\\\\
- escape = true\\\\\\\
- elseif c == \\\\\\\"\\\\\\\\\\\\\\\"\\\\\\\" then\\\\\\\
- setC( self.syntax.string, c )\\\\\\\
- break\\\\\\\
- else\\\\\\\
- setC( self.syntax.string, c )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- end\\\\\\\
- elseif c:find \\\\\\\"[a-zA-Z_]\\\\\\\" then\\\\\\\
- local word = c\\\\\\\
- c = nextC( )\\\\\\\
- while c:find \\\\\\\"[a-zA-Z0-9_]\\\\\\\" do\\\\\\\
- word = word .. c\\\\\\\
- c = nextC( )\\\\\\\
- end\\\\\\\
- for i = 1, #word do\\\\\\\
- if self.syntax.words[word] then\\\\\\\
- setC( self.syntax.words[word], word:sub( i, i ), true )\\\\\\\
- else\\\\\\\
- setC( self.syntax.default, word:sub( i, i ), true )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- done = false\\\\\\\
- elseif self.syntax.symbols[c] then\\\\\\\
- setC( self.syntax.symbols[c], c )\\\\\\\
- end\\\\\\\
- if not done then\\\\\\\
- for i = 1, #self.syntax.blocks do\\\\\\\
- if c == self.syntax.blocks[i].start:sub( 1, 1 ) then\\\\\\\
- if str:sub( pos, pos + #self.syntax.blocks[i].start - 2 ) == self.syntax.blocks[i].start:sub( 2 ) then\\\\\\\
- local block = self.syntax.blocks[i]\\\\\\\
- setC( block, c )\\\\\\\
- for i = 1, #block.start - 1 do\\\\\\\
- c = nextC( )\\\\\\\
- setC( block, c )\\\\\\\
- end\\\\\\\
- while #c > 0 do\\\\\\\
- if str:sub( pos, pos + #block.finish - 1 ) == block.finish then\\\\\\\
- for i = 1, #block.finish do\\\\\\\
- c = nextC( )\\\\\\\
- setC( block, c )\\\\\\\
- end\\\\\\\
- break\\\\\\\
- end\\\\\\\
- c = nextC( )\\\\\\\
- setC( block, c )\\\\\\\
- end\\\\\\\
- break\\\\\\\
- end\\\\\\\
- end\\\\\\\
- end\\\\\\\
- end\\\\\\\
- setC( self.syntax.default, c )\\\\\\\
- end\\\\\\\
- self.characters = characters\\\\\\\
- if self.showLines then\\\\\\\
- self.lineWidth = math.floor( math.log( #self.lines ) / math.log( 10 ) ) + 4\\\\\\\
- else\\\\\\\
- self.lineWidth = 0\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UICode.public:update( dt )\\\\\\\
- if self.cursory > #self.lines then\\\\\\\
- self.cursory = #self.lines\\\\\\\
- end\\\\\\\
- if self.cursorx > #( self.lines[self.cursory] or \\\\\\\"\\\\\\\" ) + 1 then\\\\\\\
- self.cursorx = #( self.lines[self.cursory] or \\\\\\\"\\\\\\\" ) + 1\\\\\\\
- end\\\\\\\
- local c = self.public:getChildren( )\\\\\\\
- for i, child in ipairs( c ) do\\\\\\\
- child:update( dt )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- local tabsize = 4\\\\\\\
- \\\\\\\
- function UICode.public:draw( x, y )\\\\\\\
- local layer = stencil.addLayer( x, y, self.w, self.h )\\\\\\\
- local characters = self.characters\\\\\\\
- stencil.rectangle( x, y, self.w, self.h, self.syntax.default.bc, self.syntax.default.tc, \\\\\\\" \\\\\\\" )\\\\\\\
- if self.showLines then\\\\\\\
- for i = 1, self.h do\\\\\\\
- if i + self.scrolly <= #self.lines then\\\\\\\
- local n = tostring( i + self.scrolly )\\\\\\\
- stencil.textLine( x, y + i - 1, self.syntax.linen.bc, self.syntax.linen.tc, (\\\\\\\" \\\\\\\"):rep( self.lineWidth - 2 - #n ) .. n .. \\\\\\\" \\\\\\\" )\\\\\\\
- else\\\\\\\
- stencil.textLine( x, y + i - 1, self.syntax.linen.bc, 1, (\\\\\\\" \\\\\\\"):rep( self.lineWidth - 1 ) )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- end\\\\\\\
- local xx, yy, cx = 1, 1, 1\\\\\\\
- local tabthisline = true\\\\\\\
- local i = self.linechars[self.scrolly + 1] or 1\\\\\\\
- while i <= #characters do\\\\\\\
- if characters[i].line > yy then\\\\\\\
- xx = 1\\\\\\\
- cx = 1\\\\\\\
- yy = characters[i].line\\\\\\\
- tabthisline = true\\\\\\\
- end\\\\\\\
- \\\\\\\
- local bc, tc = characters[i].colour.bc or self.syntax.default.bc, characters[i].colour.tc or self.syntax.default.tc\\\\\\\
- if self.cursory == characters[i].line then\\\\\\\
- bc = characters[i].colour.lbc or characters[i].colour.bc or self.syntax.default.lbc or self.syntax.default.bc\\\\\\\
- tc = characters[i].colour.ltc or characters[i].colour.tc or self.syntax.default.ltc or self.syntax.default.tc\\\\\\\
- end\\\\\\\
- if self.selection then\\\\\\\
- local ssx, ssy, sex, sey\\\\\\\
- if self.selection.y < self.cursory then\\\\\\\
- ssy = self.selection.y\\\\\\\
- ssx = self.selection.x\\\\\\\
- sey = self.cursory\\\\\\\
- sex = self.cursorx\\\\\\\
- elseif self.selection.y > self.cursory then\\\\\\\
- ssy = self.cursory\\\\\\\
- ssx = self.cursorx\\\\\\\
- sey = self.selection.y\\\\\\\
- sex = self.selection.x\\\\\\\
- else\\\\\\\
- ssy = self.selection.y\\\\\\\
- ssx = math.min( self.selection.x, self.cursorx )\\\\\\\
- sey = self.cursory\\\\\\\
- sex = math.max( self.selection.x, self.cursorx )\\\\\\\
- end\\\\\\\
- if yy == ssy then\\\\\\\
- if yy == sey then\\\\\\\
- if cx >= ssx and cx <= sex then\\\\\\\
- bc = self.syntax.selection.bc\\\\\\\
- tc = self.syntax.selection.tc\\\\\\\
- end\\\\\\\
- else\\\\\\\
- if cx >= ssx then\\\\\\\
- bc = self.syntax.selection.bc\\\\\\\
- tc = self.syntax.selection.tc\\\\\\\
- end\\\\\\\
- end\\\\\\\
- elseif yy == sey then\\\\\\\
- if cx <= sex then\\\\\\\
- bc = self.syntax.selection.bc\\\\\\\
- tc = self.syntax.selection.tc\\\\\\\
- end\\\\\\\
- elseif yy > ssy and yy < sey then\\\\\\\
- bc = self.syntax.selection.bc\\\\\\\
- tc = self.syntax.selection.tc\\\\\\\
- end\\\\\\\
- end\\\\\\\
- local rx = xx - self.scrollx\\\\\\\
- local ry = yy - self.scrolly\\\\\\\
- if rx >= 1 and rx <= self.w - self.lineWidth and ry >= 1 and ry <= self.h then\\\\\\\
- if characters[i].char ~= \\\\\\\" \\\\\\\" then\\\\\\\
- tabthisline = false\\\\\\\
- stencil.pixel( x + rx - 1 + self.lineWidth, y + ry - 1, bc, tc, characters[i].char )\\\\\\\
- else\\\\\\\
- for i = 1, ( tabsize - ( xx - 1 ) % tabsize ) - 1 do\\\\\\\
- stencil.pixel( x + rx - 2 + self.lineWidth + i, y + ry - 1, bc, tc, \\\\\\\" \\\\\\\" )\\\\\\\
- end\\\\\\\
- if self.syntax.tab and tabthisline then\\\\\\\
- stencil.pixel( x + rx - 2 + self.lineWidth + ( tabsize - ( xx - 1 ) % tabsize ), y + ry - 1, self.syntax.tab.bc or bc, self.syntax.tab.tc or tc, self.syntax.tab.char or \\\\\\\" \\\\\\\" )\\\\\\\
- else\\\\\\\
- stencil.pixel( x + rx - 2 + self.lineWidth + ( tabsize - ( xx - 1 ) % tabsize ), y + ry - 1, bc, tc, \\\\\\\" \\\\\\\" )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- end\\\\\\\
- if characters[i].char == \\\\\\\" \\\\\\\" then\\\\\\\
- xx = xx + ( tabsize - ( xx - 1 ) % tabsize )\\\\\\\
- else\\\\\\\
- xx = xx + 1\\\\\\\
- end\\\\\\\
- if ry > self.h then\\\\\\\
- break\\\\\\\
- end\\\\\\\
- cx = cx + 1\\\\\\\
- i = i + 1\\\\\\\
- end\\\\\\\
- if self.focussed and not self.selection then\\\\\\\
- local xx, yy = self:getCursorPos( )\\\\\\\
- stencil.setCursorBlink( x + xx - 1 + self.lineWidth, y + yy - 1, self.syntax.default.tc )\\\\\\\
- end\\\\\\\
- local c = self.public:getChildren( )\\\\\\\
- for i, child in ipairs( c ) do\\\\\\\
- child:draw( x + child.x - 1 + self.cx, y + child.y - 1 + self.cy )\\\\\\\
- end\\\\\\\
- stencil.closeLayer( layer )\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UICode:setCursorPos( x, y )\\\\\\\
- self.cursorx = x\\\\\\\
- self.cursory = y\\\\\\\
- local ry = self.cursory\\\\\\\
- local l = self.lines[self.cursory]\\\\\\\
- local rx = 0\\\\\\\
- for i = 1, self.cursorx - 1 do\\\\\\\
- if l:sub( i, i ) == \\\\\\\" \\\\\\\" then\\\\\\\
- rx = rx + ( tabsize - rx % tabsize )\\\\\\\
- else\\\\\\\
- rx = rx + 1\\\\\\\
- end\\\\\\\
- end\\\\\\\
- rx = rx + 1\\\\\\\
- if ry <= self.scrolly then\\\\\\\
- self.scrolly = ry - 1\\\\\\\
- elseif ry >= self.scrolly + self.h then\\\\\\\
- self.scrolly = ry - self.h\\\\\\\
- end\\\\\\\
- if rx <= self.scrollx then\\\\\\\
- self.scrollx = rx - 1\\\\\\\
- elseif rx >= self.scrollx + ( self.w - self.lineWidth ) then\\\\\\\
- self.scrollx = rx - ( self.w - self.lineWidth )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UICode:getCursorPos( )\\\\\\\
- local y = self.cursory - self.scrolly\\\\\\\
- local l = self.lines[self.cursory] or \\\\\\\"\\\\\\\"\\\\\\\
- local x = 0\\\\\\\
- for i = 1, self.cursorx - 1 do\\\\\\\
- if l:sub( i, i ) == \\\\\\\" \\\\\\\" then\\\\\\\
- x = x + ( tabsize - x % tabsize )\\\\\\\
- else\\\\\\\
- x = x + 1\\\\\\\
- end\\\\\\\
- end\\\\\\\
- return x + 1 - self.scrollx, y\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UICode:coordsToCursor( x, y )\\\\\\\
- if x <= self.lineWidth then\\\\\\\
- return\\\\\\\
- end\\\\\\\
- x = x + self.scrollx - self.lineWidth\\\\\\\
- if self.lines[y+self.scrolly] then\\\\\\\
- local l = self.lines[y + self.scrolly]\\\\\\\
- local cx = 0\\\\\\\
- for i = 1, #l do\\\\\\\
- if l:sub( i, i ) == \\\\\\\" \\\\\\\" then\\\\\\\
- cx = cx + ( tabsize - ( cx ) % tabsize )\\\\\\\
- else\\\\\\\
- cx = cx + 1\\\\\\\
- end\\\\\\\
- if cx >= x then\\\\\\\
- return i, y + self.scrolly\\\\\\\
- end\\\\\\\
- end\\\\\\\
- return #l + 1, y + self.scrolly\\\\\\\
- end\\\\\\\
- return #self.lines[#self.lines] + 1, #self.lines\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UICode:getSelectionBounds( )\\\\\\\
- local ssx, ssy, sex, sey\\\\\\\
- if self.selection.y < self.cursory then\\\\\\\
- ssy = self.selection.y\\\\\\\
- ssx = self.selection.x\\\\\\\
- sey = self.cursory\\\\\\\
- sex = self.cursorx\\\\\\\
- elseif self.selection.y > self.cursory then\\\\\\\
- ssy = self.cursory\\\\\\\
- ssx = self.cursorx\\\\\\\
- sey = self.selection.y\\\\\\\
- sex = self.selection.x\\\\\\\
- else\\\\\\\
- ssy = self.selection.y\\\\\\\
- ssx = math.min( self.selection.x, self.cursorx )\\\\\\\
- sey = self.cursory\\\\\\\
- sex = math.max( self.selection.x, self.cursorx )\\\\\\\
- end\\\\\\\
- return ssx, ssy, sex, sey\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UICode:write( str )\\\\\\\
- local cx, cy = self.cursorx, self.cursory\\\\\\\
- for i = 1, #str do\\\\\\\
- if str:sub( i, i ) == \\\\\\\"\\\\\\\\n\\\\\\\" then\\\\\\\
- cy = cy + 1\\\\\\\
- cx = 1\\\\\\\
- table.insert( self.lines, cy, \\\\\\\"\\\\\\\" )\\\\\\\
- else\\\\\\\
- self.lines[cy] = self.lines[cy]:sub( 1, cx - 1 ) .. str:sub( i, i ) .. self.lines[cy]:sub( cx )\\\\\\\
- cx = cx + 1\\\\\\\
- end\\\\\\\
- end\\\\\\\
- self:setCursorPos( cx, cy )\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UICode:setSelection( str )\\\\\\\
- local ssx, ssy, sex, sey = self:getSelectionBounds( )\\\\\\\
- local line_end = self.lines[sey]:sub( sex + 1 )\\\\\\\
- self.lines[ssy] = self.lines[ssy]:sub( 1, ssx - 1 )\\\\\\\
- self:setCursorPos( ssx, ssy )\\\\\\\
- for i = ssy + 1, sey do\\\\\\\
- table.remove( self.lines, ssy + 1 )\\\\\\\
- end\\\\\\\
- self:write( str .. line_end )\\\\\\\
- self:setCursorPos( self.cursorx - #line_end, self.cursory )\\\\\\\
- self.selection = false\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UICode.public:getSelection( str )\\\\\\\
- if self.selection then\\\\\\\
- local ssx, ssy, sex, sey = self:getSelectionBounds( )\\\\\\\
- if ssy == sey then\\\\\\\
- return self.lines[ssy]:sub( ssx, sex )\\\\\\\
- end\\\\\\\
- local selected = self.lines[ssy]:sub( ssx )\\\\\\\
- for i = ssy + 1, sey - 1 do\\\\\\\
- selected = selected .. \\\\\\\"\\\\\\\\n\\\\\\\" .. self.lines[i]\\\\\\\
- end\\\\\\\
- selected = selected .. \\\\\\\"\\\\\\\\n\\\\\\\" .. self.lines[sey]:sub( 1, sex )\\\\\\\
- return selected\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UICode.public:setSelection( str )\\\\\\\
- if self.selection then\\\\\\\
- self:setSelection( str )\\\\\\\
- self.public:updateCharacters( )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UICode.public:write( str )\\\\\\\
- self:write( str )\\\\\\\
- self.public:updateCharacters( )\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UICode.public:onMouseClick( rx, ry, button )\\\\\\\
- if self.selection then\\\\\\\
- self.selection = false\\\\\\\
- end\\\\\\\
- local x, y = self:coordsToCursor( rx, ry )\\\\\\\
- if self.lastclick and self.lastclick.x == rx and self.lastclick.y == ry and os.clock( ) - self.lastclick.time <= 0.3 and button == 1 then\\\\\\\
- local bounds = { min = x, max = x }\\\\\\\
- local line = self.lines[y]\\\\\\\
- local c = line:sub( x, x )\\\\\\\
- if c:find \\\\\\\"%s\\\\\\\" then\\\\\\\
- for i = x - 1, 1, -1 do\\\\\\\
- if not line:sub( i, i ):find \\\\\\\"%s\\\\\\\" then\\\\\\\
- break\\\\\\\
- end\\\\\\\
- bounds.min = i\\\\\\\
- end\\\\\\\
- for i = x + 1, #line do\\\\\\\
- if not line:sub( i, i ):find \\\\\\\"%s\\\\\\\" then\\\\\\\
- break\\\\\\\
- end\\\\\\\
- bounds.max = i\\\\\\\
- end\\\\\\\
- elseif c:find \\\\\\\"[%w_]\\\\\\\" then\\\\\\\
- for i = x - 1, 1, -1 do\\\\\\\
- if not line:sub( i, i ):find \\\\\\\"[%w_]\\\\\\\" then\\\\\\\
- break\\\\\\\
- end\\\\\\\
- bounds.min = i\\\\\\\
- end\\\\\\\
- for i = x + 1, #line do\\\\\\\
- if not line:sub( i, i ):find \\\\\\\"[%w_]\\\\\\\" then\\\\\\\
- break\\\\\\\
- end\\\\\\\
- bounds.max = i\\\\\\\
- end\\\\\\\
- else\\\\\\\
- for i = x - 1, 1, -1 do\\\\\\\
- if line:sub( i, i ):find \\\\\\\"%s\\\\\\\" or line:sub( i, i ):find \\\\\\\"[%w_]\\\\\\\" then\\\\\\\
- break\\\\\\\
- end\\\\\\\
- bounds.min = i\\\\\\\
- end\\\\\\\
- for i = x + 1, #line do\\\\\\\
- if line:sub( i, i ):find \\\\\\\"%s\\\\\\\" or line:sub( i, i ):find \\\\\\\"[%w_]\\\\\\\" then\\\\\\\
- break\\\\\\\
- end\\\\\\\
- bounds.max = i\\\\\\\
- end\\\\\\\
- end\\\\\\\
- self.selection = { x = bounds.min, y = y }\\\\\\\
- self.cursorx = bounds.max\\\\\\\
- self.cursory = y\\\\\\\
- self.lastclick = false\\\\\\\
- return\\\\\\\
- elseif button == 1 then\\\\\\\
- self.lastclick = { x = rx, y = ry, time = os.clock( ) }\\\\\\\
- end\\\\\\\
- if x then\\\\\\\
- self:setCursorPos( x, y )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UICode.public:onMouseDrag( rx, ry, cx, cy, button )\\\\\\\
- local x, y = self:coordsToCursor( rx, ry )\\\\\\\
- if x then\\\\\\\
- if not self.selection then\\\\\\\
- self.selection = { x = self.cursorx, y = self.cursory }\\\\\\\
- end\\\\\\\
- self:setCursorPos( x, y )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UICode.public:onMouseScroll( rx, ry, dir )\\\\\\\
- if self.scrolly > 0 and dir == -1 then\\\\\\\
- self.scrolly = self.scrolly - 1\\\\\\\
- elseif self.scrolly < #self.lines - self.h and dir == 1 then\\\\\\\
- self.scrolly = self.scrolly + 1\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UICode.public:onKeyPress( key, lastkey )\\\\\\\
- if not self.focussed then\\\\\\\
- return\\\\\\\
- end\\\\\\\
- if lastkey == 29 then\\\\\\\
- if key == keys.c or key == keys.x or key == keys.b then\\\\\\\
- if key == keys.c then\\\\\\\
- if self.selection then\\\\\\\
- clipboard.set( \\\\\\\"plaintext\\\\\\\", self.public:getSelection( ) )\\\\\\\
- else\\\\\\\
- clipboard.set( \\\\\\\"plaintext\\\\\\\", self.lines[self.cursory] or \\\\\\\"\\\\\\\" )\\\\\\\
- end\\\\\\\
- elseif key == keys.x then\\\\\\\
- if self.selection then\\\\\\\
- clipboard.set( \\\\\\\"plaintext\\\\\\\", self.public:getSelection( ) )\\\\\\\
- self:setSelection \\\\\\\"\\\\\\\"\\\\\\\
- self.selection = false\\\\\\\
- else\\\\\\\
- clipboard.set( \\\\\\\"plaintext\\\\\\\", self.lines[self.cursory] or \\\\\\\"\\\\\\\" )\\\\\\\
- table.remove( self.lines, self.cursory )\\\\\\\
- end\\\\\\\
- self.public:updateCharacters( )\\\\\\\
- if self.onChange then\\\\\\\
- self.onChange( self.public, \\\\\\\"cut\\\\\\\" )\\\\\\\
- end\\\\\\\
- elseif key == keys.b then\\\\\\\
- local mode, data = clipboard.get( )\\\\\\\
- if mode == \\\\\\\"plaintext\\\\\\\" then\\\\\\\
- if self.selection then\\\\\\\
- self:setSelection( data )\\\\\\\
- self.selection = false\\\\\\\
- else\\\\\\\
- self:write( data )\\\\\\\
- end\\\\\\\
- self.public:updateCharacters( )\\\\\\\
- elseif mode == \\\\\\\"file\\\\\\\" then\\\\\\\
- if self.selection then\\\\\\\
- self:setSelection( \\\\\\\"file:\\\\\\\\\\\\\\\"\\\\\\\" .. data.name .. \\\\\\\"\\\\\\\\\\\\\\\"\\\\\\\" )\\\\\\\
- self.selection = false\\\\\\\
- else\\\\\\\
- self:write( \\\\\\\"file:\\\\\\\\\\\\\\\"\\\\\\\" .. data.name .. \\\\\\\"\\\\\\\\\\\\\\\"\\\\\\\" )\\\\\\\
- end\\\\\\\
- self.public:updateCharacters( )\\\\\\\
- elseif self.onCtrlKey then\\\\\\\
- self.onCtrlKey( self.public, key )\\\\\\\
- end\\\\\\\
- if self.onChange then\\\\\\\
- self.onChange( self.public, \\\\\\\"paste\\\\\\\" )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- return\\\\\\\
- end\\\\\\\
- if self.onCtrlKey then\\\\\\\
- self.onCtrlKey( self.public, key )\\\\\\\
- end\\\\\\\
- return\\\\\\\
- end\\\\\\\
- if key == keys.left then\\\\\\\
- if self.selection then\\\\\\\
- local ssx, ssy, sex, sey = self:getSelectionBounds( )\\\\\\\
- self:setCursorPos( ssx, ssy )\\\\\\\
- self.selection = false\\\\\\\
- else\\\\\\\
- if self.cursorx == 1 then\\\\\\\
- if self.cursory > 1 then\\\\\\\
- self:setCursorPos( #self.lines[self.cursory - 1] + 1, self.cursory - 1 )\\\\\\\
- end\\\\\\\
- else\\\\\\\
- self:setCursorPos( self.cursorx - 1, self.cursory )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- elseif key == keys.right then\\\\\\\
- if self.selection then\\\\\\\
- local ssx, ssy, sex, sey = self:getSelectionBounds( )\\\\\\\
- self:setCursorPos( sex, sey )\\\\\\\
- self.selection = false\\\\\\\
- else\\\\\\\
- if self.cursorx >= #self.lines[self.cursory] + 1 then\\\\\\\
- if self.cursory < #self.lines then\\\\\\\
- self:setCursorPos( 1, self.cursory + 1 )\\\\\\\
- else\\\\\\\
- self.cursorx = #self.lines[self.cursory] + 1\\\\\\\
- end\\\\\\\
- else\\\\\\\
- self:setCursorPos( self.cursorx + 1, self.cursory )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- elseif key == keys.up and self.cursory > 1 then\\\\\\\
- if self.selection then\\\\\\\
- local ssx, ssy, sex, sey = self:getSelectionBounds( )\\\\\\\
- self:setCursorPos( ssx, ssy )\\\\\\\
- self.selection = false\\\\\\\
- else\\\\\\\
- local x, y = self:getCursorPos( )\\\\\\\
- self:setCursorPos( self:coordsToCursor( x + self.lineWidth, y - 1 ) )\\\\\\\
- end\\\\\\\
- elseif key == keys.down and self.cursory < #self.lines then\\\\\\\
- if self.selection then\\\\\\\
- local ssx, ssy, sex, sey = self:getSelectionBounds( )\\\\\\\
- self:setCursorPos( sex, sey )\\\\\\\
- self.selection = false\\\\\\\
- else\\\\\\\
- local x, y = self:getCursorPos( )\\\\\\\
- self:setCursorPos( self:coordsToCursor( x + self.lineWidth, y + 1 ) )\\\\\\\
- end\\\\\\\
- elseif key == keys.enter then\\\\\\\
- if self.selection then\\\\\\\
- self:setSelection \\\\\\\"\\\\\\\\n\\\\\\\"\\\\\\\
- else\\\\\\\
- local whitespace = self.lines[self.cursory]:match \\\\\\\"^(%s*)\\\\\\\"\\\\\\\
- table.insert( self.lines, self.cursory + 1, whitespace .. self.lines[self.cursory]:sub( self.cursorx ) )\\\\\\\
- self.lines[self.cursory] = self.lines[self.cursory]:sub( 1, self.cursorx - 1 )\\\\\\\
- self:setCursorPos( #whitespace + 1, self.cursory + 1 )\\\\\\\
- end\\\\\\\
- self.public:updateCharacters( )\\\\\\\
- if self.onChange then\\\\\\\
- self.onChange( self.public, \\\\\\\"\\\\\\\\n\\\\\\\" )\\\\\\\
- end\\\\\\\
- elseif key == keys.tab then\\\\\\\
- if self.selection then\\\\\\\
- local ssx, ssy, sex, sey = self:getSelectionBounds( )\\\\\\\
- if ssy == sey then\\\\\\\
- self:setSelection \\\\\\\" \\\\\\\"\\\\\\\
- else\\\\\\\
- for y = ssy, sey do\\\\\\\
- if lastkey == keys.leftShift then\\\\\\\
- self.lines[y] = self.lines[y]:gsub( \\\\\\\"^ \\\\\\\", \\\\\\\"\\\\\\\", 1 )\\\\\\\
- else\\\\\\\
- self.lines[y] = \\\\\\\" \\\\\\\" .. self.lines[y]\\\\\\\
- end\\\\\\\
- end\\\\\\\
- end\\\\\\\
- else\\\\\\\
- self.lines[self.cursory] = self.lines[self.cursory]:sub( 1, self.cursorx - 1 ) .. \\\\\\\" \\\\\\\" .. self.lines[self.cursory]:sub( self.cursorx )\\\\\\\
- self:setCursorPos( self.cursorx + 1, self.cursory )\\\\\\\
- end\\\\\\\
- self.public:updateCharacters( )\\\\\\\
- if self.onChange then\\\\\\\
- self.onChange( self.public, \\\\\\\" \\\\\\\" )\\\\\\\
- end\\\\\\\
- elseif key == keys.delete then\\\\\\\
- if self.selection then\\\\\\\
- self:setSelection \\\\\\\"\\\\\\\"\\\\\\\
- else\\\\\\\
- if self.cursorx == #self.lines[self.cursory] + 1 then\\\\\\\
- if self.lines[self.cursory + 1] then\\\\\\\
- self.lines[self.cursory] = self.lines[self.cursory] .. self.lines[self.cursory + 1]\\\\\\\
- table.remove( self.lines, self.cursory + 1 )\\\\\\\
- end\\\\\\\
- else\\\\\\\
- self.lines[self.cursory] = self.lines[self.cursory]:sub( 1, self.cursorx - 1 ) .. self.lines[self.cursory]:sub( self.cursorx + 1 )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- self.public:updateCharacters( )\\\\\\\
- if self.onChange then\\\\\\\
- self.onChange( self.public, \\\\\\\"delete\\\\\\\" )\\\\\\\
- end\\\\\\\
- elseif key == keys.backspace then\\\\\\\
- if self.selection then\\\\\\\
- self:setSelection \\\\\\\"\\\\\\\"\\\\\\\
- else\\\\\\\
- if self.cursorx == 1 then\\\\\\\
- if self.cursory > 1 then\\\\\\\
- self:setCursorPos( #self.lines[self.cursory - 1] + 1, self.cursory - 1 )\\\\\\\
- self.lines[self.cursory] = self.lines[self.cursory] .. self.lines[self.cursory + 1]\\\\\\\
- table.remove( self.lines, self.cursory + 1 )\\\\\\\
- end\\\\\\\
- else\\\\\\\
- self.lines[self.cursory] = self.lines[self.cursory]:sub( 1, self.cursorx - 2 ) .. self.lines[self.cursory]:sub( self.cursorx )\\\\\\\
- self:setCursorPos( self.cursorx - 1, self.cursory )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- self.public:updateCharacters( )\\\\\\\
- if self.onChange then\\\\\\\
- self.onChange( self.public, \\\\\\\"backspace\\\\\\\" )\\\\\\\
- end\\\\\\\
- elseif key == 199 then -- home\\\\\\\
- local tabs = #( self.lines[self.cursory]:match \\\\\\\"^( *)\\\\\\\" )\\\\\\\
- self:setCursorPos( tabs + 1, self.cursory )\\\\\\\
- elseif key == 207 then -- end\\\\\\\
- self:setCursorPos( #self.lines[self.cursory] + 1, self.cursory )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UICode.public:onTextInput( text )\\\\\\\
- if not self.focussed then\\\\\\\
- return\\\\\\\
- end\\\\\\\
- if self.selection then\\\\\\\
- self:setSelection( text )\\\\\\\
- else\\\\\\\
- self.lines[self.cursory] = self.lines[self.cursory]:sub( 1, self.cursorx - 1 ) .. text .. self.lines[self.cursory]:sub( self.cursorx )\\\\\\\
- self:setCursorPos( self.cursorx + 1, self.cursory )\\\\\\\
- end\\\\\\\
- self.public:updateCharacters( )\\\\\\\
- if self.onChange then\\\\\\\
- self.onChange( self.public, text )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UICode.public:setCode( code )\\\\\\\
- self.lines = { }\\\\\\\
- local last = 1\\\\\\\
- for i = 1, #code do\\\\\\\
- if code:sub( i, i ) == \\\\\\\"\\\\\\\\n\\\\\\\" then\\\\\\\
- table.insert( self.lines, code:sub( last, i - 1 ) )\\\\\\\
- last = i + 1\\\\\\\
- end\\\\\\\
- end\\\\\\\
- table.insert( self.lines, code:sub( last ) )\\\\\\\
- self.public:updateCharacters( )\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UICode.public:getCode( )\\\\\\\
- return table.concat( self.lines, \\\\\\\"\\\\\\\\n\\\\\\\" )\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UICode.public:onFocus( )\\\\\\\
- self.focussed = true\\\\\\\
- self.handlesKeys = true\\\\\\\
- if type( self.whenFocussed ) == \\\\\\\"function\\\\\\\" then\\\\\\\
- self.whenFocussed( self.public )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UICode.public:onUnFocus( )\\\\\\\
- self.handlesKeys = false\\\\\\\
- self.focussed = false\\\\\\\
- if type( self.whenUnFocussed ) == \\\\\\\"function\\\\\\\" then\\\\\\\
- self.whenUnFocussed( self.public )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UICode.public:focusOn( )\\\\\\\
- local handler = self.public:getHandler( )\\\\\\\
- handler:setFocus( self.public )\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UICode.public:getDisplaySizeH( )\\\\\\\
- return self.w - self.lineWidth\\\\\\\
- end\\\\\\\
- function UICode.public:getDisplaySizeV( )\\\\\\\
- return self.h\\\\\\\
- end\\\\\\\
- function UICode.public:getContentSizeH( )\\\\\\\
- local max = 1\\\\\\\
- for i = 1, #self.lines do\\\\\\\
- max = math.max( max, #self.lines[i] )\\\\\\\
- end\\\\\\\
- return max\\\\\\\
- end\\\\\\\
- function UICode.public:getContentSizeV( )\\\\\\\
- return #self.lines\\\\\\\
- end\\\\\\\
- function UICode.public:getContentScrollH( )\\\\\\\
- return self.scrollx\\\\\\\
- end\\\\\\\
- function UICode.public:getContentScrollV( )\\\\\\\
- return self.scrolly\\\\\\\
- end\\\\\\\
- function UICode.public:setContentScrollH( scroll )\\\\\\\
- self.scrollx = scroll\\\\\\\
- end\\\\\\\
- function UICode.public:setContentScrollV( scroll )\\\\\\\
- self.scrolly = scroll\\\\\\\
- end\\\", meta={\\\
- type = \\\"class\\\",\\\
- }};[\\\"Image\\\"]={content=\\\"\\\\\\\
- local colourLookup = {\\\\\\\
- [\\\\\\\"0\\\\\\\"] = colours.white;\\\\\\\
- [\\\\\\\"1\\\\\\\"] = colours.orange;\\\\\\\
- [\\\\\\\"2\\\\\\\"] = colours.magenta;\\\\\\\
- [\\\\\\\"3\\\\\\\"] = colours.lightBlue;\\\\\\\
- [\\\\\\\"4\\\\\\\"] = colours.yellow;\\\\\\\
- [\\\\\\\"5\\\\\\\"] = colours.lime;\\\\\\\
- [\\\\\\\"6\\\\\\\"] = colours.pink;\\\\\\\
- [\\\\\\\"7\\\\\\\"] = colours.grey;\\\\\\\
- [\\\\\\\"8\\\\\\\"] = colours.lightGrey;\\\\\\\
- [\\\\\\\"9\\\\\\\"] = colours.cyan;\\\\\\\
- [\\\\\\\"A\\\\\\\"] = colours.purple;\\\\\\\
- [\\\\\\\"B\\\\\\\"] = colours.blue;\\\\\\\
- [\\\\\\\"C\\\\\\\"] = colours.brown;\\\\\\\
- [\\\\\\\"D\\\\\\\"] = colours.green;\\\\\\\
- [\\\\\\\"E\\\\\\\"] = colours.red;\\\\\\\
- [\\\\\\\"F\\\\\\\"] = colours.black;\\\\\\\
- [\\\\\\\" \\\\\\\"] = 0;\\\\\\\
- }\\\\\\\
- \\\\\\\
- local colourSave = { }\\\\\\\
- for k, v in pairs( colourLookup ) do\\\\\\\
- colourSave[v] = k\\\\\\\
- end\\\\\\\
- \\\\\\\
- function Image:Image( w, h )\\\\\\\
- self.pixels = { }\\\\\\\
- \\\\\\\
- for y = 1, h or 1 do\\\\\\\
- self.pixels[y] = { }\\\\\\\
- for x = 1, w or 1 do\\\\\\\
- self.pixels[y][x] = { bc = 0, tc = 0, char = \\\\\\\"\\\\\\\" }\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- return self.public\\\\\\\
- end\\\\\\\
- \\\\\\\
- function Image.public:getSize( )\\\\\\\
- return #( self.pixels[1] or { } ), #self.pixels\\\\\\\
- end\\\\\\\
- \\\\\\\
- function Image.public:foreach( f )\\\\\\\
- local w, h = #( self.pixels[1] or { } ), #self.pixels\\\\\\\
- local pixels = { }\\\\\\\
- for y = 1, h do\\\\\\\
- pixels[y] = { }\\\\\\\
- for x = 1, w do\\\\\\\
- pixels[y][x] = { }\\\\\\\
- local bc, tc, char = f( x, y, self.public:getPixel( x, y ) )\\\\\\\
- pixels[y][x].bc = bc or self.pixels[y][x].bc\\\\\\\
- pixels[y][x].tc = tc or self.pixels[y][x].tc\\\\\\\
- pixels[y][x].char = char or self.pixels[y][x].char\\\\\\\
- end\\\\\\\
- end\\\\\\\
- for y = 1, h do\\\\\\\
- for x = 1, w do\\\\\\\
- self.pixels[y][x].bc = pixels[y][x].bc\\\\\\\
- self.pixels[y][x].tc = pixels[y][x].tc\\\\\\\
- self.pixels[y][x].char = pixels[y][x].char\\\\\\\
- end\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function Image.public:pixel( x, y, bc, tc, char )\\\\\\\
- if self.pixels[y] and self.pixels[y][x] then\\\\\\\
- --if bc == 0 then bc = pixels[y][x].bc end\\\\\\\
- --if tc == 0 then char = pixels[y][x].char tc = pixels[y][x].tc end\\\\\\\
- self.pixels[y][x] = { bc = bc, tc = tc, char = char }\\\\\\\
- return true\\\\\\\
- end\\\\\\\
- return false\\\\\\\
- end\\\\\\\
- \\\\\\\
- function Image.public:getPixel( x, y )\\\\\\\
- if self.pixels[y] and self.pixels[y][x] then\\\\\\\
- return self.pixels[y][x].bc, self.pixels[y][x].tc, self.pixels[y][x].char\\\\\\\
- end\\\\\\\
- return false\\\\\\\
- end\\\\\\\
- \\\\\\\
- function Image.public:savestr( str )\\\\\\\
- local w, h = #( self.pixels[1] or { } ), #self.pixels\\\\\\\
- local str = \\\\\\\"\\\\\\\"\\\\\\\
- for y = 1, h do\\\\\\\
- for x = 1, w do\\\\\\\
- local bc = colourSave[self.pixels[y][x].bc]\\\\\\\
- local tc = colourSave[self.pixels[y][x].tc]\\\\\\\
- local char = self.pixels[y][x].char\\\\\\\
- if #char == 0 then\\\\\\\
- char = \\\\\\\" \\\\\\\"\\\\\\\
- bc = colourSave[0]\\\\\\\
- end\\\\\\\
- str = str .. bc .. tc .. char\\\\\\\
- end\\\\\\\
- str = str .. \\\\\\\"\\\\\\\\n\\\\\\\"\\\\\\\
- end\\\\\\\
- return str:sub( 1, -2 )\\\\\\\
- end\\\\\\\
- \\\\\\\
- function Image.public:loadstr( str )\\\\\\\
- local lines = { }\\\\\\\
- local last = 1\\\\\\\
- for i = 1, #str do\\\\\\\
- if str:sub( i, i ) == \\\\\\\"\\\\\\\\n\\\\\\\" then\\\\\\\
- table.insert( lines, str:sub( last, i - 1 ) )\\\\\\\
- last = i + 1\\\\\\\
- end\\\\\\\
- end\\\\\\\
- table.insert( lines, str:sub( last ) )\\\\\\\
- local width = #lines[1] / 3\\\\\\\
- local height = #lines\\\\\\\
- self.public:resize( width, height )\\\\\\\
- for i = 1, #lines do\\\\\\\
- local x = 1\\\\\\\
- for pixel in lines[i]:gmatch( \\\\\\\"[0123456789ABCDEF ][0123456789ABCDEF ].\\\\\\\" ) do\\\\\\\
- local bc = pixel:sub( 1, 1 )\\\\\\\
- local tc = pixel:sub( 2, 2 )\\\\\\\
- local ch = pixel:sub( 3, 3 )\\\\\\\
- self.public:pixel( x, i, colourLookup[bc], colourLookup[tc], ch )\\\\\\\
- x = x + 1\\\\\\\
- end\\\\\\\
- end\\\\\\\
- return self.public\\\\\\\
- end\\\\\\\
- \\\\\\\
- function Image.public:resize( w, h, bc, tc, char )\\\\\\\
- while #self.pixels > h do\\\\\\\
- table.remove( self.pixels, #self.pixels )\\\\\\\
- end\\\\\\\
- while #self.pixels < h do\\\\\\\
- local t = { }\\\\\\\
- for i = 1, #( self.pixels[1] or { } ) do\\\\\\\
- t[i] = { bc = bc or 0, tc = tc or 0, char = char or \\\\\\\"\\\\\\\" }\\\\\\\
- end\\\\\\\
- table.insert( self.pixels, t )\\\\\\\
- end\\\\\\\
- if self.pixels[1] then\\\\\\\
- local cw = #self.pixels[1]\\\\\\\
- while cw > w do\\\\\\\
- for y = 1, h do\\\\\\\
- table.remove( self.pixels[y], #self.pixels[y] )\\\\\\\
- end\\\\\\\
- cw = cw - 1\\\\\\\
- end\\\\\\\
- while cw < w do\\\\\\\
- for y = 1, h do\\\\\\\
- table.insert( self.pixels[y], { bc = bc or 0, tc = tc or 0, char = char or \\\\\\\"\\\\\\\" } )\\\\\\\
- end\\\\\\\
- cw = cw + 1\\\\\\\
- end\\\\\\\
- end\\\\\\\
- end\\\", meta={\\\
- type = \\\"class\\\",\\\
- }};[\\\"markup\\\"]={content=\\\"\\\\\\\
- require \\\\\\\"parser\\\\\\\"\\\\\\\
- \\\\\\\
- local function loadStandardAttributes( class, attributes, scripts, ids, errors, parent )\\\\\\\
- local x = type( attributes.x ) == \\\\\\\"number\\\\\\\" and attributes.x or 1\\\\\\\
- local y = type( attributes.y ) == \\\\\\\"number\\\\\\\" and attributes.y or 1\\\\\\\
- local w = type( attributes.width ) == \\\\\\\"number\\\\\\\" and attributes.width or 19\\\\\\\
- local h = type( attributes.height ) == \\\\\\\"number\\\\\\\" and attributes.height or 1\\\\\\\
- if not attributes.x or not attributes.y then\\\\\\\
- local children = parent:getChildren( )\\\\\\\
- if children[#children] then\\\\\\\
- if attributes.float == \\\\\\\"below\\\\\\\" then\\\\\\\
- x = children[#children].x\\\\\\\
- y = children[#children].y + children[#children].h\\\\\\\
- else\\\\\\\
- x = children[#children].x + children[#children].w\\\\\\\
- y = children[#children].y\\\\\\\
- end\\\\\\\
- end\\\\\\\
- end\\\\\\\
- if attributes.width == \\\\\\\"expand\\\\\\\" then\\\\\\\
- w = parent.w - x + 1\\\\\\\
- end\\\\\\\
- if attributes.height == \\\\\\\"expand\\\\\\\" then\\\\\\\
- h = parent.h - y + 1\\\\\\\
- end\\\\\\\
- if attributes.x == \\\\\\\"right\\\\\\\" then\\\\\\\
- x = parent.w - w + 1\\\\\\\
- end\\\\\\\
- if attributes.y == \\\\\\\"bottom\\\\\\\" then\\\\\\\
- y = parent.h - h + 1\\\\\\\
- end\\\\\\\
- if not ( attributes.x or attributes.y ) and ( attributes.float == \\\\\\\"right\\\\\\\" or not attributes.float ) and x + w > parent.w then\\\\\\\
- local c = parent:getChildren( )\\\\\\\
- if c[#c] then\\\\\\\
- x = c[#c].x\\\\\\\
- y = c[#c].y + c[#c].h\\\\\\\
- end\\\\\\\
- end\\\\\\\
- local ob = parent:newChild( class( x, y, w, h ) )\\\\\\\
- if attributes.x == \\\\\\\"centre\\\\\\\" then\\\\\\\
- ob:centreX( )\\\\\\\
- end\\\\\\\
- if attributes.y == \\\\\\\"centre\\\\\\\" then\\\\\\\
- ob:centreY( )\\\\\\\
- end\\\\\\\
- if attributes.id then\\\\\\\
- ids[attributes.id] = ob\\\\\\\
- end\\\\\\\
- if attributes.onFocus then\\\\\\\
- local f, err = loadstring( attributes.onFocus, \\\\\\\"onFocus\\\\\\\" )\\\\\\\
- if f then\\\\\\\
- ob.whenFocussed = f\\\\\\\
- table.insert( scripts, { script = f } )\\\\\\\
- else\\\\\\\
- table.insert( errors, err )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- if attributes.onUnFocus then\\\\\\\
- local f, err = loadstring( attributes.onUnFocus, \\\\\\\"onUnFocus\\\\\\\" )\\\\\\\
- if f then\\\\\\\
- ob.whenUnFocussed = f\\\\\\\
- table.insert( scripts, { script = f } )\\\\\\\
- else\\\\\\\
- table.insert( errors, err )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- if type( attributes.align ) == \\\\\\\"string\\\\\\\" then\\\\\\\
- local children = parent:getChildren( )\\\\\\\
- if #children > 1 then\\\\\\\
- ob:alignTo( attributes.align, children[#children-1], tonumber( attributes.spacing ) )\\\\\\\
- ob.w = w\\\\\\\
- ob.h = h\\\\\\\
- end\\\\\\\
- end\\\\\\\
- return ob\\\\\\\
- end\\\\\\\
- \\\\\\\
- local function loadColours( ob, attributes )\\\\\\\
- local bc = type( attributes.bc ) == \\\\\\\"number\\\\\\\" and attributes.bc\\\\\\\
- local tc = type( attributes.tc ) == \\\\\\\"number\\\\\\\" and attributes.tc\\\\\\\
- if colours[attributes.bc] then bc = colours[attributes.bc] end\\\\\\\
- if attributes.bc == \\\\\\\"transparent\\\\\\\" then bc = 0 end\\\\\\\
- if colours[attributes.tc] then tc = colours[attributes.tc] end\\\\\\\
- if attributes.tc == \\\\\\\"transparent\\\\\\\" then tc = 0 end\\\\\\\
- if bc then\\\\\\\
- ob.bc = bc\\\\\\\
- end\\\\\\\
- if tc then\\\\\\\
- ob.tc = tc\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- parser.registerTag( \\\\\\\"text\\\\\\\", true, false, function( parent, attributes, content, scripts, ids, errors )\\\\\\\
- if attributes.width == \\\\\\\"auto\\\\\\\" or not attributes.width then\\\\\\\
- attributes.width = #content\\\\\\\
- end\\\\\\\
- local ob = loadStandardAttributes( UIText, attributes, scripts, ids, errors, parent )\\\\\\\
- loadColours( ob, attributes )\\\\\\\
- ob.text = content\\\\\\\
- return ob\\\\\\\
- end, nil, nil )\\\\\\\
- \\\\\\\
- parser.registerTag( \\\\\\\"frame\\\\\\\", true, true, function( parent, attributes, content, scripts, ids, errors )\\\\\\\
- local ob = loadStandardAttributes( UIFrame, attributes, scripts, ids, errors, parent )\\\\\\\
- return ob\\\\\\\
- end, nil, nil )\\\\\\\
- \\\\\\\
- parser.registerTag( \\\\\\\"button\\\\\\\", true, false, function( parent, attributes, content, scripts, ids, errors )\\\\\\\
- if attributes.width == \\\\\\\"auto\\\\\\\" or not attributes.width then\\\\\\\
- attributes.width = #content\\\\\\\
- end\\\\\\\
- local ob = loadStandardAttributes( UIButton, attributes, scripts, ids, errors, parent )\\\\\\\
- loadColours( ob, attributes )\\\\\\\
- ob.text = content\\\\\\\
- if attributes.onClick then\\\\\\\
- local f, err = loadstring( attributes.onClick, \\\\\\\"onClick\\\\\\\" )\\\\\\\
- if f then\\\\\\\
- ob.onClick = f\\\\\\\
- table.insert( scripts, { script = f } )\\\\\\\
- else\\\\\\\
- table.insert( errors, err )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- return ob\\\\\\\
- end, nil, nil )\\\\\\\
- \\\\\\\
- parser.registerTag( \\\\\\\"input\\\\\\\", false, false, function( parent, attributes, content, scripts, ids, errors )\\\\\\\
- local ob = loadStandardAttributes( UIInput, attributes, scripts, ids, errors, parent )\\\\\\\
- loadColours( ob, attributes )\\\\\\\
- local fbc = type( attributes.fbc ) == \\\\\\\"number\\\\\\\" and attributes.fbc\\\\\\\
- if colours[attributes.fbc] then fbc = colours[attributes.fbc] end\\\\\\\
- if attributes.fbc == \\\\\\\"transparent\\\\\\\" then fbc = 0 end\\\\\\\
- if fbc then\\\\\\\
- ob.fbc = fbc\\\\\\\
- end\\\\\\\
- if attributes.mask then\\\\\\\
- ob.mask = tostring( attributes.mask )\\\\\\\
- end\\\\\\\
- return ob\\\\\\\
- end, nil, nil )\\\\\\\
- \\\\\\\
- parser.registerTag( \\\\\\\"image\\\\\\\", false, false, function( parent, attributes, content, scripts, ids, errors )\\\\\\\
- local ob = loadStandardAttributes( UIImage, attributes, scripts, ids, errors, parent )\\\\\\\
- if attributes.source then\\\\\\\
- local source = tostring( attributes.source )\\\\\\\
- local h = fs.open( source, \\\\\\\"r\\\\\\\" )\\\\\\\
- if h then\\\\\\\
- local content = h.readAll( )\\\\\\\
- h.close( )\\\\\\\
- ob.image = Image( )\\\\\\\
- ob.image:loadstr( content )\\\\\\\
- ob.w, ob.h = ob.image:getSize( )\\\\\\\
- else\\\\\\\
- ob:remove( )\\\\\\\
- ob = UIButton( ob.x, ob.y, ob.w, ob.h, attributes.alt or \\\\\\\"could not load image\\\\\\\" )\\\\\\\
- ob.tc = colours.lightGrey\\\\\\\
- end\\\\\\\
- else\\\\\\\
- ob.image = Image( ob.w, ob.h )\\\\\\\
- ob.image:foreach( function( )\\\\\\\
- return colours.white, 1, \\\\\\\" \\\\\\\"\\\\\\\
- end )\\\\\\\
- end\\\\\\\
- return ob\\\\\\\
- end, nil, nil )\\\\\\\
- \\\\\\\
- function load( str, frame, penv )\\\\\\\
- local body, scripts, ids, errors = parser.load( str, frame or UIFrame( 1, 1, term.getSize( ) ) )\\\\\\\
- \\\\\\\
- ids.body = body\\\\\\\
- \\\\\\\
- local env = { }\\\\\\\
- env.document = { }\\\\\\\
- local meta = { __index = penv or { } }\\\\\\\
- if penv then meta.__newindex = penv end\\\\\\\
- setmetatable( env, meta )\\\\\\\
- \\\\\\\
- function env.document.getElementById( id )\\\\\\\
- return ids[id]\\\\\\\
- end\\\\\\\
- function env.document.addContent( str, id )\\\\\\\
- if not id then id = \\\\\\\"body\\\\\\\" end\\\\\\\
- if ids[id] then\\\\\\\
- local _, errs = load( str, ids[id], penv )\\\\\\\
- for i = 1, #errs do\\\\\\\
- table.insert( errors, errs[i] )\\\\\\\
- end\\\\\\\
- return true\\\\\\\
- else\\\\\\\
- return false, \\\\\\\"no such id\\\\\\\"\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- for i = 1,#scripts do\\\\\\\
- setfenv( scripts[i].script, env )\\\\\\\
- if scripts[i].run then\\\\\\\
- scripts[i].script( )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- return frame, errors, env.document\\\\\\\
- end\\\", meta={\\\
- type = \\\"lib\\\",\\\
- }};[\\\"UIFrame\\\"]={content=\\\"\\\\\\\
- require \\\\\\\"UIElement\\\\\\\"\\\\\\\
- \\\\\\\
- UIFrame:extends( UIElement )\\\\\\\
- \\\\\\\
- UIFrame.handlesMouse = false\\\\\\\
- UIFrame.handlesScroll = true\\\\\\\
- UIFrame.isScrollTarget = true\\\\\\\
- UIFrame.scrollDirection = \\\\\\\"vertical\\\\\\\"\\\\\\\
- \\\\\\\
- UIFrame.scrolls = true\\\\\\\
- UIFrame.public \\\\\\\"scrolls\\\\\\\" \\\\\\\"boolean\\\\\\\"\\\\\\\
- \\\\\\\
- function UIFrame:UIFrame( x, y, w, h, direction )\\\\\\\
- self:UIElement( x, y, w, h )\\\\\\\
- self.scrollDirection = direction or \\\\\\\"vertical\\\\\\\"\\\\\\\
- return self.public\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIFrame.public:update( dt )\\\\\\\
- local max = self.scrollDirection == \\\\\\\"vertical\\\\\\\" and self.h or self.w\\\\\\\
- if self.scrollDirection == \\\\\\\"vertical\\\\\\\" then\\\\\\\
- for i = 1, #self.children do\\\\\\\
- max = math.max( max, self.children[i].y + self.children[i].h - 1 )\\\\\\\
- end\\\\\\\
- else\\\\\\\
- for i = 1, #self.children do\\\\\\\
- max = math.max( max, self.children[i].x + self.children[i].w - 1 )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- if self.scrollDirection == \\\\\\\"vertical\\\\\\\" then\\\\\\\
- if max - self.h < -self.cy then\\\\\\\
- self.cy = -max + self.h\\\\\\\
- end\\\\\\\
- else\\\\\\\
- if max - self.w < -self.cx then\\\\\\\
- self.cx = -max + self.w\\\\\\\
- end\\\\\\\
- end\\\\\\\
- if max == ( self.scrollDirection == \\\\\\\"vertical\\\\\\\" and self.h or self.w ) or not self.scrolls then\\\\\\\
- self.handlesScroll = false\\\\\\\
- else\\\\\\\
- self.handlesScroll = true\\\\\\\
- end\\\\\\\
- local c = self.public:getChildren( )\\\\\\\
- for i, child in ipairs( c ) do\\\\\\\
- child:update( dt )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIFrame.public:onMouseScroll( rx, ry, dir )\\\\\\\
- if self.scrollDirection == \\\\\\\"vertical\\\\\\\" then\\\\\\\
- self.cy = self.cy - dir\\\\\\\
- local max = 0\\\\\\\
- for i = 1, #self.children do\\\\\\\
- max = math.max( max, self.children[i].y + self.children[i].h )\\\\\\\
- end\\\\\\\
- max = max - self.h - 1\\\\\\\
- if self.cy < -max then\\\\\\\
- self.cy = -max\\\\\\\
- end\\\\\\\
- if self.cy > 0 then self.cy = 0 end\\\\\\\
- else\\\\\\\
- self.cx = self.cx - dir\\\\\\\
- local max = 0\\\\\\\
- for i = 1, #self.children do\\\\\\\
- max = math.max( max, self.children[i].x + self.children[i].w )\\\\\\\
- end\\\\\\\
- max = max - self.w - 1\\\\\\\
- if self.cx < -max then\\\\\\\
- self.cx = -max\\\\\\\
- end\\\\\\\
- if self.cx > 0 then self.cx = 0 end\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIFrame.public:getDisplaySizeH( )\\\\\\\
- return self.w\\\\\\\
- end\\\\\\\
- function UIFrame.public:getDisplaySizeV( )\\\\\\\
- return self.h\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIFrame.public:getContentSizeH( )\\\\\\\
- local max = self.w\\\\\\\
- for i = 1, #self.children do\\\\\\\
- max = math.max( max, self.children[i].x + self.children[i].w - 1 )\\\\\\\
- end\\\\\\\
- return max\\\\\\\
- end\\\\\\\
- function UIFrame.public:getContentSizeV( )\\\\\\\
- local max = self.h\\\\\\\
- for i = 1, #self.children do\\\\\\\
- max = math.max( max, self.children[i].y + self.children[i].h - 1 )\\\\\\\
- end\\\\\\\
- return max\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIFrame.public:getContentScrollH( )\\\\\\\
- return -self.cx\\\\\\\
- end\\\\\\\
- function UIFrame.public:getContentScrollV( )\\\\\\\
- return -self.cy\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIFrame.public:setContentScrollH( scroll )\\\\\\\
- self.cx = -scroll\\\\\\\
- end\\\\\\\
- function UIFrame.public:setContentScrollV( scroll )\\\\\\\
- self.cy = -scroll\\\\\\\
- end\\\", meta={\\\
- type = \\\"class\\\",\\\
- }};[\\\"clipboard\\\"]={content=\\\"if NovaClipboard then\\\\\\\
- set, get = NovaClipboard.set, NovaClipboard.get\\\\\\\
- else\\\\\\\
- local a=false;function set(b,c)a={mode=b,data=c}end;function get()if a then return a.mode,a.data end end\\\\\\\
- export(\\\\\\\"NovaClipboard\\\\\\\",{set=set,get=get})\\\\\\\
- end\\\", meta={\\\
- type = \\\"lib\\\",\\\
- }};[\\\"UIText\\\"]={content=\\\"\\\\\\\
- require \\\\\\\"UIElement\\\\\\\"\\\\\\\
- \\\\\\\
- UIText:extends( UIElement )\\\\\\\
- \\\\\\\
- UIText.text = \\\\\\\"\\\\\\\"\\\\\\\
- UIText.public \\\\\\\"text\\\\\\\"\\\\\\\
- \\\\\\\
- UIText.public \\\\\\\"bc\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
- UIText.public \\\\\\\"tc\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
- \\\\\\\
- function UIText:UIText( x, y, w, h, text )\\\\\\\
- self:UIElement( x, y, w, h )\\\\\\\
- self.text = text\\\\\\\
- self.bc = colours.white\\\\\\\
- self.tc = colours.grey\\\\\\\
- return self.public\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIText.public:draw( x, y )\\\\\\\
- local layer = stencil.addLayer( x, y, self.w, self.h )\\\\\\\
- local text = tostring( self.text )\\\\\\\
- if type( self.text ) == \\\\\\\"function\\\\\\\" then\\\\\\\
- text = self.text( self.public )\\\\\\\
- end\\\\\\\
- stencil.text( x, y, self.w, self.h, self.bc, self.tc, text )\\\\\\\
- local c = self.public:getChildren( )\\\\\\\
- for i, child in ipairs( c ) do\\\\\\\
- child:draw( x + child.x - 1 + self.cx, y + child.y - 1 + self.cy )\\\\\\\
- end\\\\\\\
- stencil.closeLayer( layer )\\\\\\\
- end\\\", meta={\\\
- type = \\\"class\\\",\\\
- }};[\\\"stencil\\\"]={content=\\\"\\\\\\\
- local function linewrap( str, w )\\\\\\\
- for i = 1, w + 1 do\\\\\\\
- if str:sub( i, i ) == \\\\\\\"\\\\\\\\n\\\\\\\" then\\\\\\\
- return str:sub( 1, i - 1 ), str:sub( i + 1 )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- if #str <= w then\\\\\\\
- return str, \\\\\\\"\\\\\\\"\\\\\\\
- end\\\\\\\
- if str:sub( w + 1, w + 1 ):find \\\\\\\"%s\\\\\\\" then\\\\\\\
- return str:sub( 1, w ), str:sub( w + 1 ):gsub( \\\\\\\"^%s+\\\\\\\", \\\\\\\"\\\\\\\" )\\\\\\\
- end\\\\\\\
- for i = w, 1, -1 do\\\\\\\
- if str:sub( i, i ):find \\\\\\\"%s\\\\\\\" then\\\\\\\
- return str:sub( 1, i ), str:sub( i + 1 )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- return str:sub( 1, w ), str:sub( w + 1 )\\\\\\\
- end\\\\\\\
- \\\\\\\
- local function wordwrap( str, w, h )\\\\\\\
- if w < 1 then return { } end\\\\\\\
- local lines = { }\\\\\\\
- local line\\\\\\\
- while #str > 0 do\\\\\\\
- line, str = linewrap( str, w )\\\\\\\
- table.insert( lines, line )\\\\\\\
- end\\\\\\\
- while #lines > h do\\\\\\\
- lines[#lines] = nil\\\\\\\
- end\\\\\\\
- return lines\\\\\\\
- end\\\\\\\
- \\\\\\\
- local function bbox( x1, y1, w1, h1, x2, y2, w2, h2 )\\\\\\\
- local x, y, w, h\\\\\\\
- if x1 + w1 <= x2 or y1 + h1 <= y2 or x2 + w2 <= x1 or y2 + h2 <= y1 then\\\\\\\
- return false\\\\\\\
- end\\\\\\\
- x = math.max( x1, x2 )\\\\\\\
- w = math.min( x1 + w1, x2 + w2 ) - x\\\\\\\
- y = math.max( y1, y2 )\\\\\\\
- h = math.min( y1 + h1, y2 + h2 ) - y\\\\\\\
- return x, y, w, h\\\\\\\
- end\\\\\\\
- \\\\\\\
- local layers = { }\\\\\\\
- local w, h = term.getSize( )\\\\\\\
- local limit = { x = 1, y = 1, w = w, h = h }\\\\\\\
- \\\\\\\
- function pixel( x, y, bc, tc, char )\\\\\\\
- if limit and x >= limit.x and x < limit.x + limit.w and y >= limit.y and y < limit.y + limit.h then\\\\\\\
- buffer.setPixel( x, y, bc, tc, char )\\\\\\\
- return true\\\\\\\
- end\\\\\\\
- return false\\\\\\\
- end\\\\\\\
- \\\\\\\
- function textLine( x, y, bc, tc, text )\\\\\\\
- for i = 1, #text do\\\\\\\
- pixel( x + i - 1, y, bc, tc, text:sub( i, i ) )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function rectangle( x, y, w, h, bc, tc, char )\\\\\\\
- for i = 0, h - 1 do\\\\\\\
- textLine( x, y + i, bc, tc, ( char or \\\\\\\" \\\\\\\" ):rep( w ) )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function text( x, y, w, h, bc, tc, text, pad )\\\\\\\
- local lines = wordwrap( text, w, h )\\\\\\\
- if pad then\\\\\\\
- pad( lines )\\\\\\\
- end\\\\\\\
- if bc ~= 0 then\\\\\\\
- while #lines < h do\\\\\\\
- table.insert( lines, string.rep( \\\\\\\" \\\\\\\", math.max( w, 0 ) ) )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- for i = 1, #lines do\\\\\\\
- local line = lines[i]:sub( 1, w )\\\\\\\
- if bc ~= 0 then\\\\\\\
- line = line .. string.rep( \\\\\\\" \\\\\\\", math.max( w - #line, 0 ) )\\\\\\\
- end\\\\\\\
- textLine( x, y + i - 1, bc, tc, line )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function setCursorBlink( x, y, col )\\\\\\\
- if limit and x >= limit.x and x < limit.x + limit.w and y >= limit.y and y < limit.y + limit.h then\\\\\\\
- buffer.setCursorBlink( x, y, col )\\\\\\\
- return true\\\\\\\
- end\\\\\\\
- return false\\\\\\\
- end\\\\\\\
- \\\\\\\
- function addLayer( x, y, w, h )\\\\\\\
- table.insert( layers, { x = x, y = y, w = w, h = h } )\\\\\\\
- if limit then\\\\\\\
- x, y, w, h = bbox( x, y, w, h, limit.x, limit.y, limit.w, limit.h )\\\\\\\
- if x then\\\\\\\
- limit = { x = x, y = y, w = w, h = h }\\\\\\\
- else\\\\\\\
- limit = false\\\\\\\
- end\\\\\\\
- end\\\\\\\
- return #layers\\\\\\\
- end\\\\\\\
- \\\\\\\
- function closeLayer( layer )\\\\\\\
- while layers[layer] do\\\\\\\
- table.remove( layers, layer )\\\\\\\
- end\\\\\\\
- limit = { x = 1, y = 1, w = w, h = h }\\\\\\\
- for i, l in ipairs( layers ) do\\\\\\\
- local x, y, w, h = bbox( limit.x, limit.y, limit.w, limit.h, l.x, l.y, l.w, l.h )\\\\\\\
- if x then\\\\\\\
- limit = { x = x, y = y, w = w, h = h }\\\\\\\
- else\\\\\\\
- limit = false\\\\\\\
- break\\\\\\\
- end\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function layerCount( )\\\\\\\
- return #layers\\\\\\\
- end\\\\\\\
- \\\\\\\
- function getDrawArea( )\\\\\\\
- if limit then\\\\\\\
- return limit.x, limit.y, limit.w, limit.h\\\\\\\
- end\\\\\\\
- return 0, 0, 0, 0\\\\\\\
- end\\\", meta={\\\
- type = \\\"lib\\\",\\\
- }};[\\\"UIInput\\\"]={content=\\\"\\\\\\\
- require \\\\\\\"UIElement\\\\\\\"\\\\\\\
- \\\\\\\
- UIInput:extends( UIElement )\\\\\\\
- \\\\\\\
- UIInput.handlesKeys = true\\\\\\\
- UIInput.handlesScroll = true\\\\\\\
- UIInput.tabIndex = true\\\\\\\
- \\\\\\\
- UIInput.bc = colours.grey\\\\\\\
- UIInput.fbc = colours.lightGrey\\\\\\\
- UIInput.tc = colours.black\\\\\\\
- UIInput.hbc = colours.blue\\\\\\\
- UIInput.htc = colours.white\\\\\\\
- \\\\\\\
- UIInput.public \\\\\\\"bc\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
- UIInput.public \\\\\\\"fbc\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
- UIInput.public \\\\\\\"tc\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
- UIInput.public \\\\\\\"hbc\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
- UIInput.public \\\\\\\"htc\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
- \\\\\\\
- UIInput.public \\\\\\\"onEnter\\\\\\\" \\\\\\\"function\\\\\\\"\\\\\\\
- UIInput.public \\\\\\\"onCtrlKey\\\\\\\" \\\\\\\"function\\\\\\\"\\\\\\\
- UIInput.public \\\\\\\"onChange\\\\\\\" \\\\\\\"function\\\\\\\"\\\\\\\
- \\\\\\\
- UIInput.public \\\\\\\"text\\\\\\\" \\\\\\\"string\\\\\\\"\\\\\\\
- UIInput.public \\\\\\\"mask\\\\\\\" \\\\\\\"string\\\\\\\"\\\\\\\
- \\\\\\\
- UIInput.public \\\\\\\"scroll\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
- \\\\\\\
- UIInput.public \\\\\\\"mask\\\\\\\"\\\\\\\
- function UIInput.public.mask:write( value )\\\\\\\
- if not value or type( value ) == \\\\\\\"string\\\\\\\" then\\\\\\\
- self.mask = value\\\\\\\
- else\\\\\\\
- error( \\\\\\\"expected string mask\\\\\\\", 3 )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIInput:UIInput( x, y, w, h, mask )\\\\\\\
- self:UIElement( x, y, w, h )\\\\\\\
- self.mask = mask\\\\\\\
- self.cursor = 1\\\\\\\
- self.scroll = 0\\\\\\\
- self.text = \\\\\\\"\\\\\\\"\\\\\\\
- self.focussed = false\\\\\\\
- self.selection = false\\\\\\\
- return self.public\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIInput.public:draw( x, y )\\\\\\\
- if self.cursor > #self.text + 1 then\\\\\\\
- self.cursor = #self.text + 1\\\\\\\
- end\\\\\\\
- local layer = stencil.addLayer( x, y, self.w, self.h )\\\\\\\
- local bc = self.bc\\\\\\\
- if self.focussed then\\\\\\\
- bc = self.fbc\\\\\\\
- end\\\\\\\
- local text = self.text\\\\\\\
- if self.mask then\\\\\\\
- text = text:gsub( \\\\\\\".\\\\\\\", self.mask:sub( 1, 1 ) )\\\\\\\
- end\\\\\\\
- if self.selection then\\\\\\\
- text = text .. string.rep( \\\\\\\" \\\\\\\", math.max( self.w - #text, 0 ) )\\\\\\\
- for i = 1, #text do\\\\\\\
- local bc = self.bc\\\\\\\
- local tc = self.tc\\\\\\\
- if i >= math.min( self.selection, self.cursor ) and i <= math.max( self.selection, self.cursor ) then\\\\\\\
- bc = self.hbc\\\\\\\
- tc = self.htc\\\\\\\
- elseif self.focussed then\\\\\\\
- bc = self.fbc\\\\\\\
- end\\\\\\\
- stencil.pixel( x + i - self.scroll - 1, y, bc, tc, text:sub( i, i ) )\\\\\\\
- end\\\\\\\
- else\\\\\\\
- text = text:sub( self.scroll + 1 )\\\\\\\
- text = text:sub( 1, self.w )\\\\\\\
- text = text .. string.rep( \\\\\\\" \\\\\\\", self.w - #text )\\\\\\\
- stencil.textLine( x, y, bc, self.tc, text )\\\\\\\
- if self.focussed then\\\\\\\
- stencil.setCursorBlink( x + self.cursor - 1 - self.scroll, y, self.tc )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- local c = self.public:getChildren( )\\\\\\\
- for i, child in ipairs( c ) do\\\\\\\
- child:draw( x + child.x - 1 + self.cx, y + child.y - 1 + self.cy )\\\\\\\
- end\\\\\\\
- stencil.closeLayer( layer )\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIInput:setCursorPos( n )\\\\\\\
- self.cursor = math.max( math.min( n, #self.text + 1 ), 1 )\\\\\\\
- if self.cursor > self.scroll + self.w then\\\\\\\
- self.scroll = self.cursor - self.w\\\\\\\
- end\\\\\\\
- if self.cursor - 1 <= self.scroll then\\\\\\\
- self.scroll = math.max( self.cursor - 1, 1 ) - 1\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIInput:write( str )\\\\\\\
- self.text = self.text:sub( 1, self.cursor - 1 ) .. str:gsub( \\\\\\\"\\\\\\\\n\\\\\\\", \\\\\\\" \\\\\\\" ) .. self.text:sub( self.cursor )\\\\\\\
- self:setCursorPos( self.cursor + #str )\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIInput:setSelection( text )\\\\\\\
- self.text = self.text:sub( 1, math.min( self.selection, self.cursor ) - 1 ) .. self.text:sub( math.max( self.selection, self.cursor ) + 1 )\\\\\\\
- self.cursor = math.min( self.selection, self.cursor )\\\\\\\
- self:write( text )\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIInput:getSelection( )\\\\\\\
- return self.text:sub( math.min( self.selection, self.cursor ), math.max( self.selection, self.cursor ) )\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIInput.public:select( min, max )\\\\\\\
- self.selection = min\\\\\\\
- self.cursor = max\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIInput.public:onMouseClick( rx, ry, button )\\\\\\\
- self.selection = false\\\\\\\
- self:setCursorPos( rx + self.scroll )\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIInput.public:onMouseDrag( rx, ry )\\\\\\\
- if not self.selection then\\\\\\\
- self.selection = self.cursor\\\\\\\
- end\\\\\\\
- self:setCursorPos( rx + self.scroll )\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIInput.public:onMouseScroll( rx, ry, dir )\\\\\\\
- self.scroll = math.max( math.min( self.scroll + dir, #self.text - self.w + 1 ), 0 )\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIInput.public:onKeyPress( key, lastkey )\\\\\\\
- if not self.focussed then\\\\\\\
- return\\\\\\\
- end\\\\\\\
- if lastkey == 29 then\\\\\\\
- if ( ( key == keys.c or key == keys.x ) and not self.mask ) or key == keys.b then\\\\\\\
- if key == keys.c then\\\\\\\
- if self.selection then\\\\\\\
- clipboard.set( \\\\\\\"plaintext\\\\\\\", self:getSelection( ) )\\\\\\\
- else\\\\\\\
- clipboard.set( \\\\\\\"plaintext\\\\\\\", self.text )\\\\\\\
- end\\\\\\\
- if self.onChange then self.onChange( self.public, self.text ) end\\\\\\\
- elseif key == keys.x then\\\\\\\
- if self.selection then\\\\\\\
- clipboard.set( \\\\\\\"plaintext\\\\\\\", self:getSelection( ) )\\\\\\\
- self:setSelection \\\\\\\"\\\\\\\"\\\\\\\
- self.selection = false\\\\\\\
- else\\\\\\\
- clipboard.set( \\\\\\\"plaintext\\\\\\\", self.text )\\\\\\\
- self.text = \\\\\\\"\\\\\\\"\\\\\\\
- end\\\\\\\
- if self.onChange then self.onChange( self.public, self.text ) end\\\\\\\
- elseif key == keys.b then\\\\\\\
- local mode, data = clipboard.get( )\\\\\\\
- if mode == \\\\\\\"plaintext\\\\\\\" then\\\\\\\
- if self.selection then\\\\\\\
- self:setSelection( data )\\\\\\\
- self.selection = false\\\\\\\
- else\\\\\\\
- self:write( data )\\\\\\\
- end\\\\\\\
- if self.onChange then self.onChange( self.public, self.text ) end\\\\\\\
- elseif mode == \\\\\\\"file\\\\\\\" then\\\\\\\
- if self.selection then\\\\\\\
- self:setSelection( \\\\\\\"file:\\\\\\\\\\\\\\\"\\\\\\\" .. data.name .. \\\\\\\"\\\\\\\\\\\\\\\"\\\\\\\" )\\\\\\\
- self.selection = false\\\\\\\
- else\\\\\\\
- self:write( \\\\\\\"file:\\\\\\\\\\\\\\\"\\\\\\\" .. data.name .. \\\\\\\"\\\\\\\\\\\\\\\"\\\\\\\" )\\\\\\\
- end\\\\\\\
- if self.onChange then self.onChange( self.public, self.text ) end\\\\\\\
- elseif self.onCtrlKey then\\\\\\\
- self.onCtrlKey( self.public, key )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- return\\\\\\\
- end\\\\\\\
- if self.onCtrlKey then\\\\\\\
- self.onCtrlKey( self.public, key )\\\\\\\
- end\\\\\\\
- return\\\\\\\
- end\\\\\\\
- if key == keys.enter then\\\\\\\
- if type( self.onEnter ) == \\\\\\\"function\\\\\\\" then\\\\\\\
- self.onEnter( self.public )\\\\\\\
- end\\\\\\\
- local handler = self.public:getHandler( )\\\\\\\
- if handler and handler.focus == self.public then\\\\\\\
- handler:unFocus( )\\\\\\\
- end\\\\\\\
- elseif key == keys.left then\\\\\\\
- if self.selection then\\\\\\\
- self:setCursorPos( math.min( self.selection, self.cursor ) - 1 )\\\\\\\
- self.selection = false\\\\\\\
- elseif self.cursor > 1 then\\\\\\\
- self:setCursorPos( self.cursor - 1 )\\\\\\\
- end\\\\\\\
- elseif key == keys.right then\\\\\\\
- if self.selection then\\\\\\\
- self:setCursorPos( math.max( self.selection, self.cursor ) + 1 )\\\\\\\
- self.selection = false\\\\\\\
- else\\\\\\\
- self:setCursorPos( self.cursor + 1 )\\\\\\\
- end\\\\\\\
- elseif key == keys.backspace then\\\\\\\
- if self.selection then\\\\\\\
- self:setSelection \\\\\\\"\\\\\\\"\\\\\\\
- self.selection = false\\\\\\\
- if self.onChange then self.onChange( self.public, self.text ) end\\\\\\\
- elseif self.cursor > 1 then\\\\\\\
- self.text = self.text:sub( 1, self.cursor - 2 ) .. self.text:sub( self.cursor )\\\\\\\
- self:setCursorPos( self.cursor - 1 )\\\\\\\
- if self.onChange then self.onChange( self.public, self.text ) end\\\\\\\
- end\\\\\\\
- elseif key == keys.delete then\\\\\\\
- if self.selection then\\\\\\\
- self:setSelection \\\\\\\"\\\\\\\"\\\\\\\
- self.selection = false\\\\\\\
- else\\\\\\\
- self.text = self.text:sub( 1, self.cursor - 1 ) .. self.text:sub( self.cursor + 1 )\\\\\\\
- end\\\\\\\
- if self.onChange then self.onChange( self.public, self.text ) end\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIInput.public:onTextInput( text, lastkey )\\\\\\\
- if not self.focussed then\\\\\\\
- return\\\\\\\
- end\\\\\\\
- if self.selection then\\\\\\\
- self:setSelection( text )\\\\\\\
- self.selection = false\\\\\\\
- else\\\\\\\
- self.text = self.text:sub( 1, self.cursor - 1 ) .. text .. self.text:sub( self.cursor )\\\\\\\
- self:setCursorPos( self.cursor + 1 )\\\\\\\
- end\\\\\\\
- if self.onChange then self.onChange( self.public, self.text ) end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIInput.public:onFocus( )\\\\\\\
- self.focussed = true\\\\\\\
- self.handlesKeys = true\\\\\\\
- if type( self.whenFocussed ) == \\\\\\\"function\\\\\\\" then\\\\\\\
- self.whenFocussed( self.public )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIInput.public:onUnFocus( )\\\\\\\
- self.focussed = false\\\\\\\
- self.handlesKeys = false\\\\\\\
- self.selection = false\\\\\\\
- if type( self.whenUnFocussed ) == \\\\\\\"function\\\\\\\" then\\\\\\\
- self.whenUnFocussed( self.public )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIInput.public:focusOn( )\\\\\\\
- local handler = self.public:getHandler( )\\\\\\\
- if handler then\\\\\\\
- handler:setFocus( self.public )\\\\\\\
- end\\\\\\\
- end\\\", meta={\\\
- type = \\\"class\\\",\\\
- }};[\\\"buffer\\\"]={content=\\\"\\\\\\\
- local screen = { }\\\\\\\
- local last = { }\\\\\\\
- local w, h = term.getSize( )\\\\\\\
- local bgc = colours.white\\\\\\\
- local cb\\\\\\\
- \\\\\\\
- for y = 1, h do\\\\\\\
- screen[y] = { }\\\\\\\
- last[y] = { }\\\\\\\
- for x = 1, w do\\\\\\\
- screen[y][x] = { bc = 1, tc = 32768, char = \\\\\\\" \\\\\\\" }\\\\\\\
- last[y][x] = { }\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function setPixel( x, y, bc, tc, char )\\\\\\\
- if screen[y] and screen[y][x] then\\\\\\\
- if bc ~= 0 then\\\\\\\
- if type( bc ) == \\\\\\\"function\\\\\\\" then\\\\\\\
- bc = bc( screen[y][x].bc )\\\\\\\
- end\\\\\\\
- screen[y][x].bc = bc\\\\\\\
- end\\\\\\\
- if tc == 0 then\\\\\\\
- char = \\\\\\\"\\\\\\\"\\\\\\\
- else\\\\\\\
- if type( tc ) == \\\\\\\"function\\\\\\\" then\\\\\\\
- tc = tc( screen[y][x].tc )\\\\\\\
- end\\\\\\\
- screen[y][x].tc = tc\\\\\\\
- end\\\\\\\
- if char ~= \\\\\\\"\\\\\\\" then\\\\\\\
- if type( char ) == \\\\\\\"function\\\\\\\" then\\\\\\\
- char = char( screen[y][x].char )\\\\\\\
- end\\\\\\\
- screen[y][x].char = char\\\\\\\
- end\\\\\\\
- return true\\\\\\\
- end\\\\\\\
- return false\\\\\\\
- end\\\\\\\
- \\\\\\\
- function getPixel( x, y )\\\\\\\
- if last[y] and last[y][x] then\\\\\\\
- return last[y][x].bc, last[y][x].tc, last[y][x].char\\\\\\\
- end\\\\\\\
- return false\\\\\\\
- end\\\\\\\
- \\\\\\\
- function hasChanged( x, y )\\\\\\\
- if screen[y] and screen[y][x] then\\\\\\\
- return screen[y][x].bc ~= last[y][x].bc or screen[y][x].tc ~= last[y][x].tc or screen[y][x].char ~= last[y][x].char\\\\\\\
- end\\\\\\\
- return false\\\\\\\
- end\\\\\\\
- \\\\\\\
- function setCursorBlink( x, y, col )\\\\\\\
- cb = { x = x, y = y, col = col }\\\\\\\
- end\\\\\\\
- \\\\\\\
- function drawChanges( )\\\\\\\
- local lasts = last\\\\\\\
- for y = 1, h do\\\\\\\
- local last\\\\\\\
- for x = 1, w do\\\\\\\
- if hasChanged( x, y ) then\\\\\\\
- if last then\\\\\\\
- if last.bc == screen[y][x].bc and ( last.tc == screen[y][x].tc or screen[y][x].char == \\\\\\\" \\\\\\\" ) then\\\\\\\
- last.char = last.char .. screen[y][x].char\\\\\\\
- else\\\\\\\
- term.setCursorPos( last.x, last.y )\\\\\\\
- term.setBackgroundColour( last.bc )\\\\\\\
- term.setTextColour( last.tc )\\\\\\\
- term.write( last.char )\\\\\\\
- last = { x = x, y = y, bc = screen[y][x].bc, tc = screen[y][x].tc, char = screen[y][x].char }\\\\\\\
- end\\\\\\\
- else\\\\\\\
- last = { x = x, y = y, bc = screen[y][x].bc, tc = screen[y][x].tc, char = screen[y][x].char }\\\\\\\
- end\\\\\\\
- lasts[y][x] = { bc = screen[y][x].bc, tc = screen[y][x].tc, char = screen[y][x].char }\\\\\\\
- elseif last then\\\\\\\
- term.setCursorPos( last.x, last.y )\\\\\\\
- term.setBackgroundColour( last.bc )\\\\\\\
- term.setTextColour( last.tc )\\\\\\\
- term.write( last.char )\\\\\\\
- last = nil\\\\\\\
- end\\\\\\\
- end\\\\\\\
- if last then\\\\\\\
- term.setCursorPos( last.x, last.y )\\\\\\\
- term.setBackgroundColour( last.bc )\\\\\\\
- term.setTextColour( last.tc )\\\\\\\
- term.write( last.char )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- if cb then\\\\\\\
- if cb.col then\\\\\\\
- term.setTextColour( cb.col )\\\\\\\
- end\\\\\\\
- term.setCursorPos( cb.x, cb.y )\\\\\\\
- term.setCursorBlink( true )\\\\\\\
- else\\\\\\\
- term.setCursorBlink( false )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function clear( )\\\\\\\
- for x = 1, w do\\\\\\\
- for y = 1, h do\\\\\\\
- screen[y][x].bc = bgc\\\\\\\
- screen[y][x].char = \\\\\\\" \\\\\\\"\\\\\\\
- end\\\\\\\
- end\\\\\\\
- cb = false\\\\\\\
- end\\\\\\\
- \\\\\\\
- function setBackgroundColour( col )\\\\\\\
- bgc = col\\\\\\\
- end\\\\\\\
- \\\\\\\
- function reset( )\\\\\\\
- for y = 1, h do\\\\\\\
- last[y] = { }\\\\\\\
- for x = 1, w do\\\\\\\
- last[y][x] = { }\\\\\\\
- end\\\\\\\
- end\\\\\\\
- end\\\", meta={\\\
- type = \\\"lib\\\",\\\
- }};[\\\"UIKeyHandler\\\"]={content=\\\"\\\\\\\
- require \\\\\\\"UIElement\\\\\\\"\\\\\\\
- UIKeyHandler:extends( UIElement )\\\\\\\
- \\\\\\\
- UIKeyHandler.handlesMouse = false\\\\\\\
- UIKeyHandler.handlesKeys = true\\\\\\\
- \\\\\\\
- UIKeyHandler.onKey = false\\\\\\\
- UIKeyHandler.public \\\\\\\"onKey\\\\\\\" \\\\\\\"function\\\\\\\"\\\\\\\
- UIKeyHandler.onChar = false\\\\\\\
- UIKeyHandler.public \\\\\\\"onChar\\\\\\\" \\\\\\\"function\\\\\\\"\\\\\\\
- \\\\\\\
- function UIKeyHandler:UIKeyHandler( )\\\\\\\
- self:UIElement( 0, 0, 0, 0 )\\\\\\\
- return self.public\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIKeyHandler.public:onKeyPress( key, lastkey )\\\\\\\
- if self.onKey then\\\\\\\
- self.onKey( self.public, key, lastkey )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- function UIKeyHandler.public:onTextInput( char, lastkey )\\\\\\\
- if self.onChar then\\\\\\\
- self.onChar( self.public, char, lastkey )\\\\\\\
- end\\\\\\\
- end\\\", meta={\\\
- type = \\\"class\\\",\\\
- }};[\\\"UITextbox\\\"]={content=\\\"\\\\\\\
- local function linewrap( str, w )\\\\\\\
- for i = 1, w + 1 do\\\\\\\
- if str:sub( i, i ) == \\\\\\\"\\\\\\\\n\\\\\\\" then\\\\\\\
- return str:sub( 1, i - 1 ), str:sub( i + 1 )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- if #str <= w then\\\\\\\
- return str, \\\\\\\"\\\\\\\"\\\\\\\
- end\\\\\\\
- if str:sub( w + 1, w + 1 ):find \\\\\\\"%s\\\\\\\" then\\\\\\\
- return str:sub( 1, w ), str:sub( w + 1 ):gsub( \\\\\\\"^%s+\\\\\\\", \\\\\\\"\\\\\\\" )\\\\\\\
- end\\\\\\\
- for i = w, 1, -1 do\\\\\\\
- if str:sub( i, i ):find \\\\\\\"%s\\\\\\\" then\\\\\\\
- return str:sub( 1, i ), str:sub( i + 1 )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- return str:sub( 1, w ), str:sub( w + 1 )\\\\\\\
- end\\\\\\\
- \\\\\\\
- local function wordwrap( str, w, h )\\\\\\\
- local lines = { }\\\\\\\
- local line\\\\\\\
- while #str > 0 do\\\\\\\
- line, str = linewrap( str, w )\\\\\\\
- table.insert( lines, line )\\\\\\\
- end\\\\\\\
- if h then\\\\\\\
- while #lines > h do\\\\\\\
- lines[#lines] = nil\\\\\\\
- end\\\\\\\
- end\\\\\\\
- return lines\\\\\\\
- end\\\\\\\
- \\\\\\\
- require \\\\\\\"UIElement\\\\\\\"\\\\\\\
- UITextbox:extends( UIElement )\\\\\\\
- \\\\\\\
- UITextbox.handlesKeys = true\\\\\\\
- UITextbox.tabIndex = true\\\\\\\
- \\\\\\\
- UITextbox.public \\\\\\\"text\\\\\\\"\\\\\\\
- \\\\\\\
- UITextbox.public \\\\\\\"onChange\\\\\\\" \\\\\\\"function\\\\\\\"\\\\\\\
- function UITextbox.public.text:write( value )\\\\\\\
- local t = tostring( value )\\\\\\\
- t = t:sub( 1, self.w * self.h )\\\\\\\
- while true do\\\\\\\
- local lines = wordwrap( t, self.w )\\\\\\\
- if #lines <= self.h then\\\\\\\
- break\\\\\\\
- end\\\\\\\
- t = t:sub( 1, -2 )\\\\\\\
- end\\\\\\\
- self.text = t\\\\\\\
- end\\\\\\\
- \\\\\\\
- UITextbox.bc = colours.lightGrey\\\\\\\
- UITextbox.fbc = colours.white\\\\\\\
- UITextbox.tc = colours.black\\\\\\\
- \\\\\\\
- UITextbox.public \\\\\\\"bc\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
- UITextbox.public \\\\\\\"fbc\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
- UITextbox.public \\\\\\\"tc\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
- \\\\\\\
- UITextbox.onEnter = false\\\\\\\
- UITextbox.public \\\\\\\"onEnter\\\\\\\" \\\\\\\"function\\\\\\\"\\\\\\\
- \\\\\\\
- function UITextbox:UITextbox( x, y, w, h, text, session )\\\\\\\
- self:UIElement( x, y, w, h )\\\\\\\
- \\\\\\\
- self.public.text = text\\\\\\\
- self.cursor = cursor\\\\\\\
- \\\\\\\
- return self.public\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UITextbox:getCursorXY( )\\\\\\\
- local lines = wordwrap( self.text, self.w, self.h )\\\\\\\
- local c = self.cursor\\\\\\\
- for i = 1, #lines do\\\\\\\
- if #lines[i] <= c then\\\\\\\
- c = c - #lines[i]\\\\\\\
- else\\\\\\\
- return c, i\\\\\\\
- end\\\\\\\
- end\\\\\\\
- if #lines == 0 then\\\\\\\
- return 1, 1\\\\\\\
- elseif self.text:sub( -1, -1 ) == \\\\\\\"\\\\\\\\n\\\\\\\" then\\\\\\\
- return 1, #lines + 1\\\\\\\
- else\\\\\\\
- return #lines[#lines] + 1, #lines\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UITextbox:getCursorPos( x, y )\\\\\\\
- local lines = wordwrap( self.text, self.w, self.h )\\\\\\\
- if lines[y] then\\\\\\\
- local pos = 0\\\\\\\
- for i = 1, y - 1 do\\\\\\\
- pos = pos + #lines[i]\\\\\\\
- end\\\\\\\
- if #lines[y] >= x then\\\\\\\
- pos = pos + x\\\\\\\
- else\\\\\\\
- pos = pos + #lines[y] + 1\\\\\\\
- end\\\\\\\
- return pos\\\\\\\
- else\\\\\\\
- return #self.text + 1\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UITextbox.public:draw( x, y )\\\\\\\
- local layer = stencil.addLayer( x, y, self.w, self.h )\\\\\\\
- local text = tostring( self.text )\\\\\\\
- local bc = self.bc\\\\\\\
- if self.focussed then\\\\\\\
- bc = self.fbc\\\\\\\
- end\\\\\\\
- stencil.text( x, y, self.w, self.h, bc, self.tc, self.text )\\\\\\\
- if self.focussed then\\\\\\\
- local cx, cy = self:getCursorXY( )\\\\\\\
- stencil.setCursorBlink( x + cx - 1, y + cy - 1 )\\\\\\\
- end\\\\\\\
- local c = self.public:getChildren( )\\\\\\\
- for i, child in ipairs( c ) do\\\\\\\
- child:draw( x + child.x - 1 + self.cx, y + child.y - 1 + self.cy )\\\\\\\
- end\\\\\\\
- stencil.closeLayer( layer )\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UITextbox.public:onMouseClick( rx, ry, button )\\\\\\\
- local pos = self:getCursorPos( rx, ry )\\\\\\\
- self.cursor = pos\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UITextbox.public:onKeyPress( key, lastkey )\\\\\\\
- if not self.focussed then\\\\\\\
- return\\\\\\\
- end\\\\\\\
- if key == keys.enter then\\\\\\\
- local c = self.cursor\\\\\\\
- local t = self.text\\\\\\\
- self.text = self.text:sub( 1, self.cursor - 1 ) .. \\\\\\\"\\\\\\\\n\\\\\\\" .. self.text:sub( self.cursor )\\\\\\\
- self.cursor = self.cursor + 1\\\\\\\
- if type( self.onChange ) == \\\\\\\"function\\\\\\\" then\\\\\\\
- self.onChange( self.public )\\\\\\\
- end\\\\\\\
- local lines = wordwrap( self.text, self.w )\\\\\\\
- if #lines > self.h then\\\\\\\
- self.text = t\\\\\\\
- self.cursor = c\\\\\\\
- end\\\\\\\
- elseif key == keys.left and self.cursor > 1 then\\\\\\\
- self.cursor = self.cursor - 1\\\\\\\
- elseif key == keys.right and self.cursor <= #self.text then\\\\\\\
- self.cursor = self.cursor + 1\\\\\\\
- elseif key == keys.backspace and self.cursor > 1 then\\\\\\\
- self.text = self.text:sub( 1, self.cursor - 2 ) .. self.text:sub( self.cursor )\\\\\\\
- self.cursor = self.cursor - 1\\\\\\\
- elseif key == keys.delete then\\\\\\\
- self.text = self.text:sub( 1, self.cursor - 1 ) .. self.text:sub( self.cursor + 1 )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UITextbox.public:onTextInput( text, lastkey )\\\\\\\
- if not self.focussed then\\\\\\\
- return\\\\\\\
- end\\\\\\\
- if lastkey ~= keys.leftCtrl then\\\\\\\
- local c = self.cursor\\\\\\\
- local t = self.text\\\\\\\
- self.text = self.text:sub( 1, self.cursor - 1 ) .. text .. self.text:sub( self.cursor )\\\\\\\
- self.cursor = self.cursor + 1\\\\\\\
- if type( self.onChange ) == \\\\\\\"function\\\\\\\" then\\\\\\\
- self.onChange( self.public )\\\\\\\
- end\\\\\\\
- local lines = wordwrap( self.text, self.w )\\\\\\\
- if #lines > self.h then\\\\\\\
- self.text = t\\\\\\\
- self.cursor = c\\\\\\\
- end\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UITextbox.public:onFocus( )\\\\\\\
- self.focussed = true\\\\\\\
- self.handlesKeys = true\\\\\\\
- if type( self.whenFocussed ) == \\\\\\\"function\\\\\\\" then\\\\\\\
- self.whenFocussed( self.public )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UITextbox.public:onUnFocus( )\\\\\\\
- self.focussed = false\\\\\\\
- self.handlesKeys = false\\\\\\\
- if type( self.whenUnFocussed ) == \\\\\\\"function\\\\\\\" then\\\\\\\
- self.whenUnFocussed( self.public )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UITextbox.public:focusOn( )\\\\\\\
- local handler = self.public:getHandler( )\\\\\\\
- handler:setFocus( self.public )\\\\\\\
- end\\\", meta={\\\
- type = \\\"class\\\",\\\
- }};[\\\"UIMenu\\\"]={content=\\\"\\\\\\\
- require \\\\\\\"UIElement\\\\\\\"\\\\\\\
- \\\\\\\
- UIMenu:extends( UIElement )\\\\\\\
- \\\\\\\
- UIMenu.handlesMouse = false\\\\\\\
- UIMenu.handlesScroll = true\\\\\\\
- UIMenu.isScrollTarget = true\\\\\\\
- UIMenu.scrollDirection = \\\\\\\"vertical\\\\\\\"\\\\\\\
- UIMenu.public \\\\\\\"padding\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
- \\\\\\\
- function UIMenu:UIMenu( x, y, w, h, direction )\\\\\\\
- self:UIElement( x, y, w, h )\\\\\\\
- self.scrollDirection = direction or \\\\\\\"vertical\\\\\\\"\\\\\\\
- self.padding = 0\\\\\\\
- return self.public\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIMenu.public:update( dt )\\\\\\\
- local max = self.scrollDirection == \\\\\\\"vertical\\\\\\\" and self.h or self.w\\\\\\\
- if self.scrollDirection == \\\\\\\"vertical\\\\\\\" then\\\\\\\
- for i = 1, #self.children do\\\\\\\
- max = math.max( max, self.children[i].y + self.children[i].h - 1 )\\\\\\\
- end\\\\\\\
- else\\\\\\\
- for i = 1, #self.children do\\\\\\\
- max = math.max( max, self.children[i].x + self.children[i].w - 1 )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- if self.scrollDirection == \\\\\\\"vertical\\\\\\\" then\\\\\\\
- if max - self.h < -self.cy then\\\\\\\
- self.cy = -max + self.h\\\\\\\
- end\\\\\\\
- else\\\\\\\
- if max - self.w < -self.cx then\\\\\\\
- self.cx = -max + self.w\\\\\\\
- end\\\\\\\
- end\\\\\\\
- if max == ( self.scrollDirection == \\\\\\\"vertical\\\\\\\" and self.h or self.w ) then\\\\\\\
- self.handlesScroll = false\\\\\\\
- else\\\\\\\
- self.handlesScroll = true\\\\\\\
- end\\\\\\\
- local c = self.public:getChildren( )\\\\\\\
- for i, child in ipairs( c ) do\\\\\\\
- child:update( dt )\\\\\\\
- end\\\\\\\
- local x, y = 1, 1\\\\\\\
- for i, child in ipairs( self.children ) do\\\\\\\
- child.x = x\\\\\\\
- child.y = y\\\\\\\
- if self.scrollDirection == \\\\\\\"vertical\\\\\\\" then\\\\\\\
- child.w = self.w\\\\\\\
- y = y + child.h + self.padding\\\\\\\
- else\\\\\\\
- child.h = self.h\\\\\\\
- x = x + child.w + self.padding\\\\\\\
- end\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIMenu.public:onMouseScroll( rx, ry, dir )\\\\\\\
- if self.scrollDirection == \\\\\\\"vertical\\\\\\\" then\\\\\\\
- self.cy = self.cy - dir\\\\\\\
- local max = 0\\\\\\\
- for i = 1, #self.children do\\\\\\\
- max = math.max( max, self.children[i].y + self.children[i].h )\\\\\\\
- end\\\\\\\
- max = max - self.h - 1\\\\\\\
- if self.cy < -max then\\\\\\\
- self.cy = -max\\\\\\\
- end\\\\\\\
- if self.cy > 0 then self.cy = 0 end\\\\\\\
- else\\\\\\\
- self.cx = self.cx - dir\\\\\\\
- local max = 0\\\\\\\
- for i = 1, #self.children do\\\\\\\
- max = math.max( max, self.children[i].x + self.children[i].w )\\\\\\\
- end\\\\\\\
- max = max - self.w - 1\\\\\\\
- if self.cx < -max then\\\\\\\
- self.cx = -max\\\\\\\
- end\\\\\\\
- if self.cx > 0 then self.cx = 0 end\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIMenu.public:getDisplaySizeH( )\\\\\\\
- return self.w\\\\\\\
- end\\\\\\\
- function UIMenu.public:getDisplaySizeV( )\\\\\\\
- return self.h\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIMenu.public:getContentSizeH( )\\\\\\\
- local max = self.w\\\\\\\
- for i = 1, #self.children do\\\\\\\
- max = math.max( max, self.children[i].x + self.children[i].w - 1 )\\\\\\\
- end\\\\\\\
- return max\\\\\\\
- end\\\\\\\
- function UIMenu.public:getContentSizeV( )\\\\\\\
- local max = self.h\\\\\\\
- for i = 1, #self.children do\\\\\\\
- max = math.max( max, self.children[i].y + self.children[i].h - 1 )\\\\\\\
- end\\\\\\\
- return max\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIMenu.public:getContentScrollH( )\\\\\\\
- return -self.cx\\\\\\\
- end\\\\\\\
- function UIMenu.public:getContentScrollV( )\\\\\\\
- return -self.cy\\\\\\\
- end\\\\\\\
- \\\\\\\
- function UIMenu.public:setContentScrollH( scroll )\\\\\\\
- self.cx = -scroll\\\\\\\
- end\\\\\\\
- function UIMenu.public:setContentScrollV( scroll )\\\\\\\
- self.cy = -scroll\\\\\\\
- end\\\", meta={\\\
- type = \\\"class\\\",\\\
- }};}\\\
- --/end of files\\\
- local autorun = {\\\"UIButton\\\";\\\"UIImage\\\";\\\"UIElement\\\";\\\"UIHandler\\\";\\\"UIScrollBar\\\";\\\"UIBuffer\\\";\\\"UICode\\\";\\\"Image\\\";\\\"UIFrame\\\";\\\"UIText\\\";\\\"UIInput\\\";\\\"UIKeyHandler\\\";\\\"UITextbox\\\";\\\"UIMenu\\\";\\\"main\\\"}\\\
- --@meta[type]:\\\"class\\\"\\\
- \\\
- -- Swift Class Library\\\
- -- Made by awsumben13\\\
- -- Feel free to use this in any of your projects but keep this at the top and give credit where necessary\\\
- \\\
- --[[\\\
- This class library offers:\\\
- Inheritance \\\"Class:extends( other class )\\\"\\\
- Initialiser methods \\\"function ClassName:ClassName( )\\\"\\\
- Public / private variables with customisable read/write access \\\"Class.public.x.write = false\\\"\\\
- Static variables ( accessed from the class object ) \\\"Class.static.x = 5\\\"\\\
- Custom metamethods including __index and __newindex \\\"Class.meta.index = { }\\\"\\\
- ]]\\\
- \\\
- local class = { }\\\
- \\\
- function class.new( name )\\\
- \\\
- local object = { }\\\
- local public = { }\\\
- \\\
- object.public = { }\\\
- object.private = { }\\\
- object.static = { }\\\
- object.name = name\\\
- object.extends = false\\\
- object.class = public\\\
- \\\
- public.name = name\\\
- \\\
- local customindex = false\\\
- local customnewindex = false\\\
- local objectmeta = { }\\\
- \\\
- function public:new( ... )\\\
- \\\
- local ob = { }\\\
- local pb = { }\\\
- \\\
- ob.class = public\\\
- ob.public = pb\\\
- \\\
- setmetatable( ob, {\\\
- __index = object.private;\\\
- } )\\\
- \\\
- function pb:type( full )\\\
- return ob.class:type( full )\\\
- end\\\
- \\\
- function pb:typeOf( class )\\\
- return ob.class:typeOf( class )\\\
- end\\\
- \\\
- local obmeta = { }\\\
- obmeta.__index = function( _, k )\\\
- if object.public[k] then\\\
- if object.public[k].read then\\\
- local val\\\
- if customindex then\\\
- if type( customindex ) == \\\"function\\\" then\\\
- return customindex( ob, k )\\\
- else\\\
- return customindex[k]\\\
- end\\\
- elseif type( object.public[k].read ) == \\\"function\\\" then\\\
- val = object.public[k].read( ob )\\\
- elseif object.public[k].value ~= nil then\\\
- val = object.public[k].value\\\
- else\\\
- val = ob[k]\\\
- end\\\
- if type( val ) == \\\"function\\\" then\\\
- return function( self, ... )\\\
- if self == pb then\\\
- return val( ob, ... )\\\
- end\\\
- return val( self, ... )\\\
- end\\\
- end\\\
- return val\\\
- else\\\
- error( \\\"variable has no read access\\\", 2 )\\\
- end\\\
- elseif customindex then\\\
- if type( customindex ) == \\\"function\\\" then\\\
- return customindex( ob, k )\\\
- else\\\
- return customindex[k]\\\
- end\\\
- else\\\
- error( \\\"no such variable \\\\\\\"\\\" .. tostring( k ) .. \\\"\\\\\\\"\\\", 2 )\\\
- end\\\
- end;\\\
- obmeta.__newindex = function( _, k, v )\\\
- if object.public[k] then\\\
- if object.public[k].write then\\\
- if customnewindex then\\\
- if type( customnewindex ) == \\\"function\\\" then\\\
- return customnewindex( ob, k, v )\\\
- else\\\
- customnewindex[k] = v\\\
- end\\\
- elseif type( object.public[k].write ) == \\\"function\\\" then\\\
- object.public[k].write( ob, v )\\\
- else\\\
- ob[k] = v\\\
- end\\\
- else\\\
- error( \\\"variable has no write access\\\", 2 )\\\
- end\\\
- else\\\
- error( \\\"no such variable \\\\\\\"\\\" .. tostring( k ) .. \\\"\\\\\\\"\\\", 2 )\\\
- end\\\
- end;\\\
- obmeta.__tostring = function( )\\\
- return object.name\\\
- end;\\\
- obmeta.__metatable = \\\"SwiftClassObject\\\";\\\
- \\\
- for k, v in pairs( objectmeta ) do\\\
- obmeta[\\\"__\\\" .. tostring( k )] = v\\\
- end\\\
- \\\
- setmetatable( pb, obmeta )\\\
- \\\
- local c = object\\\
- while true do\\\
- if type( c.private[c.name] ) == \\\"function\\\" then\\\
- return c.private[c.name]( ob, ... )\\\
- end\\\
- if c.extends then\\\
- c = c.extends\\\
- else\\\
- break\\\
- end\\\
- end\\\
- \\\
- return pb\\\
- end\\\
- \\\
- function public:type( full )\\\
- local str = \\\"\\\"\\\
- if full then\\\
- local c = object.extends\\\
- while c do\\\
- str = c.name .. \\\".\\\" .. str\\\
- c = c.extends\\\
- end\\\
- end\\\
- return str .. object.name\\\
- end\\\
- \\\
- function public:typeOf( other )\\\
- if type( other ) == \\\"table\\\" then\\\
- if getmetatable( other ) == \\\"SwiftClass\\\" then\\\
- local ob = object\\\
- while ob do\\\
- if ob.class == other then\\\
- return true\\\
- end\\\
- ob = ob.extends\\\
- end\\\
- end\\\
- end\\\
- return false\\\
- end\\\
- \\\
- function public:extends( ob )\\\
- ob:extend( object )\\\
- end\\\
- \\\
- function public:extend( ob )\\\
- setmetatable( ob.static, { __index = object.static } )\\\
- setmetatable( ob.public, { __index = object.public } )\\\
- setmetatable( ob.private, { __index = object.private } )\\\
- ob.extends = object\\\
- end\\\
- \\\
- local meta = { }\\\
- meta.__index = function( _, k )\\\
- if k == \\\"static\\\" then\\\
- return setmetatable( { }, {\\\
- __newindex = function( _, k, v )\\\
- object.static[k] = v\\\
- end;\\\
- __metatable = { };\\\
- } )\\\
- elseif k == \\\"public\\\" then\\\
- return setmetatable( { }, {\\\
- __newindex = function( _, k, v )\\\
- object.public[k] = {\\\
- read = true;\\\
- write = false;\\\
- value = v;\\\
- }\\\
- end;\\\
- __call = function( _, k )\\\
- object.public[k] = {\\\
- read = true;\\\
- write = true;\\\
- value = nil;\\\
- }\\\
- return function( _type )\\\
- local types = { _type }\\\
- object.public[k].write = function( ob, value )\\\
- for i = 1, #types do\\\
- if class.typeOf( value, types[i] ) then\\\
- ob[k] = value\\\
- return\\\
- end\\\
- end\\\
- if class.type( types[1] ) == \\\"Class\\\" then\\\
- error( \\\"expected \\\" .. types[1]:type( ) .. \\\" \\\" .. k, 3 )\\\
- else\\\
- error( \\\"expected \\\" .. types[1] .. \\\" \\\" .. k, 3 )\\\
- end\\\
- end\\\
- return function( _type )\\\
- table.insert( types, _type )\\\
- end\\\
- end\\\
- end;\\\
- __index = function( _, k )\\\
- if object.public[k] then\\\
- return setmetatable( { }, {\\\
- __newindex = function( _, name, v )\\\
- if name == \\\"read\\\" then\\\
- if type( v ) == \\\"boolean\\\" or type( v ) == \\\"function\\\" then\\\
- object.public[k].read = v\\\
- else\\\
- error( \\\"invalid modifier value\\\", 2 )\\\
- end\\\
- elseif name == \\\"write\\\" then\\\
- if type( v ) == \\\"boolean\\\" or type( v ) == \\\"function\\\" then\\\
- object.public[k].write = v\\\
- else\\\
- error( \\\"invalid modifier value\\\", 2 )\\\
- end\\\
- else\\\
- error( \\\"invalid modifier name\\\", 2 )\\\
- end\\\
- end;\\\
- __metatable = { };\\\
- } )\\\
- else\\\
- error( \\\"public index \\\" .. tostring( k ) .. \\\" not found\\\", 2 )\\\
- end\\\
- end;\\\
- __metatable = { };\\\
- } )\\\
- elseif k == \\\"meta\\\" then\\\
- return setmetatable( { }, {\\\
- __index = function( _, k )\\\
- if k == \\\"index\\\" then\\\
- return customindex\\\
- elseif k == \\\"newindex\\\" then\\\
- return customnewindex\\\
- else\\\
- return objectmeta[k]\\\
- end\\\
- end;\\\
- __newindex = function( _, k, v )\\\
- if k == \\\"metatable\\\" then\\\
- error( \\\"cannot change this metamethod\\\", 2 )\\\
- elseif k == \\\"index\\\" then\\\
- if type( v ) == \\\"function\\\" or type( v ) == \\\"table\\\" or v == nil then\\\
- customindex = v\\\
- else\\\
- error( \\\"cannot use type \\\" .. type( v ) .. \\\" for index metamethod\\\", 2 )\\\
- end\\\
- elseif k == \\\"newindex\\\" then\\\
- if type( v ) == \\\"function\\\" or type( v ) == \\\"table\\\" or v == nil then\\\
- customnewindex = v\\\
- else\\\
- error( \\\"cannot use type \\\" .. type( v ) .. \\\" for newindex metamethod\\\", 2 )\\\
- end\\\
- else\\\
- objectmeta[k] = v\\\
- end\\\
- end;\\\
- __metatable = { };\\\
- } )\\\
- else\\\
- return object.static[k]\\\
- end\\\
- end\\\
- meta.__newindex = function( _, k, v )\\\
- object.private[k] = v\\\
- end;\\\
- meta.__call = function( self, ... )\\\
- return self:new( ... )\\\
- end;\\\
- meta.__totring = function( )\\\
- return \\\"Class\\\"\\\
- end;\\\
- meta.__metatable = \\\"SwiftClass\\\"\\\
- \\\
- setmetatable( public, meta )\\\
- \\\
- return public\\\
- end\\\
- \\\
- function class.public( name )\\\
- local object = class.new( name )\\\
- getfenv( )[name] = object\\\
- end\\\
- \\\
- function class.type( object )\\\
- if type( object ) == \\\"table\\\" then\\\
- if getmetatable( object ) == \\\"SwiftClass\\\" then\\\
- return \\\"Class\\\"\\\
- end\\\
- if getmetatable( object ) == \\\"SwiftClassObject\\\" then\\\
- return object:type( )\\\
- end\\\
- end\\\
- return type( object )\\\
- end\\\
- \\\
- function class.typeOf( object, ... )\\\
- local types = { ... }\\\
- for i = 1, #types do\\\
- if type( object ) == \\\"table\\\" and getmetatable( object ) == \\\"SwiftClassObject\\\" then\\\
- if object:typeOf( types[i] ) then\\\
- return types[i]\\\
- end\\\
- elseif type( object ) == \\\"table\\\" and getmetatable( object ) == \\\"SwiftClass\\\" then\\\
- if types[i] == \\\"Class\\\" then\\\
- return \\\"Class\\\"\\\
- end\\\
- else\\\
- if type( object ) == types[i] then\\\
- return types[i]\\\
- end\\\
- end\\\
- end\\\
- return false\\\
- end\\\
- \\\
- setmetatable( class, { __call = function( _, ... ) return class.new( ... ) end } )\\\
- local args = { ... }\\\
- local global = getfenv( )\\\
- local custom = setmetatable( { class = class, ARGS = args }, { __index = global } )\\\
- function custom.export( variable, value )\\\
- if custom[variable] ~= nil or value then\\\
- global[variable] = custom[variable] == nil and value or custom[variable]\\\
- end\\\
- end\\\
- local required = { }\\\
- local function require( file, ... )\\\
- if not required[file] and files[file] then\\\
- required[file] = true\\\
- if files[file].meta.type == \\\"class\\\" then\\\
- custom[file] = class( file )\\\
- end\\\
- local fenv = setmetatable( { shared = custom }, { __index = custom } )\\\
- local f, err = loadstring( files[file].content, file .. \\\".lua\\\" )\\\
- if f then\\\
- setfenv( f, fenv )\\\
- local ok, data = pcall( f )\\\
- if ok then\\\
- if files[file].meta.type == \\\"lib\\\" then\\\
- if data ~= nil then\\\
- custom[file] = data\\\
- return data\\\
- else\\\
- local t = { }\\\
- for k, v in pairs( fenv ) do\\\
- t[k] = v\\\
- end\\\
- custom[file] = t\\\
- return t\\\
- end\\\
- end\\\
- return data\\\
- else\\\
- error( data, 0 )\\\
- end\\\
- else\\\
- error( err, 0 )\\\
- end\\\
- end\\\
- end\\\
- custom.require = require\\\
- \\\
- for i = 1, #autorun do\\\
- require( autorun[i] )\\\
- end\",\
- [\"NovaNet.lua\"]=\"--[[type=\\\"executable_package\\\", name=\\\"NovaNet\\\"]]\\\
- local files = {[\\\"handshake\\\"]={content=\\\"-- Handshake\\\\\\\
- -- Used with permission from 1lann\\\\\\\
- -- Compressed using Lua Minifier (https://mothereff.in/lua-minifier)\\\\\\\
- local Handshake={}Handshake.prime=625210769;Handshake.base=-1;Handshake.secret=-1;Handshake.sharedSecret=-1;function Handshake.exponentWithModulo(a,b,c)local d=a;for e=1,b-1 do d=d*d;if d>=c then d=d%c end end;return d end;function Handshake.clear()Handshake.base=-1;Handshake.secret=-1;Handshake.sharedSecret=-1 end;function Handshake.generateInitiatorData()Handshake.base=math.random(10,99999)Handshake.secret=math.random(10,99999)return{type=\\\\\\\"initiate\\\\\\\",prime=Handshake.prime,base=Handshake.base,moddedSecret=Handshake.exponentWithModulo(Handshake.base,Handshake.secret,Handshake.prime)}end;function Handshake.generateResponseData(f)local g=type(f.prime)==\\\\\\\"number\\\\\\\"local h=f.prime==Handshake.prime;local i=type(f.base)==\\\\\\\"number\\\\\\\"local j=f.type==\\\\\\\"initiate\\\\\\\"local k=type(f.moddedSecret)==\\\\\\\"number\\\\\\\"local l=g and i and k;if l and h then if j then Handshake.base=f.base;Handshake.secret=math.random(10,99999)Handshake.sharedSecret=Handshake.exponentWithModulo(f.moddedSecret,Handshake.secret,Handshake.prime)return{type=\\\\\\\"response\\\\\\\",prime=Handshake.prime,base=Handshake.base,moddedSecret=Handshake.exponentWithModulo(Handshake.base,Handshake.secret,Handshake.prime)},Handshake.sharedSecret elseif f.type==\\\\\\\"response\\\\\\\"and Handshake.base>0 and Handshake.secret>0 then Handshake.sharedSecret=Handshake.exponentWithModulo(f.moddedSecret,Handshake.secret,Handshake.prime)return Handshake.sharedSecret else return false end else return false end end;return Handshake\\\", meta={\\\
- type = \\\"lib\\\",\\\
- }};[\\\"main\\\"]={content=\\\"\\\\\\\
- require \\\\\\\"com\\\\\\\"\\\\\\\
- require \\\\\\\"encryption\\\\\\\"\\\\\\\
- require \\\\\\\"handshake\\\\\\\"\\\\\\\
- require \\\\\\\"network\\\\\\\"\\\\\\\
- \\\\\\\
- for k, v in pairs( network ) do\\\\\\\
- export( k, v )\\\\\\\
- end\\\\\\\
- \\\\\\\
- export \\\\\\\"com\\\\\\\"\\\", meta={}};[\\\"encryption\\\"]={content=\\\"\\\\\\\
- local function sum( n, ... ) -- number n, number ...additions\\\\\\\
- local t = { ... }\\\\\\\
- for i = 1, #t do\\\\\\\
- n = n + t[i]\\\\\\\
- end\\\\\\\
- return n\\\\\\\
- \\\\\\\
- -- number sum\\\\\\\
- end\\\\\\\
- \\\\\\\
- local function loop( n, lim ) -- number n, number limit\\\\\\\
- while n > lim do\\\\\\\
- n = n - lim\\\\\\\
- end\\\\\\\
- while n < 1 do\\\\\\\
- n = n + lim\\\\\\\
- end\\\\\\\
- return n\\\\\\\
- -- number limited\\\\\\\
- end\\\\\\\
- \\\\\\\
- local function shift( str, count )\\\\\\\
- local str = { str:byte( 1, #str ) }\\\\\\\
- for i = 1, #str do\\\\\\\
- str[i] = loop( str[i] + count, 255 )\\\\\\\
- end\\\\\\\
- for i = 1, #str do\\\\\\\
- str[i] = string.char( str[i] )\\\\\\\
- end\\\\\\\
- return table.concat( str, \\\\\\\"\\\\\\\" )\\\\\\\
- end\\\\\\\
- \\\\\\\
- local function tohex( b ) -- string[4] bits\\\\\\\
- local n = 0\\\\\\\
- for i = 1, 4 do\\\\\\\
- n = n * 2\\\\\\\
- n = n + tonumber( b:sub( i, i ) )\\\\\\\
- end\\\\\\\
- if n >= 10 then\\\\\\\
- local hexes = { \\\\\\\"A\\\\\\\", \\\\\\\"B\\\\\\\", \\\\\\\"C\\\\\\\", \\\\\\\"D\\\\\\\", \\\\\\\"E\\\\\\\", \\\\\\\"F\\\\\\\" }\\\\\\\
- return hexes[n - 9]\\\\\\\
- end\\\\\\\
- return tostring( n )\\\\\\\
- \\\\\\\
- -- string[1] hex\\\\\\\
- end\\\\\\\
- \\\\\\\
- local function fromhex( h ) -- string[1] hex\\\\\\\
- local n = tonumber( h )\\\\\\\
- if not n then\\\\\\\
- local hexes = { [\\\\\\\"A\\\\\\\"] = 10, [\\\\\\\"B\\\\\\\"] = 11, [\\\\\\\"C\\\\\\\"] = 12, [\\\\\\\"D\\\\\\\"] = 13, [\\\\\\\"E\\\\\\\"] = 14, [\\\\\\\"F\\\\\\\"] = 15 }\\\\\\\
- n = hexes[h]\\\\\\\
- end\\\\\\\
- local str = \\\\\\\"\\\\\\\"\\\\\\\
- for i = 1, 4 do\\\\\\\
- str = str .. n % 2\\\\\\\
- n = math.floor( n / 2 )\\\\\\\
- end\\\\\\\
- return str:reverse( )\\\\\\\
- \\\\\\\
- -- string[4] bits\\\\\\\
- end\\\\\\\
- \\\\\\\
- local function xor( n1, n2 )\\\\\\\
- if n1 > 255 or n2 > 255 or n1 < 0 or n2 < 0 then\\\\\\\
- return error \\\\\\\"expected numbers between 0 and 255\\\\\\\"\\\\\\\
- end\\\\\\\
- local bit1, bit2 = { }, { }\\\\\\\
- for i = 1, 8 do\\\\\\\
- bit1[9-i] = n1 % 2 == 1\\\\\\\
- n1 = math.floor( n1 / 2 )\\\\\\\
- end\\\\\\\
- for i = 1, 8 do\\\\\\\
- bit2[9-i] = n2 % 2 == 1\\\\\\\
- n2 = math.floor( n2 / 2 )\\\\\\\
- end\\\\\\\
- local bits = { }\\\\\\\
- for i = 1, 8 do\\\\\\\
- bits[i] = ( bit1[i] and not bit2[i] ) or ( not bit1[i] and bit2[i] )\\\\\\\
- end\\\\\\\
- local n = 0\\\\\\\
- for i = 1, 8 do\\\\\\\
- n = n * 2\\\\\\\
- n = n + ( bits[i] and 1 or 0 )\\\\\\\
- end\\\\\\\
- return n\\\\\\\
- end\\\\\\\
- \\\\\\\
- local function nand( n1, n2 )\\\\\\\
- if n1 > 255 or n2 > 255 or n1 < 0 or n2 < 0 then\\\\\\\
- return error \\\\\\\"expected numbers between 0 and 255\\\\\\\"\\\\\\\
- end\\\\\\\
- local bit1, bit2 = { }, { }\\\\\\\
- for i = 1, 8 do\\\\\\\
- bit1[9-i] = n1 % 2 == 1\\\\\\\
- n1 = math.floor( n1 / 2 )\\\\\\\
- end\\\\\\\
- for i = 1, 8 do\\\\\\\
- bit2[9-i] = n2 % 2 == 1\\\\\\\
- n2 = math.floor( n2 / 2 )\\\\\\\
- end\\\\\\\
- local bits = { }\\\\\\\
- for i = 1, 8 do\\\\\\\
- bits[i] = not bit1[i] == bit2[i]\\\\\\\
- end\\\\\\\
- local n = 0\\\\\\\
- for i = 1, 8 do\\\\\\\
- n = n * 2\\\\\\\
- n = n + ( bits[i] and 1 or 0 )\\\\\\\
- end\\\\\\\
- return n\\\\\\\
- end\\\\\\\
- \\\\\\\
- local function tobits( n )\\\\\\\
- local str = \\\\\\\"\\\\\\\"\\\\\\\
- for i = 1, 8 do\\\\\\\
- str = str .. n % 2\\\\\\\
- n = math.floor( n / 2 )\\\\\\\
- end\\\\\\\
- return str:reverse( )\\\\\\\
- end\\\\\\\
- \\\\\\\
- local function frombits( b )\\\\\\\
- local n = 0\\\\\\\
- for i = 1, 8 do\\\\\\\
- n = n * 2\\\\\\\
- n = n + tonumber( b:sub( i, i ) )\\\\\\\
- end\\\\\\\
- return n\\\\\\\
- end\\\\\\\
- \\\\\\\
- local t = os.clock( )\\\\\\\
- local function start( )\\\\\\\
- t = os.clock( )\\\\\\\
- end\\\\\\\
- local function yield( )\\\\\\\
- if os.clock( ) - t > .1 then\\\\\\\
- coroutine.yield( )\\\\\\\
- start( )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function encrypt( str, key ) -- string text, string key\\\\\\\
- local enc = \\\\\\\"\\\\\\\"\\\\\\\
- start( )\\\\\\\
- for i = 1, #str do\\\\\\\
- math.randomseed( sum( key:byte( 1, #key ) ) )\\\\\\\
- key = shift( key, math.random( 1, 100 ) )\\\\\\\
- local ki = loop( i, #key )\\\\\\\
- local a = str:sub( i, i ):byte( )\\\\\\\
- local b = key:sub( ki, ki ):byte( )\\\\\\\
- enc = enc .. tobits( xor( a, b ) )\\\\\\\
- yield( )\\\\\\\
- end\\\\\\\
- local enc2 = \\\\\\\"\\\\\\\"\\\\\\\
- for i = 1, #enc / 4 do\\\\\\\
- enc2 = enc2 .. tohex( enc:sub( i * 4 - 3, i * 4 ) )\\\\\\\
- yield( )\\\\\\\
- end\\\\\\\
- return enc2\\\\\\\
- \\\\\\\
- -- string cipher\\\\\\\
- end\\\\\\\
- \\\\\\\
- function decrypt( str, key ) -- string cipher, string key\\\\\\\
- start( )\\\\\\\
- local dec2 = \\\\\\\"\\\\\\\"\\\\\\\
- for i = 1, #str do\\\\\\\
- dec2 = dec2 .. fromhex( str:sub( i, i ) )\\\\\\\
- yield( )\\\\\\\
- end\\\\\\\
- str = dec2\\\\\\\
- local dec = \\\\\\\"\\\\\\\"\\\\\\\
- local keys = { }\\\\\\\
- for i = 1, #str / 8 do\\\\\\\
- math.randomseed( sum( key:byte( 1, #key ) ) )\\\\\\\
- keys[i] = shift( key, math.random( 1, 100 ) )\\\\\\\
- key = keys[i]\\\\\\\
- yield( )\\\\\\\
- end\\\\\\\
- for i = 1, #str / 8 do\\\\\\\
- local ki = loop( i, #key )\\\\\\\
- local a = frombits( str:sub( ( i - 1 ) * 8 + 1, i * 8 ) )\\\\\\\
- local b = string.byte( keys[i]:sub( ki, ki ) )\\\\\\\
- dec = dec .. string.char( nand( a, b ) )\\\\\\\
- yield( )\\\\\\\
- end\\\\\\\
- return dec\\\\\\\
- \\\\\\\
- -- string text\\\\\\\
- end\\\", meta={\\\
- type = \\\"lib\\\",\\\
- }};[\\\"network\\\"]={content=\\\"\\\\\\\
- require \\\\\\\"com\\\\\\\"\\\\\\\
- require \\\\\\\"handshake\\\\\\\"\\\\\\\
- \\\\\\\
- local buffers = {\\\\\\\
- established = { }; -- .listen()\\\\\\\
- }\\\\\\\
- local channels = { }\\\\\\\
- local opening = false\\\\\\\
- local channelID = 0\\\\\\\
- \\\\\\\
- function genkey( seed )\\\\\\\
- math.randomseed( seed )\\\\\\\
- local s = \\\\\\\"\\\\\\\"\\\\\\\
- for i = 1, 64 do\\\\\\\
- s = s .. string.char( math.random( 0, 255 ) )\\\\\\\
- end\\\\\\\
- return s\\\\\\\
- end\\\\\\\
- \\\\\\\
- function open( peer, protocol, insecure ) -- opens a secure channel with peer with [protocol]\\\\\\\
- if peer == os.getComputerID( ) then\\\\\\\
- local key = math.random( 1, 2^16 )\\\\\\\
- local t = { }\\\\\\\
- if insecure then\\\\\\\
- channels[t] = { peer = peer }\\\\\\\
- else\\\\\\\
- channels[t] = { peer = peer, key = genkey( key ), rawkey = key }\\\\\\\
- end\\\\\\\
- table.insert( buffers.established, { protocol = protocol, id = t } )\\\\\\\
- return t\\\\\\\
- end\\\\\\\
- local ok, err = com.send( peer, {\\\\\\\
- request = \\\\\\\"connection\\\\\\\";\\\\\\\
- protocol = type( protocol ) == \\\\\\\"string\\\\\\\" and protocol or \\\\\\\"none\\\\\\\";\\\\\\\
- handshake = handshake.generateInitiatorData( );\\\\\\\
- insecure = insecure\\\\\\\
- } )\\\\\\\
- if not ok then return false, err end\\\\\\\
- local ok, message = com.receive( peer, nil, 1 )\\\\\\\
- if ok then\\\\\\\
- local key = handshake.generateResponseData( message )\\\\\\\
- local t = { }\\\\\\\
- if insecure then\\\\\\\
- channels[t] = { peer = peer }\\\\\\\
- else\\\\\\\
- channels[t] = { peer = peer, key = genkey( key ), rawkey = key }\\\\\\\
- end\\\\\\\
- if insecure then\\\\\\\
- core.log( \\\\\\\"Network\\\\\\\", \\\\\\\"Insecure connection established with \\\\\\\" .. tostring( peer ) )\\\\\\\
- else\\\\\\\
- core.log( \\\\\\\"Network\\\\\\\", \\\\\\\"Secure connection established with \\\\\\\" .. tostring( peer ) )\\\\\\\
- end\\\\\\\
- return t\\\\\\\
- end\\\\\\\
- return false, message\\\\\\\
- end\\\\\\\
- \\\\\\\
- function close( channel, reason ) -- closes an open channel with [reason]\\\\\\\
- if not channels[channel] then\\\\\\\
- return false, \\\\\\\"channel not open\\\\\\\"\\\\\\\
- end\\\\\\\
- local ok, data = com.send( channels[channel].peer, {\\\\\\\
- request = \\\\\\\"ConnectionClose\\\\\\\";\\\\\\\
- reason = type( reason ) == \\\\\\\"string\\\\\\\" and reason or \\\\\\\"unknown\\\\\\\";\\\\\\\
- }, channels[channel].key, channels[channel].rawkey )\\\\\\\
- if ok then\\\\\\\
- channels[channel] = nil\\\\\\\
- end\\\\\\\
- return ok, data\\\\\\\
- end\\\\\\\
- \\\\\\\
- function send( channel, data ) -- sends a message over an open channel\\\\\\\
- if not channels[channel] then\\\\\\\
- return false, \\\\\\\"channel not open\\\\\\\"\\\\\\\
- end\\\\\\\
- return com.send( channels[channel].peer, data, channels[channel].key, channels[channel].rawkey )\\\\\\\
- end\\\\\\\
- \\\\\\\
- function receive( channel, timeout ) -- waits for messages on an open channel for [timeout] seconds\\\\\\\
- if not channels[channel] then\\\\\\\
- return false, \\\\\\\"channel not open\\\\\\\"\\\\\\\
- end\\\\\\\
- local ok, data = com.receive( channels[channel].peer, channels[channel].key, timeout, channels[channel].rawkey )\\\\\\\
- if not ok then\\\\\\\
- return false, data\\\\\\\
- end\\\\\\\
- if type( data ) == \\\\\\\"table\\\\\\\" and data.request == \\\\\\\"ConnectionClose\\\\\\\" then\\\\\\\
- return false, \\\\\\\"closed\\\\\\\", data.reason\\\\\\\
- end\\\\\\\
- return true, data\\\\\\\
- end\\\\\\\
- \\\\\\\
- function listen( protocol, timeout ) -- waits for a channel to be opened with [protocol] for [timeout] seconds\\\\\\\
- local time = os.clock( )\\\\\\\
- while not timeout or os.clock( ) - time <= timeout do\\\\\\\
- for i = #buffers.established, 1, -1 do\\\\\\\
- local channel = buffers.established[i]\\\\\\\
- if channel.protocol == protocol then\\\\\\\
- table.remove( buffers.established, i )\\\\\\\
- return channel.id\\\\\\\
- end\\\\\\\
- end\\\\\\\
- coroutine.yield( )\\\\\\\
- end\\\\\\\
- return false\\\\\\\
- end\\\\\\\
- \\\\\\\
- -- internal functions\\\\\\\
- \\\\\\\
- function addToBuffer( buffer, data )\\\\\\\
- buffers[buffer] = buffers[buffer] or { }\\\\\\\
- table.insert( buffers[buffer], data )\\\\\\\
- end\\\\\\\
- \\\\\\\
- function addChannel( peer, protocol, key, rawkey )\\\\\\\
- local t = { }\\\\\\\
- channels[t] = {\\\\\\\
- peer = peer;\\\\\\\
- key = key;\\\\\\\
- rawkey = rawkey;\\\\\\\
- }\\\\\\\
- addToBuffer( \\\\\\\"established\\\\\\\", { protocol = protocol, id = t } )\\\\\\\
- end\\\\\\\
- \\\\\\\
- function clearBuffers( )\\\\\\\
- for key in pairs( buffers ) do\\\\\\\
- buffers[key] = { }\\\\\\\
- end\\\\\\\
- end\\\", meta={\\\
- type = \\\"lib\\\",\\\
- }};[\\\"com\\\"]={content=\\\"\\\\\\\
- require \\\\\\\"encryption\\\\\\\"\\\\\\\
- require \\\\\\\"network\\\\\\\"\\\\\\\
- \\\\\\\
- local modem\\\\\\\
- local mside\\\\\\\
- local messages = { }\\\\\\\
- \\\\\\\
- function setModem( side )\\\\\\\
- if mside ~= side then\\\\\\\
- modem = peripheral.wrap( side )\\\\\\\
- modem.open( 2514 ) -- messages\\\\\\\
- modem.open( 2515 ) -- pings\\\\\\\
- mside = side\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function updateModems( )\\\\\\\
- for _, side in pairs( peripheral.getNames( ) ) do\\\\\\\
- if peripheral.getType( side ) == \\\\\\\"modem\\\\\\\" then\\\\\\\
- setModem( side )\\\\\\\
- return true\\\\\\\
- end\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function send( id, data, key, seed )\\\\\\\
- local channel = \\\\\\\"public\\\\\\\"\\\\\\\
- if key then\\\\\\\
- data = encryption.encrypt( textutils.serialize( data ), key )\\\\\\\
- math.randomseed( seed )\\\\\\\
- channel = math.random( 1, 32768 )\\\\\\\
- end\\\\\\\
- local t = {\\\\\\\
- target = id;\\\\\\\
- sender = os.getComputerID( );\\\\\\\
- data = data;\\\\\\\
- id = os.time( ) .. \\\\\\\":\\\\\\\" .. os.getComputerID( );\\\\\\\
- channel = channel;\\\\\\\
- }\\\\\\\
- if id == os.getComputerID( ) then\\\\\\\
- os.queueEvent( \\\\\\\"modem_message\\\\\\\", \\\\\\\"top\\\\\\\", 2514, 2514, t, 0 )\\\\\\\
- messages[t.id] = os.clock( )\\\\\\\
- return true\\\\\\\
- end\\\\\\\
- if not modem then\\\\\\\
- if not updateModems( ) then\\\\\\\
- return false, \\\\\\\"no modem\\\\\\\"\\\\\\\
- end\\\\\\\
- end\\\\\\\
- modem.transmit( 2514, 2514, t )\\\\\\\
- messages[t.id] = os.clock( )\\\\\\\
- return true\\\\\\\
- end\\\\\\\
- \\\\\\\
- function receive( id, key, timeout, seed )\\\\\\\
- if not modem then\\\\\\\
- updateModems( )\\\\\\\
- end\\\\\\\
- local timer = timeout and os.startTimer( timeout )\\\\\\\
- while true do\\\\\\\
- local ev = { coroutine.yield( ) }\\\\\\\
- if ev[1] == \\\\\\\"timer\\\\\\\" and ev[2] == timer then\\\\\\\
- return false, \\\\\\\"timeout\\\\\\\"\\\\\\\
- end\\\\\\\
- if ev[1] == \\\\\\\"modem_message\\\\\\\" and ev[3] == 2514 and ev[4] == 2514 and type( ev[5] ) == \\\\\\\"table\\\\\\\" then -- a normal message\\\\\\\
- if ev[5].target == os.getComputerID( ) then\\\\\\\
- if id == ev[5].sender then\\\\\\\
- local data = ev[5].data\\\\\\\
- if key then\\\\\\\
- data = textutils.unserialize( encryption.decrypt( data, key ) )\\\\\\\
- math.randomseed( seed )\\\\\\\
- local channel = math.random( 1, 32768 )\\\\\\\
- if data.channel == channel then\\\\\\\
- return true, data\\\\\\\
- end\\\\\\\
- else\\\\\\\
- return true, data\\\\\\\
- end\\\\\\\
- end\\\\\\\
- elseif not messages[ev[5].id] then\\\\\\\
- modem.transmit( 2514, 2514, ev[5] )\\\\\\\
- end\\\\\\\
- messages[ev[5].id] = os.clock( )\\\\\\\
- elseif ev[1] == \\\\\\\"modem_message\\\\\\\" and ev[3] == 2515 and ev[4] == 2515 and type( ev[5] ) == \\\\\\\"string\\\\\\\" and ev[5]:sub( 1, 15 ) then\\\\\\\
- local mode, id = ev[5]:match \\\\\\\":(.-):\\\\\\\", ev[5]:match \\\\\\\":.-:(%d+)\\\\\\\"\\\\\\\
- if mode and id then\\\\\\\
- if not messages[ev[5]] and tonumber( id ) ~= os.getComputerID( ) then\\\\\\\
- if mode == \\\\\\\"Request\\\\\\\" then\\\\\\\
- modem.transmit( 2515, 2515, \\\\\\\"NovaNetworkPing:Response:\\\\\\\" .. id .. \\\\\\\":\\\\\\\" .. os.getComputerID( ) )\\\\\\\
- else\\\\\\\
- modem.transmit( 2515, 2515, ev[5] )\\\\\\\
- end\\\\\\\
- messages[ev[5]] = os.clock( )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- end\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function ping( )\\\\\\\
- if not modem then\\\\\\\
- if not updateModems( ) then\\\\\\\
- return false, \\\\\\\"no modem\\\\\\\"\\\\\\\
- end\\\\\\\
- end\\\\\\\
- modem.transmit( 2515, 2515, \\\\\\\"NovaNetworkPing:Request:\\\\\\\" .. os.getComputerID( ) )\\\\\\\
- local time = os.clock( )\\\\\\\
- local devices = { }\\\\\\\
- while os.clock( ) - time < 1 do\\\\\\\
- local ev = { coroutine.yield( ) }\\\\\\\
- if ev[1] == \\\\\\\"modem_message\\\\\\\" and ev[3] == 2515 and ev[4] == 2515 and type( ev[5] ) == \\\\\\\"string\\\\\\\" and ev[5]:sub( 1, 15 ) then\\\\\\\
- local mode, id, sender = ev[5]:match \\\\\\\":(.-):\\\\\\\", ev[5]:match \\\\\\\":.-:(%d+)\\\\\\\", ev[5]:match \\\\\\\":.-:%d+:(%d+)\\\\\\\"\\\\\\\
- if mode and id and sender then\\\\\\\
- if tonumber( id ) == os.getComputerID( ) then\\\\\\\
- if mode == \\\\\\\"Response\\\\\\\" then\\\\\\\
- table.insert( devices, tonumber( sender ) )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- end\\\\\\\
- end\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function listen( event ) -- waiting for connections to establish, not pings or normal messages...\\\\\\\
- if not modem then\\\\\\\
- updateModems( )\\\\\\\
- end\\\\\\\
- if event[1] == \\\\\\\"modem_message\\\\\\\" and event[3] == 2514 and event[4] == 2514 and type( event[5] ) == \\\\\\\"table\\\\\\\" then\\\\\\\
- if event[5].target == os.getComputerID( ) then\\\\\\\
- local data = event[5].data\\\\\\\
- if type( data ) == \\\\\\\"table\\\\\\\" and data.request == \\\\\\\"connection\\\\\\\" then\\\\\\\
- local response, key = handshake.generateResponseData( data.handshake )\\\\\\\
- if data.insecure then\\\\\\\
- network.addChannel( event[5].sender, data.protocol )\\\\\\\
- else\\\\\\\
- network.addChannel( event[5].sender, data.protocol, network.genkey( key ), key )\\\\\\\
- end\\\\\\\
- send( event[5].sender, response )\\\\\\\
- if data.insecure then\\\\\\\
- core.log( \\\\\\\"Network\\\\\\\", \\\\\\\"Insecure connection established with \\\\\\\" .. tostring( event[5].sender ) )\\\\\\\
- else\\\\\\\
- core.log( \\\\\\\"Network\\\\\\\", \\\\\\\"Secure connection established with \\\\\\\" .. tostring( event[5].sender ) )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- elseif not messages[event[5].id] then\\\\\\\
- modem.transmit( 2514, 2514, event[5] )\\\\\\\
- end\\\\\\\
- messages[event[5].id] = os.clock( )\\\\\\\
- end\\\\\\\
- end\\\\\\\
- \\\\\\\
- function clear( )\\\\\\\
- local time = os.clock( )\\\\\\\
- local r = { }\\\\\\\
- for k, v in pairs( messages ) do\\\\\\\
- if time - v > 5 then\\\\\\\
- r[#r+1] = k\\\\\\\
- end\\\\\\\
- end\\\\\\\
- for i = 1, #r do\\\\\\\
- messages[r[i]] = nil\\\\\\\
- end\\\\\\\
- end\\\", meta={\\\
- type = \\\"lib\\\",\\\
- }};}\\\
- --/end of files\\\
- local autorun = {\\\"main\\\"}\\\
- \\\
- local args = { ... }\\\
- local global = getfenv( )\\\
- local custom = setmetatable( { class = class, ARGS = args }, { __index = global } )\\\
- function custom.export( variable, value )\\\
- if custom[variable] ~= nil or value then\\\
- global[variable] = custom[variable] == nil and value or custom[variable]\\\
- end\\\
- end\\\
- local required = { }\\\
- local function require( file, ... )\\\
- if not required[file] and files[file] then\\\
- required[file] = true\\\
- if files[file].meta.type == \\\"class\\\" then\\\
- custom[file] = class( file )\\\
- end\\\
- local fenv = setmetatable( { shared = custom }, { __index = custom } )\\\
- local f, err = loadstring( files[file].content, file .. \\\".lua\\\" )\\\
- if f then\\\
- setfenv( f, fenv )\\\
- local ok, data = pcall( f )\\\
- if ok then\\\
- if files[file].meta.type == \\\"lib\\\" then\\\
- if data ~= nil then\\\
- custom[file] = data\\\
- return data\\\
- else\\\
- local t = { }\\\
- for k, v in pairs( fenv ) do\\\
- t[k] = v\\\
- end\\\
- custom[file] = t\\\
- return t\\\
- end\\\
- end\\\
- return data\\\
- else\\\
- error( data, 0 )\\\
- end\\\
- else\\\
- error( err, 0 )\\\
- end\\\
- end\\\
- end\\\
- custom.require = require\\\
- \\\
- for i = 1, #autorun do\\\
- require( autorun[i] )\\\
- end\",\
- }\
- \
- function ttf( table, dir )\
- if not fs.isDir( dir ) then\
- fs.makeDir( dir )\
- end\
- for k, v in pairs( table ) do\
- if type( v ) == \"table\" then\
- ttf( v, dir..\"/\"..k )\
- elseif type( v ) == \"string\" then\
- local f = fs.open( dir..\"/\"..k, \"w\" )\
- f.write( v )\
- f.close( )\
- end\
- end\
- end\
- return function( path )\
- ttf( pack, path )\
- end", meta={
- type = "lib",
- }};}
- --/end of files
- local autorun = {"loader";"DefaultAppInstance";"Process";"WindowManager";"ProcessAppInstance";"AppManager";"Account";"App";"Session";"Window";"main"}
- --@meta[type]:"class"
- -- Swift Class Library
- -- Made by awsumben13
- -- Feel free to use this in any of your projects but keep this at the top and give credit where necessary
- --[[
- This class library offers:
- Inheritance "Class:extends( other class )"
- Initialiser methods "function ClassName:ClassName( )"
- Public / private variables with customisable read/write access "Class.public.x.write = false"
- Static variables ( accessed from the class object ) "Class.static.x = 5"
- Custom metamethods including __index and __newindex "Class.meta.index = { }"
- ]]
- local class = { }
- function class.new( name )
- local object = { }
- local public = { }
- object.public = { }
- object.private = { }
- object.static = { }
- object.name = name
- object.extends = false
- object.class = public
- public.name = name
- local customindex = false
- local customnewindex = false
- local objectmeta = { }
- function public:new( ... )
- local ob = { }
- local pb = { }
- ob.class = public
- ob.public = pb
- setmetatable( ob, {
- __index = object.private;
- } )
- function pb:type( full )
- return ob.class:type( full )
- end
- function pb:typeOf( class )
- return ob.class:typeOf( class )
- end
- local obmeta = { }
- obmeta.__index = function( _, k )
- if object.public[k] then
- if object.public[k].read then
- local val
- if customindex then
- if type( customindex ) == "function" then
- return customindex( ob, k )
- else
- return customindex[k]
- end
- elseif type( object.public[k].read ) == "function" then
- val = object.public[k].read( ob )
- elseif object.public[k].value ~= nil then
- val = object.public[k].value
- else
- val = ob[k]
- end
- if type( val ) == "function" then
- return function( self, ... )
- if self == pb then
- return val( ob, ... )
- end
- return val( self, ... )
- end
- end
- return val
- else
- error( "variable has no read access", 2 )
- end
- elseif customindex then
- if type( customindex ) == "function" then
- return customindex( ob, k )
- else
- return customindex[k]
- end
- else
- error( "no such variable \"" .. tostring( k ) .. "\"", 2 )
- end
- end;
- obmeta.__newindex = function( _, k, v )
- if object.public[k] then
- if object.public[k].write then
- if customnewindex then
- if type( customnewindex ) == "function" then
- return customnewindex( ob, k, v )
- else
- customnewindex[k] = v
- end
- elseif type( object.public[k].write ) == "function" then
- object.public[k].write( ob, v )
- else
- ob[k] = v
- end
- else
- error( "variable has no write access", 2 )
- end
- else
- error( "no such variable \"" .. tostring( k ) .. "\"", 2 )
- end
- end;
- obmeta.__tostring = function( )
- return object.name
- end;
- obmeta.__metatable = "SwiftClassObject";
- for k, v in pairs( objectmeta ) do
- obmeta["__" .. tostring( k )] = v
- end
- setmetatable( pb, obmeta )
- local c = object
- while true do
- if type( c.private[c.name] ) == "function" then
- return c.private[c.name]( ob, ... )
- end
- if c.extends then
- c = c.extends
- else
- break
- end
- end
- return pb
- end
- function public:type( full )
- local str = ""
- if full then
- local c = object.extends
- while c do
- str = c.name .. "." .. str
- c = c.extends
- end
- end
- return str .. object.name
- end
- function public:typeOf( other )
- if type( other ) == "table" then
- if getmetatable( other ) == "SwiftClass" then
- local ob = object
- while ob do
- if ob.class == other then
- return true
- end
- ob = ob.extends
- end
- end
- end
- return false
- end
- function public:extends( ob )
- ob:extend( object )
- end
- function public:extend( ob )
- setmetatable( ob.static, { __index = object.static } )
- setmetatable( ob.public, { __index = object.public } )
- setmetatable( ob.private, { __index = object.private } )
- ob.extends = object
- end
- local meta = { }
- meta.__index = function( _, k )
- if k == "static" then
- return setmetatable( { }, {
- __newindex = function( _, k, v )
- object.static[k] = v
- end;
- __metatable = { };
- } )
- elseif k == "public" then
- return setmetatable( { }, {
- __newindex = function( _, k, v )
- object.public[k] = {
- read = true;
- write = false;
- value = v;
- }
- end;
- __call = function( _, k )
- object.public[k] = {
- read = true;
- write = true;
- value = nil;
- }
- return function( _type )
- local types = { _type }
- object.public[k].write = function( ob, value )
- for i = 1, #types do
- if class.typeOf( value, types[i] ) then
- ob[k] = value
- return
- end
- end
- if class.type( types[1] ) == "Class" then
- error( "expected " .. types[1]:type( ) .. " " .. k, 3 )
- else
- error( "expected " .. types[1] .. " " .. k, 3 )
- end
- end
- return function( _type )
- table.insert( types, _type )
- end
- end
- end;
- __index = function( _, k )
- if object.public[k] then
- return setmetatable( { }, {
- __newindex = function( _, name, v )
- if name == "read" then
- if type( v ) == "boolean" or type( v ) == "function" then
- object.public[k].read = v
- else
- error( "invalid modifier value", 2 )
- end
- elseif name == "write" then
- if type( v ) == "boolean" or type( v ) == "function" then
- object.public[k].write = v
- else
- error( "invalid modifier value", 2 )
- end
- else
- error( "invalid modifier name", 2 )
- end
- end;
- __metatable = { };
- } )
- else
- error( "public index " .. tostring( k ) .. " not found", 2 )
- end
- end;
- __metatable = { };
- } )
- elseif k == "meta" then
- return setmetatable( { }, {
- __index = function( _, k )
- if k == "index" then
- return customindex
- elseif k == "newindex" then
- return customnewindex
- else
- return objectmeta[k]
- end
- end;
- __newindex = function( _, k, v )
- if k == "metatable" then
- error( "cannot change this metamethod", 2 )
- elseif k == "index" then
- if type( v ) == "function" or type( v ) == "table" or v == nil then
- customindex = v
- else
- error( "cannot use type " .. type( v ) .. " for index metamethod", 2 )
- end
- elseif k == "newindex" then
- if type( v ) == "function" or type( v ) == "table" or v == nil then
- customnewindex = v
- else
- error( "cannot use type " .. type( v ) .. " for newindex metamethod", 2 )
- end
- else
- objectmeta[k] = v
- end
- end;
- __metatable = { };
- } )
- else
- return object.static[k]
- end
- end
- meta.__newindex = function( _, k, v )
- object.private[k] = v
- end;
- meta.__call = function( self, ... )
- return self:new( ... )
- end;
- meta.__totring = function( )
- return "Class"
- end;
- meta.__metatable = "SwiftClass"
- setmetatable( public, meta )
- return public
- end
- function class.public( name )
- local object = class.new( name )
- getfenv( )[name] = object
- end
- function class.type( object )
- if type( object ) == "table" then
- if getmetatable( object ) == "SwiftClass" then
- return "Class"
- end
- if getmetatable( object ) == "SwiftClassObject" then
- return object:type( )
- end
- end
- return type( object )
- end
- function class.typeOf( object, ... )
- local types = { ... }
- for i = 1, #types do
- if type( object ) == "table" and getmetatable( object ) == "SwiftClassObject" then
- if object:typeOf( types[i] ) then
- return types[i]
- end
- elseif type( object ) == "table" and getmetatable( object ) == "SwiftClass" then
- if types[i] == "Class" then
- return "Class"
- end
- else
- if type( object ) == types[i] then
- return types[i]
- end
- end
- end
- return false
- end
- setmetatable( class, { __call = function( _, ... ) return class.new( ... ) end } )
- local args = { ... }
- local global = getfenv( )
- local custom = setmetatable( { class = class, ARGS = args }, { __index = global } )
- function custom.export( variable, value )
- if custom[variable] ~= nil or value then
- global[variable] = custom[variable] == nil and value or custom[variable]
- end
- end
- local required = { }
- local function require( file, ... )
- if not required[file] and files[file] then
- required[file] = true
- if files[file].meta.type == "class" then
- custom[file] = class( file )
- end
- local fenv = setmetatable( { shared = custom }, { __index = custom } )
- local f, err = loadstring( files[file].content, file .. ".lua" )
- if f then
- setfenv( f, fenv )
- local ok, data = pcall( f )
- if ok then
- if files[file].meta.type == "lib" then
- if data ~= nil then
- custom[file] = data
- return data
- else
- local t = { }
- for k, v in pairs( fenv ) do
- t[k] = v
- end
- custom[file] = t
- return t
- end
- end
- return data
- else
- error( data, 0 )
- end
- else
- error( err, 0 )
- end
- end
- end
- custom.require = require
- for i = 1, #autorun do
- require( autorun[i] )
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement