Advertisement
awsumben13

Nova 1.3

Dec 13th, 2014
110
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 348.61 KB | None | 0 0
  1. --[[type="executable_package", name="n"]]
  2. local files = {["sha256"]={content="-- Adaptation of the Secure Hashing Algorithm (SHA-244/256)\
  3. -- Found Here: http://lua-users.org/wiki/SecureHashAlgorithm\
  4. -- Using an adapted version of the bit library\
  5. -- Found Here: https://bitbucket.org/Boolsheet/bslf/src/1ee664885805/bit.lua\
  6. -- Compressed using Lua Minifier (https://mothereff.in/lua-minifier)\
  7. 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={
  8.   type = "lib",
  9. }};["kernel"]={content="\
  10. require \"Session\"\
  11. \
  12. core.UI = NovaUI.UIHandler( )\
  13. \
  14. local lt, lk\
  15. \
  16. core.UIHost = Process \"UIManager\"\
  17. core.UIHost:newThread( function( ... )\
  18.     local ev = { ... }\
  19.     while true do\
  20.         if ev[1] == \"key\" and ev[2] == 28 and lk == 29 and os.clock() - lt <= 0.3 and core.session then\
  21.             core.session:menu( )\
  22.         elseif ev[1] == \"update\" then\
  23.             core.UI:update( ev[2] )\
  24.             core.UI:draw( )\
  25.         else\
  26.             core.UI:event( ev )\
  27.         end\
  28.         if ev[1] == \"key\" then\
  29.             lk = ev[2]\
  30.             lt = os.clock( )\
  31.         end\
  32.         ev = { coroutine.yield( ) }\
  33.     end\
  34. end )\
  35. function core.UIHost:onException( err )\
  36.     core.log( \"UI error\", err )\
  37.     self:restartThread( 1 )\
  38. end\
  39. \
  40. -- sessions\
  41. \
  42. Session()", meta={
  43.   type = "lib",
  44. }};["DefaultAppInstance"]={content="\
  45. local function sandbox( args, app, instance )\
  46.     local t = {\
  47.         -- lua\
  48.         _VERSION = _VERSION;\
  49.         pairs = pairs;\
  50.         ipairs = ipairs;\
  51.         select = select;\
  52.         unpack = unpack;\
  53.         setfenv = setfenv;\
  54.         getfenv = getfenv;\
  55.         setmetatable = setmetatable;\
  56.         getmetatable = getmetatable;\
  57.         next = next;\
  58.         rawset = rawset;\
  59.         rawget = rawget;\
  60.         rawequal = rawequal;\
  61.         type = type;\
  62.         tostring = tostring;\
  63.         tonumber = tonumber;\
  64.         pcall = pcall;\
  65.         xpcall = xpcall;\
  66.         loadstring = loadstring;\
  67.         assert = assert;\
  68.         error = error;\
  69.         __inext = __inext;\
  70.         math = math;\
  71.         string = string;\
  72.         table = table;\
  73.         coroutine = coroutine;\
  74.         \
  75.         -- computercraft\
  76.         sha256 = sha256;\
  77.         encryption = encryption;\
  78.         stringutils = stringutils;\
  79.         keys = keys;\
  80.         colours = colours;\
  81.         colors = colors;\
  82.         vector = vector;\
  83.         bit = bit;\
  84.         http = http;\
  85.         textutils = textutils;\
  86.         rednet = rednet;\
  87.         os = os;\
  88.         sleep = sleep;\
  89.     }\
  90.     t._G = t\
  91.     t.ARGS = args\
  92. \
  93.     if app.conf.runmode == \"app\" then\
  94.         t.class = class;\
  95.         t.NovaUI = NovaUI;\
  96.         t.NovaFS = NovaFS;\
  97.         t.NovaNet = NovaNet;\
  98. \
  99.         t.Nova = {\
  100.             name = core.name;\
  101.             version = core.version;\
  102.             platform = core.platform;\
  103.             clipboard = clipboard;\
  104.             name = core.name;\
  105.             path = core.path;\
  106.             author = core.author;\
  107. \
  108.             user = { };\
  109.             app = { };\
  110.         }\
  111. \
  112.         function t.Nova.getUpdateInfo( )\
  113.             return core.getUpdateInfo( )\
  114.         end\
  115.         function t.Nova.compareVersions( )\
  116.             return core.compareVersions( )\
  117.         end\
  118.         function t.Nova.update( )\
  119.             return core.update( )\
  120.         end\
  121.         function t.Nova.shutdown( )\
  122.             instance.app.session:logout( )\
  123.             core.finish( )\
  124.             os.shutdown( )\
  125.         end\
  126.         function t.Nova.restart( )\
  127.             instance.app.session:logout( )\
  128.             core.finish( )\
  129.             os.reboot( )\
  130.         end\
  131.         function t.Nova.log( ... )\
  132.             return core.log( app.name, ... )\
  133.         end\
  134. \
  135.         function t.Nova.time() return os.time() end\
  136.         function t.Nova.clock() return os.clock() end\
  137.         function t.Nova.irltime() return core.getirltime() end\
  138.         function t.Nova.irldate() return core.getirldate() end\
  139.         function t.Nova.day() return os.day() end\
  140.         function t.Nova.date() return error \"not yet implemented\" end\
  141.         \
  142.         function t.Nova.sleep( n )\
  143.             local t = os.clock()\
  144.             while os.clock() - t < n do\
  145.                 coroutine.yield()\
  146.             end\
  147.         end\
  148.         function t.Nova.pullEvent( )\
  149.             local ev = { coroutine.yield( ) }\
  150.             if ev[1] == \"terminate\" then\
  151.                 instance:close \"terminate\"\
  152.             end\
  153.             return unpack( ev )\
  154.         end\
  155.         function t.Nova.pullEventRaw( )\
  156.             return coroutine.yield( )\
  157.         end\
  158.         function t.Nova.versionTable( )\
  159.             return { major = core.version_major, minor = core.version_minor, patch = core.version_patch }\
  160.         end\
  161.         function t.Nova.getLabel( )\
  162.             return os.getComputerLabel( )\
  163.         end\
  164.         function t.Nova.setLabel( label )\
  165.             return os.setComputerLabel( label )\
  166.         end\
  167.         function t.Nova.getID( )\
  168.             return os.getComputerID( )\
  169.         end\
  170. \
  171.         t.Nova.app.window = instance.window\
  172.         function t.Nova.app.launch( name, args )\
  173.             return not not core.session.appManager:launch( name, args )\
  174.         end\
  175.         function t.Nova.app.close( )\
  176.             instance:close \"internal\"\
  177.         end\
  178.         function t.Nova.app.newThread( f )\
  179.             return instance.process:newThread( f )\
  180.         end\
  181.         t.Nova.app.callback = setmetatable( { }, { __newindex = function( _, k, v )\
  182.             if type( v ) ~= \"function\" then\
  183.                 return error \"expected function\"\
  184.             end\
  185.             instance.callbacks[k] = v\
  186.         end } )\
  187.         function t.Nova.app.setData( index, value )\
  188.             return core.session.account:setData( app.name, index, value )\
  189.         end\
  190.         function t.Nova.app.getData( index )\
  191.             return core.session.account:getData( app.name, index, value )\
  192.         end\
  193.         function t.Nova.app.isRegistered( path )\
  194.             return core.session.appManager.app_paths[path] and core.session.appManager.app_paths[path].name or false\
  195.         end\
  196. \
  197.     end\
  198. \
  199.     if type( app.conf.permissions ) == \"table\" then\
  200.         if app.conf.permissions.listProcesses then\
  201.             function t.Nova.listProcesses( )\
  202.                 return Process.list( )\
  203.             end\
  204.         end\
  205.     end\
  206. \
  207.     return t\
  208. end\
  209. \
  210. DefaultAppInstance.public \"process\"\
  211. DefaultAppInstance.public.process.write = false\
  212. \
  213. DefaultAppInstance.public \"window\"\
  214. DefaultAppInstance.public.window.write = false\
  215. \
  216. DefaultAppInstance.public \"environment\"\
  217. DefaultAppInstance.public.environment.write = false\
  218. \
  219. DefaultAppInstance.public \"callbacks\"\
  220. DefaultAppInstance.public.callbacks.write = false\
  221. \
  222. DefaultAppInstance.public \"running\"\
  223. DefaultAppInstance.public.running.write = false\
  224. \
  225. function DefaultAppInstance:DefaultAppInstance( session, app, args )\
  226.     self.running = true\
  227.     self.callbacks = { }\
  228.     self.process = session.appManager:newProcess( app.name )\
  229.     local position = session.account:getData( \"windowPosition\", app.name )\
  230.     if position then\
  231.         self.window = session.windowManager:newWindow( \"custom:\" .. position.w .. \"x\" .. position.h )\
  232.         self.window.x, self.window.y = position.x, position.y\
  233.     elseif app.conf.runmode == \"app\" then\
  234.         if type( app.conf.width ) == \"number\" and type( app.conf.height ) == \"number\" then\
  235.             self.window = session.windowManager:newWindow( \"custom:\" .. app.conf.width .. \"x\" .. app.conf.height )\
  236.         else\
  237.             self.window = session.windowManager:newWindow( type( app.conf.size ) == \"string\" and app.conf.size or \"default\" )\
  238.         end\
  239.     else\
  240.         self.window = session.windowManager:newWindow \"full\"\
  241.     end\
  242. \
  243.     self.window.title = app.name\
  244.     self.window.process = self.process\
  245.     function self.window.onClose( )\
  246.         local r = self.public:callback( \"canClose\", \"user\" )\
  247.         if r or r == nil then\
  248.             self.public:close \"user\"\
  249.         end\
  250.     end\
  251.     function self.window.onMove( window, w, h )\
  252.         self.public:callback( \"onMove\", w, h, window )\
  253.         session.account:setData( \"windowPosition\", app.name, { x = window.x, y = window.y, w = w, h = h } )\
  254.     end\
  255.     function self.window.onResize( window, w, h )\
  256.         self.public:callback( \"onResize\", w, h, window )\
  257.         session.account:setData( \"windowPosition\", app.name, { x = window.x, y = window.y, w = w, h = h } )\
  258.     end\
  259. \
  260.     self.environment = sandbox( args, app, self.public )\
  261.     self.process.environment = self.environment\
  262. \
  263.     local f, err = loadstring( app.main, \"main.lua\" )\
  264.     if f then\
  265.         self.process:newThread( f ) \
  266.     else\
  267.         self.newProcess:newThread( function( )\
  268.             error( err, 0 )\
  269.         end )\
  270.     end\
  271.     self.process:newThread( function( )\
  272.         while true do\
  273.             local ev, window = coroutine.yield( )\
  274.             if ev[1] == \"window_close\" and window == self.window then\
  275.                 self.public:close( )\
  276.             end\
  277.         end\
  278.     end )\
  279. \
  280.     local core = core\
  281.     function self.process.onException( _, err )\
  282.         self.process:newThread( function( )\
  283.             core.log( err )\
  284.             NovaUI.display.alert( self.window.display, err, true )\
  285.             self.public:close( )\
  286.         end )\
  287.     end\
  288. \
  289.     return self.public\
  290. end\
  291. \
  292. function DefaultAppInstance.public:close( reason )\
  293.     if not self.running then return end\
  294.     self.public:callback( \"onClose\", reason )\
  295.     self.process:stop( )\
  296.     self.window:close( )\
  297.     self.running = false\
  298. end\
  299. \
  300. function DefaultAppInstance.public:callback( name, ... )\
  301.     if type( self.callbacks[name] ) == \"function\" then\
  302.         return self.callbacks[name]( ... )\
  303.     end\
  304.     return nil\
  305. end", meta={
  306.   type = "class",
  307. }};["Process"]={content="\
  308. local processes = { }\
  309. \
  310. Process.public \"environment\"\
  311. function Process.public.environment:read( )\
  312.     return self.env\
  313. end\
  314. function Process.public.environment:write( value )\
  315.     if type( value ) ~= \"table\" then\
  316.         error( \"expected table\", 3 )\
  317.     end\
  318.     self.env = value\
  319. end\
  320. \
  321. Process.public \"threadcount\"\
  322. function Process.public.threadcount:read( )\
  323.     return #self.threads\
  324. end\
  325. \
  326. Process.public \"onException\" \"function\"\
  327. Process.public \"onFinish\" \"function\"\
  328. Process.public \"name\" \"string\"\
  329. \
  330. function Process:Process( name )\
  331.     self.name = name\
  332.     self.state = \"running\"\
  333.     self.threads = { }\
  334.     self.env = getfenv( )\
  335.     self.threadID = 1\
  336.     self.eventqueue = { }\
  337. \
  338.     table.insert( processes, self.public )\
  339. \
  340.     return self.public\
  341. end\
  342. \
  343. function Process.public:queueEvent( ... )\
  344.     table.insert( self.eventqueue, { ... } )\
  345. end\
  346. \
  347. function Process.public:newThread( f )\
  348.     local env = setmetatable( { process = self.public }, { __index = function( _, k )\
  349.         return self.env[k]\
  350.     end, __newindex = function( _, k, v )\
  351.         self.env[k] = v\
  352.     end } )\
  353.     setfenv( f, env )\
  354.     local t = {\
  355.         f = f;\
  356.         co = coroutine.create( f );\
  357.         id = self.threadID;\
  358.     }\
  359.     self.threadID = self.threadID + 1\
  360.     table.insert( self.threads, t )\
  361.     return t.id\
  362. end\
  363. \
  364. function Process.public:pause( )\
  365.     if self.state == \"running\" then\
  366.         self.state = \"paused\"\
  367.         return true\
  368.     end\
  369.     return false\
  370. end\
  371. \
  372. function Process.public:resume( )\
  373.     if self.state == \"paused\" then\
  374.         self.state = \"running\"\
  375.         return true\
  376.     end\
  377.     return false\
  378. end\
  379. \
  380. function Process.public:restartThread( threadID )\
  381.     for i = 1, #self.threads do\
  382.         if self.threads[i].id == threadID then\
  383.             self.threads[i].co = coroutine.create( self.threads[i].f )\
  384.         end\
  385.     end\
  386.     self.state = self.state == \"paused\" and \"paused\" or \"running\"\
  387. end\
  388. \
  389. function Process.public:stopThread( threadID )\
  390.     for i = 1, #self.threads do\
  391.         if self.threads[i].id == threadID then\
  392.             table.remove( self.threads, i )\
  393.             return\
  394.         end\
  395.     end\
  396. end\
  397. \
  398. function Process.public:isThreadRunning( threadID )\
  399.     for i = 1, #self.threads do\
  400.         if self.threads[i].id == threadID then\
  401.             return coroutine.status( self.threads[i].co ) ~= \"dead\"\
  402.         end\
  403.     end\
  404.     return false\
  405. end\
  406. \
  407. function Process.public:stop( )\
  408.     self.state = \"stopped\"\
  409. end\
  410. \
  411. function Process.public:isRunning( )\
  412.     return self.state == \"running\"\
  413. end\
  414. \
  415. function Process.public:isPaused( )\
  416.     return self.state == \"paused\"\
  417. end\
  418. \
  419. function Process.public:update( args )\
  420.     if self.state == \"stopped\" then\
  421.         return false, \"finished\"\
  422.     end\
  423.     if self.state ~= \"running\" then\
  424.         return false, \"not running\"\
  425.     end\
  426.     local t = self.eventqueue[#self.eventqueue]\
  427.     if t then\
  428.         self.eventqueue[#self.eventqueue] = nil\
  429.         self.public:update( t )\
  430.     end\
  431.     for i = #self.threads, 1, -1 do\
  432.         local thread = self.threads[i]\
  433.         local ok, data = coroutine.resume( thread.co, unpack( args ) )\
  434.         if not ok then\
  435.             if type( self.onException ) == \"function\" then\
  436.                 pcall( self.onException, self.public, data, thread.id )\
  437.             else\
  438.                 self.state = \"stopped\"\
  439.                 core.log( \"Thread[\" .. thread.id .. \"] error in \" .. self.name, data )\
  440.             end\
  441.             if self.state == \"stopped\" then\
  442.                 return false, \"finished\"\
  443.             end\
  444.         end\
  445.         if coroutine.status( thread.co ) == \"dead\" then -- give it a chance to restart\
  446.             if type( self.onFinish ) == \"function\" then\
  447.                 pcall( self.onFinish, self.public, thread.id )\
  448.             end\
  449.             if coroutine.status( thread.co ) == \"dead\" then\
  450.                 for i = 1, #self.threads do\
  451.                     if self.threads[i] == thread then\
  452.                         table.remove( self.threads, i )\
  453.                     end\
  454.                 end\
  455.             end\
  456.         end\
  457.     end\
  458.     return true\
  459. end\
  460. \
  461. function Process.static.update( args )\
  462.     for i = #processes, 1, -1 do\
  463.         local ok, data = processes[i]:update( args )\
  464.         if not ok and data == \"finished\" then\
  465.             table.remove( processes, i )\
  466.         end\
  467.     end\
  468. end\
  469. \
  470. function Process.static.list( )\
  471.     local t = { }\
  472.     for i = 1, #processes do\
  473.         local p = processes[i]\
  474.         table.insert( t, { name = processes[i].name, threadcount = processes[i].threadcount, stop = function( )\
  475.             p:stop( )\
  476.         end } )\
  477.     end\
  478.     return t\
  479. end", meta={
  480.   type = "class",
  481. }};["stringutils"]={content="\
  482. function gfind( str, pat, pos )\
  483.     local t = { }\
  484.     local params = { str:find( pat, pos ) }\
  485.     while params[1] do\
  486.         table.insert( t, params )\
  487.         params = { str:find( pat, params[2] + 1 ) }\
  488.     end\
  489.     local i = 0\
  490.     return function( )\
  491.         i = i + 1\
  492.         if t[i] then\
  493.             return unpack( t[i] )\
  494.         end\
  495.     end\
  496. end\
  497. \
  498. function split( str, pat, pos )\
  499.     local last = 1\
  500.     local parts = { }\
  501.     for s, f in gfind( str, pat, pos ) do\
  502.         table.insert( parts, str:sub( last, s - 1 ) )\
  503.         last = f + 1\
  504.     end\
  505.     table.insert( parts, str:sub( last ) )\
  506.     return parts\
  507. end\
  508. \
  509. local function linewrap( str, w )\
  510.     for i = 1, w do\
  511.         if str:sub( i, i ) == \"\\n\" then\
  512.             return str:sub( 1, i - 1 ), str:sub( i + 1 )\
  513.         end\
  514.     end\
  515.     if #str < w then\
  516.         return str, \"\"\
  517.     end\
  518.     for i = w + 1, 1, -1 do\
  519.         if str:sub( i, i ):find \"%s\" then\
  520.             return str:sub( 1, i - 1 ), str:sub( i + 1 )\
  521.         end\
  522.     end\
  523.     return str:sub( 1, w ), str:sub( w + 1 )\
  524. end\
  525. \
  526. function wordwrap( str, w, h )\
  527.     local s, f = linewrap( str, w )\
  528.     local lines = { s }\
  529.     while #f > 0 do\
  530.         s, f = linewrap( f, w )\
  531.         table.insert( lines, s )\
  532.     end\
  533.     while #lines > h do\
  534.         table.remove( lines, #lines )\
  535.     end\
  536.     return lines\
  537. end", meta={
  538.   type = "lib",
  539. }};["WindowManager"]={content="\
  540. WindowManager:extends( NovaUI.UIElement )\
  541. \
  542. WindowManager.public \"default_mode\" \"string\"\
  543. WindowManager.public \"desktop\" (NovaUI.UIElement)\
  544. \
  545. function WindowManager:WindowManager( )\
  546.     self:UIElement( 1, 1, term.getSize( ) )\
  547. \
  548.     self.windows = { }\
  549.     self.addorder = { }\
  550.     self.default_mode = \"custom:20x10\"\
  551. \
  552.     self.desktop = self.public:newChild( NovaUI.UIFrame( 1, 1, self.w, self.h ) )\
  553. \
  554.     return self.public\
  555. end\
  556. \
  557. function WindowManager.public:newWindow( mode )\
  558.     local w, h\
  559.     local static = false\
  560.     if mode == \"alert\" then\
  561.         w = self.w\
  562.         h = 6\
  563.         static = true\
  564.     elseif mode == \"full\" then\
  565.         w = self.w\
  566.         h = self.h - 2\
  567.         static = true\
  568.     elseif mode == \"default\" then\
  569.         return self.public:newWindow( self.default_mode )\
  570.     elseif mode:sub( 1, 7 ) == \"custom:\" then\
  571.         local ww, hh = mode:match \"custom:(%d+)x(%d+)\"\
  572.         if ww and hh then\
  573.             w, h = tonumber( ww ), tonumber( hh )\
  574.         end\
  575.     else\
  576.         w = self.w\
  577.         h = self.h - 2\
  578.     end\
  579.     local x, y = math.floor( ( self.w - w ) / 2 ) + 1, math.floor( ( self.h - h ) / 2 )\
  580.     local window = Window( self.public, x, y, w, h )\
  581.     table.insert( self.addorder, window )\
  582.     table.insert( self.windows, window )\
  583.     self.public:newChild( window.content )\
  584.     if static then\
  585.         window.minw = w\
  586.         window.minh = h\
  587.         window.maxw = w\
  588.         window.maxh = h\
  589.     end\
  590.     return window\
  591. end\
  592. \
  593. function WindowManager.public:removeWindow( window )\
  594.     for i = #self.windows, 1, -1 do\
  595.         if self.windows[i] == window then\
  596.             table.remove( self.windows, i )\
  597.         end\
  598.     end\
  599.     for i = #self.addorder, 1, -1 do\
  600.         if self.addorder[i] == window then\
  601.             table.remove( self.addorder, i )\
  602.         end\
  603.     end\
  604. end\
  605. \
  606. function WindowManager.public:focusOn( window )\
  607.     for i = 1, #self.windows do\
  608.         if self.windows[i] == window then\
  609.             self.public:removeChild( window.content )\
  610.             table.remove( self.windows, i )\
  611.         end\
  612.     end\
  613.     self.public:newChild( window.content )\
  614.     table.insert( self.windows, window )\
  615. end\
  616. \
  617. function WindowManager.public:listWindows( )\
  618.     local t = { }\
  619.     for i = 1, #self.addorder do\
  620.         table.insert( t, self.addorder[i] )\
  621.     end\
  622.     return t\
  623. end", meta={
  624.   type = "class",
  625. }};["ProcessAppInstance"]={content="\
  626. function ProcessAppInstance( session, app, args )\
  627. \
  628.     self.process = session.appManager:newProcess( app.name )\
  629.     \
  630. \
  631. end", meta={
  632.   type = "class",
  633. }};["appttf"]={content="\
  634. local pack = {\
  635. Run={\
  636. [\"main.lua\"]=\"\\\
  637. if not Nova then\\\
  638.     error( \\\"Cannot run outside Nova\\\", 0 )\\\
  639. end\\\
  640. \\\
  641. local window = Nova.app.window\\\
  642. local display = window.display\\\
  643. \\\
  644. window.minw = 20\\\
  645. window.minh = 8\\\
  646. \\\
  647. local buffer = display:newChild( NovaUI.UIBuffer( 1, 1, display.w, display.h ) )\\\
  648. \\\
  649. function Nova.app.callback.onResize( w, h )\\\
  650.     buffer:resize( w, h )\\\
  651.     buffer:passEvent \\\"term_resize\\\"\\\
  652. end\\\
  653. \\\
  654. if type( ARGS[1] ) ~= \\\"string\\\" then\\\
  655.     ARGS[1] = \\\"C:/rom/programs/shell\\\"\\\
  656. end\\\
  657. \\\
  658. local ARGS = ARGS\\\
  659. local Nova = Nova\\\
  660. \\\
  661. local data, err = NovaFS.readfile( ARGS[1] )\\\
  662. if data then\\\
  663.     local f, err = loadstring( data.content, NovaFS.getName( ARGS[1], true ) )\\\
  664.     if f then\\\
  665.         buffer:setTask( function( )\\\
  666.             local ok, err = pcall( f, unpack( ARGS, 2 ) )\\\
  667.             if not ok then\\\
  668.                 printError( err )\\\
  669.             end\\\
  670.             print \\\"Click anywhere to close...\\\"\\\
  671.             parallel.waitForAny( function( )\\\
  672.                 while coroutine.yield() ~= \\\"key\\\" do end\\\
  673.             end, function( )\\\
  674.                 while coroutine.yield() ~= \\\"mouse_click\\\" do end\\\
  675.             end )\\\
  676.             Nova.app.close( )\\\
  677.         end )\\\
  678.     else\\\
  679.         buffer:setTask( function( )\\\
  680.             error( err, 0 )\\\
  681.         end )\\\
  682.     end\\\
  683. else\\\
  684.     buffer:setTask( function( )\\\
  685.         error( err, 0 )\\\
  686.     end )\\\
  687. end\\\
  688. \\\
  689. buffer:passEvent( )\\\
  690. buffer:getHandler( ):setFocus( buffer )\\\
  691. \\\
  692. while true do\\\
  693.     local event = { coroutine.yield( ) }\\\
  694.     if event[1] ~= \\\"mouse_click\\\"\\\
  695.     and event[1] ~= \\\"mouse_drag\\\"\\\
  696.     and event[1] ~= \\\"mouse_scroll\\\"\\\
  697.     and event[1] ~= \\\"key\\\"\\\
  698.     and event[1] ~= \\\"char\\\"\\\
  699.     and event[1] ~= \\\"update\\\" then\\\
  700.         buffer:passEvent( unpack( event ) )\\\
  701.     end\\\
  702. end\",\
  703. [\"appconfig.txt\"]=\"runmode = \\\"app\\\";\\\
  704. size = \\\"custom:20x8\\\";\\\
  705. handles = {\\\
  706.     \\\"unknown\\\";\\\
  707.     \\\"lua\\\";\\\
  708. };\",\
  709. }\
  710. ,\
  711. Files={\
  712. [\"main.lua\"]=\"\\\
  713. if not Nova then\\\
  714.     error( \\\"Cannot run outside Nova\\\", 0 )\\\
  715. end\\\
  716. \\\
  717. local icons = { }\\\
  718. icons.app = NovaUI.Image( 5, 3 ):loadstr [[BF BF BF BF BF \\\
  719. 3F 3Ea3Ep3Ep3F \\\
  720. 9F 9F 9F 9F 9F ]]\\\
  721. icons.archive = NovaUI.Image( 5, 3 ):loadstr [[4F 4F 4F 0F 0F \\\
  722. 1F 1F 1F 1F 1F \\\
  723. 1F 1F 1F 1F 1F ]]\\\
  724. icons.class = NovaUI.Image( 5, 3 ):loadstr [[01c01l01a01s01s\\\
  725. 00 08-08-08-00 \\\
  726. 00 08-08-08-00 ]]\\\
  727. icons.design_file = NovaUI.Image( 5, 3 ):loadstr [[ F BF BF  F  F \\\
  728. F BF  F BF  F \\\
  729. F BF BF  F  F ]]\\\
  730. icons.design_project = NovaUI.Image( 5, 3 ):loadstr [[ F BF BF  F  F \\\
  731. F BF  3 BF  F \\\
  732. 3PB3rB3j 3c 3t]]\\\
  733. icons.folder = NovaUI.Image( 5, 3 ):loadstr [[10 10 10 0F 0F \\\
  734. 40 40 40 4F 4F \\\
  735. 40 40 40 40 40 ]]\\\
  736. icons.lib = NovaUI.Image( 5, 3 ):loadstr [[0F 09l09i09b0F \\\
  737. 00 08-08-08-00 \\\
  738. 00 08-08-08-00 ]]\\\
  739. icons.lua = NovaUI.Image( 5, 3 ):loadstr [[0F 03l03u03a0F \\\
  740. 00 08-08-08-00 \\\
  741. 00 08-08-08-00 ]]\\\
  742. icons.nova_image = NovaUI.Image( 5, 3 ):loadstr [[3Bi3Bm3B 3B 4B \\\
  743. 3B 3B 3B 3B 3B \\\
  744. DB DB DB DB DB ]]\\\
  745. icons.shortcut = NovaUI.Image( 5, 3 ):loadstr [[08s08h08o08r08t\\\
  746. 03 08c08u08t08 \\\
  747. 08-08-08-08-08-]]\\\
  748. icons.text = NovaUI.Image( 5, 3 ):loadstr [[0F 0Ft0Fx0Ft08 \\\
  749. 0F 08-08-08-08 \\\
  750. 0F 08-08-08-08 ]]\\\
  751. icons.unknown = NovaUI.Image( 5, 3 ):loadstr [[0F 08-08-08-0F \\\
  752. 0F 08-08-08-0F \\\
  753. 0F 08-08-08-0F ]]\\\
  754. \\\
  755. local window = Nova.app.window\\\
  756. local display = window.display\\\
  757. \\\
  758. if ARGS.width or ARGS.height then\\\
  759.     window:resize( ARGS.width or display.w, ARGS.height or display.h )\\\
  760. end\\\
  761. \\\
  762. window.minw = 12\\\
  763. window.minh = 6\\\
  764. \\\
  765. local fpath, mount\\\
  766. local loadfiles, openpath, refresh, renaming\\\
  767. local showFolderOptions, showFileOptions, showRightClickOptions\\\
  768. local history, historyindex = { }, 0\\\
  769. local loading, loadstage, loaddir, loader, ltime = false, 0, 1, nil, 0\\\
  770. \\\
  771. local header, sidebar, filecontent, keyhandler\\\
  772. local backbutton, forwardbutton, upbutton, pathinput\\\
  773. local headerb, sidebarb, filecontentb, filelist, scrollbar\\\
  774. local resizeDisplay, resizer\\\
  775. \\\
  776. local favourites = Nova.app.getData \\\"favourites\\\" or { }\\\
  777. local sortorder = Nova.app.getData \\\"sortorder\\\" or { mode = \\\"name\\\", direction = \\\"ascending\\\" }\\\
  778. local viewhidden = Nova.app.getData \\\"viewhidden\\\" or false\\\
  779. local defaults = Nova.app.getData \\\"defaults\\\" or { }\\\
  780. for k, v in pairs( defaults ) do\\\
  781.     NovaFS.setDefaultHandler( k, v )\\\
  782. end\\\
  783. \\\
  784. local function charweight( char )\\\
  785.     return string.byte( char:lower( ) )\\\
  786. end\\\
  787. local function compstring( s1, s2 ) -- s1 > s2\\\
  788.     for i = 1, #s1 do\\\
  789.         if #s2 < i then\\\
  790.             return true\\\
  791.         end\\\
  792.         local c1, c2 = s1:sub( i, i ), s2:sub( i, i )\\\
  793.         local w1, w2 = charweight( c1 ), charweight( c2 )\\\
  794.         if w1 > w2 then\\\
  795.             return true\\\
  796.         elseif w1 < w2 then\\\
  797.             return false\\\
  798.         end\\\
  799.     end\\\
  800.     return false\\\
  801. end\\\
  802. \\\
  803. local function formatSize( s )\\\
  804.     if s < 1024 then\\\
  805.         return s .. \\\" bytes\\\"\\\
  806.     end\\\
  807.     s = s / 1024\\\
  808.     if s < 1024 then\\\
  809.         return math.floor( s ) .. \\\"KB\\\"\\\
  810.     end\\\
  811.     s = s / 1024\\\
  812.     if s < 1024 then\\\
  813.         return math.floor( s ) .. \\\"MB\\\"\\\
  814.     end\\\
  815. end\\\
  816. \\\
  817. local activedropdown\\\
  818. local function dropdown( x, y, options )\\\
  819.     local frame = display:newChild( NovaUI.UIFrame( 1, 1, display.w, display.h ) )\\\
  820.     local close = frame:newChild( NovaUI.UIButton( 1, 1, frame.w, frame.h, \\\"\\\" ) )\\\
  821.     close.bc = 0\\\
  822.     close.align = false\\\
  823.     function close:onClick( )\\\
  824.         activedropdown = nil\\\
  825.         frame:remove( )\\\
  826.     end\\\
  827.     local f = frame:newChild( NovaUI.UIFrame( x, y, 15, 10 ) )\\\
  828.     activedropdown = frame\\\
  829.     NovaUI.display.menu( f, options )\\\
  830.     if f.x + f.w > display.w then\\\
  831.         f.x = display.w - f.w + 1\\\
  832.     end\\\
  833.     if f.y + f.h > display.h then\\\
  834.         f.y = display.h - f.h + 1\\\
  835.     end\\\
  836.     if f.y < 1 then\\\
  837.         f.h = f.h + f.y - 1\\\
  838.         f.y = 1\\\
  839.     end\\\
  840. end\\\
  841. \\\
  842. function Nova.app.callback.onClose( )\\\
  843.     if mount then\\\
  844.         NovaFS.unmount( mount )\\\
  845.     end\\\
  846. end\\\
  847. \\\
  848. function Nova.app.callback.onResize( w, h )\\\
  849.     resizeDisplay( w, h )\\\
  850.     refresh( )\\\
  851. end\\\
  852. \\\
  853. function resizeDisplay( w, h )\\\
  854.     header.w = w\\\
  855.     if w < 35 then\\\
  856.         sidebar.active = false\\\
  857.         sidebarb.active = false\\\
  858.         filecontent.x = 1\\\
  859.         filecontent.w = w\\\
  860.     else\\\
  861.         sidebar.active = true\\\
  862.         sidebarb.active = true\\\
  863.         filecontent.x = 16\\\
  864.         filecontent.w = w - 15\\\
  865.     end\\\
  866.     if w > 18 then\\\
  867.         pathinput.x = 14\\\
  868.         forwardbutton.active = true\\\
  869.         upbutton.x = 10\\\
  870.         upbutton.active = true\\\
  871.     elseif w > 14 then\\\
  872.         pathinput.x = 10\\\
  873.         forwardbutton.active = false\\\
  874.         upbutton.x = 6\\\
  875.         upbutton.active = true\\\
  876.     elseif w > 10 then\\\
  877.         pathinput.x = 6\\\
  878.         forwardbutton.active = false\\\
  879.         upbutton.active = false\\\
  880.     end\\\
  881.     pathinput.w = w - pathinput.x\\\
  882.     sidebar.h = h - 3\\\
  883.     filecontent.h = h - 3\\\
  884.     headerb.w, headerb.h = header.w, header.h\\\
  885.     sidebarb.x, sidebarb.h = sidebar.w + 1, sidebar.h\\\
  886.     filecontentb.w, filecontentb.h = filecontent.w, filecontent.h\\\
  887.     filelist.w = filecontent.w - 4\\\
  888.     filelist.h = filecontent.h - 2\\\
  889.     scrollbar.x = filecontent.w - 1\\\
  890.     scrollbar.h = filecontent.h - 2\\\
  891.     loader.x = filecontent.x\\\
  892.     loader.y = display.h\\\
  893.     resizer.x, resizer.y = w, h\\\
  894. end\\\
  895. \\\
  896. header = display:newChild( NovaUI.UIFrame( 1, 1, display.w, 3 ) )\\\
  897. sidebar = display:newChild( NovaUI.UIFrame( 1, 4, 14, display.h - 3 ) )\\\
  898. filecontent = display:newChild( NovaUI.UIFrame( 16, 4, display.w - 15, display.h - 3 ) )\\\
  899. keyhandler = display:newChild( NovaUI.UIKeyHandler( ) )\\\
  900. resizer = display:newChild( NovaUI.UIButton( display.w, display.h, 1, 1, \\\"@\\\" ) )\\\
  901. local xx, yy\\\
  902. function resizer:onClick( rx, ry, button )\\\
  903.     if button == 1 then xx, yy = rx, ry end\\\
  904. end\\\
  905. function resizer:onDrag( rx, ry, cx, cy, button )\\\
  906.     if button == 1 and xx then\\\
  907.         window:resize( display.w + rx - 1, display.h + ry - 1 )\\\
  908.     end\\\
  909. end\\\
  910. loader = display:newChild( NovaUI.UIText( 16, display.h, 5, 1, function( )\\\
  911.     if loading then\\\
  912.         if os.clock() - ltime >= 0.2 then\\\
  913.             loadstage = loadstage + loaddir\\\
  914.             if loadstage == 1 then loaddir = 1\\\
  915.             elseif loadstage == 5 then loaddir = -1\\\
  916.             end\\\
  917.             ltime = os.clock( )\\\
  918.         end\\\
  919.         return string.rep( \\\".\\\", loadstage - 1 ) .. \\\"o\\\" .. string.rep( \\\".\\\", math.max( 5 - loadstage - 1, 0 ) )\\\
  920.     end\\\
  921.     return \\\"\\\"\\\
  922. end ) )\\\
  923. \\\
  924. headerb = header:newChild( NovaUI.UIText( 1, 1, header.w, header.h, \\\"\\\" ) )\\\
  925. headerb.bc = colours.grey\\\
  926. sidebarb = display:newChild( NovaUI.UIText( sidebar.w + 1, 4, 1, sidebar.h, \\\"\\\" ) )\\\
  927. sidebarb.bc = colours.lightGrey\\\
  928. filecontentb = filecontent:newChild( NovaUI.UIButton( 1, 1, filecontent.w, filecontent.h, \\\"\\\" ) )\\\
  929. filecontentb.bc = colours.white\\\
  930. filelist = filecontent:newChild( NovaUI.UIFrame( 2, 2, filecontent.w - 4, filecontent.h - 2 ) )\\\
  931. scrollbar = filecontent:newChild( NovaUI.UIScrollBar( filecontent.w - 1, 2, 1, filecontent.h, filelist ) )\\\
  932. \\\
  933. backbutton = header:newChild( NovaUI.UIButton( 2, 2, 3, 1, \\\"<\\\" ) )\\\
  934. backbutton.bc = colours.lightGrey\\\
  935. backbutton.tc = colours.black\\\
  936. forwardbutton = header:newChild( NovaUI.UIButton( 6, 2, 3, 1, \\\">\\\" ) )\\\
  937. forwardbutton.bc = colours.lightGrey\\\
  938. forwardbutton.tc = colours.black\\\
  939. upbutton = header:newChild( NovaUI.UIButton( 10, 2, 3, 1, \\\"^\\\" ) )\\\
  940. upbutton.bc = colours.lightGrey\\\
  941. upbutton.tc = colours.black\\\
  942. pathinput = header:newChild( NovaUI.UIInput( 14, 2, display.w - 14, 1 ) )\\\
  943. pathinput.bc = colours.lightGrey\\\
  944. pathinput.fbc = colours.white\\\
  945. \\\
  946. function backbutton:onClick( )\\\
  947.     if history[historyindex - 1] then\\\
  948.         historyindex = historyindex - 1\\\
  949.         loadfiles( history[historyindex] )\\\
  950.         pathinput.text = history[historyindex]\\\
  951.         fpath = history[historyindex]\\\
  952.     end\\\
  953. end\\\
  954. function forwardbutton:onClick( )\\\
  955.     if history[historyindex + 1] then\\\
  956.         historyindex = historyindex + 1\\\
  957.         loadfiles( history[historyindex] )\\\
  958.         pathinput.text = history[historyindex]\\\
  959.         fpath = history[historyindex]\\\
  960.     end\\\
  961. end\\\
  962. \\\
  963. function upbutton:onClick( )\\\
  964.     local up = ( fpath:match \\\"(.+/)\\\" or fpath .. \\\" \\\" ):sub( 1, -2 )\\\
  965.     if up == fpath and fpath:find \\\":.\\\" then\\\
  966.         up = ( fpath:match \\\"(.+:.)\\\" or fpath .. \\\" \\\" ):sub( 1, -2 )\\\
  967.     end\\\
  968.     if up ~= fpath then\\\
  969.         openpath( up )\\\
  970.     end\\\
  971. end\\\
  972. \\\
  973. function pathinput:onEnter( )\\\
  974.     openpath( self.text )\\\
  975. end\\\
  976. \\\
  977. function filecontentb:onClick( rx, ry, button )\\\
  978.     if button == 2 then\\\
  979.         showRightClickOptions( filecontent.x + rx - 1, filecontent.y + ry - 1 )\\\
  980.     end\\\
  981. end\\\
  982. \\\
  983. function keyhandler:onKey( key, lastkey )\\\
  984.     if key == keys.backspace and not lastkey then\\\
  985.         if history[historyindex - 1] then\\\
  986.             historyindex = historyindex - 1\\\
  987.             loadfiles( history[historyindex] )\\\
  988.             pathinput.text = history[historyindex]\\\
  989.             fpath = history[historyindex]\\\
  990.         end\\\
  991.     end\\\
  992. end\\\
  993. \\\
  994. local lt\\\
  995. function loadfiles( path, openAs )\\\
  996.     if lt then process:stopThread( lt ) loading = false end\\\
  997.     lt = Nova.app.newThread( function( )\\\
  998.         local l = loading\\\
  999.         loading = true\\\
  1000.         path = path or fpath\\\
  1001. \\\
  1002.         if mount and path:sub( 1, #mount ) ~= mount then\\\
  1003.             NovaFS.unmount( mount )\\\
  1004.             mount = nil\\\
  1005.         end\\\
  1006. \\\
  1007.         local _dirs, _files = { }, { }\\\
  1008.         local files = NovaFS.listFiles( path, function( files )\\\
  1009.             for i = 1, #files do\\\
  1010.                 if files[i]:sub( 1, 1 ) ~= \\\".\\\" or viewhidden then\\\
  1011.                     if NovaFS.isDirectory( NovaFS.merge( path, files[i] ) ) then\\\
  1012.                         table.insert( _dirs, { name = files[i], size = NovaFS.getSize( NovaFS.merge( path, files[i] ) ) } )\\\
  1013.                     else\\\
  1014.                         table.insert( _files, { name = files[i], size = NovaFS.getSize( NovaFS.merge( path, files[i] ) ) } )\\\
  1015.                     end\\\
  1016.                 end\\\
  1017.                 coroutine.yield( )\\\
  1018.             end\\\
  1019.         end )\\\
  1020. \\\
  1021.         if not files then\\\
  1022.             loading = l\\\
  1023.             if not openAs then\\\
  1024.                 openAs = NovaFS.getType( path )\\\
  1025.             end\\\
  1026.             if openAs == \\\"shortcut\\\" then\\\
  1027.                 local ok, data = NovaFS.getFileMetaValue( path, \\\"destination\\\" )\\\
  1028.                 if ok then\\\
  1029.                     openpath( data )\\\
  1030.                 else\\\
  1031.                     Nova.app.newThread( function( )\\\
  1032.                         NovaUI.display.alert( display, \\\"Malformed shorcut\\\", true )\\\
  1033.                         Nova.app.close( )\\\
  1034.                     end )\\\
  1035.                 end\\\
  1036.                 return\\\
  1037.             elseif openAs == \\\"archive\\\" then\\\
  1038.                 local archive = NovaFS.Archive( path )\\\
  1039.                 local pass\\\
  1040.                 if NovaFS.isProtected( path ) then\\\
  1041.                     local r = NovaUI.display.response( display, \\\"Please enter archive password\\\" )\\\
  1042.                     if r then\\\
  1043.                         pass = r\\\
  1044.                     else\\\
  1045.                         return\\\
  1046.                     end\\\
  1047.                 end\\\
  1048.                 archive:loadFile( path, pass )\\\
  1049.                 mount = NovaFS.getName( path, true )\\\
  1050.                 NovaFS.mount( archive:createDrive( ), mount )\\\
  1051.                 openpath( mount .. \\\":\\\" )\\\
  1052.                 return\\\
  1053.             end\\\
  1054.             if NovaFS.exists( path ) then\\\
  1055.                 local handlers = NovaFS.getHandlers( openAs )\\\
  1056.                 if handlers[#handlers] then\\\
  1057.                     Nova.app.close( )\\\
  1058.                     Nova.app.launch( handlers[#handlers], { path, openAs } )\\\
  1059.                 else\\\
  1060.                     Nova.app.newThread( function( )\\\
  1061.                         NovaUI.display.alert( display, \\\"Unknown file type.\\\", true )\\\
  1062.                         Nova.app.close( )\\\
  1063.                     end )\\\
  1064.                 end\\\
  1065.             else\\\
  1066.                 Nova.app.newThread( function( )\\\
  1067.                     NovaUI.display.alert( display, \\\"File doesn't exist\\\", true )\\\
  1068.                     Nova.app.close( )\\\
  1069.                 end )\\\
  1070.             end\\\
  1071.             return\\\
  1072.         end\\\
  1073. \\\
  1074.         if #files > 0 then\\\
  1075.             coroutine.yield( )\\\
  1076.         end\\\
  1077. \\\
  1078.         table.sort( _dirs, function( i1, i2 )\\\
  1079.             if sortorder.mode == \\\"name\\\" then\\\
  1080.                 return not compstring( i1.name, i2.name )\\\
  1081.             elseif sortorder.mode == \\\"size\\\" then\\\
  1082.                 return i1.size < i2.size\\\
  1083.             end\\\
  1084.         end )\\\
  1085.         coroutine.yield( )\\\
  1086.         table.sort( _files, function( i1, i2 )\\\
  1087.             if sortorder.mode == \\\"name\\\" then\\\
  1088.                 return not compstring( i1.name, i2.name )\\\
  1089.             elseif sortorder.mode == \\\"size\\\" then\\\
  1090.                 return i1.size < i2.size\\\
  1091.             end\\\
  1092.         end )\\\
  1093.         coroutine.yield( )\\\
  1094.         for i = 1, #_dirs do\\\
  1095.             _dirs[i].path = NovaFS.merge( path, _dirs[i].name )\\\
  1096.         end\\\
  1097.         for i = 1, #_files do\\\
  1098.             _files[i].path = NovaFS.merge( path, _files[i].name )\\\
  1099.         end\\\
  1100.         if sortorder.direction == \\\"descending\\\" then\\\
  1101.             for i = 1, math.ceil( #_dirs / 2 ) do\\\
  1102.                 _dirs[i], _dirs[#_dirs-i + 1] = _dirs[#_dirs-i + 1], _dirs[i]\\\
  1103.             end\\\
  1104.             coroutine.yield( )\\\
  1105.             for i = 1, math.ceil( #_files / 2 ) do\\\
  1106.                 _files[i], _files[#_files-i + 1] = _files[#_files-i + 1], _files[i]\\\
  1107.             end\\\
  1108.             coroutine.yield( )\\\
  1109.         end\\\
  1110. \\\
  1111.         local y = 1\\\
  1112. \\\
  1113.         local function loadDirs( )\\\
  1114.             for i = 1, #_dirs do\\\
  1115.                 local frame = filelist:newChild( NovaUI.UIFrame( 1, y, filelist.w, 3 ) )\\\
  1116.                 local app_registered = not viewhidden and Nova.app.isRegistered( _dirs[i].path:gsub( \\\"^C:/?\\\", \\\"\\\", 1 ) )\\\
  1117.                 if app_registered then\\\
  1118.                     local icon = frame:newChild( NovaUI.UIImage( 1, 1, 5, 3, icons.app ) )\\\
  1119.                 else\\\
  1120.                     local icon = frame:newChild( NovaUI.UIImage( 1, 1, 5, 3, icons.folder ) )\\\
  1121.                 end\\\
  1122.                 local name = frame:newChild( NovaUI.UIText( 7, 1, frame.w - 6, 1, NovaFS.getName( _dirs[i].path ) ) )\\\
  1123.                 local size = frame:newChild( NovaUI.UIText( 7, 2, frame.w - 7, 1, formatSize( _dirs[i].size ) ) )\\\
  1124.                 size.tc = colours.lightGrey\\\
  1125.                 local desc = frame:newChild( NovaUI.UIText( 7, 3, frame.w - 7, 1, app_registered and \\\"Nova app\\\" or \\\"Folder\\\" ) )\\\
  1126.                 desc.tc = colours.lightGrey\\\
  1127. \\\
  1128.                 local overlay = frame:newChild( NovaUI.UIButton( 1, 1, frame.w, frame.h, \\\"\\\" ) )\\\
  1129.                 overlay.bc = 0\\\
  1130.                 overlay.align = false\\\
  1131.                 function overlay:onClick( x, y, button )\\\
  1132.                     if button == 1 or button == \\\"enter\\\" then\\\
  1133.                         if app_registered then\\\
  1134.                             Nova.app.launch( app_registered, { } )\\\
  1135.                         else\\\
  1136.                             openpath( _dirs[i].path )\\\
  1137.                         end\\\
  1138.                     else\\\
  1139.                         showFolderOptions( filelist.x + frame.x + x - 2, filelist.y + frame.y + y - 2 + filelist.cy, _dirs[i].path, frame )\\\
  1140.                     end\\\
  1141.                 end\\\
  1142.                 y = y + 4\\\
  1143.             end\\\
  1144.         end\\\
  1145. \\\
  1146.         local cy = filelist.cy\\\
  1147.         filelist:clearChildren( )\\\
  1148. \\\
  1149.         if #files == 0 then\\\
  1150.             local nofiles = filelist:newChild( NovaUI.UIText( 0, 1, 9, 1, \\\"No files!\\\" ) )\\\
  1151.             nofiles:centreX( )\\\
  1152.         end\\\
  1153. \\\
  1154.         if sortorder.direction == \\\"ascending\\\" then\\\
  1155.             loadDirs( )\\\
  1156.             coroutine.yield( )\\\
  1157.         end\\\
  1158.         for i = 1, #_files do\\\
  1159.             local filetype = NovaFS.getType( _files[i].path )\\\
  1160. \\\
  1161.             local app_registered = not viewhidden and Nova.app.isRegistered( _files[i].path:gsub( \\\"^C:/?\\\", \\\"\\\", 1 ) )\\\
  1162.             local frame = filelist:newChild( NovaUI.UIFrame( 1, y, filelist.w, 3 ) )\\\
  1163.             if app_registered then\\\
  1164.                 local icon = frame:newChild( NovaUI.UIImage( 1, 1, 5, 3, icons.app ) )\\\
  1165.             else\\\
  1166.                 local icon = frame:newChild( NovaUI.UIImage( 1, 1, 5, 3, icons[filetype] or icons.unknown ) )\\\
  1167.             end\\\
  1168.             local name = frame:newChild( NovaUI.UIText( 7, 1, frame.w - 6, 1, NovaFS.getName( _files[i].path, true ) ) )\\\
  1169.             local size = frame:newChild( NovaUI.UIText( 7, 2, frame.w - 7, 1, formatSize( _files[i].size ) ) )\\\
  1170.             size.tc = colours.lightGrey\\\
  1171.             local desc = frame:newChild( NovaUI.UIText( 7, 3, frame.w - 7, 1, app_registered and \\\"Nova app\\\" or NovaFS.getTypeDescription( filetype ) or filetype ) )\\\
  1172.             desc.tc = colours.lightGrey\\\
  1173. \\\
  1174.             local overlay = frame:newChild( NovaUI.UIButton( 1, 1, frame.w, frame.h, \\\"\\\" ) )\\\
  1175.             overlay.bc = 0\\\
  1176.             overlay.align = false\\\
  1177.             function overlay:onClick( x, y, button )\\\
  1178.                 if button == 1 or button == \\\"enter\\\" then\\\
  1179.                     if app_registered then\\\
  1180.                         Nova.app.launch( app_registered, { } )\\\
  1181.                     else\\\
  1182.                         if filetype == \\\"shortcut\\\" then\\\
  1183.                             local ok, data = NovaFS.getFileMetaValue( _files[i].path, \\\"destination\\\" )\\\
  1184.                             if ok then\\\
  1185.                                 if NovaFS.isDirectory( data ) then\\\
  1186.                                     openpath( data )\\\
  1187.                                 else\\\
  1188.                                     Nova.app.launch( \\\"Files\\\", { data, width = display.w, height = display.h } )\\\
  1189.                                 end\\\
  1190.                             else\\\
  1191.                                 Nova.app.newThread( function( )\\\
  1192.                                     NovaUI.display.alert( display, \\\"Malformed shorcut\\\", true )\\\
  1193.                                     Nova.app.close( )\\\
  1194.                                 end )\\\
  1195.                             end\\\
  1196.                             return\\\
  1197.                         elseif filetype == \\\"archive\\\" then\\\
  1198.                             Nova.app.newThread( function( )\\\
  1199.                                 local l = loading\\\
  1200.                                 loading = true\\\
  1201.                                 local archive = NovaFS.Archive( _files[i].path )\\\
  1202.                                 local pass\\\
  1203.                                 if NovaFS.isProtected( _files[i].path ) then\\\
  1204.                                     local r = NovaUI.display.response( display, \\\"Please enter archive password\\\" )\\\
  1205.                                     if r then\\\
  1206.                                         pass = r\\\
  1207.                                     else\\\
  1208.                                         return\\\
  1209.                                     end\\\
  1210.                                 end\\\
  1211.                                 archive:loadFile( _files[i].path, pass )\\\
  1212.                                 mount = NovaFS.getName( _files[i].path, true )\\\
  1213.                                 NovaFS.mount( archive:createDrive( ), mount )\\\
  1214.                                 openpath( mount .. \\\":\\\" )\\\
  1215.                                 loading = l\\\
  1216.                             end )\\\
  1217.                             return\\\
  1218.                         else\\\
  1219.                             Nova.app.launch( \\\"Files\\\", { _files[i].path, filetype, width = display.w, height = display.h } )\\\
  1220.                         end\\\
  1221.                     end\\\
  1222.                 else\\\
  1223.                     showFileOptions( filelist.x + frame.x + x - 2, filelist.y + frame.y + y - 2 + filelist.cy, _files[i].path, frame )\\\
  1224.                 end\\\
  1225.             end\\\
  1226. \\\
  1227.             y = y + 4\\\
  1228.         end\\\
  1229.         coroutine.yield( )\\\
  1230.         if sortorder.direction == \\\"descending\\\" then\\\
  1231.             loadDirs( )\\\
  1232.             coroutine.yield( )\\\
  1233.         end\\\
  1234. \\\
  1235.         filelist.cy = cy\\\
  1236. \\\
  1237.         loading = l\\\
  1238.     end )\\\
  1239. end\\\
  1240. \\\
  1241. function refresh( )\\\
  1242.     Nova.app.newThread( function( )\\\
  1243.         loadfiles( fpath )\\\
  1244.     end )\\\
  1245. end\\\
  1246. \\\
  1247. function openpath( path, openAs )\\\
  1248.     if not viewhidden and Nova.app.isRegistered( path:gsub( \\\"^C:/?\\\", \\\"\\\", 1 ) ) then\\\
  1249.         Nova.app.launch( Nova.app.isRegistered( path:gsub( \\\"^C:/?\\\", \\\"\\\", 1 ) ), { } )\\\
  1250.         return\\\
  1251.     end\\\
  1252.     filelist.cy = 0\\\
  1253.     path = NovaFS.merge( path )\\\
  1254.     historyindex = historyindex + 1\\\
  1255.     while history[historyindex] do\\\
  1256.         table.remove( history, historyindex )\\\
  1257.     end\\\
  1258.     table.insert( history, path )\\\
  1259.     pathinput.text = path\\\
  1260.     fpath = path\\\
  1261.     loadfiles( fpath, openAs )\\\
  1262. end\\\
  1263. \\\
  1264. local sidebarcontent\\\
  1265. \\\
  1266. Nova.app.newThread( function( )\\\
  1267.     while true do\\\
  1268.         local cy = sidebarcontent and sidebarcontent.cy or 0\\\
  1269.         local t = {\\\
  1270.             \\\"space\\\";\\\
  1271.             { type = \\\"label\\\", name = \\\"Drives\\\" };\\\
  1272.         }\\\
  1273.         local drives = NovaFS.listmounted( )\\\
  1274.         for i = 1, #drives do\\\
  1275.             t[#t + 1] = {\\\
  1276.                 type = \\\"button\\\";\\\
  1277.                 name = drives[i] .. \\\":\\\";\\\
  1278.                 onClick = function( self, _, button )\\\
  1279.                     if button == 1 then\\\
  1280.                         openpath( drives[i] .. \\\":\\\" )\\\
  1281.                     else\\\
  1282.                         local x, y = self:positionIn( display )\\\
  1283.                         dropdown( x, y, {\\\
  1284.                             spacing = false;\\\
  1285.                             shadow = colours.grey;\\\
  1286.                             height = 5;\\\
  1287.                             \\\"space\\\";\\\
  1288.                             { type = \\\"button\\\", name = \\\"open\\\", onClick = function( _, frame )\\\
  1289.                                 if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
  1290.                                 openpath( drives[i] .. \\\":\\\" )\\\
  1291.                             end };\\\
  1292.                             { type = \\\"button\\\", name = \\\"unmount\\\", onClick = function( _, frame )\\\
  1293.                                 if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
  1294.                                 if drives[i] ~= \\\"C\\\" then\\\
  1295.                                     NovaFS.unmount( drives[i] )\\\
  1296.                                 else\\\
  1297.                                     NovaUI.display.alert( display, \\\"Cannot unmount this drive.\\\" )\\\
  1298.                                 end\\\
  1299.                             end };\\\
  1300.                         } )\\\
  1301.                     end\\\
  1302.                 end;\\\
  1303.             }\\\
  1304.         end\\\
  1305.         table.insert( t, \\\"space\\\" )\\\
  1306.         table.insert( t, { type = \\\"label\\\", name = \\\"Favourites\\\" } )\\\
  1307.         for k, v in pairs( favourites ) do\\\
  1308.             table.insert( t, { type = \\\"button\\\", name = NovaFS.getName( k, true ), onClick = function( self, frame, button )\\\
  1309.                 if button == 1 then\\\
  1310.                     openPath( k )\\\
  1311.                 else\\\
  1312.                     local x, y = self:positionIn( display )\\\
  1313.                     dropdown( x, y, {\\\
  1314.                         spacing = false;\\\
  1315.                         shadow = colours.grey;\\\
  1316.                         height = 5;\\\
  1317.                         \\\"space\\\";\\\
  1318.                         { type = \\\"button\\\", name = \\\"open\\\", onClick = function( _, frame )\\\
  1319.                             if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
  1320.                             openPath( k )\\\
  1321.                         end };\\\
  1322.                         { type = \\\"button\\\", name = \\\"remove\\\", onClick = function( _, frame )\\\
  1323.                             if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
  1324.                             favourites[k] = nil;\\\
  1325.                             Nova.app.setData( \\\"favourites\\\", favourites )\\\
  1326.                         end };\\\
  1327.                     } )\\\
  1328.                 end\\\
  1329.             end } )\\\
  1330.         end\\\
  1331.         sidebarcontent = NovaUI.display.menu( sidebar, t )\\\
  1332.         sidebarcontent.cy = cy\\\
  1333.         local t = os.clock( )\\\
  1334.         while os.clock( ) - t < 0.5 do\\\
  1335.             coroutine.yield( )\\\
  1336.         end\\\
  1337.     end\\\
  1338. end )\\\
  1339. \\\
  1340. if ARGS[1] then\\\
  1341.     openpath( ARGS[1], ARGS[2] )\\\
  1342. else\\\
  1343.     openpath \\\"C:/\\\"\\\
  1344. end\\\
  1345. \\\
  1346. resizeDisplay( display.w, display.h )\\\
  1347. \\\
  1348. function showRightClickOptions( x, y )\\\
  1349.     dropdown( x, y, {\\\
  1350.         spacing = false;\\\
  1351.         shadow = colours.grey;\\\
  1352.         height = 11;\\\
  1353.         { type = \\\"button\\\", name = \\\"new file\\\", onClick = function( )\\\
  1354.             if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
  1355.             local p = NovaFS.merge( fpath, \\\"New File\\\" )\\\
  1356.             if NovaFS.exists( p ) then\\\
  1357.                 local i = 1\\\
  1358.                 while NovaFS.exists( p .. \\\" (\\\" .. i .. \\\")\\\" ) do\\\
  1359.                     i = i + 1\\\
  1360.                 end\\\
  1361.                 p = p .. \\\" (\\\" .. i .. \\\")\\\"\\\
  1362.             end\\\
  1363.             NovaFS.newFile( p )\\\
  1364.             refresh( )\\\
  1365.         end };\\\
  1366.         { type = \\\"button\\\", name = \\\"new folder\\\", onClick = function( )\\\
  1367.             if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
  1368.             local p = NovaFS.merge( fpath, \\\"New Folder\\\" )\\\
  1369.             if NovaFS.exists( p ) then\\\
  1370.                 local i = 1\\\
  1371.                 while NovaFS.exists( p .. \\\" (\\\" .. i .. \\\")\\\" ) do\\\
  1372.                     i = i + 1\\\
  1373.                 end\\\
  1374.                 p = p .. \\\" (\\\" .. i .. \\\")\\\"\\\
  1375.             end\\\
  1376.             NovaFS.newDirectory( p )\\\
  1377.             refresh( )\\\
  1378.         end };\\\
  1379.         \\\"rule\\\";\\\
  1380.         { type = \\\"button\\\", name = \\\"paste\\\", onClick = function( )\\\
  1381.             if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
  1382.             NovaFS.paste( fpath )\\\
  1383.             refresh( )\\\
  1384.         end };\\\
  1385.         \\\"rule\\\";\\\
  1386.         { type = \\\"button\\\", name = \\\"refresh\\\", onClick = function( )\\\
  1387.             if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
  1388.             refresh( )\\\
  1389.         end };\\\
  1390.         \\\"rule\\\";\\\
  1391.         { type = \\\"button\\\", name = viewhidden and \\\"hide hidden\\\" or \\\"show hidden\\\", onClick = function( )\\\
  1392.             if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
  1393.             viewhidden = not viewhidden\\\
  1394.             Nova.app.setData( \\\"viewhidden\\\", viewhidden )\\\
  1395.             refresh( )\\\
  1396.         end };\\\
  1397.         { type = \\\"menu\\\", name = \\\"sort by\\\", options = {\\\
  1398.             spacing = false;\\\
  1399.             shadow = colours.grey;\\\
  1400.             width = 7;\\\
  1401.             height = 3;\\\
  1402.             { type = \\\"button\\\", name = \\\"name\\\", onClick = function( )\\\
  1403.                 if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
  1404.                 sortorder.mode = \\\"name\\\"\\\
  1405.                 Nova.app.setData( \\\"sortorder\\\", sortorder )\\\
  1406.                 refresh( )\\\
  1407.             end };\\\
  1408.             { type = \\\"button\\\", name = \\\"size\\\", onClick = function( )\\\
  1409.                 if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
  1410.                 sortorder.mode = \\\"size\\\"\\\
  1411.                 Nova.app.setData( \\\"sortorder\\\", sortorder )\\\
  1412.                 refresh( )\\\
  1413.             end };\\\
  1414.         } };\\\
  1415.         { type = \\\"menu\\\", name = \\\"sort\\\", options = {\\\
  1416.             spacing = false;\\\
  1417.             shadow = colours.grey;\\\
  1418.             width = 13;\\\
  1419.             height = 3;\\\
  1420.             { type = \\\"button\\\", name = \\\"ascending\\\", onClick = function( )\\\
  1421.                 if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
  1422.                 sortorder.direction = \\\"ascending\\\"\\\
  1423.                 Nova.app.setData( \\\"sortorder\\\", sortorder )\\\
  1424.                 refresh( )\\\
  1425.             end };\\\
  1426.             { type = \\\"button\\\", name = \\\"descending\\\", onClick = function( )\\\
  1427.                 if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
  1428.                 sortorder.direction = \\\"descending\\\"\\\
  1429.                 Nova.app.setData( \\\"sortorder\\\", sortorder )\\\
  1430.                 refresh( )\\\
  1431.             end };\\\
  1432.         } };\\\
  1433.     } )\\\
  1434. end\\\
  1435. \\\
  1436. function showFileOptions( x, y, path, frame )\\\
  1437.     local archive = NovaFS.getType( path ) == \\\"archive\\\"\\\
  1438.     local _type = NovaFS.getType( path )\\\
  1439.     dropdown( x, y, {\\\
  1440.         spacing = false;\\\
  1441.         shadow = colours.grey;\\\
  1442.         height = 14;\\\
  1443.         { type = \\\"button\\\", name = \\\"open\\\", onClick = function( )\\\
  1444.             if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
  1445.             Nova.app.launch( \\\"Files\\\", { path, _type, width = display.w, height = display.h } )\\\
  1446.         end };\\\
  1447.         { type = \\\"button\\\", name = archive and \\\"extract\\\" or \\\"open with\\\", onClick = function( )\\\
  1448.             if archive then\\\
  1449.                 Nova.app.newThread( function( )\\\
  1450.                     if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
  1451.                     local p = path:gsub( \\\".nac$\\\", \\\"\\\", 1 )\\\
  1452.                     local archive = NovaFS.Archive( )\\\
  1453.                     local pass\\\
  1454.                     if NovaFS.isProtected( path ) then\\\
  1455.                         pass = NovaUI.display.response( display, \\\"Please enter password\\\" )\\\
  1456.                         if not pass then\\\
  1457.                             NovaUI.display.response( display, \\\"Archive is password protected\\\" )\\\
  1458.                             return\\\
  1459.                         end\\\
  1460.                     end\\\
  1461.                     archive:loadFile( path, pass )\\\
  1462.                     local i = 1\\\
  1463.                     while NovaFS.exists( p ) do\\\
  1464.                         p = path:gsub( \\\".nac$\\\", \\\"\\\", 1 ) .. \\\" (\\\" .. i .. \\\")\\\"\\\
  1465.                         i = i + 1\\\
  1466.                     end\\\
  1467.                     archive:saveDirectory( p )\\\
  1468.                     refresh( )\\\
  1469.                 end )\\\
  1470.             else\\\
  1471.                 if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
  1472.                 local frame = display:newChild( NovaUI.UIFrame( 1, 1, display.w, display.h ) )\\\
  1473.                 local close = frame:newChild( NovaUI.UIButton( 1, 1, frame.w, frame.h, \\\"\\\" ) )\\\
  1474.                 close.bc = 0\\\
  1475.                 close.tc = 0\\\
  1476.                 function close:onClick( )\\\
  1477.                     frame:remove( )\\\
  1478.                 end\\\
  1479.                 local content = frame:newChild( NovaUI.UIFrame( 0, 0, 25, 12 ) )\\\
  1480.                 content:centre( )\\\
  1481.                 content:newChild( NovaUI.UIText( 2, 2, 24, 11, \\\"\\\" ) ).bc = colours.grey\\\
  1482.                 content:newChild( NovaUI.UIText( 1, 1, 24, 11, \\\"\\\" ) ).bc = colours.white\\\
  1483.                 content:newChild( NovaUI.UIText( 0, 2, 12, 1, \\\"Open with...\\\" ) ):centreX( )\\\
  1484.                 content:newChild( NovaUI.UIText( 2, 3, 22, 1, \\\"----------------------\\\" ) ).tc = colours.lightGrey\\\
  1485.                 content:newChild( NovaUI.UIText( 2, 9, 22, 1, \\\"----------------------\\\" ) ).tc = colours.lightGrey\\\
  1486.                 local defaultbrackets = content:newChild( NovaUI.UIButton( 2, 10, 3, 1, \\\"[ ]\\\" ) )\\\
  1487.                 defaultbrackets.tc = colours.lightGrey\\\
  1488.                 local defaultlabel = content:newChild( NovaUI.UIButton( 5, 10, 8, 1, \\\" Default\\\" ) )\\\
  1489.                 defaultlabel.tc = colours.grey\\\
  1490.                 local default = content:newChild( NovaUI.UIButton( 3, 10, 1, 1, \\\"@\\\" ) )\\\
  1491.                 function defaultbrackets.onClick( )\\\
  1492.                     default.text = default.text == \\\"@\\\" and \\\" \\\" or \\\"@\\\"\\\
  1493.                 end\\\
  1494.                 defaultlabel.onClick = defaultbrackets.onClick\\\
  1495.                 default.onClick = defaultbrackets.onClick\\\
  1496.                 local ok = content:newChild( NovaUI.UIButton( 20, 10, 4, 1, \\\"ok\\\" ) )\\\
  1497.                 ok.tc = colours.grey\\\
  1498.                 local applist = content:newChild( NovaUI.UIFrame( 2, 4, 22, 5 ) )\\\
  1499.                 local handlers = NovaFS.getHandlers( _type )\\\
  1500.                 if #handlers == 0 then\\\
  1501.                     handlers = NovaFS.getAllHandlers( )\\\
  1502.                 end\\\
  1503.                 if #handlers + 1 > applist.h then\\\
  1504.                     applist.w = applist.w - 1\\\
  1505.                     content:newChild( NovaUI.UIScrollBar( 0, 0, 1, 0, applist ) ):align( \\\"right\\\", applist )\\\
  1506.                 end\\\
  1507.                 local current\\\
  1508.                 for i = 1, #handlers do\\\
  1509.                     local button = applist:newChild( NovaUI.UIButton( 1, i, applist.w, 1, handlers[i] ) )\\\
  1510.                     button.tc = colours.grey\\\
  1511.                     button.align = false\\\
  1512.                     function button:onClick( )\\\
  1513.                         if current == button then\\\
  1514.                             button.bc = colours.white ok.bc = colours.white current = nil return\\\
  1515.                         end\\\
  1516.                         ok.bc = colours.lightBlue\\\
  1517.                         if current then current.bc = colours.white end\\\
  1518.                         button.bc = colours.lightBlue\\\
  1519.                         current = button\\\
  1520.                     end\\\
  1521.                 end\\\
  1522.                 local showall = applist:newChild( NovaUI.UIButton( 1, #handlers + 1, applist.w, 1, \\\"Show all\\\" ) )\\\
  1523.                 showall.align = false\\\
  1524.                 function showall:onClick( )\\\
  1525.                     current = nil\\\
  1526.                     applist:clearChildren( )\\\
  1527.                     local handlers = NovaFS.getAllHandlers( )\\\
  1528.                     for i = 1, #handlers do\\\
  1529.                         local button = applist:newChild( NovaUI.UIButton( 1, i, applist.w, 1, handlers[i] ) )\\\
  1530.                         button.tc = colours.grey\\\
  1531.                         button.align = false\\\
  1532.                         function button:onClick( )\\\
  1533.                             if current == button then\\\
  1534.                                 button.bc = colours.white ok.bc = colours.lightGrey current = nil return\\\
  1535.                             end\\\
  1536.                             ok.bc = colours.lightBlue\\\
  1537.                             if current then current.bc = colours.white end\\\
  1538.                             button.bc = colours.lightBlue\\\
  1539.                             current = button\\\
  1540.                         end\\\
  1541.                     end\\\
  1542.                 end\\\
  1543.                 function ok:onClick( )\\\
  1544.                     if not current then return end\\\
  1545.                     local app = current.text\\\
  1546.                     if default.text == \\\"@\\\" then\\\
  1547.                         if defaults[_type] ~= app then\\\
  1548.                             defaults[_type] = app\\\
  1549.                             Nova.app.setData( \\\"defaults\\\", defaults )\\\
  1550.                         end\\\
  1551.                         NovaFS.setDefaultHandler( _type, app )\\\
  1552.                     end\\\
  1553.                     frame:remove( )\\\
  1554.                     Nova.app.launch( app, { path, _type } )\\\
  1555.                 end\\\
  1556.             end\\\
  1557.         end };\\\
  1558.         \\\"rule\\\";\\\
  1559.         { type = \\\"button\\\", name = \\\"copy\\\", onClick = function( )\\\
  1560.             if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
  1561.             NovaFS.copy( path )\\\
  1562.         end };\\\
  1563.         { type = \\\"button\\\", name = \\\"cut\\\", onClick = function( )\\\
  1564.             if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
  1565.             NovaFS.cut( path )\\\
  1566.             refresh( )\\\
  1567.         end };\\\
  1568.         { type = \\\"button\\\", name = \\\"delete\\\", onClick = function( )\\\
  1569.             if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
  1570.             NovaFS.delete( path )\\\
  1571.             refresh( )\\\
  1572.         end };\\\
  1573.         { type = \\\"button\\\", name = \\\"rename\\\", onClick = function( )\\\
  1574.             if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
  1575.             renaming = true\\\
  1576.             local input = filelist:newChild( NovaUI.UIInput( frame.x + 6, frame.y, frame.w - 7, 1 ) )\\\
  1577.             input.bc = 0\\\
  1578.             input.fbc = 0\\\
  1579.             input.text = NovaFS.getName( path )\\\
  1580.             input:focusOn( )\\\
  1581.             input:select( 1, #NovaFS.getName( path, true ) )\\\
  1582. \\\
  1583.             function input:onEnter( )\\\
  1584.                 local p = NovaFS.merge( filepath, self.text )\\\
  1585.                 if p == path then return end\\\
  1586.                 if NovaFS.exists( p ) then\\\
  1587.                     NovaUI.display.alert( display, \\\"File already exists\\\" )\\\
  1588.                 else\\\
  1589.                     pcall( NovaFS.move, path, p )\\\
  1590.                     refresh( )\\\
  1591.                 end\\\
  1592.             end\\\
  1593.             function input:whenUnFocussed( )\\\
  1594.                 input:remove( )\\\
  1595.                 renaming = false\\\
  1596.             end\\\
  1597.         end };\\\
  1598.         \\\"rule\\\";\\\
  1599.         { type = \\\"button\\\", name = \\\"shortcut\\\", onClick = function( )\\\
  1600.             if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
  1601.             local fd = FileData \\\"\\\"\\\
  1602.             fd.meta.destination = path\\\
  1603.             fd.meta.type = \\\"shortcut\\\"\\\
  1604.             local p = NovaFS.merge( NovaFS.getDirectory( path ), NovaFS.getName( path, true ) ) .. \\\" - Shortcut\\\"\\\
  1605.             if NovaFS.exists( p ) then\\\
  1606.                 local i = 1\\\
  1607.                 while NovaFS.exists( p .. \\\" (\\\" .. i .. \\\")\\\" ) do\\\
  1608.                     i = i + 1\\\
  1609.                 end\\\
  1610.                 p = p .. \\\" (\\\" .. i .. \\\")\\\"\\\
  1611.             end\\\
  1612.             NovaFS.writefile( p, fd )\\\
  1613.             refresh( )\\\
  1614.         end };\\\
  1615.         { type = \\\"button\\\", name = \\\"properties\\\" };\\\
  1616.         \\\"rule\\\";\\\
  1617.         { type = \\\"button\\\", name = NovaFS.isProtected( path ) and \\\"unprotect\\\" or \\\"protect\\\", onClick = function( )\\\
  1618.             if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
  1619.             Nova.app.newThread( function( )\\\
  1620.                 local r\\\
  1621.                 local p = NovaFS.isProtected( path )\\\
  1622.                 if p then\\\
  1623.                     r = NovaUI.display.response( display, \\\"Password to decrypt file with.\\\" )\\\
  1624.                 else\\\
  1625.                     r = NovaUI.display.response( display, \\\"Password to encrypt file with.\\\" )\\\
  1626.                 end\\\
  1627.                 loading = true\\\
  1628.                 if r then\\\
  1629.                     local data, err = NovaFS.readfile( path, p and r )\\\
  1630.                     if not p then\\\
  1631.                         data.password = r\\\
  1632.                     end\\\
  1633.                     data.meta.password = not p and true or nil\\\
  1634.                     NovaFS.writefile( path, data )\\\
  1635.                     refresh( )\\\
  1636.                 end\\\
  1637.                 loading = false\\\
  1638.             end )\\\
  1639.         end };\\\
  1640.         { type = \\\"button\\\", name = \\\"favourite\\\", onClick = function( )\\\
  1641.             if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
  1642.             if not favourites[path] then\\\
  1643.                 favourites[path] = true\\\
  1644.                 Nova.app.setData( \\\"favourites\\\", favourites )\\\
  1645.             end\\\
  1646.         end };\\\
  1647.     } )\\\
  1648. end\\\
  1649. \\\
  1650. function showFolderOptions( x, y, path, frame )\\\
  1651.     dropdown( x, y, {\\\
  1652.         spacing = false;\\\
  1653.         shadow = colours.grey;\\\
  1654.         height = 14;\\\
  1655.         { type = \\\"button\\\", name = \\\"open\\\", onClick = function( )\\\
  1656.             if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
  1657.             openpath( path )\\\
  1658.         end };\\\
  1659.         { type = \\\"button\\\", name = \\\"open in new\\\", onClick = function( )\\\
  1660.             if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
  1661.             Nova.app.launch( \\\"Files\\\", { path, width = display.w, height = display.h } )\\\
  1662.         end };\\\
  1663.         \\\"rule\\\";\\\
  1664.         { type = \\\"button\\\", name = \\\"copy\\\", onClick = function( )\\\
  1665.             if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
  1666.             NovaFS.copy( path )\\\
  1667.         end };\\\
  1668.         { type = \\\"button\\\", name = \\\"cut\\\", onClick = function( )\\\
  1669.             if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
  1670.             NovaFS.cut( path )\\\
  1671.             refresh( )\\\
  1672.         end };\\\
  1673.         { type = \\\"button\\\", name = \\\"delete\\\", onClick = function( )\\\
  1674.             if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
  1675.             NovaFS.delete( path )\\\
  1676.             refresh( )\\\
  1677.         end };\\\
  1678.         { type = \\\"button\\\", name = \\\"rename\\\", onClick = function( )\\\
  1679.             if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
  1680.             renaming = true\\\
  1681.             local input = filelist:newChild( NovaUI.UIInput( frame.x + 6, frame.y, frame.w - 7, 1 ) )\\\
  1682.             input.bc = 0\\\
  1683.             input.fbc = 0\\\
  1684.             input.text = NovaFS.getName( path )\\\
  1685.             input:focusOn( )\\\
  1686.             input:select( 1, #NovaFS.getName( path, true ) )\\\
  1687. \\\
  1688.             function input:onEnter( )\\\
  1689.                 local p = NovaFS.merge( filepath, self.text )\\\
  1690.                 if p == path then return end\\\
  1691.                 if NovaFS.exists( p ) then\\\
  1692.                     NovaUI.display.alert( display, \\\"File already exists\\\" )\\\
  1693.                 else\\\
  1694.                     pcall( NovaFS.move, path, p )\\\
  1695.                     refresh( )\\\
  1696.                 end\\\
  1697.             end\\\
  1698.             function input:whenUnFocussed( ) \\\
  1699.                 input:remove( )\\\
  1700.                 renaming = false\\\
  1701.             end\\\
  1702.         end };\\\
  1703.         \\\"rule\\\";\\\
  1704.         { type = \\\"button\\\", name = \\\"shortcut\\\", onClick = function( )\\\
  1705.             if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
  1706.             local fd = FileData \\\"\\\"\\\
  1707.             fd.meta.destination = path\\\
  1708.             fd.meta.type = \\\"shortcut\\\"\\\
  1709.             local p = NovaFS.merge( NovaFS.getDirectory( path ), NovaFS.getName( path, true ) ) .. \\\" - Shortcut\\\"\\\
  1710.             if NovaFS.exists( p ) then\\\
  1711.                 local i = 1\\\
  1712.                 while NovaFS.exists( p .. \\\" (\\\" .. i .. \\\")\\\" ) do\\\
  1713.                     i = i + 1\\\
  1714.                 end\\\
  1715.                 p = p .. \\\" (\\\" .. i .. \\\")\\\"\\\
  1716.             end\\\
  1717.             NovaFS.writefile( p, fd )\\\
  1718.             refresh( )\\\
  1719.         end };\\\
  1720.         { type = \\\"button\\\", name = \\\"properties\\\" };\\\
  1721.         \\\"rule\\\";\\\
  1722.         { type = \\\"button\\\", name = \\\"archive\\\", onClick = function( )\\\
  1723.             if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
  1724.             local archive = NovaFS.Archive( )\\\
  1725.             local l = loading\\\
  1726.             loading = true\\\
  1727.             archive:loadDirectory( path )\\\
  1728.             local p = path .. \\\".nac\\\"\\\
  1729.             local i = 1\\\
  1730.             while NovaFS.exists( p ) do\\\
  1731.                 p = path .. \\\" (\\\" .. i .. \\\").nac\\\"\\\
  1732.                 i = i + 1\\\
  1733.             end\\\
  1734.             archive:saveFile( p )\\\
  1735.             refresh( )\\\
  1736.             loading = l\\\
  1737.         end };\\\
  1738.         { type = \\\"button\\\", name = \\\"favourite\\\", onClick = function( )\\\
  1739.             if activedropdown then activedropdown:remove( ) activedropdown = nil end\\\
  1740.             if not favourites[path] then\\\
  1741.                 favourites[path] = true\\\
  1742.                 Nova.app.setData( \\\"favourites\\\", favourites )\\\
  1743.             end\\\
  1744.         end };\\\
  1745.     } )\\\
  1746. end\",\
  1747. [\"appconfig.txt\"]=\"runmode = \\\"app\\\";\\\
  1748. size = \\\"full\\\";\",\
  1749. }\
  1750. ,\
  1751. Tasks={\
  1752. [\"main.lua\"]=\"\\\
  1753. if not Nova then\\\
  1754.     error( \\\"Cannot run outside Nova\\\", 0 )\\\
  1755. end\\\
  1756. \\\
  1757. local window = Nova.app.window\\\
  1758. local display = window.display\\\
  1759. \\\
  1760. window.minw = 10\\\
  1761. window.minh = 4\\\
  1762. \\\
  1763. local background = display:newChild( NovaUI.UIText( 1, 1, display.w - 1, display.h - 1, \\\"\\\" ) )\\\
  1764. local list = display:newChild( NovaUI.UIFrame( 1, 1, display.w - 1, display.h - 1 ) )\\\
  1765. local scroll = display:newChild( NovaUI.UIScrollBar( display.w, 1, 1, display.h - 1, list ) )\\\
  1766. local scroll2 = display:newChild( NovaUI.UIScrollBar( 1, display.h, display.w, 1, list, \\\"horizontal\\\" ) )\\\
  1767. \\\
  1768. function Nova.app.callback.onResize( w, h )\\\
  1769.     background.w = w - 1\\\
  1770.     background.h = h - 1\\\
  1771.     list.w = w - 1\\\
  1772.     list.h = h - 1\\\
  1773.     scroll.x = w\\\
  1774.     scroll.h = h - 1\\\
  1775.     scroll2.w = w\\\
  1776.     scroll2.y = h\\\
  1777. end\\\
  1778. \\\
  1779. Nova.app.newThread( function( )\\\
  1780.     while true do\\\
  1781.         local processes = Nova.listProcesses( )\\\
  1782.         local max = 4\\\
  1783.         for i = 1, #processes do\\\
  1784.             local title = list:newChild( NovaUI.UIText( 1, i, #processes[i].name, 1, processes[i].name ) )\\\
  1785.             max = math.max( #processes[i].name + 1, max )\\\
  1786.         end\\\
  1787.         local rm = max + 1\\\
  1788.         max = max + 3\\\
  1789.         for i = 1, #processes do\\\
  1790.             local c = tostring( processes[i].threadcount )\\\
  1791.             local count = list:newChild( NovaUI.UIText( rm, i, #c, 1, c ) )\\\
  1792.             count.tc = colours.lightGrey\\\
  1793.             max = math.max( rm + #c, max )\\\
  1794.         end\\\
  1795.         for i = 1, #processes do\\\
  1796.             local close = list:newChild( NovaUI.UIButton( max + 1, i, 4, 1, \\\"stop\\\" ) )\\\
  1797.             close.tc = colours.red\\\
  1798.             function close:onClick( )\\\
  1799.                 processes[i].stop( )\\\
  1800.             end\\\
  1801.         end\\\
  1802.         coroutine.yield( )\\\
  1803.         list:clearChildren( )\\\
  1804.     end\\\
  1805. end )\",\
  1806. [\"appconfig.txt\"]=\"runmode = \\\"app\\\";\\\
  1807. width = 23;\\\
  1808. height = 10;\\\
  1809. permissions = {\\\
  1810.     listProcesses = true;\\\
  1811. }\",\
  1812. }\
  1813. ,\
  1814. Edit={\
  1815. [\"main.lua\"]=\"\\\
  1816. local window, display = { }, { }\\\
  1817. local ARGS = ARGS\\\
  1818. local running = true\\\
  1819. local threads = { }\\\
  1820. \\\
  1821. local function newThread( f, wait )\\\
  1822.     if Nova then\\\
  1823.         local tID = Nova.app.newThread( f )\\\
  1824.         if wait then\\\
  1825.             while process:isThreadRunning( tID ) do\\\
  1826.                 coroutine.yield( )\\\
  1827.             end\\\
  1828.         end\\\
  1829.     else\\\
  1830.         local co = coroutine.create( f )\\\
  1831.         table.insert( threads, co )\\\
  1832.         if wait then\\\
  1833.             while coroutine.status( co ) ~= \\\"dead\\\" do\\\
  1834.                 coroutine.yield( )\\\
  1835.             end\\\
  1836.         end\\\
  1837.     end\\\
  1838. end\\\
  1839. \\\
  1840. if not NovaUI then os.loadAPI \\\"NovaUI\\\" end\\\
  1841. if not NovaFS then os.loadAPI \\\"NovaFS\\\" end\\\
  1842. \\\
  1843. if Nova then\\\
  1844.     window = Nova.app.window\\\
  1845.     display = window.display\\\
  1846.     window.minw = 25\\\
  1847.     window.minh = 10\\\
  1848. else\\\
  1849.     display = NovaUI.UIHandler( )\\\
  1850.     ARGS = { ... }\\\
  1851.     NovaUI.buffer.reset( )\\\
  1852. end\\\
  1853. \\\
  1854. local function copytable( t )\\\
  1855.     local t2 = { }\\\
  1856.     for k, v in pairs( t ) do t2[k] = v end\\\
  1857.     return t2\\\
  1858. end\\\
  1859. \\\
  1860. local syntax = {\\\
  1861.     lua = {\\\
  1862.         blocks = {\\\
  1863.             { start = \\\"--[[\\\", finish = \\\"]]\\\", tc = colours.green };\\\
  1864.             { start = \\\"--\\\", finish = \\\"\\\\n\\\", tc = colours.green };\\\
  1865.         };\\\
  1866.         words = {\\\
  1867.             [\\\"while\\\"] = { tc = colours.blue };\\\
  1868.             [\\\"do\\\"] = { tc = colours.blue };\\\
  1869.             [\\\"if\\\"] = { tc = colours.blue };\\\
  1870.             [\\\"elseif\\\"] = { tc = colours.blue };\\\
  1871.             [\\\"else\\\"] = { tc = colours.blue };\\\
  1872.             [\\\"then\\\"] = { tc = colours.blue };\\\
  1873.             [\\\"for\\\"] = { tc = colours.blue };\\\
  1874.             [\\\"in\\\"] = { tc = colours.blue };\\\
  1875.             [\\\"end\\\"] = { tc = colours.blue };\\\
  1876.             [\\\"local\\\"] = { tc = colours.blue };\\\
  1877.             [\\\"not\\\"] = { tc = colours.blue };\\\
  1878.             [\\\"and\\\"] = { tc = colours.blue };\\\
  1879.             [\\\"or\\\"] = { tc = colours.blue };\\\
  1880.             [\\\"function\\\"] = { tc = colours.blue };\\\
  1881.             [\\\"return\\\"] = { tc = colours.blue };\\\
  1882.             [\\\"for\\\"] = { tc = colours.blue };\\\
  1883.             [\\\"break\\\"] = { tc = colours.blue };\\\
  1884. \\\
  1885.             [\\\"true\\\"] = { tc = colours.blue };\\\
  1886.             [\\\"false\\\"] = { tc = colours.blue };\\\
  1887.             [\\\"nil\\\"] = { tc = colours.blue };\\\
  1888.             [\\\"self\\\"] = { tc = colours.blue };\\\
  1889. \\\
  1890.             [\\\"_VERSION\\\"] = { tc = colours.purple };\\\
  1891.             [\\\"pairs\\\"] = { tc = colours.cyan };\\\
  1892.             [\\\"ipairs\\\"] = { tc = colours.cyan };\\\
  1893.             [\\\"select\\\"] = { tc = colours.cyan };\\\
  1894.             [\\\"unpack\\\"] = { tc = colours.cyan };\\\
  1895.             [\\\"setfenv\\\"] = { tc = colours.cyan };\\\
  1896.             [\\\"getfenv\\\"] = { tc = colours.cyan };\\\
  1897.             [\\\"setmetatable\\\"] = { tc = colours.cyan };\\\
  1898.             [\\\"getmetatable\\\"] = { tc = colours.cyan };\\\
  1899.             [\\\"next\\\"] = { tc = colours.cyan };\\\
  1900.             [\\\"rawset\\\"] = { tc = colours.cyan };\\\
  1901.             [\\\"rawget\\\"] = { tc = colours.cyan };\\\
  1902.             [\\\"rawequal\\\"] = { tc = colours.cyan };\\\
  1903.             [\\\"type\\\"] = { tc = colours.cyan };\\\
  1904.             [\\\"tostring\\\"] = { tc = colours.cyan };\\\
  1905.             [\\\"tonumber\\\"] = { tc = colours.cyan };\\\
  1906.             [\\\"pcall\\\"] = { tc = colours.cyan };\\\
  1907.             [\\\"xpcall\\\"] = { tc = colours.cyan };\\\
  1908.             [\\\"loadstring\\\"] = { tc = colours.cyan };\\\
  1909.             [\\\"assert\\\"] = { tc = colours.cyan };\\\
  1910.             [\\\"error\\\"] = { tc = colours.cyan };\\\
  1911.             [\\\"__inext\\\"] = { tc = colours.cyan };\\\
  1912.             [\\\"math\\\"] = { tc = colours.purple };\\\
  1913.             [\\\"string\\\"] = { tc = colours.purple };\\\
  1914.             [\\\"table\\\"] = { tc = colours.purple };\\\
  1915.             [\\\"coroutine\\\"] = { tc = colours.purple };\\\
  1916.         };\\\
  1917.         default = { bc = colours.white, tc = colours.grey };\\\
  1918.         linen = { bc = colours.grey, tc = colours.lightGrey };\\\
  1919.         string = { tc = colours.red, escape = { tc = colours.brown } };\\\
  1920.         tab = { tc = colours.lightGrey, char = \\\":\\\" };\\\
  1921.         symbols = {\\\
  1922.             [\\\"=\\\"] = { tc = colours.brown };\\\
  1923.             [\\\"+\\\"] = { tc = colours.brown };\\\
  1924.             [\\\"*\\\"] = { tc = colours.brown };\\\
  1925.             [\\\"/\\\"] = { tc = colours.brown };\\\
  1926.             [\\\"^\\\"] = { tc = colours.brown };\\\
  1927.             [\\\"%\\\"] = { tc = colours.brown };\\\
  1928.             [\\\">\\\"] = { tc = colours.brown };\\\
  1929.             [\\\"<\\\"] = { tc = colours.brown };\\\
  1930.             [\\\"#\\\"] = { tc = colours.brown };\\\
  1931.             [\\\"~\\\"] = { tc = colours.brown };\\\
  1932.         };\\\
  1933.         selection = { bc = colours.blue, tc = colours.white };\\\
  1934.     };\\\
  1935.     default = {\\\
  1936.         blocks = { };\\\
  1937.         words = { a = { colours.orange } };\\\
  1938.         default = { bc = colours.white, tc = colours.grey };\\\
  1939.         linen = { bc = colours.grey, tc = colours.lightGrey };\\\
  1940.         string = { };\\\
  1941.         symbols = { };\\\
  1942.         selection = { bc = colours.blue, tc = colours.white };\\\
  1943.     };\\\
  1944. }\\\
  1945. \\\
  1946. local taskbar = display:newChild( NovaUI.UIFrame( 1, 1, display.w, 1 ) )\\\
  1947. local tb = taskbar:newChild( NovaUI.UIButton( 1, 1, taskbar.w, 1, \\\"\\\" ) )\\\
  1948. tb.bc = colours.grey\\\
  1949. \\\
  1950. local codefield = display:newChild( NovaUI.UICode( 1, 2, display.w - 1, display.h - 2, syntax.lua ) )\\\
  1951. codefield:focusOn( )\\\
  1952. \\\
  1953. local scrollv = display:newChild( NovaUI.UIScrollBar( display.w, 2, 1, display.h - 2, codefield, \\\"vertical\\\" ) )\\\
  1954. local scrollh = display:newChild( NovaUI.UIScrollBar( 1, display.h, display.w - 1, 1, codefield, \\\"horizontal\\\" ) )\\\
  1955. local resizer = display:newChild( NovaUI.UIButton( display.w, display.h, 1, 1, \\\"@\\\" ) )\\\
  1956. resizer.bc = colours.lightGrey\\\
  1957. resizer.tc = colours.white\\\
  1958. local xx, yy\\\
  1959. function resizer:onClick( rx, ry, button )\\\
  1960.     if button == 1 then xx, yy = rx, ry end\\\
  1961. end\\\
  1962. function resizer:onDrag( rx, ry, cx, cy, button )\\\
  1963.     if button == 1 and xx then\\\
  1964.         window:resize( display.w + rx - 1, display.h + ry - 1 )\\\
  1965.     end\\\
  1966. end\\\
  1967. \\\
  1968. local save = {\\\
  1969.     path = false;\\\
  1970.     meta = false;\\\
  1971.     changed = false;\\\
  1972. }\\\
  1973. local activedropdown, activemenu\\\
  1974. local history, historyindex = { \\\"\\\" }, 1\\\
  1975. local menus = { }\\\
  1976. local asterix\\\
  1977. \\\
  1978. local function final( )\\\
  1979.     while history[historyindex + 1] do\\\
  1980.         table.remove( history, historyindex + 1 )\\\
  1981.     end\\\
  1982. end\\\
  1983. local function contentChanged( )\\\
  1984.     if save.changed then return end\\\
  1985.     save.changed = true\\\
  1986.     if Nova then\\\
  1987.         window.title = \\\"Edit* - \\\" .. ( save.path or \\\"unknown\\\" )\\\
  1988.     else\\\
  1989.         for k, v in pairs( menus ) do\\\
  1990.             v.x = v.x + 2\\\
  1991.         end\\\
  1992.         asterix = taskbar:newChild( NovaUI.UIText( 1, 1, 1, 1, \\\"*\\\" ) )\\\
  1993.         asterix.bc = 0\\\
  1994.         asterix.tc = 1\\\
  1995.     end\\\
  1996. end\\\
  1997. local function contentSaved( )\\\
  1998.     if not save.changed then return end\\\
  1999.     if asterix then\\\
  2000.         asterix:remove( )\\\
  2001.     end\\\
  2002.     save.changed = false\\\
  2003.     if Nova then\\\
  2004.         window.title = \\\"Edit - \\\" .. ( save.path or \\\"unknown\\\" )\\\
  2005.     else\\\
  2006.         for k, v in pairs( menus ) do\\\
  2007.             v.x = v.x - 2\\\
  2008.         end\\\
  2009.     end\\\
  2010. end\\\
  2011. \\\
  2012. local function copy( )\\\
  2013.     if codefield.selected then\\\
  2014.         local text = codefield:getSelection( )\\\
  2015.         Nova.clipboard.set( \\\"plaintext\\\", text )\\\
  2016.     end\\\
  2017. end\\\
  2018. local function cut( )\\\
  2019.     if codefield.selected then\\\
  2020.         local text = codefield:getSelection( )\\\
  2021.         Nova.clipboard.set( \\\"plaintext\\\", text )\\\
  2022.         codefield:setSelection \\\"\\\"\\\
  2023.         final( )\\\
  2024.         table.insert( history, codefield:getCode( ) )\\\
  2025.         historyindex = #history\\\
  2026.         contentChanged( )\\\
  2027.     end\\\
  2028. end\\\
  2029. local function paste( )\\\
  2030.     local mode, data = Nova.clipboard.get( )\\\
  2031.     if mode ~= \\\"plaintext\\\" then\\\
  2032.         return\\\
  2033.     end\\\
  2034.     if codefield.selected then\\\
  2035.         codefield:setSelection( data )\\\
  2036.     else\\\
  2037.         codefield:write( data )\\\
  2038.     end\\\
  2039.     final( )\\\
  2040.     table.insert( history, codefield:getCode( ) )\\\
  2041.     historyindex = #history\\\
  2042.     contentChanged( )\\\
  2043. end\\\
  2044. \\\
  2045. function codefield:onChange( mode )\\\
  2046.     if mode == \\\" \\\" or mode == \\\"\\\\n\\\" or mode == \\\" \\\" or mode == \\\"paste\\\" or mode == \\\"cut\\\" or mode == \\\"backspace\\\" or mode == \\\"delete\\\" then\\\
  2047.         final( )\\\
  2048.         table.insert( history, self:getCode( ) )\\\
  2049.     else\\\
  2050.         final( )\\\
  2051.         history[math.max( #history, 1 )] = self:getCode( )\\\
  2052.     end\\\
  2053.     historyindex = #history\\\
  2054.     contentChanged( )\\\
  2055. end\\\
  2056. \\\
  2057. local function undo( )\\\
  2058.     if history[historyindex-1] then\\\
  2059.         historyindex = historyindex - 1\\\
  2060.         codefield:setCode( history[historyindex] )\\\
  2061.         contentChanged( )\\\
  2062.     end\\\
  2063. end\\\
  2064. local function redo( )\\\
  2065.     if history[historyindex+1] then\\\
  2066.         historyindex = historyindex + 1\\\
  2067.         codefield:setCode( history[historyindex] )\\\
  2068.         contentChanged( )\\\
  2069.     end\\\
  2070. end\\\
  2071. \\\
  2072. local function savefile( wait )\\\
  2073.     newThread( function( )\\\
  2074.         if not save.path then\\\
  2075.             save.path = NovaUI.display.response( display, \\\"Where would you like to save?\\\" )\\\
  2076.         end\\\
  2077.         if not save.path then\\\
  2078.             return\\\
  2079.         end\\\
  2080.         if NovaFS then\\\
  2081.             local fd = NovaFS.FileData( codefield:getCode( ) )\\\
  2082.             fd.meta = save.meta or { }\\\
  2083.             NovaFS.writefile( save.path, fd )\\\
  2084.         else\\\
  2085.             local h = fs.open( save.path, \\\"w\\\" )\\\
  2086.             if h then\\\
  2087.                 h.write( codefield:getCode( ) )\\\
  2088.                 h.close( )\\\
  2089.             else\\\
  2090.                 NovaUI.display.alert( display, \\\"Could not save file!\\\" )\\\
  2091.             end\\\
  2092.         end\\\
  2093.         contentSaved( )\\\
  2094.     end, wait )\\\
  2095. end\\\
  2096. \\\
  2097. local function saved( )\\\
  2098.     if save.changed then\\\
  2099.         local result = NovaUI.display.confirm( display, \\\"You have unsaved changes, would you like to save?\\\" )\\\
  2100.         if result then\\\
  2101.             savefile( true )\\\
  2102.         end\\\
  2103.         if result == nil then\\\
  2104.             return false\\\
  2105.         end\\\
  2106.     end\\\
  2107.     return true\\\
  2108. end\\\
  2109. \\\
  2110. local function openfile( path, mode )\\\
  2111.     if not mode and NovaFS then\\\
  2112.         mode = NovaFS.getType( path )\\\
  2113.     elseif not mode then\\\
  2114.         mode = \\\"lua\\\"\\\
  2115.     end\\\
  2116.     save.path = path\\\
  2117.     local content, meta\\\
  2118.     if NovaFS then\\\
  2119.         local filedata, err = NovaFS.readfile( path )\\\
  2120.         if filedata then\\\
  2121.             content = filedata.content\\\
  2122.             meta = filedata.meta\\\
  2123.         else\\\
  2124.             NovaUI.display.alert( display, err )\\\
  2125.             return\\\
  2126.         end\\\
  2127.     else\\\
  2128.         local h = fs.open( path, \\\"r\\\" )\\\
  2129.         if h then\\\
  2130.             content = h.readAll( )\\\
  2131.             meta = { }\\\
  2132.             h.close( )\\\
  2133.         else\\\
  2134.             NovaUI.display.alert( display, \\\"could not open file\\\" )\\\
  2135.             return\\\
  2136.         end\\\
  2137.     end\\\
  2138.     if mode == \\\"lua\\\" or mode == \\\"lib\\\" then\\\
  2139.         codefield.syntax = syntax.lua\\\
  2140.     elseif mode == \\\"class\\\" then\\\
  2141.         local s = copytable( syntax.lua )\\\
  2142.         local name = path:gsub( \\\"^.+/\\\", \\\"\\\" ) -- remove directories\\\
  2143.         if name:sub( 2 ):find \\\"%.\\\" then\\\
  2144.             name = name:sub( 1, 1 ) .. name:sub( 2 ):gsub( \\\"%.%w+$\\\", \\\"\\\" ) -- remove extension\\\
  2145.         end\\\
  2146.         s.words[name] = { tc = colours.cyan }\\\
  2147.         s.words[\\\"extends\\\"] = { tc = colours.lightGrey }\\\
  2148.         s.words[\\\"public\\\"] = { tc = colours.lightGrey }\\\
  2149.         codefield.syntax = s\\\
  2150.     else\\\
  2151.         codefield.syntax = syntax.default\\\
  2152.     end\\\
  2153.     save.meta = meta\\\
  2154.     codefield:setCode( content )\\\
  2155.     contentSaved( )\\\
  2156.     history = { content }\\\
  2157.     historyindex = 1\\\
  2158.     if Nova then\\\
  2159.         window.title = \\\"Edit - \\\" .. path\\\
  2160.     end\\\
  2161. end\\\
  2162. \\\
  2163. if Nova then\\\
  2164.     function Nova.app.callback.onResize( w, h )\\\
  2165.         taskbar.w = w\\\
  2166.         tb.w = w\\\
  2167.         codefield.w, codefield.h = w - 1, h - 2\\\
  2168.         scrollv.x = w\\\
  2169.         scrollv.h = h - 2\\\
  2170.         scrollh.y = h\\\
  2171.         scrollh.w = w - 1\\\
  2172.         resizer.x, resizer.y = w, h\\\
  2173.     end\\\
  2174.     function Nova.app.callback.canClose( reason )\\\
  2175.         if ( reason == \\\"user\\\" or reason == \\\"terminate\\\" ) and save.changed then\\\
  2176.             newThread( function( )\\\
  2177.                 if saved( ) then\\\
  2178.                     Nova.app.close( )\\\
  2179.                 end\\\
  2180.             end )\\\
  2181.             return false\\\
  2182.         end\\\
  2183.         return true\\\
  2184.     end\\\
  2185. end\\\
  2186. \\\
  2187. menus.file = taskbar:newChild( NovaUI.UIButton( 1, 1, 4, 1, \\\"File\\\" ) )\\\
  2188. menus.edit = taskbar:newChild( NovaUI.UIButton( 7, 1, 4, 1, \\\"Edit\\\" ) )\\\
  2189. menus.syntax = taskbar:newChild( NovaUI.UIButton( 13, 1, 6, 1, \\\"Syntax\\\" ) )\\\
  2190. menus.debug = taskbar:newChild( NovaUI.UIButton( 21, 1, 5, 1, \\\"Debug\\\" ) )\\\
  2191. local menuoptions = { }\\\
  2192. menuoptions.file = {\\\
  2193.     width = 10;\\\
  2194.     height = 8;\\\
  2195.     spacing = false;\\\
  2196.     shadow = colours.grey;\\\
  2197.     { type = \\\"button\\\", name = \\\"New\\\", onClick = function( )\\\
  2198.         activedropdown:remove( )\\\
  2199.         if activemenu then activemenu.tc = colours.cyan activemenu = nil end\\\
  2200.         newThread( function( )\\\
  2201.             if saved( ) then\\\
  2202.                 codefield:setCode \\\"\\\"\\\
  2203.                 history = { \\\"\\\", \\\"\\\" }\\\
  2204.                 historyindex = 1\\\
  2205.                 contentChanged( )\\\
  2206.             end\\\
  2207.         end )\\\
  2208.     end };\\\
  2209.     { type = \\\"button\\\", name = \\\"Open\\\", onClick = function( )\\\
  2210.         activedropdown:remove( )\\\
  2211.         if activemenu then activemenu.tc = colours.cyan activemenu = nil end\\\
  2212.         newThread( function( )\\\
  2213.             if saved( ) then\\\
  2214.                 local path = NovaUI.display.response( display, \\\"Path to open from\\\" )\\\
  2215.                 if path then\\\
  2216.                     openfile( path )\\\
  2217.                 end\\\
  2218.             end\\\
  2219.         end )\\\
  2220.     end };\\\
  2221.     \\\"rule\\\";\\\
  2222.     { type = \\\"button\\\", name = \\\"Save\\\", onClick = function( )\\\
  2223.         activedropdown:remove( )\\\
  2224.         if activemenu then activemenu.tc = colours.cyan activemenu = nil end\\\
  2225.         savefile( )\\\
  2226.     end };\\\
  2227.     { type = \\\"button\\\", name = \\\"Save As\\\", onClick = function( )\\\
  2228.         activedropdown:remove( )\\\
  2229.         newThread( function( )\\\
  2230.             local path = NovaUI.display.response( display, \\\"Path to save as\\\" )\\\
  2231.             if path then\\\
  2232.                 savepath = path\\\
  2233.                 savefile( )\\\
  2234.             end\\\
  2235.         end )\\\
  2236.     end };\\\
  2237.     \\\"rule\\\";\\\
  2238.     { type = \\\"button\\\", name = \\\"Exit\\\", onClick = function( )\\\
  2239.         activedropdown:remove( )\\\
  2240.         if activemenu then activemenu.tc = colours.cyan activemenu = nil end\\\
  2241.         newThread( function( )\\\
  2242.             if saved( ) then\\\
  2243.                 if Nova then\\\
  2244.                     Nova.app.close( )\\\
  2245.                 else\\\
  2246.                     running = false\\\
  2247.                 end\\\
  2248.             end\\\
  2249.         end )\\\
  2250.     end };\\\
  2251. }\\\
  2252. menuoptions.edit = {\\\
  2253.     width = 9;\\\
  2254.     height = 8;\\\
  2255.     spacing = false;\\\
  2256.     shadow = colours.grey;\\\
  2257.     { type = \\\"button\\\", name = \\\"Undo\\\", onClick = function( )\\\
  2258.         undo( )\\\
  2259.         activedropdown:remove( )\\\
  2260.         if activemenu then activemenu.tc = colours.cyan activemenu = nil end\\\
  2261.     end };\\\
  2262.     { type = \\\"button\\\", name = \\\"Redo\\\", onClick = function( )\\\
  2263.         redo( )\\\
  2264.         activedropdown:remove( )\\\
  2265.         if activemenu then activemenu.tc = colours.cyan activemenu = nil end\\\
  2266.     end };\\\
  2267.     \\\"rule\\\";\\\
  2268.     { type = \\\"button\\\", name = \\\"Copy\\\", onClick = function( )\\\
  2269.         copy( )\\\
  2270.         activedropdown:remove( )\\\
  2271.         if activemenu then activemenu.tc = colours.cyan activemenu = nil end\\\
  2272.     end };\\\
  2273.     { type = \\\"button\\\", name = \\\"Cut\\\", onClick = function( )\\\
  2274.         cut( )\\\
  2275.         activedropdown:remove( )\\\
  2276.         if activemenu then activemenu.tc = colours.cyan activemenu = nil end\\\
  2277.     end };\\\
  2278.     \\\"rule\\\";\\\
  2279.     { type = \\\"button\\\", name = \\\"Paste\\\", onClick = function( )\\\
  2280.         paste( )\\\
  2281.         activedropdown:remove( )\\\
  2282.         if activemenu then activemenu.tc = colours.cyan activemenu = nil end\\\
  2283.     end };\\\
  2284. }\\\
  2285. menuoptions.syntax = {\\\
  2286.     width = 12;\\\
  2287.     height = 3;\\\
  2288.     spacing = false;\\\
  2289.     shadow = colours.grey;\\\
  2290.     { type = \\\"button\\\", name = \\\"Plain text\\\", onClick = function( )\\\
  2291.         codefield.syntax = syntax.default\\\
  2292.         codefield:updateCharacters( )\\\
  2293.         activedropdown:remove( )\\\
  2294.         if activemenu then activemenu.tc = colours.cyan activemenu = nil end\\\
  2295.     end };\\\
  2296.     { type = \\\"button\\\", name = \\\"Lua\\\", onClick = function( )\\\
  2297.         codefield.syntax = syntax.lua\\\
  2298.         codefield:updateCharacters( )\\\
  2299.         activedropdown:remove( )\\\
  2300.         if activemenu then activemenu.tc = colours.cyan activemenu = nil end\\\
  2301.     end };\\\
  2302. }\\\
  2303. menuoptions.debug = {\\\
  2304.     width = 18;\\\
  2305.     height = 5;\\\
  2306.     spacing = false;\\\
  2307.     shadow = colours.grey;\\\
  2308.     { type = \\\"button\\\", name = \\\"Run\\\", onClick = function( )\\\
  2309.         activedropdown:remove( )\\\
  2310.         if activemenu then activemenu.tc = colours.cyan activemenu = nil end\\\
  2311.         newThread( function( )\\\
  2312.             local f, err = loadstring( codefield:getCode( ), \\\"program\\\" )\\\
  2313.             if f then\\\
  2314.                 local frame = display:newChild( NovaUI.UIFrame( 1, 1, display.w, display.h ) )\\\
  2315.                 local title = frame:newChild( NovaUI.UIText( 1, 1, display.w - 5, 1, \\\"Running...\\\" ) ) title.bc = colours.black title.tc = colours.white\\\
  2316.                 local close = frame:newChild( NovaUI.UIButton( display.w - 4, 1, 5, 1, \\\"close\\\" ) ) close.bc = colours.black close.tc = colours.red\\\
  2317.                 function close:onClick( ) frame:remove( ) end\\\
  2318.                 local canvas = frame:newChild( NovaUI.UIBuffer( 1, 2, display.w, display.h - 1 ) ) canvas:setTask( f ) canvas:passEvent( )\\\
  2319.             else\\\
  2320.                 NovaUI.display.help( display, \\\"Syntax error\\\", err:gsub( \\\"main:433: \\\", \\\"\\\" ) )\\\
  2321.             end\\\
  2322.         end )\\\
  2323.     end };\\\
  2324.     { type = \\\"button\\\", name = \\\"Run with params\\\", onClick = function( )\\\
  2325.         activedropdown:remove( )\\\
  2326.         if activemenu then activemenu.tc = colours.cyan activemenu = nil end\\\
  2327.         newThread( function( )\\\
  2328.             local r = NovaUI.display.response( display, \\\"Please enter params, separated by a space.\\\" )\\\
  2329.             local p = { }\\\
  2330.             if r then\\\
  2331.                 local last = 1\\\
  2332.                 for i = 1, #r do\\\
  2333.                     if r:sub( i, i ) == \\\" \\\" then\\\
  2334.                         p[#p+1] = r:sub( last, i - 1 )\\\
  2335.                         last = i + 1\\\
  2336.                     end\\\
  2337.                 end\\\
  2338.                 p[#p+1] = r:sub( last )\\\
  2339.             end\\\
  2340.             local f, err = loadstring( codefield:getCode( ), \\\"program\\\" )\\\
  2341.             if f then\\\
  2342.                 local frame = display:newChild( NovaUI.UIFrame( 1, 1, display.w, display.h ) )\\\
  2343.                 local title = frame:newChild( NovaUI.UIText( 1, 1, display.w - 5, 1, \\\"Running...\\\" ) ) title.bc = colours.black title.tc = colours.white\\\
  2344.                 local close = frame:newChild( NovaUI.UIButton( display.w - 4, 1, 5, 1, \\\"close\\\" ) ) close.bc = colours.black close.tc = colours.red\\\
  2345.                 function close:onClick( ) frame:remove( ) end\\\
  2346.                 local canvas = frame:newChild( NovaUI.UIBuffer( 1, 2, display.w, display.h - 1 ) ) canvas:setTask( f ) canvas:passEvent( unpack( p ) )\\\
  2347.             else\\\
  2348.                 NovaUI.display.help( display, \\\"Syntax error\\\", err:gsub( \\\"main:461: \\\", \\\"\\\" ) )\\\
  2349.             end\\\
  2350.         end )\\\
  2351.     end };\\\
  2352.     \\\"rule\\\";\\\
  2353.     { type = \\\"button\\\", name = \\\"Check syntax\\\", onClick = function( )\\\
  2354.         activedropdown:remove( )\\\
  2355.         if activemenu then activemenu.tc = colours.cyan activemenu = nil end\\\
  2356.         local f, err = loadstring( codefield:getCode( ), \\\"program\\\" )\\\
  2357.         if f then\\\
  2358.             NovaUI.display.alert( display, \\\"Everything looks good!\\\" )\\\
  2359.         else\\\
  2360.             NovaUI.display.help( display, \\\"Syntax error\\\", err:gsub( \\\"main:477: \\\", \\\"\\\" ) )\\\
  2361.         end\\\
  2362.     end };\\\
  2363. }\\\
  2364. \\\
  2365. for k, v in pairs( menus ) do\\\
  2366.     v.bc = 0\\\
  2367.     v.tc = colours.cyan\\\
  2368.     function v:onClick( )\\\
  2369.         if activemenu == v then\\\
  2370.             activedropdown:remove( )\\\
  2371.             activemenu.tc = colours.cyan\\\
  2372.             activemenu = nil\\\
  2373.             return\\\
  2374.         end\\\
  2375.         activemenu = v\\\
  2376.         v.tc = colours.lightBlue\\\
  2377.         local frame = display:newChild( NovaUI.UIFrame( 1, 1, display.w, display.h ) )\\\
  2378.         activedropdown = frame\\\
  2379.         local close = frame:newChild( NovaUI.UIButton( 1, 1, display.w, display.h, \\\"\\\" ) )\\\
  2380.         close.bc = 0\\\
  2381.         close.align = false\\\
  2382.         function close:onClick( x, y, button )\\\
  2383.             v.tc = colours.cyan\\\
  2384.             frame:remove( )\\\
  2385.             if y == 1 then\\\
  2386.                 if Nova then\\\
  2387.                     os.queueEvent( \\\"mouse_click\\\", button, window.x + x - 1, window.y + 1 )\\\
  2388.                 else\\\
  2389.                     os.queueEvent( \\\"mouse_click\\\", button, x, 1 )\\\
  2390.                 end\\\
  2391.             else\\\
  2392.                 activemenu = nil\\\
  2393.             end\\\
  2394.         end\\\
  2395.         local miniframe = frame:newChild( NovaUI.UIFrame( v.x, v.y + 1, 0, 0 ) )\\\
  2396.         NovaUI.display.menu( miniframe, menuoptions[k] )\\\
  2397.         if miniframe.x + miniframe.w > frame.w then\\\
  2398.             miniframe.x = frame.w - miniframe.w + 1\\\
  2399.         end\\\
  2400.     end\\\
  2401. end\\\
  2402. \\\
  2403. local keyhandler = display:newChild( NovaUI.UIKeyHandler( ) )\\\
  2404. \\\
  2405. function keyhandler:onKey( key, lastkey )\\\
  2406.     if lastkey == 29 then\\\
  2407.         if key == 31 then -- ctrl-s\\\
  2408.             savefile( )\\\
  2409.         elseif key == 24 then -- ctrl-o\\\
  2410.             newThread( function( )\\\
  2411.                 if saved( ) then\\\
  2412.                     local path = NovaUI.display.response( display, \\\"Path to open from\\\" )\\\
  2413.                     if path then\\\
  2414.                         openfile( path )\\\
  2415.                     end\\\
  2416.                 end\\\
  2417.             end )\\\
  2418.         elseif key == keys.z then\\\
  2419.             undo( )\\\
  2420.         elseif key == keys.y then\\\
  2421.             redo( )\\\
  2422.         end\\\
  2423.     end\\\
  2424. end\\\
  2425. function codefield:onCtrlKey( key )\\\
  2426.     if key == 31 then -- ctrl-s\\\
  2427.         savefile( )\\\
  2428.     elseif key == 24 then -- ctrl-o\\\
  2429.         newThread( function( )\\\
  2430.             if saved( ) then\\\
  2431.                 local path = NovaUI.display.response( display, \\\"Path to open from\\\" )\\\
  2432.                 if path then\\\
  2433.                     openfile( path )\\\
  2434.                 end\\\
  2435.             end\\\
  2436.         end )\\\
  2437.     elseif key == keys.z then\\\
  2438.         undo( )\\\
  2439.     elseif key == keys.y then\\\
  2440.         redo( )\\\
  2441.     end\\\
  2442. end\\\
  2443. \\\
  2444. if ARGS[1] then\\\
  2445.     openfile( ARGS[1], ARGS[2] )\\\
  2446. end\\\
  2447. \\\
  2448. if not Nova then\\\
  2449.     local function update( event, dt )\\\
  2450.         if event[1] ~= \\\"update\\\" then\\\
  2451.             display:event( event )\\\
  2452.             for i = #threads, 1, -1 do\\\
  2453.                 local ok, err = coroutine.resume( threads[i] )\\\
  2454.                 if not ok or coroutine.status( threads[i] ) == \\\"dead\\\" then\\\
  2455.                     table.remove( threads, i )\\\
  2456.                 end\\\
  2457.             end\\\
  2458.         else\\\
  2459.             display:update( event[2] )\\\
  2460.             display:draw( )\\\
  2461.             NovaUI.buffer.drawChanges( )\\\
  2462.             NovaUI.buffer.clear( )\\\
  2463.         end\\\
  2464.     end\\\
  2465. \\\
  2466.     local ok, err = pcall( function( )\\\
  2467.         local time = os.clock( )\\\
  2468.         local timer = os.startTimer( 0 )\\\
  2469.         os.queueEvent \\\"start\\\"\\\
  2470.         while running do\\\
  2471.             local ev = { coroutine.yield( ) }\\\
  2472.             local dt = os.clock( ) - time\\\
  2473.             time = os.clock( )\\\
  2474.             if ev[1] == \\\"timer\\\" and ev[2] == timer then\\\
  2475.                 update( { \\\"update\\\" }, dt )\\\
  2476.                 timer = os.startTimer( 0.05 )\\\
  2477.             else\\\
  2478.                 update( ev, dt )\\\
  2479.             end\\\
  2480.         end\\\
  2481.     end )\\\
  2482.     if not ok then\\\
  2483.         print( err )\\\
  2484.     end\\\
  2485.     term.setBackgroundColour( colours.black )\\\
  2486.     term.scroll( 1 )\\\
  2487.     term.setCursorPos( 1, ({ term.getSize( ) })[2] )\\\
  2488.     term.setTextColour( colours.blue )\\\
  2489.     print \\\"Thank you for using Nova Edit\\\"\\\
  2490. end\",\
  2491. [\"appconfig.txt\"]=\"runmode = \\\"app\\\";\\\
  2492. size = \\\"full\\\";\\\
  2493. handles = {\\\
  2494.     \\\"text\\\";\\\
  2495.     \\\"lua\\\";\\\
  2496.     \\\"unknown\\\";\\\
  2497.     \\\"lib\\\";\\\
  2498.     \\\"class\\\";\\\
  2499. };\",\
  2500. }\
  2501. ,\
  2502. }\
  2503. \
  2504. function ttf( table, dir )\
  2505.     if not fs.isDir( dir ) then\
  2506.         fs.makeDir( dir )\
  2507.     end\
  2508.     for k, v in pairs( table ) do\
  2509.         if type( v ) == \"table\" then\
  2510.             ttf( v, dir..\"/\"..k )\
  2511.         elseif type( v ) == \"string\" then\
  2512.             local f = fs.open( dir..\"/\"..k, \"w\" )\
  2513.             f.write( v )\
  2514.             f.close( )\
  2515.         end\
  2516.     end\
  2517. end\
  2518. return function( path )\
  2519.     ttf( pack, path )\
  2520. end", meta={
  2521.   type = "lib",
  2522. }};["main"]={content="\
  2523. require \"clipboard\"\
  2524. require \"core\"\
  2525. require \"encryption\"\
  2526. require \"login\"\
  2527. require \"sha256\"\
  2528. require \"stringutils\"\
  2529. \
  2530. local w, h = term.getSize( )\
  2531. \
  2532. if _G.NovaRunning then\
  2533.     return\
  2534. end\
  2535. _G.NovaRunning = true\
  2536. \
  2537. if not term.isColour( ) then\
  2538.     error( \"Nova requires an advanced computer\", 0 )\
  2539. end\
  2540. \
  2541. term.setBackgroundColour( colours.black )\
  2542. term.clear( )\
  2543. sleep( 0.1 )\
  2544. term.setBackgroundColour( colours.grey )\
  2545. term.clear( )\
  2546. sleep( 0.1 )\
  2547. term.setBackgroundColour( colours.lightGrey )\
  2548. term.clear( )\
  2549. sleep( 0.1 )\
  2550. \
  2551. local kernelLoader = Process \"kernelLoader\"\
  2552. kernelLoader:newThread( function( )\
  2553.     require \"kernel\"\
  2554.     while not NovaFS.updateIndex( ) do end\
  2555.     kernelLoader:stop( )\
  2556. end )\
  2557. \
  2558. NovaNet.com.updateModems( )\
  2559. \
  2560. local function update( event, dt )\
  2561.     if event[1] == \"update\" then\
  2562.         Process.update( { \"update\", dt } )\
  2563.         NovaUI.buffer.drawChanges( )\
  2564.         NovaUI.buffer.clear( )\
  2565.         NovaFS.updateIndex( )\
  2566.     else\
  2567.         NovaNet.com.listen( event )\
  2568.         NovaNet.com.clear( )\
  2569.         Process.update( { unpack( event ) }, true )\
  2570.         NovaNet.clearBuffers( )\
  2571.     end\
  2572.     NovaFS.clearCache( )\
  2573. end\
  2574. \
  2575. ok, err = pcall( function( )\
  2576.     local time = os.clock( )\
  2577.     local timer = os.startTimer( 0 )\
  2578.     os.queueEvent \"start\"\
  2579.     while core.running and not core.error_message do\
  2580.         local ev = { coroutine.yield( ) }\
  2581.         local dt = os.clock( ) - time\
  2582.         time = os.clock( )\
  2583.         if ev[1] == \"timer\" and ev[2] == timer then\
  2584.             update( { \"update\" }, dt )\
  2585.             timer = os.startTimer( 0.05 )\
  2586.         else\
  2587.             update( ev, dt )\
  2588.         end\
  2589.     end\
  2590. end )\
  2591. \
  2592. core.finish( )\
  2593. \
  2594. if not ok or core.error_message then\
  2595.     core.log( \"fatal error\", core.error_message or err )\
  2596.     local w, h = term.getSize( )\
  2597.     term.setBackgroundColour( colours.blue )\
  2598.     term.clear( )\
  2599.     term.setTextColour( colours.white )\
  2600.     term.setCursorPos( 1, 1 )\
  2601.     term.write \"Nova has encountered a fatal error.\"\
  2602.     term.setCursorPos( 1, 2 )\
  2603.     print \"Please report this message to the creator\\n\"\
  2604.     term.setTextColour( colours.white )\
  2605.     print( core.error_message or err )\
  2606.     parallel.waitForAny( function( )\
  2607.         os.pullEvent \"key\"\
  2608.     end, function( )\
  2609.         os.pullEvent \"mouse_click\"\
  2610.     end )\
  2611. end", meta={}};["AppManager"]={content="\
  2612. AppManager.public \"app_paths\"\
  2613. AppManager.public.app_paths.write = false\
  2614. \
  2615. function AppManager:AppManager( session )\
  2616.     self.apps = { }\
  2617.     self.app_paths = { }\
  2618.     self.instances = { }\
  2619.     self.processes = { }\
  2620. \
  2621.     self.session = session\
  2622. \
  2623.     local t = fs.list( core.path .. \"/apps\" )\
  2624.     for i = 1, #t do\
  2625.         t[i] = core.path .. \"/apps/\" .. t[i]\
  2626.     end\
  2627.     for _, p in pairs( fs.list( session.account.userpath .. \"apps\" ) ) do\
  2628.         t[#t+1] = session.account.userpath .. \"apps/\" .. p\
  2629.     end\
  2630. \
  2631.     for i = 1, #t do\
  2632.         self.public:register( t[i] )\
  2633.     end\
  2634. \
  2635.     session.process:newThread( function( )\
  2636.         while true do\
  2637.             for i = #self.instances, 1, -1 do\
  2638.                 if not self.instances[i].process:isRunning( ) and not self.instances[i].process:isPaused( ) then\
  2639.                     self.instances[i]:close \"process stopped\"\
  2640.                 end\
  2641.                 if not self.instances[i].running then\
  2642.                     table.remove( self.instances, i )\
  2643.                 end\
  2644.             end\
  2645.             for i = #self.processes, 1, -1 do\
  2646.                 if not self.processes[i]:isRunning( ) and not self.processes[i]:isPaused( ) then\
  2647.                     table.remove( self.processes, i )\
  2648.                 end\
  2649.             end\
  2650.             coroutine.yield( )\
  2651.         end\
  2652.     end )\
  2653. \
  2654.     return self.public\
  2655. end\
  2656. \
  2657. function AppManager.public:register( path )\
  2658.     local app, err = App( self.session, path )\
  2659.     if app then\
  2660.         self.apps[app.name] = app\
  2661.         self.app_paths[path] = app\
  2662.     else\
  2663.         self.session.process:newThread( function( )\
  2664.             local window = self.session.windowManager:newWindow \"alert\"\
  2665.             window.title = NovaFS.getName( path, true )\
  2666.             NovaUI.display.alert( window.display, err, true )\
  2667.             window:close( )\
  2668.         end )\
  2669.     end\
  2670. end\
  2671. \
  2672. function AppManager.public:launch( name, args )\
  2673.     if self.apps[name] then\
  2674.         local instance = self.apps[name]:launch( args )\
  2675.         table.insert( self.instances, instance )\
  2676.         return instance\
  2677.     end\
  2678. end\
  2679. \
  2680. function AppManager.public:newProcess( name )\
  2681.     local process = Process( name )\
  2682.     table.insert( self.processes, process )\
  2683.     return process\
  2684. end\
  2685. \
  2686. function AppManager.public:stop( reason )\
  2687.     for i = 1, #self.instances do\
  2688.         self.instances[i]:close( reason )\
  2689.     end\
  2690.     for i = 1, #self.processes do\
  2691.         self.processes[i]:stop( )\
  2692.     end\
  2693. end", meta={
  2694.   type = "class",
  2695. }};["Sandbox"]={content="function self.environment.Nova.app.register( path )\
  2696.     local a, err = instance.session:registerApp( path )\
  2697.     if not a then\
  2698.         return false, err\
  2699.     end\
  2700.     return true\
  2701. end\
  2702. function self.environment.Nova.app.listNames( )\
  2703.     local t = { }\
  2704.     for k, v in pairs( instance.session.apps ) do\
  2705.         table.insert( t, k )\
  2706.     end\
  2707.     return t\
  2708. end\
  2709. function self.environment.Nova.app.getInstallPath( name )\
  2710.     if instance.session.apps[name] then\
  2711.         return instance.session.apps[name].installpath\
  2712.     end\
  2713.     return false\
  2714. end\
  2715. function self.environment.Nova.app.getIcon( name )\
  2716.     if instance.session.apps[name] then\
  2717.         return instance.session.apps[name].icon\
  2718.     end\
  2719.     return false\
  2720. end\
  2721. function self.environment.Nova.app.require( file, ... )\
  2722.     file = file:gsub( \"%.\", \"/\" )\
  2723.     local content = instance.app:readFile( tostring( file ) .. \".lua\" )\
  2724.     if content then\
  2725.         local f, err = loadstring( content, filesystem.getName( file ) )\
  2726.         if f then\
  2727.             local t = instance:newThread( Thread( f ) )\
  2728.             return t\
  2729.         else\
  2730.             instance:showError( err:gsub( \"Sandbox.lua:103: \", \"\" ) )\
  2731.         end\
  2732.     else\
  2733.         instance:showError \"no such file\"\
  2734.     end\
  2735. end\
  2736. \
  2737. function self.environment.Nova.user.getName( )\
  2738.     return instance.app.session.account.username\
  2739. end\
  2740. function self.environment.Nova.user.rename( name )\
  2741.     return instance.app.session.account:rename( name )\
  2742. end\
  2743. function self.environment.Nova.user.changePassword( pass )\
  2744.     return instance.app.session.account:changePassword( pass )\
  2745. end\
  2746. function self.environment.Nova.user.logout( )\
  2747.     Thread( function( )\
  2748.         instance.app.session:logout( )\
  2749.         core.session = Session( )\
  2750.     end )\
  2751. end\
  2752. function self.environment.Nova.user.delete( )\
  2753.     Thread( function( )\
  2754.         fs.delete( instance.app.session.account.userpath )\
  2755.         instance.app.session:logout( )\
  2756.         core.session = Session( )\
  2757.     end )\
  2758. end\
  2759. function self.environment.Nova.user.create( name, pass )\
  2760.     return Account.create( name, pass )\
  2761. end\
  2762. function self.environment.Nova.user.switch( name, pass )\
  2763.     Thread( function( )\
  2764.         instance.app.session:logout( )\
  2765.         core.session = Session( name, pass )\
  2766.     end )\
  2767. end", meta={}};["core"]={content="\
  2768. require \"appttf\"\
  2769. require \"libttf\"\
  2770. \
  2771. name = \"Nova Horizon\"\
  2772. version_major = 1\
  2773. version_minor = 3\
  2774. version_patch = 0\
  2775. version = (\"%d.%d.%02d\"):format( version_major, version_minor, version_patch )\
  2776. author = \"Benedict Allen\"\
  2777. \
  2778. platform = \"computer\"\
  2779. local w, h = term.getSize( )\
  2780. if turtle then\
  2781.     platform = \"turtle\"\
  2782. elseif pocket or w < 39 then\
  2783.     platform = \"pocket\"\
  2784. end\
  2785. \
  2786. running = true\
  2787. path = \"Nova\"\
  2788. \
  2789. session = false\
  2790. UI = false\
  2791. \
  2792. if not fs.isDir( path ) then\
  2793.     fs.makeDir( path )\
  2794. end\
  2795. if not fs.isDir( path .. \"/apps\" ) then\
  2796.     fs.makeDir( path .. \"/apps\" )\
  2797.     appttf( path .. \"/apps\" )\
  2798. end\
  2799. if not fs.isDir( path .. \"/user\" ) then\
  2800.     fs.makeDir( path .. \"/user\" )\
  2801. end\
  2802. if not fs.isDir( path .. \"/lib\" ) then\
  2803.     fs.makeDir( path .. \"/lib\" )\
  2804.     libttf( path .. \"/lib\" )\
  2805. end\
  2806. \
  2807. start_time = os.clock( )\
  2808. \
  2809. error_message = false\
  2810. \
  2811. function error( message )\
  2812.     core.error_message = message\
  2813. end\
  2814. \
  2815. local file = path .. \"/log.txt\"\
  2816. local h = fs.open( file, \"w\" )\
  2817. if h then\
  2818.     h.close( )\
  2819. end\
  2820. \
  2821. function log( pre, ... )\
  2822.     local data\
  2823.     if ... then\
  2824.         local t = { ... }\
  2825.         data = tostring( t[1] )\
  2826.         for i = 2, #t do\
  2827.             data = data .. \", \" .. tostring( t[i] )\
  2828.         end\
  2829.     end\
  2830.     local h = fs.open( file, \"a\" )\
  2831.     if h then\
  2832.         local time = \"[\" .. os.clock( ) - start_time .. \"] \"\
  2833.         if data then\
  2834.             h.write( time .. tostring( pre ) .. \": \" .. textutils.serialize( data ) .. \"\\n\" )\
  2835.         else\
  2836.             h.write( time .. tostring( pre ) .. \"\\n\" )\
  2837.         end\
  2838.         h.close( )\
  2839.         return true\
  2840.     end\
  2841.     return false\
  2842. end\
  2843. \
  2844. function getRunTime( )\
  2845.     return os.clock( ) - start_time\
  2846. end\
  2847. \
  2848. function finish( )\
  2849.     if core.session then\
  2850.         core.session:logout( )\
  2851.     end\
  2852.     core.log \"stopping\"\
  2853.     _G.NovaRunning = false\
  2854. end\
  2855. \
  2856. function getUpdateInfo( )\
  2857.     local t = {\
  2858.         name = \"Nova Horizon\";\
  2859.         version_major = version_major;\
  2860.         version_minor = version_minor;\
  2861.         version_patch = version_patch;\
  2862.     }\
  2863.     pcall( function( )\
  2864.         local h = http.get \"http://pastebin.com/raw.php?i=K8MKteNu\"\
  2865.         if h then\
  2866.             t = textutils.unserialize( h.readAll( ) ) or t\
  2867.             h.close( )\
  2868.         end\
  2869.     end )\
  2870.     return t\
  2871. end\
  2872. \
  2873. function compareVersions( t )\
  2874.     t = t or getUpdateInfo( )\
  2875.     if t.version_major > version_major then\
  2876.         return true\
  2877.     elseif t.version_major == version_major then\
  2878.         if t.version_minor > version_minor then\
  2879.             return true\
  2880.         elseif t.version_minor == version_minor then\
  2881.             return t.version_patch > version_patch\
  2882.         end\
  2883.     end\
  2884.     return false\
  2885. end\
  2886. \
  2887. function update( updateInfo )\
  2888.     updateInfo = updateInfo or getUpdateInfo( )\
  2889.     local h = http.get( \"http://pastebin.com/raw.php?i=\" .. updateInfo.link )\
  2890.     if h then\
  2891.         if type( updateInfo.pre_update ) == \"function\" then\
  2892.             setfenv( updateInfo.pre_update, getfenv( ) )\
  2893.             updateInfo.pre_update( )\
  2894.         end\
  2895.         local content = h.readAll( )\
  2896.         h.close( )\
  2897.         filesystem.delete( \"C:/\" .. path .. \"/apps\" )\
  2898.         filesystem.delete \"C:/startup\"\
  2899.         filesystem.writefile( \"startup\", FileData( content ) )\
  2900.         if type( updateInfo.post_update ) == \"function\" then\
  2901.             setfenv( updateInfo.post_update, getfenv( ) )\
  2902.             updateInfo.post_update( )\
  2903.         end\
  2904.         return true\
  2905.     end\
  2906.     return false\
  2907. end\
  2908. \
  2909. -- Credits to lbphacker for code below\
  2910. 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\
  2911. --time since last update\
  2912. local firstepoch, firstclock, tslu\
  2913. function getirltime(gmtoffset)\
  2914.     if not firstepoch or os.clock( ) - tslu > 300 then\
  2915.         pcall( function( )\
  2916.             local httpResponseHandle = http.get \"http://lbphacker.hu/cctime.php\"\
  2917.             if not httpResponseHandle then\
  2918.                 return false\
  2919.             end\
  2920.             firstepoch = tonumber(httpResponseHandle.readAll())\
  2921.             if not firstepoch then\
  2922.                 return false\
  2923.             end\
  2924.             firstclock = os.clock()\
  2925.             httpResponseHandle.close()\
  2926.             tslu = os.clock( )\
  2927.         end )\
  2928.     end\
  2929.     if firstepoch then\
  2930.         local y, j, m, d, w, h, n, s = gmtime(firstepoch + math.floor(os.clock() - firstclock) + (gmtoffset or 0) * 3600)\
  2931.         return {h = h, m = n, s = s}\
  2932.     end\
  2933.     return { h = math.floor( os.time( ) / 60 ), m = os.time( ) % 60, s = 0 }\
  2934. end\
  2935. \
  2936. local months = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }\
  2937. \
  2938. function getirldate(gmtoffset)\
  2939.     if not firstepoch or os.clock( ) - tslu > 300 then\
  2940.         pcall( function( )\
  2941.             local httpResponseHandle = http.get \"http://lbphacker.hu/cctime.php\"\
  2942.             if not httpResponseHandle then\
  2943.                 return false\
  2944.             end\
  2945.             firstepoch = tonumber(httpResponseHandle.readAll())\
  2946.             if not firstepoch then\
  2947.                 return false\
  2948.             end\
  2949.             firstclock = os.clock()\
  2950.             httpResponseHandle.close()\
  2951.             tslu = os.clock( )\
  2952.         end )\
  2953.     end\
  2954.     if firstepoch then\
  2955.         local y, j, m, d, w, h, n, s = gmtime(firstepoch + math.floor(os.clock() - firstclock) + (gmtoffset or 0) * 3600)\
  2956.         return {y = y, j = j, m = m, d = d, w = w}\
  2957.     end\
  2958.     local day = os.day( )\
  2959.     local month = 12\
  2960.     local year = math.floor( day / 365 )\
  2961.     day = day - year * 365\
  2962.     for i = 1, #months do\
  2963.         if day < months[i] then\
  2964.             month = i\
  2965.             break\
  2966.         else\
  2967.             day = day - months[i]\
  2968.         end\
  2969.     end\
  2970.     return { y = year, j = 0, m = month, d = day, w = 0 }\
  2971. end", meta={
  2972.   type = "lib",
  2973. }};["ServiceAppInstance"]={content="--@meta[type]:\"class\"", meta={}};["Account"]={content="\
  2974. Account.public \"username\"\
  2975. Account.public.username.write = false\
  2976. Account.public \"password\"\
  2977. Account.public.password.write = false\
  2978. Account.public \"shaPassword\"\
  2979. Account.public.shaPassword.write = false\
  2980. Account.public \"userpath\"\
  2981. Account.public.userpath.write = false\
  2982. \
  2983. function Account:Account( name, pass )\
  2984.     if not fs.isDir( core.path .. \"/user/\" .. name ) then\
  2985.         return false, \"no such user\"\
  2986.     end\
  2987. \
  2988.     -- try to login\
  2989.     local userpath = core.path .. \"/user/\" .. name .. \"/\"\
  2990.     local h = fs.open( userpath .. \"passcheck.txt\", \"r\" )\
  2991.     if h then\
  2992.         local passcheck = h.readAll( )\
  2993.         h.close( )\
  2994.         if encryption.decrypt( passcheck, sha256( pass ) ) ~= name then\
  2995.             return false, \"incorrect password\"\
  2996.         end\
  2997.     else\
  2998.         return false, \"corrupt user\"\
  2999.     end\
  3000. \
  3001.     self.username = name\
  3002.     self.password = pass\
  3003.     self.shaPassword = sha256( pass )\
  3004.     self.userpath = userpath\
  3005. \
  3006.     return self.public\
  3007. end\
  3008. \
  3009. function Account.public:rename( name )\
  3010.     if fs.exists( core.path .. \"/user/\" .. name ) then\
  3011.         return false, \"user exists\"\
  3012.     end\
  3013.     local h = fs.open( self.userpath .. \"passcheck.txt\", \"w\" )\
  3014.     if h then\
  3015.         h.write( encryption.encrypt( name, self.shaPassword ) )\
  3016.         h.close( )\
  3017.         if not pcall( function( )\
  3018.             fs.move( self.userpath, core.path .. \"/user/\" .. name )\
  3019.         end ) then\
  3020.             local h = fs.open( self.userpath .. \"passcheck.txt\", \"w\" )\
  3021.             if h then\
  3022.                 h.write( encryption.encrypt( self.username, self.shaPassword ) )\
  3023.                 h.close( )\
  3024.             end\
  3025.             return false\
  3026.         end\
  3027.         self.username = name\
  3028.         self.userpath = core.path .. \"/user/\" .. name .. \"/\"\
  3029.         return true\
  3030.     end\
  3031.     return false\
  3032. end\
  3033. \
  3034. function Account.public:changePassword( pass )\
  3035.     local h = fs.open( self.userpath .. \"passcheck.txt\", \"w\" )\
  3036.     if h then\
  3037.         self.password = pass\
  3038.         self.shaPassword = sha256( pass )\
  3039.         h.write( encryption.encrypt( self.username, self.shaPassword ) )\
  3040.         h.close( )\
  3041.     end\
  3042.     return false\
  3043. end\
  3044. \
  3045. function Account.public:setData( index, entry, value )\
  3046.     local data\
  3047.     local h = fs.open( self.userpath .. \"data/\" .. index, \"r\" )\
  3048.     if h then\
  3049.         data = textutils.unserialize( h.readAll( ) ) or { }\
  3050.         h.close( )\
  3051.     else\
  3052.         data = { }\
  3053.     end\
  3054.     data[entry] = value\
  3055.     local h = fs.open( self.userpath .. \"data/\" .. index, \"w\" )\
  3056.     if h then\
  3057.         h.write( textutils.serialize( data ) )\
  3058.         h.close( )\
  3059.         return true\
  3060.     end\
  3061.     return false\
  3062. end\
  3063. \
  3064. function Account.public:getData( index, entry )\
  3065.     local data\
  3066.     local h = fs.open( self.userpath .. \"data/\" .. index, \"r\" )\
  3067.     if h then\
  3068.         data = textutils.unserialize( h.readAll( ) ) or { }\
  3069.         h.close( )\
  3070.         return data[entry]\
  3071.     end\
  3072. end\
  3073. \
  3074. function Account.public:hasAccess( request ) -- things like root filesystem access, settings, etc\
  3075.     return true\
  3076. end\
  3077. \
  3078. function Account.static.create( name, pass )\
  3079.     if fs.exists( core.path .. \"/user/\" .. name ) then\
  3080.         return false, \"account already exists\"\
  3081.     end\
  3082.     fs.makeDir( core.path .. \"/user/\" .. name )\
  3083.     local h = fs.open( core.path .. \"/user/\" .. name .. \"/passcheck.txt\", \"w\" )\
  3084.     if h then\
  3085.         h.write( encryption.encrypt( name, sha256( pass ) ) )\
  3086.         h.close( )\
  3087.     else\
  3088.         fs.delete( core.path .. \"/user/\" .. name )\
  3089.     end\
  3090.     fs.makeDir( core.path .. \"/user/\" .. name .. \"/data\" )\
  3091.     fs.makeDir( core.path .. \"/user/\" .. name .. \"/apps\" )\
  3092.     fs.makeDir( core.path .. \"/user/\" .. name .. \"/files\" )\
  3093.     fs.makeDir( core.path .. \"/user/\" .. name .. \"/files/screenshots\" )\
  3094. end", meta={
  3095.   type = "class",
  3096. }};["clipboard"]={content="if NovaClipboard then\
  3097.     set, get = NovaClipboard.set, NovaClipboard.get\
  3098. else\
  3099.     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\
  3100.     export(\"NovaClipboard\",{set=set,get=get})\
  3101. end", meta={
  3102.   type = "lib",
  3103. }};["App"]={content="\
  3104. App.public \"name\"\
  3105. App.public \"readfile\"\
  3106. App.public \"conf\"\
  3107. App.public \"path\"\
  3108. App.public \"main\"\
  3109. \
  3110. App.public.name.write = false\
  3111. App.public.readfile.write = false\
  3112. App.public.conf.write = false\
  3113. App.public.path.write = false\
  3114. App.public.main.write = false\
  3115. \
  3116. function App:App( session, path )\
  3117.     local readfile\
  3118.     if NovaFS.isDirectory( path ) then\
  3119.         function readfile( p )\
  3120.             local data = NovaFS.readfile( path .. \"/\" .. p )\
  3121.             if data then\
  3122.                 return data.content\
  3123.             end\
  3124.         end\
  3125.     elseif NovaFS.exists( path ) and NovaFS.getType( path ) == \"archive\" then\
  3126.         return false, \"cannot load apps from archives just now...\"\
  3127.         -- read from an archive\
  3128.     elseif NovaFS.exists( path ) then\
  3129.         function readfile( p )\
  3130.             if p == \"main.lua\" then\
  3131.                 local data = NovaFS.readfile( path )\
  3132.                 if data then\
  3133.                     return data.content\
  3134.                 end\
  3135.             else\
  3136.                 return false\
  3137.             end\
  3138.         end\
  3139.     else\
  3140.         return false, \"app not found\"\
  3141.     end\
  3142. \
  3143.     self.session = session\
  3144.     self.path = path\
  3145.     self.name = NovaFS.getName( path, true )\
  3146.     self.conf = { }\
  3147.     self.readfile = readfile\
  3148. \
  3149.     local main = readfile \"main.lua\"\
  3150.     if main then\
  3151.         self.main = main\
  3152.     else\
  3153.         return false, \"couldn't load main file\"\
  3154.     end\
  3155. \
  3156.     local conf = readfile \"appconfig.txt\"\
  3157.     if conf and type( textutils.unserialize( \"{\" .. conf .. \"}\" ) == \"table\" ) then\
  3158.         self.conf = textutils.unserialize( \"{\" .. conf .. \"}\" ) or { }\
  3159.     end\
  3160. \
  3161.     local icon = readfile \"icon.nim\"\
  3162.     if icon then\
  3163.         self.icon = NovaUI.Image( 7, 3 )\
  3164.         self.icon:loadstr( icon )\
  3165.     end\
  3166. \
  3167.     if type( self.conf.name ) == \"string\" then\
  3168.         self.name = self.conf.name\
  3169.     end\
  3170. \
  3171.     if type( self.conf.handles ) == \"table\" then\
  3172.         for i = 1, #self.conf.handles do\
  3173.             NovaFS.addHandler( self.conf.handles[i], self.name )\
  3174.         end\
  3175.     end\
  3176.     if type( self.conf.filetypes ) == \"table\" then\
  3177.         for k, v in pairs( self.conf.filetypes ) do\
  3178.             NovaFS.addTypeDescription( k, v )\
  3179.         end\
  3180.     end\
  3181.     if type( self.conf.extensions ) == \"table\" then\
  3182.         for k, v in pairs( self.conf.extensions ) do\
  3183.             NovaFS.addExtension( k, v )\
  3184.         end\
  3185.     end\
  3186. \
  3187.     return self.public\
  3188. end\
  3189. \
  3190. function App.public:launch( args )\
  3191.     args = type( args ) == \"table\" and args or { }\
  3192.     if self.conf.runmode == \"service\" then\
  3193.         return ServiceAppInstance( self.session, self.public, args )\
  3194.     elseif self.conf.runmode == \"process\" then\
  3195.         return ProcessAppInstance( self.session, self.public, args )\
  3196.     else\
  3197.         return DefaultAppInstance( self.session, self.public, args )\
  3198.     end\
  3199. end", meta={
  3200.   type = "class",
  3201. }};["login"]={content="\
  3202. local function login( )\
  3203.     if #( fs.list( core.path .. \"/user\" ) or {} ) == 0 then\
  3204.         local w, h = term.getSize( )\
  3205. \
  3206.         local finished = false\
  3207. \
  3208.         local frame = core.UI:newChild( NovaUI.UIFrame( 2, 3, w - 2, h - 3 ) )\
  3209.         local title = frame:newChild( NovaUI.UIText( 1, 1, core.UI.w - 2, 2, \"Please set up a user account.\" ) )\
  3210.         title.tc = colours.grey\
  3211.         title:centreX( )\
  3212.         local ut = frame:newChild( NovaUI.UIText( 0, 6, 8, 1, \"username\" ) )\
  3213.         ut.tc = colours.lightGrey\
  3214.         ut:centreX( )\
  3215.         local username = frame:newChild( NovaUI.UIInput( 0, 7, math.floor( w * 2 / 3 ), 1 ) )\
  3216.         username.tc = 1\
  3217.         username:centreX( )\
  3218.         username:focusOn( )\
  3219.         local pt = frame:newChild( NovaUI.UIText( 0, 9, 8, 1, \"password\" ) )\
  3220.         pt.tc = colours.lightGrey\
  3221.         pt:centreX( )\
  3222.         local password = frame:newChild( NovaUI.UIInput( 0, 10, math.floor( w * 2 / 3 ), 1, \"*\" ) )\
  3223.         password.tc = 1\
  3224.         password:centreX( )\
  3225. \
  3226.         local continue = frame:newChild( NovaUI.UIButton( 0, frame.h - 2, 20, 3, \"continue\" ) )\
  3227.         continue:centreX( )\
  3228.         continue.tc = 1\
  3229.         function continue:onClick( ) finished = true end\
  3230.         continue.tabIndex = true\
  3231.         function continue:whenFocussed( ) self.bc = colours.lightBlue end\
  3232.         function continue:whenUnFocussed( ) self.bc = colours.lightGrey end\
  3233. \
  3234.         function username:onEnter( ) password:focusOn( ) end\
  3235.         function password:onEnter( ) finished = true end\
  3236. \
  3237.         while not finished do\
  3238.             coroutine.yield( )\
  3239.         end\
  3240.         frame:remove( )\
  3241. \
  3242.         Account.create( username.text, password.text )\
  3243.         core.log( \"created user\", username.text )\
  3244. \
  3245.         return Account( username.text, password.text )\
  3246.     end\
  3247. \
  3248.     local loginProcess = Process \"loginProcess\"\
  3249. \
  3250.     local waiting = true\
  3251.     local account\
  3252. \
  3253.     local frame = core.UI:newChild( NovaUI.UIFrame( 1, 1, term.getSize( ) ) )\
  3254.     local input = { }\
  3255.     local title = { }\
  3256. \
  3257.     title = frame:newChild( NovaUI.UIText( 1, 1, frame.w, 3, \"\\n   Login\" ) )\
  3258.     title.bc = colours.grey\
  3259.     title.tc = colours.white\
  3260. \
  3261.     local userlist = frame:newChild( NovaUI.UIMenu( 2, 8, frame.w - 2, 4, \"horizontal\" ) )\
  3262.     userlist.padding = 1\
  3263.     local scrollbar = frame:newChild( NovaUI.UIScrollBar( 2, 13, frame.w - 2, 1, userlist, \"horizontal\" ) )\
  3264.     scrollbar.bc = colours.lightGrey\
  3265.     scrollbar.tc = colours.grey\
  3266. \
  3267.     local password = { }\
  3268. \
  3269.     local cols = { colours.blue, colours.red, colours.green, colours.yellow }\
  3270.     local n = 1\
  3271. \
  3272.     local users = fs.list( core.path .. \"/user\" )\
  3273.     for i = 1, #users do\
  3274.         local f = userlist:newChild( NovaUI.UIFrame( 0, 0, 10, 4 ) )\
  3275.         f:newChild( NovaUI.UIButton( 2, 1, f.w - 2, f.h - 1, \"\" ) ).bc = cols[n]\
  3276.         f:newChild( NovaUI.UIText( 1, f.h, #users[i], 1, users[i] ) ):centreX( )\
  3277.         c = f:newChild( NovaUI.UIButton( 1, 1, f.w, f.h, \"\" ) )\
  3278.         c.bc = 0\
  3279.         c.align = false\
  3280.         function c:onClick( x )\
  3281.             if password.input then\
  3282.                 password.input:remove( )\
  3283.                 password.title:remove( )\
  3284.             end\
  3285.             password.title = frame:newChild( NovaUI.UIText( 0, 15, 8, 1, \"password\" ) )\
  3286.             password.title:centreX( )\
  3287.             password.input = frame:newChild( NovaUI.UIInput( 0, 16, math.floor( term.getSize( ) / 2 ), 1 ) )\
  3288.             password.input.mask = \"*\"\
  3289.             password.input:centreX( )\
  3290.             password.input:focusOn( )\
  3291.             function password.input:whenUnFocussed( )\
  3292.                 password.input:remove( )\
  3293.                 password.title:remove( )\
  3294.                 password = { }\
  3295.             end\
  3296.             function password.input:onEnter( )\
  3297.                 local err\
  3298.                 account, err = Account( users[i], password.input.text )\
  3299.                 if account then\
  3300.                     waiting = false\
  3301.                 else\
  3302.                     loginProcess:newThread( function( )\
  3303.                         local alert = frame:newChild( NovaUI.UIText( 0, ({term.getSize()})[2] - 1, #err, 1, err ) )\
  3304.                         alert.bc = 0\
  3305.                         alert.tc = colours.red\
  3306.                         alert:centreX( )\
  3307.                         sleep( 1 )\
  3308.                         alert:remove( )\
  3309.                     end )\
  3310.                 end\
  3311.             end\
  3312.             if type( x ) == \"string\" then\
  3313.                 password.input.text = x\
  3314.                 password.input:onEnter( )\
  3315.             end\
  3316.         end\
  3317. \
  3318.         if users[i] == name then\
  3319.             c:onClick( pass )\
  3320.         end\
  3321. \
  3322.         n = n + 1\
  3323.         if n == 5 then\
  3324.             n = 1\
  3325.         end\
  3326. \
  3327.         if #users == 1 then\
  3328.             c:onClick( )\
  3329.         end\
  3330.     end\
  3331.     if #users * 11 <= userlist.w then\
  3332.         userlist.w = #users * 11 - 1\
  3333.         userlist:centreX( )\
  3334.         scrollbar:remove( )\
  3335.     end\
  3336. \
  3337.     loginProcess:newThread( function( )\
  3338.         while true do\
  3339.             local ev = { coroutine.yield( ) }\
  3340.             if password.input then\
  3341.                 if ev[1] == \"mag_swipe\" then\
  3342.                     password.input.text = ev[2]\
  3343.                     password.input:onEnter( )\
  3344.                 end\
  3345.             end\
  3346.         end\
  3347.     end )\
  3348. \
  3349.     while waiting do\
  3350.         coroutine.yield( )\
  3351.     end\
  3352.     frame:remove( )\
  3353.     loginProcess:stop( )\
  3354.     return account\
  3355. end\
  3356. \
  3357. return login", meta={
  3358.   type = "lib",
  3359. }};["Session"]={content="\
  3360. Session.public \"account\"\
  3361. Session.public.account.write = false\
  3362. Session.public \"process\"\
  3363. Session.public.process.write = false\
  3364. Session.public \"UI\"\
  3365. Session.public.UI.write = false\
  3366. Session.public \"windowManager\"\
  3367. Session.public.windowManager.write = false\
  3368. Session.public \"appManager\"\
  3369. Session.public.appManager.write = false\
  3370. \
  3371. function Session:Session( )\
  3372.     if core.session then\
  3373.         core.session:logout( )\
  3374.     end\
  3375. \
  3376.     -- login\
  3377.     self.account = login( )\
  3378.     core.log( \"starting session for \" .. self.account.username )\
  3379. \
  3380.     -- mount user drive\
  3381.     NovaFS.mount( NovaFS.Drive.redirect( self.account.userpath .. \"/files\" ), \"user\" )\
  3382. \
  3383.     -- multitasking host\
  3384.     self.process = Process \"SessionManager\"\
  3385. \
  3386.     -- create UI\
  3387.     self.UI = core.UI:newChild( NovaUI.UIFrame( 1, 1, term.getSize( ) ) )\
  3388.     self.windowManager = self.UI:newChild( WindowManager( self.public ) )\
  3389. \
  3390.     -- create AppManager\
  3391.     self.appManager = AppManager( self.public )\
  3392. \
  3393.     -- update OS\
  3394.     local updateInfo = core.getUpdateInfo( )\
  3395.     if core.compareVersions( updateInfo ) then\
  3396.         local window = self.windowManager:newWindow \"alert\"\
  3397.         window.title = \"Update\"\
  3398.         local response = NovaUI.display.confirm( window.display, \"Update found, would you like to update?\" )\
  3399.         window:close( )\
  3400.         if response then\
  3401.             if core.update( ) then\
  3402.                 if NovaUI.display.confirm( self.UI, \"Updated, would you like to restart?\" ) then\
  3403.                     core.finish( )\
  3404.                     os.reboot( )\
  3405.                 end\
  3406.             else\
  3407.                 NovaUI.display.alert( self.UI, \"Failed to download update\" )\
  3408.             end\
  3409.         end\
  3410.     end\
  3411. \
  3412.     self:desktop( )\
  3413. \
  3414.     core.session = self.public\
  3415.     return self.public\
  3416. end\
  3417. \
  3418. function Session.public:logout( )\
  3419.     NovaFS.unmount \"user\"\
  3420.     self.UI:remove( )\
  3421.     self.appManager:stop( )\
  3422.     core.session = nil\
  3423. end\
  3424. \
  3425. function Session:desktop( )\
  3426.     local desktop = self.windowManager.desktop\
  3427. \
  3428.     local background = desktop:newChild( NovaUI.UIFrame( 1, 1, desktop.w, desktop.h - 1 ) )\
  3429.     self.taskbar = desktop:newChild( NovaUI.UIFrame( 1, desktop.h, desktop.w, 1 ) )\
  3430. \
  3431.     local open = false\
  3432. \
  3433.     self.process:newThread( function( )\
  3434.         while true do\
  3435.             self.taskbar:newChild( NovaUI.UIText( 1, 1, self.taskbar.w, 1, \"\" ) ).bc = colours.grey\
  3436.             local menu = self.taskbar:newChild( NovaUI.UIButton( 1, 1, 6, 1, \" Nova \" ) )\
  3437.             menu.bc = open and colours.cyan or colours.grey\
  3438.             menu.tc = colours.white\
  3439.             function menu.onClick( )\
  3440.                 self.public:menu( )\
  3441.             end\
  3442. \
  3443.             local windows = self.windowManager:listWindows( )\
  3444.             local ww = 0\
  3445.             for i = 1, #windows do\
  3446.                 ww = ww + math.min( #windows[i].title:gsub( \" .+\", \"\" ), 15 ) + 1\
  3447.             end\
  3448.             local cutoff = 0\
  3449.             while ww > self.taskbar.w - 6 do\
  3450.                 ww = ww - #windows - 1\
  3451.                 cutoff = cutoff + 1\
  3452.             end\
  3453.             local x = 7\
  3454.             for i = 1, #windows do\
  3455.                 local w = windows[i]\
  3456.                 local display = windows[i].title:gsub( \" .+\", \"\" ):sub( 1, 15 )\
  3457.                 display = display:sub( 1, #display - cutoff )\
  3458.                 local b = self.taskbar:newChild( NovaUI.UIButton( x, 1, #display, 1, display ) )\
  3459.                 b.bc = colours.lightGrey\
  3460.                 b.tc = colours.grey\
  3461.                 x = x + #display + 1\
  3462.                 function b.onClick( )\
  3463.                     w.content.active = true\
  3464.                     self.windowManager:focusOn( w )\
  3465.                 end\
  3466.             end\
  3467.             coroutine.yield( )\
  3468.             self.taskbar:clearChildren( )\
  3469.         end\
  3470.     end )\
  3471. end\
  3472. \
  3473. function Session.public:menu( )\
  3474.     local function updateContent( content, name, close )\
  3475.         content:clearChildren( )\
  3476.         local files = NovaFS.findName( name )\
  3477. \
  3478.         local categories = {\
  3479.             { name = \"Apps\", onClick = function( item )\
  3480.                 self.appManager:launch( item.name, { } )\
  3481.             end };\
  3482.             { name = \"Pictures\", onClick = function( item )\
  3483.                 self.appManager:launch( \"Files\", { item.path } )\
  3484.             end };\
  3485.             { name = \"Lua Files\", onClick = function( item )\
  3486.                 self.appManager:launch( \"Files\", { item.path } )\
  3487.             end };\
  3488.             { name = \"Folders\", onClick = function( item )\
  3489.                 self.appManager:launch( \"Files\", { item.path } )\
  3490.             end };\
  3491.             { name = \"Files\", onClick = function( item )\
  3492.                 self.appManager:launch( \"Files\", { item.path } )\
  3493.             end };\
  3494.             { name = \"System Files\", onClick = function( item )\
  3495.                 self.appManager:launch( \"Files\", { item.path } )\
  3496.             end };\
  3497.         }\
  3498. \
  3499.         for i = 1, #files do\
  3500.             if self.appManager.app_paths[NovaFS.merge( files[i].path )] then\
  3501.                 table.insert( categories[1], self.appManager.app_paths[files[i].path] )\
  3502.             elseif files[i].path:find( \"^/?\" .. core.path .. \"/\" ) and not files[i].path:find( \"^/?\" .. core.path .. \"/user/\" .. self.account.username .. \"/\" ) then\
  3503.                 table.insert( categories[6], files[i] )\
  3504.             elseif files[i].type == \"directory\" then\
  3505.                 table.insert( categories[4], files[i] )\
  3506.             elseif files[i].filetype == \"nova_image\" then\
  3507.                 table.insert( categories[2], files[i] )\
  3508.             elseif files[i].filetype == \"lua\" or files[i].filetype == \"lib\" or files[i].filetype == \"class\" then\
  3509.                 table.insert( categories[3], files[i] )\
  3510.             else\
  3511.                 table.insert( categories[5], files[i] )\
  3512.             end\
  3513.         end\
  3514. \
  3515.         local f = false\
  3516.         local t = { }\
  3517.         for i = 1, #categories do\
  3518.             local c = categories[i]\
  3519.             if #c > 0 then\
  3520.                 f = true\
  3521.                 table.insert( t, \"space\" )\
  3522.                 table.insert( t, { type = \"label\", name = c.name } )\
  3523.                 for i = 1, #c do\
  3524.                     table.insert( t, {\
  3525.                         type = \"button\";\
  3526.                         name = c[i].name;\
  3527.                         onClick = function( )\
  3528.                             close( )\
  3529.                             c.onClick( c[i] )\
  3530.                         end;\
  3531.                     } )\
  3532.                 end\
  3533.                 while categories[i+1] do\
  3534.                     if #categories[i+1] > 0 then\
  3535.                         table.insert( t, \"space\" )\
  3536.                         table.insert( t, \"rule\" )\
  3537.                         break\
  3538.                     end\
  3539.                     i = i + 1\
  3540.                 end\
  3541.             end\
  3542.         end\
  3543.         if not f then\
  3544.             table.insert( t, \"space\" )\
  3545.             table.insert( t, { type = \"label\", name = \"Nothing found!\" } )\
  3546.         end\
  3547. \
  3548.         NovaUI.display.menu( content, t )\
  3549. \
  3550.         for i = 1, #categories do\
  3551.             if #categories[i] > 0 then\
  3552.                 return function( )\
  3553.                     categories[i].onClick( categories[i][1] )\
  3554.                 end\
  3555.             end\
  3556.         end\
  3557.         return function( )\
  3558.             Nova.display.alert( display, \"No search results\" )\
  3559.         end\
  3560.     end\
  3561. \
  3562.     open = true\
  3563.     local close = self.UI:newChild( NovaUI.UIButton( 1, 1, self.UI.w, self.UI.h, \"\" ) )\
  3564.     close.bc = 0\
  3565.     close.tc = 0\
  3566.     local dropdown = self.UI:newChild( NovaUI.UIFrame( 1, self.UI.h - 15, 26, 15 ) )\
  3567.     function close:onClick( )\
  3568.         open = false\
  3569.         close:remove( )\
  3570.         dropdown:remove( )\
  3571.     end\
  3572.     dropdown:newChild( NovaUI.UIText( 1, 1, 26, 15, \"\" ) ).bc = colours.cyan\
  3573. \
  3574.     local content = dropdown:newChild( NovaUI.UIFrame( 2, 2, dropdown.w - 2, dropdown.h - 4 ) )\
  3575.     local enter = updateContent( content, \"*\", function( )\
  3576.         close:onClick( )\
  3577.     end )\
  3578. \
  3579.     local search = dropdown:newChild( NovaUI.UIInput( 2, dropdown.h - 1, dropdown.w - 2, 1 ) )\
  3580.     search.bc = colours.white\
  3581.     search.fbc = colours.white\
  3582. \
  3583.     function search.onEnter( )\
  3584.         close:onClick( )\
  3585.         enter( )\
  3586.     end\
  3587.     function search:onChange( )\
  3588.         enter = updateContent( content, self.text, function( )\
  3589.             close:onClick( )\
  3590.         end )\
  3591.     end\
  3592. \
  3593.     search:focusOn( )\
  3594. end", meta={
  3595.   type = "class",
  3596. }};["encryption"]={content="\
  3597. local function sum( n, ... ) -- number n, number ...additions\
  3598.     local t = { ... }\
  3599.     for i = 1, #t do\
  3600.         n = n + t[i]\
  3601.     end\
  3602.     return n\
  3603. \
  3604.     -- number sum\
  3605. end\
  3606. \
  3607. local function loop( n, lim ) -- number n, number limit\
  3608.     while n > lim do\
  3609.         n = n - lim\
  3610.     end\
  3611.     while n < 1 do\
  3612.         n = n + lim\
  3613.     end\
  3614.     return n\
  3615.     -- number limited\
  3616. end\
  3617. \
  3618. local function shift( str, count )\
  3619.     local str = { str:byte( 1, #str ) }\
  3620.     for i = 1, #str do\
  3621.         str[i] = loop( str[i] + count, 255 )\
  3622.     end\
  3623.     for i = 1, #str do\
  3624.         str[i] = string.char( str[i] )\
  3625.     end\
  3626.     return table.concat( str, \"\" )\
  3627. end\
  3628. \
  3629. local function tohex( b ) -- string[4] bits\
  3630.     local n = 0\
  3631.     for i = 1, 4 do\
  3632.         n = n * 2\
  3633.         n = n + tonumber( b:sub( i, i ) )\
  3634.     end\
  3635.     if n >= 10 then\
  3636.         local hexes = { \"A\", \"B\", \"C\", \"D\", \"E\", \"F\" }\
  3637.         return hexes[n - 9]\
  3638.     end\
  3639.     return tostring( n )\
  3640. \
  3641.     -- string[1] hex\
  3642. end\
  3643. \
  3644. local function fromhex( h ) -- string[1] hex\
  3645.     local n = tonumber( h )\
  3646.     if not n then\
  3647.         local hexes = { [\"A\"] = 10, [\"B\"] = 11, [\"C\"] = 12, [\"D\"] = 13, [\"E\"] = 14, [\"F\"] = 15 }\
  3648.         n = hexes[h]\
  3649.     end\
  3650.     local str = \"\"\
  3651.     for i = 1, 4 do\
  3652.         str = str .. n % 2\
  3653.         n = math.floor( n / 2 )\
  3654.     end\
  3655.     return str:reverse( )\
  3656. \
  3657.     -- string[4] bits\
  3658. end\
  3659. \
  3660. local function xor( n1, n2 )\
  3661.     if n1 > 255 or n2 > 255 or n1 < 0 or n2 < 0 then\
  3662.         return error \"expected numbers between 0 and 255\"\
  3663.     end\
  3664.     local bit1, bit2 = { }, { }\
  3665.     for i = 1, 8 do\
  3666.         bit1[9-i] = n1 % 2 == 1\
  3667.         n1 = math.floor( n1 / 2 )\
  3668.     end\
  3669.     for i = 1, 8 do\
  3670.         bit2[9-i] = n2 % 2 == 1\
  3671.         n2 = math.floor( n2 / 2 )\
  3672.     end\
  3673.     local bits = { }\
  3674.     for i = 1, 8 do\
  3675.         bits[i] = ( bit1[i] and not bit2[i] ) or ( not bit1[i] and bit2[i] )\
  3676.     end\
  3677.     local n = 0\
  3678.     for i = 1, 8 do\
  3679.         n = n * 2\
  3680.         n = n + ( bits[i] and 1 or 0 )\
  3681.     end\
  3682.     return n\
  3683. end\
  3684. \
  3685. local function nand( n1, n2 )\
  3686.     if n1 > 255 or n2 > 255 or n1 < 0 or n2 < 0 then\
  3687.         return error \"expected numbers between 0 and 255\"\
  3688.     end\
  3689.     local bit1, bit2 = { }, { }\
  3690.     for i = 1, 8 do\
  3691.         bit1[9-i] = n1 % 2 == 1\
  3692.         n1 = math.floor( n1 / 2 )\
  3693.     end\
  3694.     for i = 1, 8 do\
  3695.         bit2[9-i] = n2 % 2 == 1\
  3696.         n2 = math.floor( n2 / 2 )\
  3697.     end\
  3698.     local bits = { }\
  3699.     for i = 1, 8 do\
  3700.         bits[i] = not bit1[i] == bit2[i]\
  3701.     end\
  3702.     local n = 0\
  3703.     for i = 1, 8 do\
  3704.         n = n * 2\
  3705.         n = n + ( bits[i] and 1 or 0 )\
  3706.     end\
  3707.     return n\
  3708. end\
  3709. \
  3710. local function tobits( n )\
  3711.     local str = \"\"\
  3712.     for i = 1, 8 do\
  3713.         str = str .. n % 2\
  3714.         n = math.floor( n / 2 )\
  3715.     end\
  3716.     return str:reverse( )\
  3717. end\
  3718. \
  3719. local function frombits( b )\
  3720.     local n = 0\
  3721.     for i = 1, 8 do\
  3722.         n = n * 2\
  3723.         n = n + tonumber( b:sub( i, i ) )\
  3724.     end\
  3725.     return n\
  3726. end\
  3727. \
  3728. local t = os.clock( )\
  3729. local function start( )\
  3730.     t = os.clock( )\
  3731. end\
  3732. local function yield( )\
  3733.     if os.clock( ) - t > .1 then\
  3734.         coroutine.yield( )\
  3735.         start( )\
  3736.     end\
  3737. end\
  3738. \
  3739. function encrypt( str, key ) -- string text, string key\
  3740.     local enc = \"\"\
  3741.     start( )\
  3742.     for i = 1, #str do\
  3743.         math.randomseed( sum( key:byte( 1, #key ) ) )\
  3744.         key = shift( key, math.random( 1, 100 ) )\
  3745.         local ki = loop( i, #key )\
  3746.         local a = str:sub( i, i ):byte( )\
  3747.         local b = key:sub( ki, ki ):byte( )\
  3748.         enc = enc .. tobits( xor( a, b ) )\
  3749.         yield( )\
  3750.     end\
  3751.     local enc2 = \"\"\
  3752.     for i = 1, #enc / 4 do\
  3753.         enc2 = enc2 .. tohex( enc:sub( i * 4 - 3, i * 4 ) )\
  3754.         yield( )\
  3755.     end\
  3756.     return enc2\
  3757. \
  3758.     -- string cipher\
  3759. end\
  3760. \
  3761. function decrypt( str, key ) -- string cipher, string key\
  3762.     start( )\
  3763.     local dec2 = \"\"\
  3764.     for i = 1, #str do\
  3765.         dec2 = dec2 .. fromhex( str:sub( i, i ) )\
  3766.         yield( )\
  3767.     end\
  3768.     str = dec2\
  3769.     local dec = \"\"\
  3770.     local keys = { }\
  3771.     for i = 1, #str / 8 do\
  3772.         math.randomseed( sum( key:byte( 1, #key ) ) )\
  3773.         keys[i] = shift( key, math.random( 1, 100 ) )\
  3774.         key = keys[i]\
  3775.         yield( )\
  3776.     end\
  3777.     for i = 1, #str / 8 do\
  3778.         local ki = loop( i, #key )\
  3779.         local a = frombits( str:sub( ( i - 1 ) * 8 + 1, i * 8 ) )\
  3780.         local b = string.byte( keys[i]:sub( ki, ki ) )\
  3781.         dec = dec .. string.char( nand( a, b ) )\
  3782.         yield( )\
  3783.     end\
  3784.     return dec\
  3785. \
  3786.     -- string text\
  3787. end", meta={
  3788.   type = "lib",
  3789. }};["loader"]={content="\
  3790. require \"core\"\
  3791. \
  3792. local function loadExternalAPI( name )\
  3793.     local path = core.path .. \"/lib/\" .. name .. \".lua\"\
  3794.     local f, err = loadfile( path )\
  3795.     if not f then\
  3796.         error( err, 0 )\
  3797.     end\
  3798.     local env = setmetatable( { }, { __index = getfenv( ) } )\
  3799.     setfenv( f, env )\
  3800.     f( )\
  3801.     local t = { }\
  3802.     for k, v in pairs( env ) do\
  3803.         t[k] = v\
  3804.     end\
  3805.     shared[name] = t\
  3806.     if env.NovaClipboard then\
  3807.         shared.NovaClipboard = env.NovaClipboard\
  3808.     end\
  3809. end\
  3810. \
  3811. loadExternalAPI \"NovaFS\"\
  3812. loadExternalAPI \"NovaNet\"\
  3813. loadExternalAPI \"NovaUI\"", meta={
  3814.   type = "pre-class",
  3815. }};["Window"]={content="\
  3816. require \"Process\"\
  3817. \
  3818. Window.title = \"Blank window\"\
  3819. Window.public \"title\" \"string\"\
  3820. \
  3821. Window.public \"overlay\" (NovaUI.UIElement)\
  3822. Window.public \"active\" \"boolean\"\
  3823. \
  3824. Window.public \"process\" (Process)\
  3825. \
  3826. Window.public \"x\"\
  3827. Window.public \"y\"\
  3828. \
  3829. function Window.public.x:write( value )\
  3830.     self.content.x = tonumber( value ) or self.content.x\
  3831. end\
  3832. function Window.public.x:read( )\
  3833.     return self.content.x\
  3834. end\
  3835. function Window.public.y:write( value )\
  3836.     self.content.y = tonumber( value ) or self.content.y\
  3837. end\
  3838. function Window.public.y:read( )\
  3839.     return self.content.y\
  3840. end\
  3841. \
  3842. Window.public \"minw\" \"number\"\
  3843. Window.public \"minh\" \"number\"\
  3844. Window.public \"maxw\" \"number\"\
  3845. Window.public \"maxh\" \"number\"\
  3846. \
  3847. Window.public \"content\"\
  3848. Window.public.content.write = false\
  3849. Window.public \"display\"\
  3850. Window.public.display.write = false\
  3851. \
  3852. Window.public \"onClose\" \"function\"\
  3853. Window.public \"onResize\" \"function\"\
  3854. Window.public \"onMove\" \"function\"\
  3855. \
  3856. function Window:Window( windowmanager, x, y, w, h )\
  3857.     self.active = true\
  3858.     self.manager = windowmanager\
  3859. \
  3860.     self.minw = 8\
  3861.     self.minh = 1\
  3862.     self.maxw = windowmanager.w\
  3863.     self.maxh = windowmanager.h - 1\
  3864. \
  3865.     self.width = w\
  3866.     self.height = h\
  3867.     self.content = NovaUI.UIFrame( x, math.max( y, 1 ), w, h + 1 )\
  3868.     self.background = self.content:newChild( NovaUI.UIText( 1, 1, w, h + 1, \"\" ) )\
  3869. \
  3870.     self.titlebc = colours.grey\
  3871.     self.titletc = colours.white\
  3872.     self.buttontc = colours.cyan\
  3873. \
  3874.     self.borderactive = false\
  3875.     self.border = self.content:newChild( NovaUI.UIText( 1, 1, 0, 0, \"\" ) )\
  3876.     self.titlebar = self.content:newChild( NovaUI.UIFrame( 1, 1, w, 1 ) )\
  3877.     self.display = self.content:newChild( NovaUI.UIFrame( 1, 2, w, h - 1 ) )\
  3878. \
  3879.     self.titledisplay = self.titlebar:newChild( NovaUI.UIButton( 1, 1, w - 4, 1, function( )\
  3880.         return self.title\
  3881.     end ) )\
  3882.     self.titledisplay.align = false\
  3883.     self.borderbutton = self.titlebar:newChild( NovaUI.UIButton( w - 3, 1, 1, 1, \"b\" ) )\
  3884.     self.minimisebutton = self.titlebar:newChild( NovaUI.UIButton( w - 2, 1, 1, 1, \"_\" ) )\
  3885.     self.maximisebutton = self.titlebar:newChild( NovaUI.UIButton( w - 1, 1, 1, 1, \"+\" ) )\
  3886.     self.closebutton = self.titlebar:newChild( NovaUI.UIButton( w, 1, 1, 1, \"x\" ) )\
  3887. \
  3888.     local xx, yy, _cx, _cy\
  3889.     function self.titledisplay.onClick( _, rx, ry, button )\
  3890.         if button == 1 then\
  3891.             _cx, _cy = 0, 0\
  3892.             xx, yy = rx, ry\
  3893.             self.manager:focusOn( self.public )\
  3894.         end\
  3895.     end\
  3896.     function self.titledisplay.onDrag( _, rx, ry, cx, cy, button )\
  3897.         if button == 1 and xx then\
  3898.             if self.content.x + rx - _cx - 1 == 1 then\
  3899.                 self.content.x = 1\
  3900.                 self.content.y = 1\
  3901.                 self.public:resize( math.floor( self.manager.w / 2 ), self.manager.h - 2 )\
  3902.                 xx = nil\
  3903.             elseif self.content.x + rx - _cx - 1 == self.manager.w then\
  3904.                 self.content.y = 1\
  3905.                 self.public:resize( math.floor( self.manager.w / 2 ), self.manager.h - 2 )\
  3906.                 self.content.x = self.manager.w - self.content.w + 1\
  3907.                 xx = nil\
  3908.             else\
  3909.                 self.content.x = self.content.x + rx - xx\
  3910.                 self.content.y = self.content.y + ry - yy\
  3911.                 xx, yy = rx, ry\
  3912.             end\
  3913.             _cx = _cx + cx\
  3914.             _cy = _cy + cy\
  3915.             self.manager:focusOn( self.public )\
  3916.             if self.onMove then\
  3917.                 self.onMove( self.public, self.width, self.height )\
  3918.             end\
  3919.         end\
  3920.     end\
  3921. \
  3922.     function self.borderbutton.onClick( )\
  3923.         self.manager:focusOn( self.public )\
  3924.         if self.borderactive then\
  3925.             self.public:hideBorder( )\
  3926.         else\
  3927.             self.public:showBorder( )\
  3928.         end\
  3929.     end\
  3930.     function self.minimisebutton.onClick( )\
  3931.         self.public:minimise( )\
  3932.     end\
  3933.     function self.maximisebutton.onClick( )\
  3934.         self.public:maximise( )\
  3935.     end\
  3936.     function self.closebutton.onClick( )\
  3937.         if self.onClose then\
  3938.             self.onClose( self.public )\
  3939.         else\
  3940.             self.public:close( )\
  3941.         end\
  3942.     end\
  3943. \
  3944.     self:updateColours( )\
  3945. \
  3946.     self.public:resize( w, h )\
  3947. \
  3948.     return self.public\
  3949. end\
  3950. \
  3951. function Window:updateColours( )\
  3952.     self.titledisplay.bc = self.titlebc\
  3953.     self.titledisplay.tc = self.titletc\
  3954.     self.borderbutton.bc = self.titlebc\
  3955.     self.minimisebutton.bc = self.titlebc\
  3956.     self.maximisebutton.bc = self.titlebc\
  3957.     self.closebutton.bc = self.titlebc\
  3958.     self.borderbutton.tc = self.buttontc\
  3959.     self.minimisebutton.tc = self.buttontc\
  3960.     self.maximisebutton.tc = self.buttontc\
  3961.     self.closebutton.tc = self.buttontc\
  3962.     if self.borderactive then\
  3963.         self.border.bc = self.titlebc\
  3964.         self.resizer.bc = self.titlebc\
  3965.         self.resizer.tc = self.buttontc\
  3966.     end\
  3967. end\
  3968. \
  3969. function Window.public:resize( w, h )\
  3970.     w = math.min( math.max( w, self.minw ), self.maxw )\
  3971.     h = math.min( math.max( h, self.minh ), self.maxh )\
  3972.     self.width = w\
  3973.     self.height = h\
  3974.     if self.borderactive then\
  3975.         self.border.w = w + 2\
  3976.         self.border.h = h + 2\
  3977.         self.content.w = w + 2\
  3978.         self.content.h = h + 2\
  3979.         self.titlebar.w = w\
  3980.         self.resizer.x = w + 2\
  3981.         self.resizer.y = h + 2\
  3982.         self.background.w = w + 2\
  3983.         self.background.h = h + 2\
  3984.     else\
  3985.         self.content.w = w\
  3986.         self.content.h = h + 1\
  3987.         self.background.w = w\
  3988.         self.background.h = h + 1\
  3989.         self.titlebar.w = w\
  3990.     end\
  3991.     self.titledisplay.w = self.titlebar.w - 4\
  3992.     self.borderbutton.x = self.titlebar.w - 3\
  3993.     self.minimisebutton.x = self.titlebar.w - 2\
  3994.     self.maximisebutton.x = self.titlebar.w - 1\
  3995.     self.closebutton.x = self.titlebar.w\
  3996.     self.display.w = w\
  3997.     self.display.h = h\
  3998.     if self.onResize then\
  3999.         self.onResize( self.public, w, h )\
  4000.     elseif self.process then\
  4001.         self.process:queueEvent( \"window_resize\", w, h )\
  4002.     end\
  4003. end\
  4004. \
  4005. function Window.public:minimise( )\
  4006.     self.content.active = false\
  4007. end\
  4008. \
  4009. function Window.public:maximise( )\
  4010.     if self.manager.w > self.maxw or self.manager.h - 2 > self.maxh then\
  4011.         return\
  4012.     end\
  4013.     if self.borderactive then\
  4014.         self.public:hideBorder( )\
  4015.     end\
  4016.     self.content.x = 1\
  4017.     self.content.y = 1\
  4018.     self.public:resize( self.manager.w, self.manager.h - 2 )\
  4019.     self.manager:focusOn( self.public )\
  4020. end\
  4021. \
  4022. function Window.public:close( )\
  4023.     if self.process then\
  4024.         self.process:queueEvent( \"window_close\", self.public )\
  4025.     end\
  4026.     self.content:remove( )\
  4027.     self.manager:removeWindow( self.public )\
  4028. end\
  4029. \
  4030. function Window.public:showBorder( )\
  4031.     self.borderactive = true\
  4032.     self.border.active = true\
  4033.     self.border.bc = self.titlebc\
  4034.     self.display.x = 2\
  4035.     self.titlebar.x = 2\
  4036.     self.content.x = self.content.x - 1\
  4037.     self.resizer = self.content:newChild( NovaUI.UIButton( self.width + 2, self.height + 1, 1, 1, \"@\" ) )\
  4038.     self.resizer.bc = self.titlebc\
  4039.     self.resizer.tc = self.buttontc\
  4040.     function self.resizer.onDrag( _, rx, ry, _, _, button )\
  4041.         if button == 1 then\
  4042.             self.public:resize( self.width + rx - 1, self.height + ry - 1 )\
  4043.         end\
  4044.     end\
  4045.     self.public:resize( self.width, self.height )\
  4046. end\
  4047. \
  4048. function Window.public:hideBorder( )\
  4049.     self.border.active = false\
  4050.     self.resizer:remove( )\
  4051.     self.borderactive = false\
  4052.     self.display.x = 1\
  4053.     self.titlebar.x = 1\
  4054.     self.content.x = self.content.x + 1\
  4055.     self.public:resize( self.width, self.height )\
  4056. end", meta={
  4057.   type = "class",
  4058. }};["libttf"]={content="\
  4059. local pack = {\
  4060. [\"NovaFS.lua\"]=\"--[[type=\\\"executable_package\\\", name=\\\"NovaFS\\\"]]\\\
  4061. local files = {[\\\"filesystem\\\"]={content=\\\"\\\\\\\
  4062. -- drives\\\\\\\
  4063. \\\\\\\
  4064. local drives = { }\\\\\\\
  4065. drives.C = Drive.redirect \\\\\\\"\\\\\\\"\\\\\\\
  4066. \\\\\\\
  4067. local function formatDriveName( name )\\\\\\\
  4068.     if type( name ) == \\\\\\\"string\\\\\\\" then\\\\\\\
  4069.         return name\\\\\\\
  4070.     end\\\\\\\
  4071.     return false\\\\\\\
  4072. end\\\\\\\
  4073. \\\\\\\
  4074. local function getDrive( path )\\\\\\\
  4075.     if not path:find \\\\\\\":\\\\\\\" then\\\\\\\
  4076.         return drives.C, \\\\\\\"no such drive C\\\\\\\"\\\\\\\
  4077.     end\\\\\\\
  4078.     local drive = formatDriveName( path:gsub( \\\\\\\":.*\\\\\\\", \\\\\\\"\\\\\\\" ) )\\\\\\\
  4079.     if drives[drive] then\\\\\\\
  4080.         return drives[drive]\\\\\\\
  4081.     end\\\\\\\
  4082.     return false, \\\\\\\"no such drive \\\\\\\" .. drive\\\\\\\
  4083. end\\\\\\\
  4084. \\\\\\\
  4085. function mount( drive, name )\\\\\\\
  4086.     name = formatDriveName( name )\\\\\\\
  4087.     if not name then\\\\\\\
  4088.         return false, \\\\\\\"expected string name\\\\\\\"\\\\\\\
  4089.     end\\\\\\\
  4090.     if drives[name] then\\\\\\\
  4091.         return false, \\\\\\\"drive already mounted\\\\\\\"\\\\\\\
  4092.     end\\\\\\\
  4093.     if not class.typeOf( drive, Drive ) then\\\\\\\
  4094.         return false, \\\\\\\"expected Drive object\\\\\\\"\\\\\\\
  4095.     end\\\\\\\
  4096.     drives[name] = drive\\\\\\\
  4097.     return true\\\\\\\
  4098. end\\\\\\\
  4099. \\\\\\\
  4100. function unmount( name )\\\\\\\
  4101.     drives[formatDriveName( name )] = nil\\\\\\\
  4102. end\\\\\\\
  4103. \\\\\\\
  4104. function getmounted( name )\\\\\\\
  4105.     return drives[formatDriveName( name )]\\\\\\\
  4106. end\\\\\\\
  4107. \\\\\\\
  4108. function listmounted( )\\\\\\\
  4109.     local t = { }\\\\\\\
  4110.     for name in pairs( drives ) do\\\\\\\
  4111.         t[#t+1] = name\\\\\\\
  4112.     end\\\\\\\
  4113.     return t\\\\\\\
  4114. end\\\\\\\
  4115. \\\\\\\
  4116. -- types\\\\\\\
  4117. \\\\\\\
  4118. local extensions = {\\\\\\\
  4119.     txt = \\\\\\\"text\\\\\\\";\\\\\\\
  4120.     lua = \\\\\\\"lua\\\\\\\";\\\\\\\
  4121.     nim = \\\\\\\"nova_image\\\\\\\";\\\\\\\
  4122.     nac = \\\\\\\"archive\\\\\\\";\\\\\\\
  4123. }\\\\\\\
  4124. local types = {\\\\\\\
  4125.     text = \\\\\\\"Plain text file\\\\\\\";\\\\\\\
  4126.     lua = \\\\\\\"Lua script file\\\\\\\";\\\\\\\
  4127.     unknown = \\\\\\\"Unknown file type\\\\\\\";\\\\\\\
  4128.     archive = \\\\\\\"Nova archive\\\\\\\";\\\\\\\
  4129.     class = \\\\\\\"Class file\\\\\\\";\\\\\\\
  4130.     folder = \\\\\\\"Folder\\\\\\\";\\\\\\\
  4131.     lib = \\\\\\\"Lua library\\\\\\\";\\\\\\\
  4132.     nova_image = \\\\\\\"Nova image file\\\\\\\";\\\\\\\
  4133.     shortcut = \\\\\\\"Shortcut\\\\\\\";\\\\\\\
  4134.     -- design_file\\\\\\\
  4135.     -- design_project\\\\\\\
  4136. }\\\\\\\
  4137. local handlers = { }\\\\\\\
  4138. \\\\\\\
  4139. function addExtension( ext, type )\\\\\\\
  4140.     extensions[ext] = type\\\\\\\
  4141. end\\\\\\\
  4142. \\\\\\\
  4143. function addTypeDescription( type, description )\\\\\\\
  4144.     types[type] = description\\\\\\\
  4145. end\\\\\\\
  4146. \\\\\\\
  4147. function getTypeDescription( type )\\\\\\\
  4148.     return types[type]\\\\\\\
  4149. end\\\\\\\
  4150. \\\\\\\
  4151. function addHandler( type, handler )\\\\\\\
  4152.     handlers[type] = handlers[type] or { }\\\\\\\
  4153.     for i = 1, #handlers[type] do\\\\\\\
  4154.         if handlers[type][i] == handler then\\\\\\\
  4155.             return\\\\\\\
  4156.         end\\\\\\\
  4157.     end\\\\\\\
  4158.     table.insert( handlers[type], handler )\\\\\\\
  4159. end\\\\\\\
  4160. \\\\\\\
  4161. function setDefaultHandler( type, handler )\\\\\\\
  4162.     handlers[type] = handlers[type] or { }\\\\\\\
  4163.     for i = 1, #handlers[type] do\\\\\\\
  4164.         if handlers[type][i] == handler then\\\\\\\
  4165.             table.remove( handlers[type], i )\\\\\\\
  4166.             break\\\\\\\
  4167.         end\\\\\\\
  4168.     end\\\\\\\
  4169.     table.insert( handlers[type], handler )\\\\\\\
  4170. end\\\\\\\
  4171. \\\\\\\
  4172. function getHandlers( type )\\\\\\\
  4173.     return { unpack( handlers[type] or { } ) }\\\\\\\
  4174. end\\\\\\\
  4175. \\\\\\\
  4176. function getAllHandlers( )\\\\\\\
  4177.     local t = { }\\\\\\\
  4178.     for k, v in pairs( handlers ) do\\\\\\\
  4179.         for i = 1, #v do\\\\\\\
  4180.             t[v[i]] = true\\\\\\\
  4181.         end\\\\\\\
  4182.     end\\\\\\\
  4183.     local t2 = { }\\\\\\\
  4184.     for k, v in pairs( t ) do\\\\\\\
  4185.         t2[#t2+1] = k\\\\\\\
  4186.     end\\\\\\\
  4187.     return t2\\\\\\\
  4188. end\\\\\\\
  4189. \\\\\\\
  4190. -- caching\\\\\\\
  4191. \\\\\\\
  4192. local cache = { }\\\\\\\
  4193. local function filecacheset( path, value )\\\\\\\
  4194.     path = path:lower( )\\\\\\\
  4195.     cache[path] = value\\\\\\\
  4196. end\\\\\\\
  4197. local function filecacheget( path )\\\\\\\
  4198.     path = path:lower( )\\\\\\\
  4199.     return cache[path]\\\\\\\
  4200. end\\\\\\\
  4201. \\\\\\\
  4202. function clearCache( )\\\\\\\
  4203.     cache = { }\\\\\\\
  4204. end\\\\\\\
  4205. \\\\\\\
  4206. -- read/write stuff (cache stuff!)\\\\\\\
  4207. \\\\\\\
  4208. local function read( path )\\\\\\\
  4209.     if type( filecacheget( path ) ) == \\\\\\\"table\\\\\\\" then return false, \\\\\\\"could not read folder\\\\\\\" end\\\\\\\
  4210.     local drive, err = getDrive( path )\\\\\\\
  4211.     if not drive then\\\\\\\
  4212.         return false, err\\\\\\\
  4213.     end\\\\\\\
  4214.     local content = filecacheget( path )\\\\\\\
  4215.     if content then\\\\\\\
  4216.         return content\\\\\\\
  4217.     end\\\\\\\
  4218.     local h = drive.open( path:gsub( \\\\\\\".-:\\\\\\\", \\\\\\\"\\\\\\\", 1 ), \\\\\\\"r\\\\\\\" )\\\\\\\
  4219.     if h then\\\\\\\
  4220.         local content = h.readAll( )\\\\\\\
  4221.         h.close( )\\\\\\\
  4222.         return content\\\\\\\
  4223.     else\\\\\\\
  4224.         return false, \\\\\\\"could not open file\\\\\\\"\\\\\\\
  4225.     end\\\\\\\
  4226. end\\\\\\\
  4227. local function write( path, content )\\\\\\\
  4228.     if type( filecacheget( path ) ) == \\\\\\\"table\\\\\\\" then return false, \\\\\\\"could not write to folder\\\\\\\" end\\\\\\\
  4229.     local drive, err = getDrive( path )\\\\\\\
  4230.     if not drive then\\\\\\\
  4231.         return false, err\\\\\\\
  4232.     end\\\\\\\
  4233.     local h = drive.open( path:gsub( \\\\\\\".-:\\\\\\\", \\\\\\\"\\\\\\\", 1 ), \\\\\\\"w\\\\\\\" )\\\\\\\
  4234.     if h then\\\\\\\
  4235.         h.write( content )\\\\\\\
  4236.         h.close( )\\\\\\\
  4237.         filecacheset( path, content )\\\\\\\
  4238.         return true\\\\\\\
  4239.     else\\\\\\\
  4240.         return false, \\\\\\\"could not open file\\\\\\\"\\\\\\\
  4241.     end\\\\\\\
  4242. end\\\\\\\
  4243. \\\\\\\
  4244. -- name operations\\\\\\\
  4245. \\\\\\\
  4246. function getDirectory( path )\\\\\\\
  4247.     if not path:find \\\\\\\"/\\\\\\\" then return \\\\\\\"\\\\\\\" end\\\\\\\
  4248.     return path:gsub( \\\\\\\"/.-$\\\\\\\", \\\\\\\"\\\\\\\" ) -- remove filename\\\\\\\
  4249. end\\\\\\\
  4250. \\\\\\\
  4251. function getPath( path )\\\\\\\
  4252.     if not path:find \\\\\\\"/\\\\\\\" then return \\\\\\\"\\\\\\\" end\\\\\\\
  4253.     return path:gsub( \\\\\\\"/.-$\\\\\\\", \\\\\\\"\\\\\\\" ) -- remove filename\\\\\\\
  4254. end\\\\\\\
  4255. \\\\\\\
  4256. function getName( path, trimExt )\\\\\\\
  4257.     path = path:gsub( \\\\\\\"^.+/\\\\\\\", \\\\\\\"\\\\\\\" ) -- remove directories\\\\\\\
  4258.     if trimExt and path:sub( 2 ):find \\\\\\\"%.\\\\\\\" then\\\\\\\
  4259.         return path:sub( 1, 1 ) .. path:sub( 2 ):gsub( \\\\\\\"%.%w+$\\\\\\\", \\\\\\\"\\\\\\\" ) -- remove extension\\\\\\\
  4260.     end\\\\\\\
  4261.     return path\\\\\\\
  4262. end\\\\\\\
  4263. \\\\\\\
  4264. function getExtension( path )\\\\\\\
  4265.     path = path:gsub( \\\\\\\"^.+/\\\\\\\", \\\\\\\"\\\\\\\" )\\\\\\\
  4266.     if not path:sub( 2 ):find \\\\\\\"%.\\\\\\\" then\\\\\\\
  4267.         return \\\\\\\"\\\\\\\"\\\\\\\
  4268.     end\\\\\\\
  4269.     return path:gsub( \\\\\\\".+%.\\\\\\\", \\\\\\\"\\\\\\\" ) -- remove directories, then stuff before the .\\\\\\\
  4270. end\\\\\\\
  4271. \\\\\\\
  4272. function merge( path, ... )\\\\\\\
  4273.     local paths = { ... }\\\\\\\
  4274.     for i = 1, #paths do\\\\\\\
  4275.         path = path .. \\\\\\\"/\\\\\\\" .. tostring( paths[i] )\\\\\\\
  4276.     end\\\\\\\
  4277.     return path:gsub( \\\\\\\"//+\\\\\\\", \\\\\\\"/\\\\\\\" ):gsub( \\\\\\\"^/\\\\\\\", \\\\\\\"\\\\\\\" ):gsub( \\\\\\\"/$\\\\\\\", \\\\\\\"\\\\\\\" )\\\\\\\
  4278. end\\\\\\\
  4279. \\\\\\\
  4280. -- file methods\\\\\\\
  4281. \\\\\\\
  4282. function readfile( path, password )\\\\\\\
  4283.     local content, err = read( path )\\\\\\\
  4284.     if content then\\\\\\\
  4285.         return FileData( content, password )\\\\\\\
  4286.     end\\\\\\\
  4287.     return false, err\\\\\\\
  4288. end\\\\\\\
  4289. \\\\\\\
  4290. function writefile( path, data, password )\\\\\\\
  4291.     if password then\\\\\\\
  4292.         data.password = password\\\\\\\
  4293.     end\\\\\\\
  4294.     return write( path, data:compile( ) )\\\\\\\
  4295. end\\\\\\\
  4296. \\\\\\\
  4297. function appendfile( path, new, password, readpass )\\\\\\\
  4298.     local data, err = readfile( path, readpass or password )\\\\\\\
  4299.     if data then\\\\\\\
  4300.         data.content = data.content .. new\\\\\\\
  4301.         if password then\\\\\\\
  4302.             data.password = password\\\\\\\
  4303.         end\\\\\\\
  4304.         return write( path, password, data:compile( ) )\\\\\\\
  4305.     end\\\\\\\
  4306.     return false, err\\\\\\\
  4307. end\\\\\\\
  4308. \\\\\\\
  4309. function getType( path )\\\\\\\
  4310.     if type( filecacheget( path ) ) ~= \\\\\\\"string\\\\\\\" and ( type( filecacheget( path ) ) == \\\\\\\"table\\\\\\\" or isDirectory( path ) ) then\\\\\\\
  4311.         filecacheset( path, { } )\\\\\\\
  4312.         return \\\\\\\"folder\\\\\\\"\\\\\\\
  4313.     end\\\\\\\
  4314.     local content, err = read( path )\\\\\\\
  4315.     if not content then\\\\\\\
  4316.         return \\\\\\\"unknown\\\\\\\"\\\\\\\
  4317.     end\\\\\\\
  4318.     local data = FileData( content )\\\\\\\
  4319.     if data.meta.type then\\\\\\\
  4320.         return data.meta.type\\\\\\\
  4321.     end\\\\\\\
  4322.     local ext = getExtension( path )\\\\\\\
  4323.     if #ext == 0 then return \\\\\\\"unknown\\\\\\\" end\\\\\\\
  4324.     return extensions[ext] or \\\\\\\".\\\\\\\" .. ext\\\\\\\
  4325. end\\\\\\\
  4326. \\\\\\\
  4327. function getFileMetaValue( path, index )\\\\\\\
  4328.     if type( filecacheget( path ) ) == \\\\\\\"table\\\\\\\" or ( type( filecacheget( path ) ) ~= \\\\\\\"string\\\\\\\" and isDirectory( path ) ) then\\\\\\\
  4329.         return false, \\\\\\\"folders can't have metadata\\\\\\\"\\\\\\\
  4330.     end\\\\\\\
  4331.     local content, err = read( path )\\\\\\\
  4332.     if not content then\\\\\\\
  4333.         return false, err\\\\\\\
  4334.     end\\\\\\\
  4335.     return true, FileData( content ).meta[index]\\\\\\\
  4336. end\\\\\\\
  4337. \\\\\\\
  4338. function isProtected( path )\\\\\\\
  4339.     if type( filecacheget( path ) ) == \\\\\\\"table\\\\\\\" then return false end\\\\\\\
  4340.     local content, err = read( path )\\\\\\\
  4341.     if content then\\\\\\\
  4342.         local fd = FileData( content )\\\\\\\
  4343.         return fd.meta.password\\\\\\\
  4344.     end\\\\\\\
  4345.     return false, err\\\\\\\
  4346. end\\\\\\\
  4347. \\\\\\\
  4348. function newFile( path, content, password )\\\\\\\
  4349.     if filecacheget( path ) then return false, \\\\\\\"file exists\\\\\\\" end\\\\\\\
  4350.     if exists( path ) then return false, \\\\\\\"file exists\\\\\\\" end\\\\\\\
  4351.     local fd = FileData( content )\\\\\\\
  4352.     if password then\\\\\\\
  4353.         fd.password = password\\\\\\\
  4354.     end\\\\\\\
  4355.     write( path, fd:compile( ) )\\\\\\\
  4356.     return true\\\\\\\
  4357. end\\\\\\\
  4358. \\\\\\\
  4359. -- directory methods\\\\\\\
  4360. \\\\\\\
  4361. function newDirectory( path )\\\\\\\
  4362.     if type( filecacheget( path ) ) == \\\\\\\"string\\\\\\\" then return false, \\\\\\\"file exists\\\\\\\" end\\\\\\\
  4363.     if filecacheget( path ) then return true end\\\\\\\
  4364.     local drive, err = getDrive( path )\\\\\\\
  4365.     if not drive then\\\\\\\
  4366.         return false, err\\\\\\\
  4367.     end\\\\\\\
  4368.     if drive.exists( path:gsub( \\\\\\\"^.-:\\\\\\\", \\\\\\\"\\\\\\\", 1 ) ) then\\\\\\\
  4369.         if drive.isDir( path:gsub( \\\\\\\"^.-:\\\\\\\", \\\\\\\"\\\\\\\", 1 ) ) then\\\\\\\
  4370.             filecacheset( path, { } )\\\\\\\
  4371.         else\\\\\\\
  4372.             return false, \\\\\\\"file exists\\\\\\\"\\\\\\\
  4373.         end\\\\\\\
  4374.     else\\\\\\\
  4375.         drive.makeDir( path:gsub( \\\\\\\"^.-:\\\\\\\", \\\\\\\"\\\\\\\", 1 ) )\\\\\\\
  4376.         filecacheset( path, { } )\\\\\\\
  4377.     end\\\\\\\
  4378. end\\\\\\\
  4379. \\\\\\\
  4380. function listFiles( path, sorter )\\\\\\\
  4381.     if type( filecacheget( path ) ) == \\\\\\\"string\\\\\\\" then return false, \\\\\\\"not a directory\\\\\\\" end\\\\\\\
  4382.     local drive, err = getDrive( path )\\\\\\\
  4383.     if not drive then\\\\\\\
  4384.         return false, err\\\\\\\
  4385.     end\\\\\\\
  4386.     if type( filecacheget( path ) ) == \\\\\\\"table\\\\\\\" or drive.isDir( path:gsub( \\\\\\\"^.-:\\\\\\\", \\\\\\\"\\\\\\\", 1 ) ) then\\\\\\\
  4387.         local files = drive.list( path:gsub( \\\\\\\"^.-:\\\\\\\", \\\\\\\"\\\\\\\", 1 ) )\\\\\\\
  4388.         if files and type( sorter ) == \\\\\\\"function\\\\\\\" then\\\\\\\
  4389.             sorter( files )\\\\\\\
  4390.         end\\\\\\\
  4391.         return files\\\\\\\
  4392.     end\\\\\\\
  4393.     return false, \\\\\\\"not a directory\\\\\\\"\\\\\\\
  4394. end\\\\\\\
  4395. \\\\\\\
  4396. function emptyDirectory( path, filter )\\\\\\\
  4397. \\\\\\\
  4398. end\\\\\\\
  4399. \\\\\\\
  4400. -- existance checking\\\\\\\
  4401. \\\\\\\
  4402. function isFile( path ) -- bool isFile\\\\\\\
  4403.     if type( filecacheget( path ) ) == \\\\\\\"string\\\\\\\" then return true end\\\\\\\
  4404.     if type( filecacheget( path ) ) == \\\\\\\"table\\\\\\\" then return false end\\\\\\\
  4405.     local drive, err = getDrive( path )\\\\\\\
  4406.     if not drive then\\\\\\\
  4407.         return false\\\\\\\
  4408.     end\\\\\\\
  4409.     return drive.exists( path:gsub( \\\\\\\"^.-:\\\\\\\", \\\\\\\"\\\\\\\", 1 ) ) and not drive.isDir( path:gsub( \\\\\\\"^.-:\\\\\\\", \\\\\\\"\\\\\\\", 1 ) )\\\\\\\
  4410. end\\\\\\\
  4411. \\\\\\\
  4412. function isDirectory( path )\\\\\\\
  4413.     if type( filecacheget( path ) ) == \\\\\\\"string\\\\\\\" then return false end\\\\\\\
  4414.     if type( filecacheget( path ) ) == \\\\\\\"table\\\\\\\" then return true end\\\\\\\
  4415.     local drive, err = getDrive( path )\\\\\\\
  4416.     if not drive then\\\\\\\
  4417.         return false\\\\\\\
  4418.     end\\\\\\\
  4419.     local isdir = drive.isDir( path:gsub( \\\\\\\"^.-:\\\\\\\", \\\\\\\"\\\\\\\", 1 ) )\\\\\\\
  4420.     if isdir then filecacheset( path, { } ) end\\\\\\\
  4421.     return isdir\\\\\\\
  4422. end\\\\\\\
  4423. \\\\\\\
  4424. function exists( path )\\\\\\\
  4425.     if filecacheget( path ) then return true end\\\\\\\
  4426.     local drive, err = getDrive( path )\\\\\\\
  4427.     if not drive then\\\\\\\
  4428.         return false\\\\\\\
  4429.     end\\\\\\\
  4430.     return drive.exists( path:gsub( \\\\\\\"^.-:\\\\\\\", \\\\\\\"\\\\\\\", 1 ) )\\\\\\\
  4431. end\\\\\\\
  4432. \\\\\\\
  4433. -- space checking\\\\\\\
  4434. \\\\\\\
  4435. function getSize( path )\\\\\\\
  4436.     local drive, err = getDrive( path )\\\\\\\
  4437.     if not drive then\\\\\\\
  4438.         return 0\\\\\\\
  4439.     end\\\\\\\
  4440.     return drive.getSize( path:gsub( \\\\\\\"^.-:\\\\\\\", \\\\\\\"\\\\\\\", 1 ) )\\\\\\\
  4441. end\\\\\\\
  4442. \\\\\\\
  4443. function getSpace( path )\\\\\\\
  4444.     local drive, err = getDrive( path )\\\\\\\
  4445.     if not drive then\\\\\\\
  4446.         return 0\\\\\\\
  4447.     end\\\\\\\
  4448.     return drive.getFreeSpace( path:gsub( \\\\\\\"^.-:\\\\\\\", \\\\\\\"\\\\\\\", 1 ) )\\\\\\\
  4449. end\\\\\\\
  4450. \\\\\\\
  4451. function isReadOnly( path )\\\\\\\
  4452.     local drive, err = getDrive( path )\\\\\\\
  4453.     if not drive then\\\\\\\
  4454.         return false\\\\\\\
  4455.     end\\\\\\\
  4456.     return drive.isReadOnly( path:gsub( \\\\\\\"^.-:\\\\\\\", \\\\\\\"\\\\\\\", 1 ) )\\\\\\\
  4457. end\\\\\\\
  4458. \\\\\\\
  4459. -- mixed methods\\\\\\\
  4460. \\\\\\\
  4461. function delete( path )\\\\\\\
  4462.     local drive, err = getDrive( path )\\\\\\\
  4463.     if not drive then\\\\\\\
  4464.         return false, err\\\\\\\
  4465.     end\\\\\\\
  4466.     filecacheset( path, nil )\\\\\\\
  4467.     return drive.delete( path:gsub( \\\\\\\"^.-:\\\\\\\", \\\\\\\"\\\\\\\", 1 ) )\\\\\\\
  4468. end\\\\\\\
  4469. \\\\\\\
  4470. function copy( path )\\\\\\\
  4471.     local drive, err = getDrive( path )\\\\\\\
  4472.     if not drive then\\\\\\\
  4473.         return false, err\\\\\\\
  4474.     end\\\\\\\
  4475.     if isFile( path ) then\\\\\\\
  4476.         local data, err = readfile( path )\\\\\\\
  4477.         if data then\\\\\\\
  4478.             clipboard.set( \\\\\\\"file\\\\\\\", { name = getName( path ), file = data } )\\\\\\\
  4479.         else\\\\\\\
  4480.             return false, err\\\\\\\
  4481.         end\\\\\\\
  4482.     elseif isDirectory( path ) then\\\\\\\
  4483.         local a = Archive( )\\\\\\\
  4484.         a:loadDirectory( path )\\\\\\\
  4485.         clipboard.set( \\\\\\\"file\\\\\\\", { name = getName( path ), file = a } )\\\\\\\
  4486.     else\\\\\\\
  4487.         return false, \\\\\\\"no such file\\\\\\\"\\\\\\\
  4488.     end\\\\\\\
  4489.     return true\\\\\\\
  4490. end\\\\\\\
  4491. \\\\\\\
  4492. function cut( path )\\\\\\\
  4493.     local drive, err = getDrive( path )\\\\\\\
  4494.     if not drive then\\\\\\\
  4495.         return false, err\\\\\\\
  4496.     end\\\\\\\
  4497.     if isFile( path ) then\\\\\\\
  4498.         local data, err = readfile( path )\\\\\\\
  4499.         if data then\\\\\\\
  4500.             clipboard.set( \\\\\\\"file\\\\\\\", { name = getName( path ), file = data } )\\\\\\\
  4501.         else\\\\\\\
  4502.             return false, err\\\\\\\
  4503.         end\\\\\\\
  4504.     elseif isDirectory( path ) then\\\\\\\
  4505.         local a = Archive( )\\\\\\\
  4506.         a:loadDirectory( path )\\\\\\\
  4507.         clipboard.set( \\\\\\\"file\\\\\\\", { name = getName( path ), file = a } )\\\\\\\
  4508.     else\\\\\\\
  4509.         return false, \\\\\\\"no such file\\\\\\\"\\\\\\\
  4510.     end\\\\\\\
  4511.     delete( path )\\\\\\\
  4512.     return true\\\\\\\
  4513. end\\\\\\\
  4514. \\\\\\\
  4515. function paste( up_path )\\\\\\\
  4516.     up_path = type( up_path ) == \\\\\\\"string\\\\\\\" and up_path or \\\\\\\"\\\\\\\"\\\\\\\
  4517.     local mode, data = clipboard.get( )\\\\\\\
  4518.     if mode == \\\\\\\"file\\\\\\\" then\\\\\\\
  4519.         local n = up_path .. \\\\\\\"/\\\\\\\" .. data.name\\\\\\\
  4520.         if filesystem.exists( n ) then\\\\\\\
  4521.             local name = up_path .. \\\\\\\"/\\\\\\\" .. getName( data.name, true ) .. \\\\\\\" - Copy (\\\\\\\"\\\\\\\
  4522.             local ext = getExtension( data.name )\\\\\\\
  4523.             if #ext > 0 then\\\\\\\
  4524.                 ext = \\\\\\\".\\\\\\\" .. ext\\\\\\\
  4525.             end\\\\\\\
  4526.             local i = 1\\\\\\\
  4527.             while filesystem.exists( name .. i .. \\\\\\\")\\\\\\\" .. ext ) do\\\\\\\
  4528.                 i = i + 1\\\\\\\
  4529.             end\\\\\\\
  4530.             n = name .. i .. \\\\\\\").\\\\\\\" .. ext\\\\\\\
  4531.         end\\\\\\\
  4532.         if data.file:type( ) == \\\\\\\"Archive\\\\\\\" then\\\\\\\
  4533.             data.file:saveDirectory( n )\\\\\\\
  4534.         else\\\\\\\
  4535.             write( n, data.file:compile( ) )\\\\\\\
  4536.         end\\\\\\\
  4537.     end\\\\\\\
  4538. end\\\\\\\
  4539. \\\\\\\
  4540. function rename( path, name )\\\\\\\
  4541.     local drive, err = getDrive( path )\\\\\\\
  4542.     if not drive then\\\\\\\
  4543.         return false, err\\\\\\\
  4544.     end\\\\\\\
  4545.     filecacheset( path, nil )\\\\\\\
  4546.     local path = path:gsub( \\\\\\\"^.-:\\\\\\\", \\\\\\\"\\\\\\\", 1 )\\\\\\\
  4547.     return drive.move( path, path:gsub( getName( path ) .. \\\\\\\"$\\\\\\\", name, 1 ) )\\\\\\\
  4548. end\\\\\\\
  4549. \\\\\\\
  4550. function move( path, path2 )\\\\\\\
  4551.     local drive, err = getDrive( path )\\\\\\\
  4552.     if not drive then\\\\\\\
  4553.         return false, err\\\\\\\
  4554.     end\\\\\\\
  4555.     filecacheset( path, nil )\\\\\\\
  4556.     local path = path:gsub( \\\\\\\"^.-:\\\\\\\", \\\\\\\"\\\\\\\", 1 )\\\\\\\
  4557.     local path2 = path2:gsub( \\\\\\\"^.-:\\\\\\\", \\\\\\\"\\\\\\\", 1 )\\\\\\\
  4558.     return drive.move( path, path2 )\\\\\\\
  4559. end\\\\\\\
  4560. \\\\\\\
  4561. local index = { }\\\\\\\
  4562. local function updateInternal( )\\\\\\\
  4563.     while true do\\\\\\\
  4564.         local nindex = { }\\\\\\\
  4565.         local function listDir( p )\\\\\\\
  4566.             local files = listFiles( p )\\\\\\\
  4567.             for i = 1, #files do\\\\\\\
  4568.                 local path = merge( p, files[i] )\\\\\\\
  4569.                 nindex[path] = { name = files[i] }\\\\\\\
  4570.                 if isDirectory( \\\\\\\"C:/\\\\\\\" .. path ) then\\\\\\\
  4571.                     nindex[path].type = \\\\\\\"directory\\\\\\\"\\\\\\\
  4572.                     listDir( path, nindex )\\\\\\\
  4573.                 else\\\\\\\
  4574.                     nindex[path].type = \\\\\\\"file\\\\\\\"\\\\\\\
  4575.                     nindex[path].filetype = getType( \\\\\\\"C:/\\\\\\\" .. path )\\\\\\\
  4576.                 end\\\\\\\
  4577.                 coroutine.yield( )\\\\\\\
  4578.             end\\\\\\\
  4579.         end\\\\\\\
  4580.         listDir \\\\\\\"\\\\\\\"\\\\\\\
  4581.         index = nindex\\\\\\\
  4582.         coroutine.yield \\\\\\\"full\\\\\\\"\\\\\\\
  4583.     end\\\\\\\
  4584. end\\\\\\\
  4585. local co = coroutine.create( updateInternal )\\\\\\\
  4586. function updateIndex( )\\\\\\\
  4587.     local ok, data = coroutine.resume( co )\\\\\\\
  4588.     if not ok then\\\\\\\
  4589.         error( data, 0 )\\\\\\\
  4590.     end\\\\\\\
  4591.     return data == \\\\\\\"full\\\\\\\"\\\\\\\
  4592. end\\\\\\\
  4593. \\\\\\\
  4594. function findType( _type )\\\\\\\
  4595.     local t = { }\\\\\\\
  4596.     for k, v in pairs( index ) do\\\\\\\
  4597.         if v.type == \\\\\\\"file\\\\\\\" and v.filetype == _type then\\\\\\\
  4598.             table.insert( t, {\\\\\\\
  4599.                 path = k;\\\\\\\
  4600.                 name = v.name;\\\\\\\
  4601.                 type = v.type;\\\\\\\
  4602.                 filetype = v.filetype;\\\\\\\
  4603.             } )\\\\\\\
  4604.         end\\\\\\\
  4605.     end\\\\\\\
  4606.     return t\\\\\\\
  4607. end\\\\\\\
  4608. \\\\\\\
  4609. function findName( name )\\\\\\\
  4610.     name = name:lower( ):gsub( \\\\\\\"%*\\\\\\\", \\\\\\\".*\\\\\\\" )\\\\\\\
  4611.     local t = { }\\\\\\\
  4612.     for k, v in pairs( index ) do\\\\\\\
  4613.         if k:lower( ):find( name ) then\\\\\\\
  4614.             table.insert( t, {\\\\\\\
  4615.                 path = k;\\\\\\\
  4616.                 name = v.name;\\\\\\\
  4617.                 type = v.type;\\\\\\\
  4618.                 filetype = v.filetype;\\\\\\\
  4619.             } )\\\\\\\
  4620.         end\\\\\\\
  4621.     end\\\\\\\
  4622.     return t\\\\\\\
  4623. end\\\", meta={\\\
  4624.  type = \\\"lib\\\",\\\
  4625. }};[\\\"clipboard\\\"]={content=\\\"if NovaClipboard then\\\\\\\
  4626.     set, get = NovaClipboard.set, NovaClipboard.get\\\\\\\
  4627. else\\\\\\\
  4628.     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\\\\\\\
  4629.     export(\\\\\\\"NovaClipboard\\\\\\\",{set=set,get=get})\\\\\\\
  4630. end\\\", meta={\\\
  4631.  type = \\\"lib\\\",\\\
  4632. }};[\\\"Archive\\\"]={content=\\\"\\\\\\\
  4633. require \\\\\\\"Drive\\\\\\\"\\\\\\\
  4634. \\\\\\\
  4635. local rfs = fs\\\\\\\
  4636. \\\\\\\
  4637. function Archive:Archive( savepath )\\\\\\\
  4638.     self.files = { }\\\\\\\
  4639.     self.savepath = savepath\\\\\\\
  4640. \\\\\\\
  4641.     return self.public\\\\\\\
  4642. end\\\\\\\
  4643. \\\\\\\
  4644. function Archive.public:loadDirectory( path )\\\\\\\
  4645.     if not filesystem.isDirectory( path ) then\\\\\\\
  4646.         return false, \\\\\\\"not a directory\\\\\\\"\\\\\\\
  4647.     end\\\\\\\
  4648.     local function loadDirectory( path, t )\\\\\\\
  4649.         local files = filesystem.listFiles( path )\\\\\\\
  4650.         for i = 1, #files do\\\\\\\
  4651.             if filesystem.isDirectory( path .. \\\\\\\"/\\\\\\\" .. files[i] ) then\\\\\\\
  4652.                 t[files[i]] = { }\\\\\\\
  4653.                 loadDirectory( path .. \\\\\\\"/\\\\\\\" .. files[i], t[files[i]] )\\\\\\\
  4654.             else\\\\\\\
  4655.                 local filedata, err = filesystem.readfile( path .. \\\\\\\"/\\\\\\\" .. files[i] )\\\\\\\
  4656.                 if filedata then\\\\\\\
  4657.                     t[files[i]] = filedata:compile( )\\\\\\\
  4658.                 end\\\\\\\
  4659.             end\\\\\\\
  4660.         end\\\\\\\
  4661.     end\\\\\\\
  4662.     self.files = { }\\\\\\\
  4663.     loadDirectory( path, self.files )\\\\\\\
  4664.     return self.public\\\\\\\
  4665. end\\\\\\\
  4666. \\\\\\\
  4667. function Archive.public:loadFile( path, password )\\\\\\\
  4668.     if not filesystem.isFile( path ) then\\\\\\\
  4669.         return false, \\\\\\\"not a file\\\\\\\"\\\\\\\
  4670.     end\\\\\\\
  4671.     local filedata, err = filesystem.readfile( path, password )\\\\\\\
  4672.     if not filedata then\\\\\\\
  4673.         return false, err\\\\\\\
  4674.     end\\\\\\\
  4675.     if filedata.meta.password then\\\\\\\
  4676.         return false, \\\\\\\"password protected\\\\\\\"\\\\\\\
  4677.     end\\\\\\\
  4678.     local files = textutils.unserialize( filedata.content )\\\\\\\
  4679.     if type( files ) == \\\\\\\"table\\\\\\\" then\\\\\\\
  4680.         self.files = files\\\\\\\
  4681.         return true\\\\\\\
  4682.     end\\\\\\\
  4683.     return false, \\\\\\\"corrupted archive\\\\\\\"\\\\\\\
  4684. end\\\\\\\
  4685. \\\\\\\
  4686. function Archive.public:saveDirectory( path )\\\\\\\
  4687.     if filesystem.isFile( path ) then\\\\\\\
  4688.         return false, \\\\\\\"path exists\\\\\\\"\\\\\\\
  4689.     elseif not filesystem.exists( path ) then\\\\\\\
  4690.         filesystem.newDirectory( path )\\\\\\\
  4691.     end\\\\\\\
  4692.     local function saveFile( file, p )\\\\\\\
  4693.         if type( file ) == \\\\\\\"table\\\\\\\" then\\\\\\\
  4694.             for k, v in pairs( file ) do\\\\\\\
  4695.                 saveFile( v, p .. \\\\\\\"/\\\\\\\" .. k )\\\\\\\
  4696.             end\\\\\\\
  4697.         else\\\\\\\
  4698.             filesystem.writefile( path .. p, FileData( file ) )\\\\\\\
  4699.         end\\\\\\\
  4700.     end\\\\\\\
  4701.     saveFile( self.files, \\\\\\\"\\\\\\\" )\\\\\\\
  4702.     return true\\\\\\\
  4703. end\\\\\\\
  4704. \\\\\\\
  4705. function Archive.public:saveFile( path, password )\\\\\\\
  4706.     if filesystem.exists( path ) then\\\\\\\
  4707.         return false, \\\\\\\"file exists\\\\\\\"\\\\\\\
  4708.     end\\\\\\\
  4709.     local content = \\\\\\\"--@meta[type]:\\\\\\\\\\\\\\\"archive\\\\\\\\\\\\\\\"\\\\\\\\n\\\\\\\" .. textutils.serialize( self.files )\\\\\\\
  4710.     local filedata = FileData( content )\\\\\\\
  4711.     if password then\\\\\\\
  4712.         filedata.password = password\\\\\\\
  4713.     end\\\\\\\
  4714.     return filesystem.writefile( path, filedata )\\\\\\\
  4715. end\\\\\\\
  4716. \\\\\\\
  4717. function Archive.public:createDrive( )\\\\\\\
  4718.     \\\\\\\
  4719.     local fs = { }\\\\\\\
  4720.     local function splitPath( path )\\\\\\\
  4721.         local parts = { }\\\\\\\
  4722.         for part in path:gmatch \\\\\\\"([^/]+)\\\\\\\" do\\\\\\\
  4723.             table.insert( parts, part )\\\\\\\
  4724.         end\\\\\\\
  4725.         return parts\\\\\\\
  4726.     end\\\\\\\
  4727.     local function getDir( path )\\\\\\\
  4728.         if type( path ) ~= \\\\\\\"string\\\\\\\" then\\\\\\\
  4729.             return false, \\\\\\\"string expected\\\\\\\"\\\\\\\
  4730.         end\\\\\\\
  4731.         if path == \\\\\\\"\\\\\\\" or path == \\\\\\\"/\\\\\\\" then\\\\\\\
  4732.             return true, { [\\\\\\\"\\\\\\\"] = self.files }, \\\\\\\"\\\\\\\"\\\\\\\
  4733.         end\\\\\\\
  4734.         local parts = splitPath( path )\\\\\\\
  4735.         local f = self.files\\\\\\\
  4736.         for i = 1, #parts - 1 do\\\\\\\
  4737.             if type( f[parts[i]] ) == \\\\\\\"nil\\\\\\\" then\\\\\\\
  4738.                 f[parts[i]] = { }\\\\\\\
  4739.             end\\\\\\\
  4740.             if type( f[parts[i]] ) == \\\\\\\"table\\\\\\\" then\\\\\\\
  4741.                 f = f[parts[i]]\\\\\\\
  4742.             else\\\\\\\
  4743.                 return false, \\\\\\\"invalid path\\\\\\\"\\\\\\\
  4744.             end\\\\\\\
  4745.         end\\\\\\\
  4746.         return true, f, parts[#parts]\\\\\\\
  4747.     end\\\\\\\
  4748. \\\\\\\
  4749.     function fs.open( path, mode )\\\\\\\
  4750.         local ok, data, file = getDir( path )\\\\\\\
  4751.         if not ok then\\\\\\\
  4752.             return false, data\\\\\\\
  4753.         end\\\\\\\
  4754.         if type( data[file] ) == \\\\\\\"table\\\\\\\" then\\\\\\\
  4755.             return false, \\\\\\\"cannot open directory\\\\\\\"\\\\\\\
  4756.         elseif mode == \\\\\\\"r\\\\\\\" and data[file] == nil then\\\\\\\
  4757.             return false, \\\\\\\"file doesn't exist\\\\\\\"\\\\\\\
  4758.         end\\\\\\\
  4759.         local handle = { }\\\\\\\
  4760.         local open = true\\\\\\\
  4761.         function handle.close( )\\\\\\\
  4762.             open = false\\\\\\\
  4763.         end\\\\\\\
  4764. \\\\\\\
  4765.         if mode == \\\\\\\"w\\\\\\\" then\\\\\\\
  4766.             data[file] = \\\\\\\"\\\\\\\"\\\\\\\
  4767.         elseif mode == \\\\\\\"a\\\\\\\" then\\\\\\\
  4768.             data[file] = data[file] or \\\\\\\"\\\\\\\"\\\\\\\
  4769.         elseif mode ~= \\\\\\\"r\\\\\\\" then\\\\\\\
  4770.             return false, \\\\\\\"unsupported mode\\\\\\\"\\\\\\\
  4771.         end\\\\\\\
  4772.         local filedata = FileData( data[file] )\\\\\\\
  4773. \\\\\\\
  4774.         if mode == \\\\\\\"w\\\\\\\" or mode == \\\\\\\"a\\\\\\\" then\\\\\\\
  4775.             function handle.write( str )\\\\\\\
  4776.                 if not open then return end\\\\\\\
  4777.                 filedata.content = filedata.content .. tostring( str )\\\\\\\
  4778.             end\\\\\\\
  4779.             function handle.writeLine( )\\\\\\\
  4780.                 if not open then return end\\\\\\\
  4781.                 filedata.content = filedata.content .. tostring( str ) .. \\\\\\\"\\\\\\\\n\\\\\\\"\\\\\\\
  4782.             end\\\\\\\
  4783.             function handle.flush( )\\\\\\\
  4784.                 if not open then return end\\\\\\\
  4785.                 data[file] = filedata:compile( )\\\\\\\
  4786.                 if self.savepath then\\\\\\\
  4787.                     filesystem.delete( self.savepath )\\\\\\\
  4788.                     self.public:saveFile( self.savepath )\\\\\\\
  4789.                 end\\\\\\\
  4790.             end\\\\\\\
  4791.             function handle.close( )\\\\\\\
  4792.                 if not open then return end\\\\\\\
  4793.                 handle.flush( )\\\\\\\
  4794.                 open = false\\\\\\\
  4795.             end\\\\\\\
  4796.         elseif mode == \\\\\\\"r\\\\\\\" then\\\\\\\
  4797.             local lcontent = filedata.content\\\\\\\
  4798.             function handle.readLine( )\\\\\\\
  4799.                 if not open then return end\\\\\\\
  4800.                 local line = lcontent:match \\\\\\\"(.-)\\\\\\\\n\\\\\\\"\\\\\\\
  4801.                 if line then\\\\\\\
  4802.                     lcontent = lcontent:gsub( \\\\\\\".-\\\\\\\\n\\\\\\\", \\\\\\\"\\\\\\\" )\\\\\\\
  4803.                 else\\\\\\\
  4804.                     if #lcontent > 0 then\\\\\\\
  4805.                         line = lcontent\\\\\\\
  4806.                         lcontent = \\\\\\\"\\\\\\\"\\\\\\\
  4807.                     else\\\\\\\
  4808.                         line = nil\\\\\\\
  4809.                     end\\\\\\\
  4810.                 end\\\\\\\
  4811.                 return line\\\\\\\
  4812.             end\\\\\\\
  4813.             function handle.readAll( )\\\\\\\
  4814.                 if not open then return end\\\\\\\
  4815.                 return filedata.content\\\\\\\
  4816.             end\\\\\\\
  4817.         end\\\\\\\
  4818. \\\\\\\
  4819.         return handle\\\\\\\
  4820.     end\\\\\\\
  4821. \\\\\\\
  4822.     function fs.list( path )\\\\\\\
  4823.         local ok, data, file = getDir( path )\\\\\\\
  4824.         if not ok then\\\\\\\
  4825.             return false, data\\\\\\\
  4826.         end\\\\\\\
  4827.         if type( data[file] ) ~= \\\\\\\"table\\\\\\\" then\\\\\\\
  4828.             return false, \\\\\\\"not a directory\\\\\\\"\\\\\\\
  4829.         end\\\\\\\
  4830.         local t = { }\\\\\\\
  4831.         for k, v in pairs( data[file] ) do\\\\\\\
  4832.             t[#t+1] = k\\\\\\\
  4833.         end\\\\\\\
  4834.         return t\\\\\\\
  4835.     end\\\\\\\
  4836.     function fs.exists( p )\\\\\\\
  4837.         local ok, data, file = getDir( p )\\\\\\\
  4838.         if not ok then\\\\\\\
  4839.             return false, data\\\\\\\
  4840.         end\\\\\\\
  4841.         return data[file] ~= nil\\\\\\\
  4842.     end\\\\\\\
  4843.     function fs.isDir( p )\\\\\\\
  4844.         local ok, data, file = getDir( p )\\\\\\\
  4845.         if not ok then\\\\\\\
  4846.             return false, data\\\\\\\
  4847.         end\\\\\\\
  4848.         return type( data[file] ) == \\\\\\\"table\\\\\\\"\\\\\\\
  4849.     end\\\\\\\
  4850.     function fs.isReadOnly( p )\\\\\\\
  4851.         return false\\\\\\\
  4852.     end\\\\\\\
  4853.     function fs.getDrive( )\\\\\\\
  4854.         return \\\\\\\"archive\\\\\\\"\\\\\\\
  4855.     end\\\\\\\
  4856.     function fs.getSize( path )\\\\\\\
  4857.         local ok, data, file = getDir( path )\\\\\\\
  4858.         if not ok then\\\\\\\
  4859.             return false, data\\\\\\\
  4860.         end\\\\\\\
  4861.         if not data[file] then\\\\\\\
  4862.             return false, \\\\\\\"file doesn't exist\\\\\\\"\\\\\\\
  4863.         end\\\\\\\
  4864.         if type( data[file] ) == \\\\\\\"table\\\\\\\" then\\\\\\\
  4865.             local n = 0\\\\\\\
  4866.             for k, v in pairs( data[file] ) do\\\\\\\
  4867.                 n = n + fs.getSize( path .. \\\\\\\"/\\\\\\\" .. k )\\\\\\\
  4868.             end\\\\\\\
  4869.             return n\\\\\\\
  4870.         end\\\\\\\
  4871.         return #data[file] + #path -- each character is a byte right? 2^8 (a byte) = 256, ascii is 256 characters\\\\\\\
  4872.     end\\\\\\\
  4873.     function fs.getFreeSpace( )\\\\\\\
  4874.         return math.huge\\\\\\\
  4875.     end\\\\\\\
  4876.     function fs.makeDir( path )\\\\\\\
  4877.         local ok, data, file = getDir( path )\\\\\\\
  4878.         if not ok then\\\\\\\
  4879.             return false, data\\\\\\\
  4880.         end\\\\\\\
  4881.         if type( data[file] ) == \\\\\\\"string\\\\\\\" then\\\\\\\
  4882.             return false, \\\\\\\"path is a file\\\\\\\"\\\\\\\
  4883.         end\\\\\\\
  4884.         data[file] = data[file] or { }\\\\\\\
  4885.         if self.savepath then\\\\\\\
  4886.             filesystem.delete( self.savepath )\\\\\\\
  4887.             self.public:saveFile( self.savepath )\\\\\\\
  4888.         end\\\\\\\
  4889.         return true\\\\\\\
  4890.     end\\\\\\\
  4891.     function fs.move( path1, path2 )\\\\\\\
  4892.         local ok, data, file = getDir( path )\\\\\\\
  4893.         if not ok then\\\\\\\
  4894.             return false, data\\\\\\\
  4895.         end\\\\\\\
  4896.         local ok, data2, file2 = getDir( path )\\\\\\\
  4897.         if not ok then\\\\\\\
  4898.             return false, data2\\\\\\\
  4899.         end\\\\\\\
  4900.         if data[file] then\\\\\\\
  4901.             data2[file2] = data[file]\\\\\\\
  4902.             data[file] = nil\\\\\\\
  4903.         end\\\\\\\
  4904.         if self.savepath then\\\\\\\
  4905.             filesystem.delete( self.savepath )\\\\\\\
  4906.             self.public:saveFile( self.savepath )\\\\\\\
  4907.         end\\\\\\\
  4908.         return true\\\\\\\
  4909.     end\\\\\\\
  4910.     function fs.copy( path1, path2 )\\\\\\\
  4911.         local ok, data, file = getDir( path )\\\\\\\
  4912.         if not ok then\\\\\\\
  4913.             return false, data\\\\\\\
  4914.         end\\\\\\\
  4915.         local ok, data2, file2 = getDir( path )\\\\\\\
  4916.         if not ok then\\\\\\\
  4917.             return false, data2\\\\\\\
  4918.         end\\\\\\\
  4919.         if data[file] then\\\\\\\
  4920.             data2[file2] = data[file]\\\\\\\
  4921.         end\\\\\\\
  4922.         if self.savepath then\\\\\\\
  4923.             filesystem.delete( self.savepath )\\\\\\\
  4924.             self.public:saveFile( self.savepath )\\\\\\\
  4925.         end\\\\\\\
  4926.         return true\\\\\\\
  4927.     end\\\\\\\
  4928.     function fs.delete( path )\\\\\\\
  4929.         local ok, data, file = getDir( path )\\\\\\\
  4930.         if not ok then\\\\\\\
  4931.             return false, data\\\\\\\
  4932.         end\\\\\\\
  4933.         data[file] = nil\\\\\\\
  4934.         if self.savepath then\\\\\\\
  4935.             filesystem.delete( self.savepath )\\\\\\\
  4936.             self.public:saveFile( self.savepath )\\\\\\\
  4937.         end\\\\\\\
  4938.         return true\\\\\\\
  4939.     end\\\\\\\
  4940.     function fs.find( path )\\\\\\\
  4941.         error( \\\\\\\"not supported\\\\\\\", 2 )\\\\\\\
  4942.     end\\\\\\\
  4943.     fs.combine = rfs.combine\\\\\\\
  4944.     fs.getDir = rfs.getDir\\\\\\\
  4945.     fs.getName = rfs.getName\\\\\\\
  4946. \\\\\\\
  4947.     return Drive( fs )\\\\\\\
  4948. end\\\", meta={\\\
  4949.  type = \\\"class\\\",\\\
  4950. }};[\\\"main\\\"]={content=\\\"\\\\\\\
  4951. require \\\\\\\"filesystem\\\\\\\"\\\\\\\
  4952. \\\\\\\
  4953. for k, v in pairs( filesystem ) do\\\\\\\
  4954.     export( k, v )\\\\\\\
  4955. end\\\\\\\
  4956. \\\\\\\
  4957. export \\\\\\\"Archive\\\\\\\"\\\\\\\
  4958. export \\\\\\\"FileData\\\\\\\"\\\\\\\
  4959. export \\\\\\\"Drive\\\\\\\"\\\", meta={}};[\\\"Drive\\\"]={content=\\\"\\\\\\\
  4960. function Drive:Drive( methods ) -- table methods\\\\\\\
  4961.     if type( methods ) ~= \\\\\\\"table\\\\\\\" then\\\\\\\
  4962.         error( \\\\\\\"expected table methods\\\\\\\", 2 )\\\\\\\
  4963.     end\\\\\\\
  4964.     for k, v in pairs( fs ) do\\\\\\\
  4965.         if not methods[k] then\\\\\\\
  4966.             error( \\\\\\\"expected \\\\\\\" .. k .. \\\\\\\" method in table\\\\\\\", 2 )\\\\\\\
  4967.         end\\\\\\\
  4968.     end\\\\\\\
  4969. \\\\\\\
  4970.     self.methods = methods\\\\\\\
  4971. \\\\\\\
  4972.     return self.public\\\\\\\
  4973. end\\\\\\\
  4974. \\\\\\\
  4975. function Drive.meta:index( key )\\\\\\\
  4976.     return self.methods[key]\\\\\\\
  4977. end\\\\\\\
  4978. \\\\\\\
  4979. for k, v in pairs( fs ) do\\\\\\\
  4980.     Drive.public( k )\\\\\\\
  4981.     Drive.public[k].write = false\\\\\\\
  4982.     Drive.public[k].read = function( self )\\\\\\\
  4983.         return self.methods[k]\\\\\\\
  4984.     end\\\\\\\
  4985. end\\\\\\\
  4986. \\\\\\\
  4987. local function combine( p1, p2 )\\\\\\\
  4988.     if type( p2 ) ~= \\\\\\\"string\\\\\\\" then\\\\\\\
  4989.         return error \\\\\\\"expected string\\\\\\\"\\\\\\\
  4990.     end\\\\\\\
  4991.     return fs.combine( p1, p2 )\\\\\\\
  4992. end\\\\\\\
  4993. \\\\\\\
  4994. function Drive.static.redirect( path )\\\\\\\
  4995. \\\\\\\
  4996.     local mfs = setmetatable( { }, { __index = fs } )\\\\\\\
  4997.     mfs.open = function( p, mode )\\\\\\\
  4998.         return fs.open( combine( path, p ), mode )\\\\\\\
  4999.     end\\\\\\\
  5000.     mfs.list = function( p )\\\\\\\
  5001.         return fs.list( combine( path, p ) )\\\\\\\
  5002.     end\\\\\\\
  5003.     mfs.exists = function( p )\\\\\\\
  5004.         return fs.exists( combine( path, p ) )\\\\\\\
  5005.     end\\\\\\\
  5006.     mfs.isDir = function( p )\\\\\\\
  5007.         return fs.isDir( combine( path, p ) )\\\\\\\
  5008.     end\\\\\\\
  5009.     mfs.isReadOnly = function( p )\\\\\\\
  5010.         return fs.isReadOnly( combine( path, p ) )\\\\\\\
  5011.     end\\\\\\\
  5012.     mfs.getDrive = function( p )\\\\\\\
  5013.         return fs.getDrive( combine( path, p ) )\\\\\\\
  5014.     end\\\\\\\
  5015.     mfs.getSize = function( p )\\\\\\\
  5016.         if mfs.isDir( p ) then\\\\\\\
  5017.             local files = mfs.list( p )\\\\\\\
  5018.             local n = 0\\\\\\\
  5019.             for i = 1, #files do\\\\\\\
  5020.                 n = n + mfs.getSize( p .. \\\\\\\"/\\\\\\\" .. files[i] )\\\\\\\
  5021.             end\\\\\\\
  5022.             return n\\\\\\\
  5023.         else\\\\\\\
  5024.             return fs.getSize( combine( path, p ) )\\\\\\\
  5025.         end\\\\\\\
  5026.     end\\\\\\\
  5027.     mfs.getFreeSpace = function( p )\\\\\\\
  5028.         return fs.getFreeSpace( combine( path, p ) )\\\\\\\
  5029.     end\\\\\\\
  5030.     mfs.makeDir = function( p )\\\\\\\
  5031.         return fs.makeDir( combine( path, p ) )\\\\\\\
  5032.     end\\\\\\\
  5033.     mfs.move = function( p1, p2 )\\\\\\\
  5034.         return fs.move( combine( path, p1 ), combine( path, p2 ) )\\\\\\\
  5035.     end\\\\\\\
  5036.     mfs.copy = function( p1, p2 )\\\\\\\
  5037.         return fs.copy( combine( path, p1 ), combine( path, p2 ) )\\\\\\\
  5038.     end\\\\\\\
  5039.     mfs.delete = function( p )\\\\\\\
  5040.         return fs.delete( combine( path, p ) )\\\\\\\
  5041.     end\\\\\\\
  5042.     mfs.find = function( p )\\\\\\\
  5043.         return fs.find( combine( path, p ) )\\\\\\\
  5044.     end\\\\\\\
  5045. \\\\\\\
  5046.     return Drive( mfs )\\\\\\\
  5047. \\\\\\\
  5048. end\\\", meta={\\\
  5049.  type = \\\"class\\\",\\\
  5050. }};[\\\"FileData\\\"]={content=\\\"\\\\\\\
  5051. FileData.public \\\\\\\"content\\\\\\\" \\\\\\\"string\\\\\\\"\\\\\\\
  5052. FileData.public \\\\\\\"meta\\\\\\\"\\\\\\\
  5053. FileData.public \\\\\\\"password\\\\\\\" \\\\\\\"string\\\\\\\"\\\\\\\
  5054. \\\\\\\
  5055. local function parseMeta( content )\\\\\\\
  5056.     local meta = { }\\\\\\\
  5057.     while content:sub( 1, 7 ) == \\\\\\\"--@meta\\\\\\\" do\\\\\\\
  5058.         local name = content:match \\\\\\\"%-%-@meta%[(%w+)%]\\\\\\\"\\\\\\\
  5059.         if name then\\\\\\\
  5060.             local value = content:match \\\\\\\"%-%-@meta%[%w+%]:(.-)\\\\\\\\n\\\\\\\"\\\\\\\
  5061.             if value then\\\\\\\
  5062.                 content = content:gsub( \\\\\\\"%-%-@meta%[%w+%]:(.-)\\\\\\\\n\\\\\\\", \\\\\\\"\\\\\\\", 1 )\\\\\\\
  5063.                 local f, err = loadstring( \\\\\\\"return \\\\\\\" .. value )\\\\\\\
  5064.                 if f then\\\\\\\
  5065.                     local ok, data = pcall( f )\\\\\\\
  5066.                     if ok then\\\\\\\
  5067.                         meta[name] = data\\\\\\\
  5068.                     end\\\\\\\
  5069.                 end\\\\\\\
  5070.             else\\\\\\\
  5071.                 break\\\\\\\
  5072.             end\\\\\\\
  5073.         else\\\\\\\
  5074.             break\\\\\\\
  5075.         end\\\\\\\
  5076.     end\\\\\\\
  5077.     return meta, content\\\\\\\
  5078. end\\\\\\\
  5079. \\\\\\\
  5080. function FileData.public.meta:write( value )\\\\\\\
  5081.     if type( value ) == \\\\\\\"table\\\\\\\" then\\\\\\\
  5082.         self.metadata = value\\\\\\\
  5083.     else\\\\\\\
  5084.         error( \\\\\\\"expected table meta\\\\\\\", 3 )\\\\\\\
  5085.     end\\\\\\\
  5086. end\\\\\\\
  5087. function FileData.public.meta:read( )\\\\\\\
  5088.     return self.metadata\\\\\\\
  5089. end\\\\\\\
  5090. \\\\\\\
  5091. function FileData:FileData( content, password ) -- string content\\\\\\\
  5092.     \\\\\\\
  5093.     self.metadata, self.content = parseMeta( content )\\\\\\\
  5094. \\\\\\\
  5095.     if self.metadata.protected then\\\\\\\
  5096.         if password then\\\\\\\
  5097.             self.content = encryption.decrypt( self.content, password )\\\\\\\
  5098.         end\\\\\\\
  5099.         self.metadata.password = nil\\\\\\\
  5100.     end\\\\\\\
  5101. \\\\\\\
  5102.     self.password = false\\\\\\\
  5103. \\\\\\\
  5104.     return self.public\\\\\\\
  5105. end\\\\\\\
  5106. \\\\\\\
  5107. function FileData.public:compile( )\\\\\\\
  5108.     local content = self.content\\\\\\\
  5109.     if self.password then\\\\\\\
  5110.         content = encryption.encrypt( content, self.password )\\\\\\\
  5111.         self.metadata.protected = true\\\\\\\
  5112.     end\\\\\\\
  5113.     local metadata = \\\\\\\"\\\\\\\"\\\\\\\
  5114.     for k, v in pairs( self.metadata ) do\\\\\\\
  5115.         metadata = metadata .. \\\\\\\"--@meta[\\\\\\\" .. k ..\\\\\\\"]:\\\\\\\" .. textutils.serialize( v ) .. \\\\\\\"\\\\\\\\n\\\\\\\"\\\\\\\
  5116.     end\\\\\\\
  5117.     return metadata .. \\\\\\\"\\\\\\\" .. content\\\\\\\
  5118. end\\\", meta={\\\
  5119.  type = \\\"class\\\",\\\
  5120. }};}\\\
  5121. --/end of files\\\
  5122. local autorun = {\\\"Archive\\\";\\\"Drive\\\";\\\"FileData\\\";\\\"main\\\"}\\\
  5123. --@meta[type]:\\\"class\\\"\\\
  5124. \\\
  5125. -- Swift Class Library\\\
  5126. -- Made by awsumben13\\\
  5127. -- Feel free to use this in any of your projects but keep this at the top and give credit where necessary\\\
  5128. \\\
  5129. --[[\\\
  5130.     This class library offers:\\\
  5131.         Inheritance \\\"Class:extends( other class )\\\"\\\
  5132.         Initialiser methods \\\"function ClassName:ClassName( )\\\"\\\
  5133.         Public / private variables with customisable read/write access \\\"Class.public.x.write = false\\\"\\\
  5134.         Static variables ( accessed from the class object ) \\\"Class.static.x = 5\\\"\\\
  5135.         Custom metamethods including __index and __newindex \\\"Class.meta.index = { }\\\"\\\
  5136. ]]\\\
  5137. \\\
  5138. local class = { }\\\
  5139. \\\
  5140. function class.new( name )\\\
  5141. \\\
  5142.     local object = { }\\\
  5143.     local public = { }\\\
  5144. \\\
  5145.     object.public = { }\\\
  5146.     object.private = { }\\\
  5147.     object.static = { }\\\
  5148.     object.name = name\\\
  5149.     object.extends = false\\\
  5150.     object.class = public\\\
  5151. \\\
  5152.     public.name = name\\\
  5153. \\\
  5154.     local customindex = false\\\
  5155.     local customnewindex = false\\\
  5156.     local objectmeta = { }\\\
  5157. \\\
  5158.     function public:new( ... )\\\
  5159. \\\
  5160.         local ob = { }\\\
  5161.         local pb = { }\\\
  5162. \\\
  5163.         ob.class = public\\\
  5164.         ob.public = pb\\\
  5165. \\\
  5166.         setmetatable( ob, {\\\
  5167.             __index = object.private;\\\
  5168.         } )\\\
  5169. \\\
  5170.         function pb:type( full )\\\
  5171.             return ob.class:type( full )\\\
  5172.         end\\\
  5173. \\\
  5174.         function pb:typeOf( class )\\\
  5175.             return ob.class:typeOf( class )\\\
  5176.         end\\\
  5177. \\\
  5178.         local obmeta = { }\\\
  5179.         obmeta.__index = function( _, k )\\\
  5180.             if object.public[k] then\\\
  5181.                 if object.public[k].read then\\\
  5182.                     local val\\\
  5183.                     if customindex then\\\
  5184.                         if type( customindex ) == \\\"function\\\" then\\\
  5185.                             return customindex( ob, k )\\\
  5186.                         else\\\
  5187.                             return customindex[k]\\\
  5188.                         end\\\
  5189.                     elseif type( object.public[k].read ) == \\\"function\\\" then\\\
  5190.                         val = object.public[k].read( ob )\\\
  5191.                     elseif object.public[k].value ~= nil then\\\
  5192.                         val = object.public[k].value\\\
  5193.                     else\\\
  5194.                         val = ob[k]\\\
  5195.                     end\\\
  5196.                     if type( val ) == \\\"function\\\" then\\\
  5197.                         return function( self, ... )\\\
  5198.                             if self == pb then\\\
  5199.                                 return val( ob, ... )\\\
  5200.                             end\\\
  5201.                             return val( self, ... )\\\
  5202.                         end\\\
  5203.                     end\\\
  5204.                     return val\\\
  5205.                 else\\\
  5206.                     error( \\\"variable has no read access\\\", 2 )\\\
  5207.                 end\\\
  5208.             elseif customindex then\\\
  5209.                 if type( customindex ) == \\\"function\\\" then\\\
  5210.                     return customindex( ob, k )\\\
  5211.                 else\\\
  5212.                     return customindex[k]\\\
  5213.                 end\\\
  5214.             else\\\
  5215.                 error( \\\"no such variable \\\\\\\"\\\" .. tostring( k ) .. \\\"\\\\\\\"\\\", 2 )\\\
  5216.             end\\\
  5217.         end;\\\
  5218.         obmeta.__newindex = function( _, k, v )\\\
  5219.             if object.public[k] then\\\
  5220.                 if object.public[k].write then\\\
  5221.                     if customnewindex then\\\
  5222.                         if type( customnewindex ) == \\\"function\\\" then\\\
  5223.                             return customnewindex( ob, k, v )\\\
  5224.                         else\\\
  5225.                             customnewindex[k] = v\\\
  5226.                         end\\\
  5227.                     elseif type( object.public[k].write ) == \\\"function\\\" then\\\
  5228.                         object.public[k].write( ob, v )\\\
  5229.                     else\\\
  5230.                         ob[k] = v\\\
  5231.                     end\\\
  5232.                 else\\\
  5233.                     error( \\\"variable has no write access\\\", 2 )\\\
  5234.                 end\\\
  5235.             else\\\
  5236.                 error( \\\"no such variable \\\\\\\"\\\" .. tostring( k ) .. \\\"\\\\\\\"\\\", 2 )\\\
  5237.             end\\\
  5238.         end;\\\
  5239.         obmeta.__tostring = function( )\\\
  5240.             return object.name\\\
  5241.         end;\\\
  5242.         obmeta.__metatable = \\\"SwiftClassObject\\\";\\\
  5243. \\\
  5244.         for k, v in pairs( objectmeta ) do\\\
  5245.             obmeta[\\\"__\\\" .. tostring( k )] = v\\\
  5246.         end\\\
  5247. \\\
  5248.         setmetatable( pb, obmeta )\\\
  5249. \\\
  5250.         local c = object\\\
  5251.         while true do\\\
  5252.             if type( c.private[c.name] ) == \\\"function\\\" then\\\
  5253.                 return c.private[c.name]( ob, ... )\\\
  5254.             end\\\
  5255.             if c.extends then\\\
  5256.                 c = c.extends\\\
  5257.             else\\\
  5258.                 break\\\
  5259.             end\\\
  5260.         end\\\
  5261. \\\
  5262.         return pb\\\
  5263.     end\\\
  5264. \\\
  5265.     function public:type( full )\\\
  5266.         local str = \\\"\\\"\\\
  5267.         if full then\\\
  5268.             local c = object.extends\\\
  5269.             while c do\\\
  5270.                 str = c.name .. \\\".\\\" .. str\\\
  5271.                 c = c.extends\\\
  5272.             end\\\
  5273.         end\\\
  5274.         return str .. object.name\\\
  5275.     end\\\
  5276. \\\
  5277.     function public:typeOf( other )\\\
  5278.         if type( other ) == \\\"table\\\" then\\\
  5279.             if getmetatable( other ) == \\\"SwiftClass\\\" then\\\
  5280.                 local ob = object\\\
  5281.                 while ob do\\\
  5282.                     if ob.class == other then\\\
  5283.                         return true\\\
  5284.                     end\\\
  5285.                     ob = ob.extends\\\
  5286.                 end\\\
  5287.             end\\\
  5288.         end\\\
  5289.         return false\\\
  5290.     end\\\
  5291. \\\
  5292.     function public:extends( ob )\\\
  5293.         ob:extend( object )\\\
  5294.     end\\\
  5295. \\\
  5296.     function public:extend( ob )\\\
  5297.         setmetatable( ob.static, { __index = object.static } )\\\
  5298.         setmetatable( ob.public, { __index = object.public } )\\\
  5299.         setmetatable( ob.private, { __index = object.private } )\\\
  5300.         ob.extends = object\\\
  5301.     end\\\
  5302. \\\
  5303.     local meta = { }\\\
  5304.     meta.__index = function( _, k )\\\
  5305.         if k == \\\"static\\\" then\\\
  5306.             return setmetatable( { }, {\\\
  5307.                 __newindex = function( _, k, v )\\\
  5308.                     object.static[k] = v\\\
  5309.                 end;\\\
  5310.                 __metatable = { };\\\
  5311.             } )\\\
  5312.         elseif k == \\\"public\\\" then\\\
  5313.             return setmetatable( { }, {\\\
  5314.                 __newindex = function( _, k, v )\\\
  5315.                     object.public[k] = {\\\
  5316.                         read = true;\\\
  5317.                         write = false;\\\
  5318.                         value = v;\\\
  5319.                     }\\\
  5320.                 end;\\\
  5321.                 __call = function( _, k )\\\
  5322.                     object.public[k] = {\\\
  5323.                         read = true;\\\
  5324.                         write = true;\\\
  5325.                         value = nil;\\\
  5326.                     }\\\
  5327.                     return function( _type )\\\
  5328.                         local types = { _type }\\\
  5329.                         object.public[k].write = function( ob, value )\\\
  5330.                             for i = 1, #types do\\\
  5331.                                 if class.typeOf( value, types[i] ) then\\\
  5332.                                     ob[k] = value\\\
  5333.                                     return\\\
  5334.                                 end\\\
  5335.                             end\\\
  5336.                             if class.type( types[1] ) == \\\"Class\\\" then\\\
  5337.                                 error( \\\"expected \\\" .. types[1]:type( ) .. \\\" \\\" .. k, 3 )\\\
  5338.                             else\\\
  5339.                                 error( \\\"expected \\\" .. types[1] .. \\\" \\\" .. k, 3 )\\\
  5340.                             end\\\
  5341.                         end\\\
  5342.                         return function( _type )\\\
  5343.                             table.insert( types, _type )\\\
  5344.                         end\\\
  5345.                     end\\\
  5346.                 end;\\\
  5347.                 __index = function( _, k )\\\
  5348.                     if object.public[k] then\\\
  5349.                         return setmetatable( { }, {\\\
  5350.                             __newindex = function( _, name, v )\\\
  5351.                                 if name == \\\"read\\\" then\\\
  5352.                                     if type( v ) == \\\"boolean\\\" or type( v ) == \\\"function\\\" then\\\
  5353.                                         object.public[k].read = v\\\
  5354.                                     else\\\
  5355.                                         error( \\\"invalid modifier value\\\", 2 )\\\
  5356.                                     end\\\
  5357.                                 elseif name == \\\"write\\\" then\\\
  5358.                                     if type( v ) == \\\"boolean\\\" or type( v ) == \\\"function\\\" then\\\
  5359.                                         object.public[k].write = v\\\
  5360.                                     else\\\
  5361.                                         error( \\\"invalid modifier value\\\", 2 )\\\
  5362.                                     end\\\
  5363.                                 else\\\
  5364.                                     error( \\\"invalid modifier name\\\", 2 )\\\
  5365.                                 end\\\
  5366.                             end;\\\
  5367.                             __metatable = { };\\\
  5368.                         } )\\\
  5369.                     else\\\
  5370.                         error( \\\"public index \\\" .. tostring( k ) .. \\\" not found\\\", 2 )\\\
  5371.                     end\\\
  5372.                 end;\\\
  5373.                 __metatable = { };\\\
  5374.             } )\\\
  5375.         elseif k == \\\"meta\\\" then\\\
  5376.             return setmetatable( { }, {\\\
  5377.                 __index = function( _, k )\\\
  5378.                     if k == \\\"index\\\" then\\\
  5379.                         return customindex\\\
  5380.                     elseif k == \\\"newindex\\\" then\\\
  5381.                         return customnewindex\\\
  5382.                     else\\\
  5383.                         return objectmeta[k]\\\
  5384.                     end\\\
  5385.                 end;\\\
  5386.                 __newindex = function( _, k, v )\\\
  5387.                     if k == \\\"metatable\\\" then\\\
  5388.                         error( \\\"cannot change this metamethod\\\", 2 )\\\
  5389.                     elseif k == \\\"index\\\" then\\\
  5390.                         if type( v ) == \\\"function\\\" or type( v ) == \\\"table\\\" or v == nil then\\\
  5391.                             customindex = v\\\
  5392.                         else\\\
  5393.                             error( \\\"cannot use type \\\" .. type( v ) .. \\\" for index metamethod\\\", 2 )\\\
  5394.                         end\\\
  5395.                     elseif k == \\\"newindex\\\" then\\\
  5396.                         if type( v ) == \\\"function\\\" or type( v ) == \\\"table\\\" or v == nil then\\\
  5397.                             customnewindex = v\\\
  5398.                         else\\\
  5399.                             error( \\\"cannot use type \\\" .. type( v ) .. \\\" for newindex metamethod\\\", 2 )\\\
  5400.                         end\\\
  5401.                     else\\\
  5402.                         objectmeta[k] = v\\\
  5403.                     end\\\
  5404.                 end;\\\
  5405.                 __metatable = { };\\\
  5406.             } )\\\
  5407.         else\\\
  5408.             return object.static[k]\\\
  5409.         end\\\
  5410.     end\\\
  5411.     meta.__newindex = function( _, k, v )\\\
  5412.         object.private[k] = v\\\
  5413.     end;\\\
  5414.     meta.__call = function( self, ... )\\\
  5415.         return self:new( ... )\\\
  5416.     end;\\\
  5417.     meta.__totring = function( )\\\
  5418.         return \\\"Class\\\"\\\
  5419.     end;\\\
  5420.     meta.__metatable = \\\"SwiftClass\\\"\\\
  5421. \\\
  5422.     setmetatable( public, meta )\\\
  5423. \\\
  5424.     return public\\\
  5425. end\\\
  5426. \\\
  5427. function class.public( name )\\\
  5428.     local object = class.new( name )\\\
  5429.     getfenv( )[name] = object\\\
  5430. end\\\
  5431. \\\
  5432. function class.type( object )\\\
  5433.     if type( object ) == \\\"table\\\" then\\\
  5434.         if getmetatable( object ) == \\\"SwiftClass\\\" then\\\
  5435.             return \\\"Class\\\"\\\
  5436.         end\\\
  5437.         if getmetatable( object ) == \\\"SwiftClassObject\\\" then\\\
  5438.             return object:type( )\\\
  5439.         end\\\
  5440.     end\\\
  5441.     return type( object )\\\
  5442. end\\\
  5443. \\\
  5444. function class.typeOf( object, ... )\\\
  5445.     local types = { ... }\\\
  5446.     for i = 1, #types do\\\
  5447.         if type( object ) == \\\"table\\\" and getmetatable( object ) == \\\"SwiftClassObject\\\" then\\\
  5448.             if object:typeOf( types[i] ) then\\\
  5449.                 return types[i]\\\
  5450.             end\\\
  5451.         elseif type( object ) == \\\"table\\\" and getmetatable( object ) == \\\"SwiftClass\\\" then\\\
  5452.             if types[i] == \\\"Class\\\" then\\\
  5453.                 return \\\"Class\\\"\\\
  5454.             end\\\
  5455.         else\\\
  5456.             if type( object ) == types[i] then\\\
  5457.                 return types[i]\\\
  5458.             end\\\
  5459.         end\\\
  5460.     end\\\
  5461.     return false\\\
  5462. end\\\
  5463. \\\
  5464. setmetatable( class, { __call = function( _, ... ) return class.new( ... ) end } )\\\
  5465. local args = { ... }\\\
  5466. local global = getfenv( )\\\
  5467. local custom = setmetatable( { class = class, ARGS = args }, { __index = global } )\\\
  5468. function custom.export( variable, value )\\\
  5469.     if custom[variable] ~= nil or value then\\\
  5470.         global[variable] = custom[variable] == nil and value or custom[variable]\\\
  5471.     end\\\
  5472. end\\\
  5473. local required = { }\\\
  5474. local function require( file, ... )\\\
  5475.     if not required[file] and files[file] then\\\
  5476.         required[file] = true\\\
  5477.         if files[file].meta.type == \\\"class\\\" then\\\
  5478.             custom[file] = class( file )\\\
  5479.         end\\\
  5480.         local fenv = setmetatable( { shared = custom }, { __index = custom } )\\\
  5481.         local f, err = loadstring( files[file].content, file .. \\\".lua\\\" )\\\
  5482.         if f then\\\
  5483.             setfenv( f, fenv )\\\
  5484.             local ok, data = pcall( f )\\\
  5485.             if ok then\\\
  5486.                 if files[file].meta.type == \\\"lib\\\" then\\\
  5487.                     if data ~= nil then\\\
  5488.                         custom[file] = data\\\
  5489.                         return data\\\
  5490.                     else\\\
  5491.                         local t = { }\\\
  5492.                         for k, v in pairs( fenv ) do\\\
  5493.                             t[k] = v\\\
  5494.                         end\\\
  5495.                         custom[file] = t\\\
  5496.                         return t\\\
  5497.                     end\\\
  5498.                 end\\\
  5499.                 return data\\\
  5500.             else\\\
  5501.                 error( data, 0 )\\\
  5502.             end\\\
  5503.         else\\\
  5504.             error( err, 0 )\\\
  5505.         end\\\
  5506.     end\\\
  5507. end\\\
  5508. custom.require = require\\\
  5509. \\\
  5510. for i = 1, #autorun do\\\
  5511.     require( autorun[i] )\\\
  5512. end\",\
  5513. [\"NovaUI.lua\"]=\"--[[type=\\\"executable_package\\\", name=\\\"NovaUI\\\"]]\\\
  5514. local files = {[\\\"shader\\\"]={content=\\\"\\\\\\\
  5515. local maps = {}\\\\\\\
  5516. maps.darken = {\\\\\\\
  5517.     [colours.white] = colours.lightGrey, [colours.orange] = colours.red, [colours.magenta] = colours.purple, [colours.lightBlue] = colours.cyan;\\\\\\\
  5518.     [colours.yellow] = colours.orange, [colours.lime] = colours.green, [colours.pink] = colours.magenta, [colours.grey] = colours.black;\\\\\\\
  5519.     [colours.lightGrey] = colours.grey, [colours.cyan] = colours.blue, [colours.purple] = colours.black, [colours.blue] = colours.black;\\\\\\\
  5520.     [colours.brown] = colours.black, [colours.green] = colours.black, [colours.red] = colours.brown, [colours.black] = colours.black;\\\\\\\
  5521. }\\\\\\\
  5522. maps.lighten = {\\\\\\\
  5523.     [colours.white] = colours.white, [colours.orange] = colours.yellow, [colours.magenta] = colours.pink, [colours.lightBlue] = colours.white;\\\\\\\
  5524.     [colours.yellow] = colours.white, [colours.lime] = colours.white, [colours.pink] = colours.white, [colours.grey] = colours.lightGrey;\\\\\\\
  5525.     [colours.lightGrey] = colours.white, [colours.cyan] = colours.lightBlue, [colours.purple] = colours.magenta, [colours.blue] = colours.cyan;\\\\\\\
  5526.     [colours.brown] = colours.red, [colours.green] = colours.lime, [colours.red] = colours.orange, [colours.black] = colours.grey;\\\\\\\
  5527. }\\\\\\\
  5528. maps.greyscale = {\\\\\\\
  5529.     [colours.white] = 1, [colours.orange] = 256, [colours.magenta] = 256, [colours.lightBlue] = 256;\\\\\\\
  5530.     [colours.yellow] = 1, [colours.lime] = 256, [colours.pink] = 1, [colours.grey] = 256;\\\\\\\
  5531.     [colours.lightGrey] = 256, [colours.cyan] = 128, [colours.purple] = 128, [colours.blue] = 32768;\\\\\\\
  5532.     [colours.brown] = 32768, [colours.green] = 128, [colours.red] = 128, [colours.black] = 32768;\\\\\\\
  5533. }\\\\\\\
  5534. maps.sepia = {\\\\\\\
  5535.     [colours.white] = 1, [colours.orange] = 2, [colours.magenta] = 2, [colours.lightBlue] = 2;\\\\\\\
  5536.     [colours.yellow] = 1, [colours.lime] = 2, [colours.pink] = 1, [colours.grey] = 2;\\\\\\\
  5537.     [colours.lightGrey] = 2, [colours.cyan] = 16, [colours.purple] = 16, [colours.blue] = 4096;\\\\\\\
  5538.     [colours.brown] = 4096, [colours.green] = 16, [colours.red] = 16, [colours.black] = 4096;\\\\\\\
  5539. }\\\\\\\
  5540. \\\\\\\
  5541. function darken( col )\\\\\\\
  5542.     return maps.darken[col] or 0\\\\\\\
  5543. end\\\\\\\
  5544. \\\\\\\
  5545. function lighten( col )\\\\\\\
  5546.     return maps.lighten[col] or 0\\\\\\\
  5547. end\\\\\\\
  5548. \\\\\\\
  5549. function greyscale( col )\\\\\\\
  5550.     return maps.greyscale[col] or 0\\\\\\\
  5551. end\\\\\\\
  5552. \\\\\\\
  5553. function sepia( col )\\\\\\\
  5554.     return maps.sepia[col] or 0\\\\\\\
  5555. end\\\", meta={\\\
  5556.  type = \\\"lib\\\",\\\
  5557. }};[\\\"UIButton\\\"]={content=\\\"\\\\\\\
  5558. require \\\\\\\"UIElement\\\\\\\"\\\\\\\
  5559. \\\\\\\
  5560. UIButton:extends( UIElement )\\\\\\\
  5561. \\\\\\\
  5562. UIButton.text = \\\\\\\"\\\\\\\"\\\\\\\
  5563. UIButton.public \\\\\\\"text\\\\\\\"\\\\\\\
  5564. \\\\\\\
  5565. UIButton.public \\\\\\\"bc\\\\\\\" \\\\\\\"number\\\\\\\" \\\\\\\"function\\\\\\\"\\\\\\\
  5566. UIButton.public \\\\\\\"tc\\\\\\\" \\\\\\\"number\\\\\\\" \\\\\\\"function\\\\\\\"\\\\\\\
  5567. \\\\\\\
  5568. UIButton.onClick = false\\\\\\\
  5569. UIButton.public \\\\\\\"onClick\\\\\\\" \\\\\\\"function\\\\\\\"\\\\\\\
  5570. UIButton.onDrag = false\\\\\\\
  5571. UIButton.public \\\\\\\"onDrag\\\\\\\" \\\\\\\"function\\\\\\\"\\\\\\\
  5572. \\\\\\\
  5573. UIButton.align = true\\\\\\\
  5574. UIButton.public \\\\\\\"align\\\\\\\" \\\\\\\"boolean\\\\\\\"\\\\\\\
  5575. \\\\\\\
  5576. UIButton.handlesEnter = true\\\\\\\
  5577. \\\\\\\
  5578. function UIButton:UIButton( x, y, w, h, text )\\\\\\\
  5579.     self:UIElement( x, y, w, h )\\\\\\\
  5580.     self.text = text\\\\\\\
  5581.     self.bc = colours.white\\\\\\\
  5582.     self.tc = colours.blue\\\\\\\
  5583.     return self.public\\\\\\\
  5584. end\\\\\\\
  5585. \\\\\\\
  5586. function UIButton.public:draw( x, y )\\\\\\\
  5587.     local layer = stencil.addLayer( x, y, self.w, self.h )\\\\\\\
  5588.     local text = tostring( self.text )\\\\\\\
  5589.     if type( self.text ) == \\\\\\\"function\\\\\\\" then\\\\\\\
  5590.         text = self.text( self.public )\\\\\\\
  5591.     end\\\\\\\
  5592.     if self.align then\\\\\\\
  5593.         stencil.text( x, y, self.w, self.h, self.bc, self.tc, text, function( lines )\\\\\\\
  5594.             while #lines < self.h do\\\\\\\
  5595.                 table.insert( lines, 1, string.rep( \\\\\\\" \\\\\\\", self.w ) )\\\\\\\
  5596.                 table.insert( lines, string.rep( \\\\\\\" \\\\\\\", self.w ) )\\\\\\\
  5597.             end\\\\\\\
  5598.             if #lines > self.h then\\\\\\\
  5599.                 table.remove( lines, 1 )\\\\\\\
  5600.             end\\\\\\\
  5601.             for i = 1, #lines do\\\\\\\
  5602.                 lines[i] = string.rep( \\\\\\\" \\\\\\\", math.floor( ( self.w - #lines[i] ) / 2 ) ) .. lines[i]\\\\\\\
  5603.             end\\\\\\\
  5604.         end )\\\\\\\
  5605.     else\\\\\\\
  5606.         stencil.text( x, y, self.w, self.h, self.bc, self.tc, text )\\\\\\\
  5607.     end\\\\\\\
  5608.     local c = self.public:getChildren( )\\\\\\\
  5609.     for i, child in ipairs( c ) do\\\\\\\
  5610.         child:draw( x + child.x - 1 + self.cx, y + child.y - 1 + self.cy )\\\\\\\
  5611.     end\\\\\\\
  5612.     stencil.closeLayer( layer )\\\\\\\
  5613. end\\\\\\\
  5614. \\\\\\\
  5615. function UIButton.public:onMouseClick( rx, ry, button )\\\\\\\
  5616.     if self.last and self.last.x == rx and self.last.y == ry and os.clock( ) - self.last.time <= 0.5 then\\\\\\\
  5617.         if self.onDoubleClick then\\\\\\\
  5618.             self.onDoubleClick( self.public, rx, ry, button )\\\\\\\
  5619.         end\\\\\\\
  5620.     else\\\\\\\
  5621.         if self.onClick then\\\\\\\
  5622.             self.onClick( self.public, rx, ry, button )\\\\\\\
  5623.         end\\\\\\\
  5624.     end\\\\\\\
  5625.     self.last = {\\\\\\\
  5626.         x = rx;\\\\\\\
  5627.         y = ry;\\\\\\\
  5628.         time = os.clock( );\\\\\\\
  5629.     }\\\\\\\
  5630. end\\\\\\\
  5631. \\\\\\\
  5632. function UIButton.public:onMouseDrag( x, y, cx, cy, button )\\\\\\\
  5633.     if self.onDrag then\\\\\\\
  5634.         self.onDrag( self.public, x, y, cx, cy, button )\\\\\\\
  5635.     end\\\\\\\
  5636. end\\\\\\\
  5637. \\\\\\\
  5638. function UIButton.public:onKeyPress( key )\\\\\\\
  5639.     if key == keys.enter and self.onClick then\\\\\\\
  5640.         self.onClick( self.public, 1, 1, \\\\\\\"enter\\\\\\\" )\\\\\\\
  5641.     end\\\\\\\
  5642. end\\\", meta={\\\
  5643.  type = \\\"class\\\",\\\
  5644. }};[\\\"UIImage\\\"]={content=\\\"\\\\\\\
  5645. require \\\\\\\"Image\\\\\\\"\\\\\\\
  5646. require \\\\\\\"UIElement\\\\\\\"\\\\\\\
  5647. \\\\\\\
  5648. UIImage.public \\\\\\\"image\\\\\\\" (Image)\\\\\\\
  5649. UIImage.public \\\\\\\"onClick\\\\\\\" \\\\\\\"function\\\\\\\"\\\\\\\
  5650. UIImage.public \\\\\\\"onDrag\\\\\\\" \\\\\\\"function\\\\\\\"\\\\\\\
  5651. UIImage.public \\\\\\\"onScroll\\\\\\\" \\\\\\\"function\\\\\\\"\\\\\\\
  5652. \\\\\\\
  5653. UIImage:extends( UIElement )\\\\\\\
  5654. \\\\\\\
  5655. function UIImage:UIImage( x, y, w, h, image ) -- Image object\\\\\\\
  5656.     self:UIElement( x, y, w, h )\\\\\\\
  5657.     self.image = image\\\\\\\
  5658.     return self.public\\\\\\\
  5659. end\\\\\\\
  5660. \\\\\\\
  5661. function UIImage.public:draw( x, y )\\\\\\\
  5662.     local layer = stencil.addLayer( x, y, self.w, self.h )\\\\\\\
  5663.     if not self.image then return end\\\\\\\
  5664.     for xx = 1, self.w do\\\\\\\
  5665.         for yy = 1, self.h do\\\\\\\
  5666.             local bc, tc, char = self.image:getPixel( xx, yy )\\\\\\\
  5667.             if bc then\\\\\\\
  5668.                 if bc ~= 0 or char ~= \\\\\\\" \\\\\\\" then\\\\\\\
  5669.                     stencil.pixel( x + xx - 1, y + yy - 1, bc, tc, char )\\\\\\\
  5670.                 end\\\\\\\
  5671.             end\\\\\\\
  5672.         end\\\\\\\
  5673.     end\\\\\\\
  5674.     local c = self.public:getChildren( )\\\\\\\
  5675.     for i, child in ipairs( c ) do\\\\\\\
  5676.         child:draw( x + child.x - 1 + self.cx, y + child.y - 1 + self.cy )\\\\\\\
  5677.     end\\\\\\\
  5678.     stencil.closeLayer( layer )\\\\\\\
  5679. end\\\\\\\
  5680. \\\\\\\
  5681. function UIImage.public:onMouseClick( rx, ry, button )\\\\\\\
  5682.     if self.onClick then\\\\\\\
  5683.         self.onClick( self.public, rx, ry, button )\\\\\\\
  5684.     end\\\\\\\
  5685. end\\\\\\\
  5686. \\\\\\\
  5687. function UIImage.public:onMouseDrag( rx, ry, cx, cy, button )\\\\\\\
  5688.     if self.onDrag then\\\\\\\
  5689.         self.onDrag( self.public, rx, ry, cx, cy, button )\\\\\\\
  5690.     end\\\\\\\
  5691. end\\\\\\\
  5692. \\\\\\\
  5693. function UIImage.public:onMouseScroll( rx, ry, dir )\\\\\\\
  5694.     if self.onScroll then\\\\\\\
  5695.         self.onScroll( self.public, rx, ry, dir )\\\\\\\
  5696.     end\\\\\\\
  5697. end\\\", meta={\\\
  5698.  type = \\\"class\\\",\\\
  5699. }};[\\\"display\\\"]={content=\\\"\\\\\\\
  5700. local function bluebox( frame, height )\\\\\\\
  5701.     local w, h = frame.w, frame.h\\\\\\\
  5702.     local f = frame:newChild( UIFrame( 1, math.ceil( h / 2 - height / 2 ) + 1, w, height ) )\\\\\\\
  5703.     f:newChild( UIText( 1, 1, f.w, f.h, \\\\\\\"\\\\\\\" ) ).bc = colours.blue\\\\\\\
  5704.     return f\\\\\\\
  5705. end\\\\\\\
  5706. \\\\\\\
  5707. function alert( frame, message, wait )\\\\\\\
  5708.     local active = true\\\\\\\
  5709.     local close = frame:newChild( UIButton( 1, 1, frame.w, frame.h, \\\\\\\"\\\\\\\" ) )\\\\\\\
  5710.     close.bc = 0\\\\\\\
  5711.     close.align = false\\\\\\\
  5712.     local f = bluebox( frame, 6 )\\\\\\\
  5713.     function close:onClick( )\\\\\\\
  5714.         close:remove( )\\\\\\\
  5715.         f:remove( )\\\\\\\
  5716.         active = false\\\\\\\
  5717.     end\\\\\\\
  5718.     local title = f:newChild( UIText( 0, 2, math.min( #message, f.w - 2 ), 2, message ) )\\\\\\\
  5719.     title.bc = 0\\\\\\\
  5720.     title.tc = colours.lightBlue\\\\\\\
  5721.     title:centreX( )\\\\\\\
  5722.     local ok = f:newChild( UIButton( 0, 5, 2, 1, \\\\\\\"ok\\\\\\\" ) )\\\\\\\
  5723.     ok.bc = 0\\\\\\\
  5724.     ok.tc = colours.white\\\\\\\
  5725.     ok:centreX( )\\\\\\\
  5726.     function ok:onClick( )\\\\\\\
  5727.         close:remove( )\\\\\\\
  5728.         f:remove( )\\\\\\\
  5729.         active = false\\\\\\\
  5730.     end\\\\\\\
  5731.     if wait then\\\\\\\
  5732.         while active do\\\\\\\
  5733.             coroutine.yield( )\\\\\\\
  5734.         end\\\\\\\
  5735.     end\\\\\\\
  5736. end\\\\\\\
  5737. \\\\\\\
  5738. function response( frame, title, mask )\\\\\\\
  5739.     local active = true\\\\\\\
  5740.     local r = false\\\\\\\
  5741.     local close = frame:newChild( UIButton( 1, 1, frame.w, frame.h, \\\\\\\"\\\\\\\" ) )\\\\\\\
  5742.     close.bc = 0\\\\\\\
  5743.     close.align = false\\\\\\\
  5744.     local f = bluebox( frame, 6 )\\\\\\\
  5745.     function close:onClick( )\\\\\\\
  5746.         close:remove( )\\\\\\\
  5747.         f:remove( )\\\\\\\
  5748.         active = false\\\\\\\
  5749.     end\\\\\\\
  5750.     local title = f:newChild( UIText( 0, 2, math.min( #title, f.w - 2 ), 2, title ) )\\\\\\\
  5751.     title.bc = 0\\\\\\\
  5752.     title.tc = colours.lightBlue\\\\\\\
  5753.     title:centreX( )\\\\\\\
  5754.     local input = f:newChild( UIInput( 2, 5, frame.w - 2, 1, mask ) )\\\\\\\
  5755.     input.bc = 0\\\\\\\
  5756.     input.fbc = 0\\\\\\\
  5757.     input.tc = colours.white\\\\\\\
  5758.     input:focusOn( )\\\\\\\
  5759.     function input:onEnter( )\\\\\\\
  5760.         close:remove( )\\\\\\\
  5761.         f:remove( )\\\\\\\
  5762.         active = false\\\\\\\
  5763.         r = self.text\\\\\\\
  5764.     end\\\\\\\
  5765.     while active do\\\\\\\
  5766.         coroutine.yield( )\\\\\\\
  5767.     end\\\\\\\
  5768.     return r\\\\\\\
  5769. end\\\\\\\
  5770. \\\\\\\
  5771. function confirm( frame, title )\\\\\\\
  5772.     local result = \\\\\\\"none\\\\\\\"\\\\\\\
  5773.     local close = frame:newChild( UIButton( 1, 1, frame.w, frame.h, \\\\\\\"\\\\\\\" ) )\\\\\\\
  5774.     close.bc = 0\\\\\\\
  5775.     close.align = false\\\\\\\
  5776.     local f = bluebox( frame, 6 )\\\\\\\
  5777.     function close:onClick( )\\\\\\\
  5778.         close:remove( )\\\\\\\
  5779.         f:remove( )\\\\\\\
  5780.         result = nil\\\\\\\
  5781.     end\\\\\\\
  5782.     local t = f:newChild( UIText( 2, 2, math.min( #title, f.w - 2 ), 2, title ) )\\\\\\\
  5783.     t.bc = colours.blue\\\\\\\
  5784.     t.tc = colours.lightBlue\\\\\\\
  5785.     t:centreX( )\\\\\\\
  5786.     local yes = f:newChild( UIButton( 2, 5, math.floor( ( f.w - 2 ) / 2 ), 1, \\\\\\\"yes\\\\\\\" ) )\\\\\\\
  5787.     yes.bc = colours.blue\\\\\\\
  5788.     yes.tc = colours.white\\\\\\\
  5789.     function yes:onClick( )\\\\\\\
  5790.         f:remove( )\\\\\\\
  5791.         close:remove( )\\\\\\\
  5792.         result = true\\\\\\\
  5793.     end\\\\\\\
  5794.     local no = f:newChild( UIButton( f.w - yes.w, 5, math.floor( ( f.w - 2 ) / 2 ), 1, \\\\\\\"no\\\\\\\" ) )\\\\\\\
  5795.     no.bc = colours.blue\\\\\\\
  5796.     no.tc = colours.white\\\\\\\
  5797.     function no:onClick( )\\\\\\\
  5798.         f:remove( )\\\\\\\
  5799.         close:remove( )\\\\\\\
  5800.         result = false\\\\\\\
  5801.     end\\\\\\\
  5802.     while result == \\\\\\\"none\\\\\\\" do\\\\\\\
  5803.         coroutine.yield( )\\\\\\\
  5804.     end\\\\\\\
  5805.     return result\\\\\\\
  5806. end\\\\\\\
  5807. \\\\\\\
  5808. function sframe( frame, x, y, w, h ) -- scrolling frame (auto scrollbar)\\\\\\\
  5809. \\\\\\\
  5810. end\\\\\\\
  5811. \\\\\\\
  5812. function help( frame, title, content )\\\\\\\
  5813.     local close = frame:newChild( UIButton( 1, 1, frame.w, frame.h, \\\\\\\"\\\\\\\" ) )\\\\\\\
  5814.     close.bc = 0\\\\\\\
  5815.     close.align = false\\\\\\\
  5816.     local f = bluebox( frame, 14 )\\\\\\\
  5817.     local t = f:newChild( UIText( 2, 2, #title, 1, title ) )\\\\\\\
  5818.     t.bc = colours.blue\\\\\\\
  5819.     t.tc = colours.white\\\\\\\
  5820.     t:centreX( )\\\\\\\
  5821.     f:newChild( UIText( 3, 5, f.w - 3, f.h - 7, \\\\\\\"\\\\\\\" ) ).bc = colours.grey\\\\\\\
  5822.     local c = f:newChild( UIText( 2, 4, f.w - 3, f.h - 7, content ) )\\\\\\\
  5823.     c.tc = colours.grey\\\\\\\
  5824.     local ok = f:newChild( UIButton( 2, f.h - 1, f.w - 2, 1, \\\\\\\"Got it!\\\\\\\" ) )\\\\\\\
  5825.     ok.bc = colours.blue\\\\\\\
  5826.     ok.tc = colours.white\\\\\\\
  5827.     function ok:onClick( )\\\\\\\
  5828.         close:remove( )\\\\\\\
  5829.         f:remove( )\\\\\\\
  5830.     end\\\\\\\
  5831.     function close:onClick( )\\\\\\\
  5832.         close:remove( )\\\\\\\
  5833.         f:remove( )\\\\\\\
  5834.     end\\\\\\\
  5835. end\\\\\\\
  5836. \\\\\\\
  5837. function menu( frame, options )\\\\\\\
  5838.     frame:clearChildren( )\\\\\\\
  5839.     if options.width then\\\\\\\
  5840.         frame.w = options.width\\\\\\\
  5841.     end\\\\\\\
  5842.     if options.height then\\\\\\\
  5843.         frame.h = options.height\\\\\\\
  5844.     end\\\\\\\
  5845.     if options.shadow then\\\\\\\
  5846.         frame.w = frame.w - 1\\\\\\\
  5847.         frame.h = frame.h - 1\\\\\\\
  5848.         frame:newChild( UIText( 2, 2, frame.w, frame.h, \\\\\\\"\\\\\\\" ) ).bc = options.shadow\\\\\\\
  5849.     end\\\\\\\
  5850.     local sframe = frame\\\\\\\
  5851.     local y = 1\\\\\\\
  5852.     local bc = options.colour or 1\\\\\\\
  5853.     frame:newChild( UIButton( 1, 1, frame.w, frame.h, \\\\\\\"\\\\\\\" ) ).bc = bc\\\\\\\
  5854.     if #options > frame.h then\\\\\\\
  5855.         sframe = frame:newChild( UIFrame( 1, 1, frame.w - 1, frame.h ) )\\\\\\\
  5856.         local scrollbar = frame:newChild( UIScrollBar( frame.w, 1, 1, frame.h, sframe ) )\\\\\\\
  5857.     end\\\\\\\
  5858.     for i = 1, #options do\\\\\\\
  5859.         if options[i] == \\\\\\\"rule\\\\\\\" then\\\\\\\
  5860.             local r = sframe:newChild( UIText( 2, y, sframe.w - 2, 1, (\\\\\\\"-\\\\\\\"):rep( math.max( sframe.w - 2, 0 ) ) ) )\\\\\\\
  5861.             r.tc = colours.lightGrey\\\\\\\
  5862.             r.bc = 0\\\\\\\
  5863.         elseif options[i] ~= \\\\\\\"space\\\\\\\" then\\\\\\\
  5864.             if options[i].type == \\\\\\\"menu\\\\\\\" then\\\\\\\
  5865.                 local arrow = sframe:newChild( UIText( sframe.w - 1, y, 1, 1, \\\\\\\">\\\\\\\" ) )\\\\\\\
  5866.                 arrow.bc = 0\\\\\\\
  5867.                 arrow.tc = colours.blue\\\\\\\
  5868.                 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 ) )\\\\\\\
  5869.                 button.bc = 0\\\\\\\
  5870.                 button.tc = colours.grey\\\\\\\
  5871.                 button.align = false\\\\\\\
  5872.                 local cx, cy = frame.x + frame.w, y - 1\\\\\\\
  5873.                 function button:onClick( )\\\\\\\
  5874.                     if frame.parent then\\\\\\\
  5875.                         local close = frame:newChild( UIButton( 1, 1, frame.w, frame.h, \\\\\\\"\\\\\\\" ) )\\\\\\\
  5876.                         close.bc, close.align = 0, false\\\\\\\
  5877.                         local f = UIFrame( cx, cy + frame.y + sframe.cy, frame.w, frame.h )\\\\\\\
  5878.                         local w, h = frame.parent.w, frame.parent.h\\\\\\\
  5879.                         menu( f, options[i].options )\\\\\\\
  5880.                         if f.x + f.w > w + 1 then\\\\\\\
  5881.                             f.x = w - f.w + 1\\\\\\\
  5882.                         end\\\\\\\
  5883.                         if f.y + f.h > h + 1 then\\\\\\\
  5884.                             f.y = h - f.h + 1\\\\\\\
  5885.                         end\\\\\\\
  5886.                         frame.parent:newChild( f )\\\\\\\
  5887.                         function close:onClick( )\\\\\\\
  5888.                             f:remove( )\\\\\\\
  5889.                             close:remove( )\\\\\\\
  5890.                         end\\\\\\\
  5891.                     end\\\\\\\
  5892.                 end\\\\\\\
  5893.             elseif options[i].type == \\\\\\\"button\\\\\\\" then\\\\\\\
  5894.                 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 ) )\\\\\\\
  5895.                 button.bc = 0\\\\\\\
  5896.                 button.tc = options[i].blue and colours.blue or colours.grey\\\\\\\
  5897.                 button.align = false\\\\\\\
  5898.                 function button:onClick( x, y, b )\\\\\\\
  5899.                     if type( options[i].onClick ) == \\\\\\\"function\\\\\\\" then\\\\\\\
  5900.                         options[i].onClick( button, frame, b )\\\\\\\
  5901.                     end\\\\\\\
  5902.                 end\\\\\\\
  5903.             elseif options[i].type == \\\\\\\"display\\\\\\\" then\\\\\\\
  5904.                 local text = sframe:newChild( UIText( options.spacing == false and 2 or 4, y, sframe.w - ( options.spacing == false and 2 or 4 ), 1, function( )\\\\\\\
  5905.                     if type( options[i].getDisplay ) == \\\\\\\"function\\\\\\\" then\\\\\\\
  5906.                         return options[i].name .. tostring( options[i].getDisplay( ) )\\\\\\\
  5907.                     end\\\\\\\
  5908.                     return options[i].name\\\\\\\
  5909.                 end ) )\\\\\\\
  5910.                 text.bc = 0\\\\\\\
  5911.                 text.tc = colours.grey\\\\\\\
  5912.             elseif options[i].type == \\\\\\\"input\\\\\\\" then\\\\\\\
  5913.                 if options[i].label then\\\\\\\
  5914.                     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 ) )\\\\\\\
  5915.                     label.bc = 0\\\\\\\
  5916.                     label.tc = colours.lightGrey\\\\\\\
  5917.                     y = y + 1\\\\\\\
  5918.                 end\\\\\\\
  5919.                 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 ) )\\\\\\\
  5920.                 input.bc = colours.lightGrey\\\\\\\
  5921.                 input.fbc = colours.lightGrey\\\\\\\
  5922.                 function input:onEnter( )\\\\\\\
  5923.                     if type( options[i].onEnter ) == \\\\\\\"function\\\\\\\" then\\\\\\\
  5924.                         options[i].onEnter( input )\\\\\\\
  5925.                     end\\\\\\\
  5926.                 end\\\\\\\
  5927.                 function input:whenUnFocussed( )\\\\\\\
  5928.                     self.text = \\\\\\\"\\\\\\\"\\\\\\\
  5929.                 end\\\\\\\
  5930.             elseif options[i].type == \\\\\\\"label\\\\\\\" then\\\\\\\
  5931.                 local text = sframe:newChild( UIText( 2, y, sframe.w - 2, 1, options[i].name ) )\\\\\\\
  5932.                 text.bc = 0\\\\\\\
  5933.                 text.tc = colours.lightGrey\\\\\\\
  5934.             end\\\\\\\
  5935.         end\\\\\\\
  5936.         y = y + 1\\\\\\\
  5937.     end\\\\\\\
  5938.     if options.shadow then\\\\\\\
  5939.         frame.w = frame.w + 1\\\\\\\
  5940.         frame.h = frame.h + 1\\\\\\\
  5941.     end\\\\\\\
  5942.     return sframe\\\\\\\
  5943. end\\\", meta={\\\
  5944.  type = \\\"lib\\\",\\\
  5945. }};[\\\"UIElement\\\"]={content=\\\"\\\\\\\
  5946. require \\\\\\\"UIHandler\\\\\\\"\\\\\\\
  5947. \\\\\\\
  5948. UIElement.public \\\\\\\"x\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
  5949. UIElement.public \\\\\\\"y\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
  5950. UIElement.public \\\\\\\"w\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
  5951. UIElement.public \\\\\\\"h\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
  5952. UIElement.public \\\\\\\"cx\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
  5953. UIElement.public \\\\\\\"cy\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
  5954. \\\\\\\
  5955. UIElement.public \\\\\\\"parent\\\\\\\"\\\\\\\
  5956. function UIElement.public.parent:write( value )\\\\\\\
  5957.     if value == false or class.typeOf( value, UIElement ) or class.typeOf( value, UIHandler ) then\\\\\\\
  5958.         self.parent = value\\\\\\\
  5959.     else\\\\\\\
  5960.         error( \\\\\\\"expected UIElement parent\\\\\\\", 3 )\\\\\\\
  5961.     end\\\\\\\
  5962. end\\\\\\\
  5963. \\\\\\\
  5964. UIElement.handlesMouse = true\\\\\\\
  5965. UIElement.handlesScroll = false\\\\\\\
  5966. UIElement.handlesTab = false\\\\\\\
  5967. UIElement.tabIndex = false\\\\\\\
  5968. \\\\\\\
  5969. UIElement.active = true\\\\\\\
  5970. UIElement.public \\\\\\\"active\\\\\\\" \\\\\\\"boolean\\\\\\\"\\\\\\\
  5971. \\\\\\\
  5972. UIElement.public \\\\\\\"handlesMouse\\\\\\\"\\\\\\\
  5973. UIElement.public.handlesMouse.write = false\\\\\\\
  5974. UIElement.public \\\\\\\"handlesKeys\\\\\\\"\\\\\\\
  5975. UIElement.public.handlesKeys.write = false\\\\\\\
  5976. UIElement.public \\\\\\\"handlesScroll\\\\\\\"\\\\\\\
  5977. UIElement.public.handlesScroll.write = false\\\\\\\
  5978. UIElement.public \\\\\\\"handlesTab\\\\\\\"\\\\\\\
  5979. UIElement.public.handlesTab.write = false\\\\\\\
  5980. UIElement.public \\\\\\\"handlesEnter\\\\\\\"\\\\\\\
  5981. UIElement.public.handlesEnter.write = false\\\\\\\
  5982. UIElement.public \\\\\\\"tabIndex\\\\\\\" \\\\\\\"boolean\\\\\\\"\\\\\\\
  5983. \\\\\\\
  5984. UIElement.isScrollTarget = false\\\\\\\
  5985. UIElement.public \\\\\\\"isScrollTarget\\\\\\\"\\\\\\\
  5986. UIElement.public.isScrollTarget.write = false\\\\\\\
  5987. UIElement.scrollDirection = \\\\\\\"none\\\\\\\"\\\\\\\
  5988. UIElement.public \\\\\\\"scrollDirection\\\\\\\"\\\\\\\
  5989. UIElement.public.scrollDirection.write = false\\\\\\\
  5990. \\\\\\\
  5991. UIElement.public \\\\\\\"whenFocussed\\\\\\\" \\\\\\\"function\\\\\\\"\\\\\\\
  5992. UIElement.public \\\\\\\"whenUnFocussed\\\\\\\" \\\\\\\"function\\\\\\\"\\\\\\\
  5993. \\\\\\\
  5994. function UIElement:UIElement( x, y, w, h )\\\\\\\
  5995.     self.x = x\\\\\\\
  5996.     self.y = y\\\\\\\
  5997.     self.w = w\\\\\\\
  5998.     self.h = h\\\\\\\
  5999.     self.cx = 0 -- children offset\\\\\\\
  6000.     self.cy = 0\\\\\\\
  6001.     self.children = { }\\\\\\\
  6002.     return self.public\\\\\\\
  6003. end\\\\\\\
  6004. \\\\\\\
  6005. -- children\\\\\\\
  6006. \\\\\\\
  6007. function UIElement.public:newChild( child )\\\\\\\
  6008.     if class.typeOf( child, UIElement ) then\\\\\\\
  6009.         table.insert( self.children, child )\\\\\\\
  6010.         child.parent = self.public\\\\\\\
  6011.         return child\\\\\\\
  6012.     else\\\\\\\
  6013.         error( \\\\\\\"expected UIElement child\\\\\\\", 2 )\\\\\\\
  6014.     end\\\\\\\
  6015. end\\\\\\\
  6016. \\\\\\\
  6017. function UIElement.public:removeChild( child )\\\\\\\
  6018.     if class.typeOf( child, UIElement ) then\\\\\\\
  6019.         for i = 1, #self.children do\\\\\\\
  6020.             if self.children[i] == child then\\\\\\\
  6021.                 table.remove( self.children, i )\\\\\\\
  6022.                 child.parent = false\\\\\\\
  6023.                 return true\\\\\\\
  6024.             end\\\\\\\
  6025.         end\\\\\\\
  6026.         return false\\\\\\\
  6027.     else\\\\\\\
  6028.         error( \\\\\\\"expected UIElement child\\\\\\\", 3 )\\\\\\\
  6029.     end\\\\\\\
  6030. end\\\\\\\
  6031. \\\\\\\
  6032. function UIElement.public:getChildren( )\\\\\\\
  6033.     local t = { }\\\\\\\
  6034.     for i = 1, #self.children do\\\\\\\
  6035.         if self.children[i].active then\\\\\\\
  6036.             t[#t+1] = self.children[i]\\\\\\\
  6037.         end\\\\\\\
  6038.     end\\\\\\\
  6039.     return t\\\\\\\
  6040. end\\\\\\\
  6041. \\\\\\\
  6042. function UIElement.public:clearChildren( )\\\\\\\
  6043.     for i = #self.children, 1, -1 do\\\\\\\
  6044.         self.children[i]:remove( )\\\\\\\
  6045.     end\\\\\\\
  6046. end\\\\\\\
  6047. \\\\\\\
  6048. local function merge( t1, t2 )\\\\\\\
  6049.     local t = { }\\\\\\\
  6050.     for i, v in ipairs( t1 ) do\\\\\\\
  6051.         table.insert( t, v )\\\\\\\
  6052.     end\\\\\\\
  6053.     for i, v in ipairs( t2 ) do\\\\\\\
  6054.         table.insert( t, v )\\\\\\\
  6055.     end\\\\\\\
  6056.     return t\\\\\\\
  6057. end\\\\\\\
  6058. \\\\\\\
  6059. function UIElement.public:getTabIndexes( )\\\\\\\
  6060.     local t = { }\\\\\\\
  6061.     if self.tabIndex then\\\\\\\
  6062.         table.insert( t, self.public )\\\\\\\
  6063.     end\\\\\\\
  6064.     for i = 1, #self.children do\\\\\\\
  6065.         local t2 = self.children[i]:getTabIndexes( )\\\\\\\
  6066.         if #t2 > 0 then\\\\\\\
  6067.             t = merge( t, t2 )\\\\\\\
  6068.         end\\\\\\\
  6069.     end\\\\\\\
  6070.     return t\\\\\\\
  6071. end\\\\\\\
  6072. \\\\\\\
  6073. -- parent dependant\\\\\\\
  6074. \\\\\\\
  6075. function UIElement.public:positionIn( parent )\\\\\\\
  6076.     local x, y = self.x, self.y\\\\\\\
  6077.     local p = self.parent\\\\\\\
  6078.     while p and p ~= parent and p:type( ) ~= \\\\\\\"UIHandler\\\\\\\" do\\\\\\\
  6079.         x = x + p.x\\\\\\\
  6080.         y = y + p.y\\\\\\\
  6081.         p = p.parent\\\\\\\
  6082.     end\\\\\\\
  6083.     return x, y\\\\\\\
  6084. end\\\\\\\
  6085. \\\\\\\
  6086. function UIElement.public:remove( )\\\\\\\
  6087.     if self.parent then\\\\\\\
  6088.         return self.parent:removeChild( self.public )\\\\\\\
  6089.     end\\\\\\\
  6090.     return false\\\\\\\
  6091. end\\\\\\\
  6092. \\\\\\\
  6093. function UIElement.public:getHandler( )\\\\\\\
  6094.     if self.parent then\\\\\\\
  6095.         local h = self.parent\\\\\\\
  6096.         while h:type( ) ~= \\\\\\\"UIHandler\\\\\\\" do\\\\\\\
  6097.             h = h.parent\\\\\\\
  6098.             if not h then return false end\\\\\\\
  6099.         end\\\\\\\
  6100.         return h\\\\\\\
  6101.     end\\\\\\\
  6102.     return false\\\\\\\
  6103. end\\\\\\\
  6104. \\\\\\\
  6105. function UIElement.public:centreX( )\\\\\\\
  6106.     if self.parent then\\\\\\\
  6107.         self.x = math.ceil( self.parent.w / 2 - self.w / 2 ) + 1\\\\\\\
  6108.     end\\\\\\\
  6109.     return self.public\\\\\\\
  6110. end\\\\\\\
  6111. \\\\\\\
  6112. function UIElement.public:centreY( )\\\\\\\
  6113.     if self.parent then\\\\\\\
  6114.         self.y = math.ceil( self.parent.h / 2 - self.h / 2 ) + 1\\\\\\\
  6115.     end\\\\\\\
  6116.     return self.public\\\\\\\
  6117. end\\\\\\\
  6118. \\\\\\\
  6119. function UIElement.public:centre( )\\\\\\\
  6120.     self.public:centreX( )\\\\\\\
  6121.     self.public:centreY( )\\\\\\\
  6122. end\\\\\\\
  6123. \\\\\\\
  6124. -- sibling dependant\\\\\\\
  6125. \\\\\\\
  6126. function UIElement.public:align( mode, sibling, spacing )\\\\\\\
  6127.     if mode == \\\\\\\"above\\\\\\\" then\\\\\\\
  6128.         self.x = sibling.x\\\\\\\
  6129.         self.w = sibling.w\\\\\\\
  6130.         self.y = sibling.y - self.h - ( spacing or 0 )\\\\\\\
  6131.     elseif mode == \\\\\\\"below\\\\\\\" then\\\\\\\
  6132.         self.x = sibling.x\\\\\\\
  6133.         self.w = sibling.w\\\\\\\
  6134.         self.y = sibling.y + sibling.h + ( spacing or 0 )\\\\\\\
  6135.     elseif mode == \\\\\\\"left\\\\\\\" then\\\\\\\
  6136.         self.y = sibling.y\\\\\\\
  6137.         self.h = sibling.h\\\\\\\
  6138.         self.x = sibling.x - self.w - ( spacing or 0 )\\\\\\\
  6139.     elseif mode == \\\\\\\"right\\\\\\\" then\\\\\\\
  6140.         self.y = sibling.y\\\\\\\
  6141.         self.h = sibling.h\\\\\\\
  6142.         self.x = sibling.x + sibling.w + ( spacing or 0 )\\\\\\\
  6143.     end\\\\\\\
  6144.     return self.public\\\\\\\
  6145. end\\\\\\\
  6146. \\\\\\\
  6147. function UIElement.public:alignTo( mode, sibling, spacing )\\\\\\\
  6148.     if mode == \\\\\\\"above\\\\\\\" then\\\\\\\
  6149.         self.x = sibling.x\\\\\\\
  6150.         self.w = sibling.w\\\\\\\
  6151.         self.y = sibling.y - self.h - ( spacing or 0 )\\\\\\\
  6152.     elseif mode == \\\\\\\"below\\\\\\\" then\\\\\\\
  6153.         self.x = sibling.x\\\\\\\
  6154.         self.w = sibling.w\\\\\\\
  6155.         self.y = sibling.y + sibling.h + ( spacing or 0 )\\\\\\\
  6156.     elseif mode == \\\\\\\"left\\\\\\\" then\\\\\\\
  6157.         self.y = sibling.y\\\\\\\
  6158.         self.h = sibling.h\\\\\\\
  6159.         self.x = sibling.x - self.w - ( spacing or 0 )\\\\\\\
  6160.     elseif mode == \\\\\\\"right\\\\\\\" then\\\\\\\
  6161.         self.y = sibling.y\\\\\\\
  6162.         self.h = sibling.h\\\\\\\
  6163.         self.x = sibling.x + sibling.w + ( spacing or 0 )\\\\\\\
  6164.     end\\\\\\\
  6165.     return self.public\\\\\\\
  6166. end\\\\\\\
  6167. \\\\\\\
  6168. -- callbacks\\\\\\\
  6169. \\\\\\\
  6170. function UIElement.public:update( dt )\\\\\\\
  6171.     local c = self.public:getChildren( )\\\\\\\
  6172.     for i, child in ipairs( c ) do\\\\\\\
  6173.         child:update( dt )\\\\\\\
  6174.     end\\\\\\\
  6175. end\\\\\\\
  6176. \\\\\\\
  6177. function UIElement.public:draw( x, y )\\\\\\\
  6178.     local layer = stencil.addLayer( x, y, self.w, self.h )\\\\\\\
  6179.     local c = self.public:getChildren( )\\\\\\\
  6180.     for i, child in ipairs( c ) do\\\\\\\
  6181.         child:draw( x + child.x - 1 + self.cx, y + child.y - 1 + self.cy )\\\\\\\
  6182.     end\\\\\\\
  6183.     stencil.closeLayer( layer )\\\\\\\
  6184. end\\\\\\\
  6185. \\\\\\\
  6186. function UIElement.public:onMouseClick( rx, ry, button )\\\\\\\
  6187. \\\\\\\
  6188. end\\\\\\\
  6189. \\\\\\\
  6190. function UIElement.public:onMouseDrag( rx, ry, cx, cy, button )\\\\\\\
  6191. \\\\\\\
  6192. end\\\\\\\
  6193. \\\\\\\
  6194. function UIElement.public:onMouseScroll( rx, ry, dir )\\\\\\\
  6195. \\\\\\\
  6196. end\\\\\\\
  6197. \\\\\\\
  6198. function UIElement.public:onKeyPress( key, lastkey )\\\\\\\
  6199. \\\\\\\
  6200. end\\\\\\\
  6201. \\\\\\\
  6202. function UIElement.public:onTextInput( text, lastkey )\\\\\\\
  6203. \\\\\\\
  6204. end\\\\\\\
  6205. \\\\\\\
  6206. function UIElement.public:onFocus( )\\\\\\\
  6207.     if type( self.whenFocussed ) == \\\\\\\"function\\\\\\\" then\\\\\\\
  6208.         self.whenFocussed( self.public )\\\\\\\
  6209.     end\\\\\\\
  6210. end\\\\\\\
  6211. \\\\\\\
  6212. function UIElement.public:onUnFocus( )\\\\\\\
  6213.     if type( self.whenUnFocussed ) == \\\\\\\"function\\\\\\\" then\\\\\\\
  6214.         self.whenUnFocussed( self.public )\\\\\\\
  6215.     end\\\\\\\
  6216. end\\\", meta={\\\
  6217.  type = \\\"class\\\",\\\
  6218. }};[\\\"UIHandler\\\"]={content=\\\"\\\\\\\
  6219. require \\\\\\\"stencil\\\\\\\"\\\\\\\
  6220. \\\\\\\
  6221. UIHandler.public.w, UIHandler.public.h = term.getSize( )\\\\\\\
  6222. \\\\\\\
  6223. UIHandler.public \\\\\\\"focus\\\\\\\"\\\\\\\
  6224. UIHandler.public.focus.write = false\\\\\\\
  6225. \\\\\\\
  6226. function UIHandler:UIHandler( )\\\\\\\
  6227.     self.children = { }\\\\\\\
  6228.     self.focus = false\\\\\\\
  6229.     self.lastx = 0\\\\\\\
  6230.     self.lasty = 0\\\\\\\
  6231.     self.lastt = false\\\\\\\
  6232.     self.lastkey = nil\\\\\\\
  6233.     self.lastkeytime = false\\\\\\\
  6234.     return self.public\\\\\\\
  6235. end\\\\\\\
  6236. \\\\\\\
  6237. function UIHandler.public:getChildren( )\\\\\\\
  6238.     local t = { }\\\\\\\
  6239.     for i = 1, #self.children do\\\\\\\
  6240.         if self.children[i].active then\\\\\\\
  6241.             t[i] = self.children[i]\\\\\\\
  6242.         end\\\\\\\
  6243.     end\\\\\\\
  6244.     return t\\\\\\\
  6245. end\\\\\\\
  6246. \\\\\\\
  6247. function UIHandler.public:draw( )\\\\\\\
  6248.     local c = self.public:getChildren( )\\\\\\\
  6249.     for i, child in ipairs( c ) do\\\\\\\
  6250.         child:draw( child.x, child.y )\\\\\\\
  6251.     end\\\\\\\
  6252. end\\\\\\\
  6253. \\\\\\\
  6254. function UIHandler.public:update( dt )\\\\\\\
  6255.     local c = self.public:getChildren( )\\\\\\\
  6256.     for i, child in ipairs( c ) do\\\\\\\
  6257.         child:update( dt )\\\\\\\
  6258.     end\\\\\\\
  6259.     if self.lastkeytime then\\\\\\\
  6260.         if os.clock( ) - self.lastkeytime > 0.3 then\\\\\\\
  6261.             self.lastkey = nil\\\\\\\
  6262.             self.lastkeytime = false\\\\\\\
  6263.         end\\\\\\\
  6264.     end\\\\\\\
  6265. end\\\\\\\
  6266. \\\\\\\
  6267. local function findMouseTarget( children, parent, handler, x, y )\\\\\\\
  6268.     x = x - parent.cx\\\\\\\
  6269.     y = y - parent.cy\\\\\\\
  6270.     for i = #children, 1, -1 do\\\\\\\
  6271.         local child = children[i]\\\\\\\
  6272.         if child.active and x >= child.x and y >= child.y and x < child.x + child.w and y < child.y + child.h then\\\\\\\
  6273.             local ok, c, data = findMouseTarget( child:getChildren( ), child, handler, x - child.x + 1, y - child.y + 1 )\\\\\\\
  6274.             if ok then\\\\\\\
  6275.                 return true, c, data\\\\\\\
  6276.             end\\\\\\\
  6277.             local ok, data = handler( child, parent, x, y )\\\\\\\
  6278.             if ok then\\\\\\\
  6279.                 return true, child, data\\\\\\\
  6280.             end\\\\\\\
  6281.         end\\\\\\\
  6282.     end\\\\\\\
  6283.     return false\\\\\\\
  6284. end\\\\\\\
  6285. \\\\\\\
  6286. local function screenCoords( child )\\\\\\\
  6287.     local x, y = child.x, child.y\\\\\\\
  6288.     while child.parent and child.parent:type( ) ~= \\\\\\\"UIHandler\\\\\\\" do -- everything but a UIHandler will have a parent\\\\\\\
  6289.         child = child.parent\\\\\\\
  6290.         x = x + child.x - 1 + child.cx\\\\\\\
  6291.         y = y + child.y - 1 + child.cy\\\\\\\
  6292.     end\\\\\\\
  6293.     return x, y\\\\\\\
  6294. end\\\\\\\
  6295. \\\\\\\
  6296. local function merge( t1, t2 )\\\\\\\
  6297.     local t = { }\\\\\\\
  6298.     for i, v in ipairs( t1 ) do\\\\\\\
  6299.         table.insert( t, v )\\\\\\\
  6300.     end\\\\\\\
  6301.     for i, v in ipairs( t2 ) do\\\\\\\
  6302.         table.insert( t, v )\\\\\\\
  6303.     end\\\\\\\
  6304.     return t\\\\\\\
  6305. end\\\\\\\
  6306. \\\\\\\
  6307. function UIHandler.public:event( event )\\\\\\\
  6308.     if event[1] == \\\\\\\"mouse_click\\\\\\\" then\\\\\\\
  6309.         self.lastx = event[3]\\\\\\\
  6310.         self.lasty = event[4]\\\\\\\
  6311.         local found, child, data = findMouseTarget( self.children, {cx=0, cy=0}, function( child, parent, x, y )\\\\\\\
  6312.             if child.handlesMouse then\\\\\\\
  6313.                 return true, { rx = x - child.x + 1, ry = y - child.y + 1 }\\\\\\\
  6314.             end\\\\\\\
  6315.         end, event[3], event[4] )\\\\\\\
  6316.         if found then\\\\\\\
  6317.             if child ~= self.focus and self.focus then\\\\\\\
  6318.                 self.focus:onUnFocus( )\\\\\\\
  6319.             end\\\\\\\
  6320.             if child ~= self.focus then\\\\\\\
  6321.                 child:onFocus( )\\\\\\\
  6322.             end\\\\\\\
  6323.             self.focus = child\\\\\\\
  6324.             child:onMouseClick( data.rx, data.ry, event[2] )\\\\\\\
  6325.             local sx, sy = screenCoords( child )\\\\\\\
  6326.             self.lastt = { child = child, sx = sx, sy = sy, x = child.x, y = child.y }\\\\\\\
  6327.         else\\\\\\\
  6328.             self.lastt = false\\\\\\\
  6329.             if self.focus then\\\\\\\
  6330.                 self.focus:onUnFocus( )\\\\\\\
  6331.             end\\\\\\\
  6332.             self.focus = false\\\\\\\
  6333.         end\\\\\\\
  6334.     elseif event[1] == \\\\\\\"mouse_scroll\\\\\\\" then\\\\\\\
  6335.         local found, child, data = findMouseTarget( self.children, {cx=0, cy=0}, function( child, parent, x, y )\\\\\\\
  6336.             if child.handlesScroll then\\\\\\\
  6337.                 return true, { rx = x - child.x + 1, ry = y - child.y + 1 }\\\\\\\
  6338.             end\\\\\\\
  6339.         end, event[3], event[4] )\\\\\\\
  6340.         if found then\\\\\\\
  6341.             child:onMouseScroll( data.rx, data.ry, event[2] )\\\\\\\
  6342.         end\\\\\\\
  6343.     elseif event[1] == \\\\\\\"mouse_drag\\\\\\\" then\\\\\\\
  6344.         if self.lastt then\\\\\\\
  6345.             local cx, cy = event[3] - self.lastx, event[4] - self.lasty\\\\\\\
  6346.             local sx, sy = self.lastt.sx + ( self.lastt.child.x - self.lastt.x ), self.lastt.sy + ( self.lastt.child.y - self.lastt.y )\\\\\\\
  6347.             self.lastt.child:onMouseDrag( event[3] - sx + 1, event[4] - sy + 1, cx, cy, event[2] )\\\\\\\
  6348.             self.lastx = event[3]\\\\\\\
  6349.             self.lasty = event[4]\\\\\\\
  6350.         end\\\\\\\
  6351.     elseif event[1] == \\\\\\\"key\\\\\\\" then\\\\\\\
  6352.         if event[2] == keys.tab then\\\\\\\
  6353. \\\\\\\
  6354.             if self.focus and self.focus.handlesTab then\\\\\\\
  6355.                 self.focus:onKeyPress( keys.tab, self.lastkey )\\\\\\\
  6356.             else\\\\\\\
  6357.                 local tabs = { }\\\\\\\
  6358.                 for i = 1, #self.children do\\\\\\\
  6359.                     local t2 = self.children[i]:getTabIndexes( )\\\\\\\
  6360.                     if #t2 > 0 then\\\\\\\
  6361.                         tabs = merge( tabs, t2 )\\\\\\\
  6362.                     end\\\\\\\
  6363.                 end\\\\\\\
  6364.                 local of = self.focus\\\\\\\
  6365.                 local found = false\\\\\\\
  6366.                 for i = 1, #tabs do\\\\\\\
  6367.                     if tabs[i] == self.focus then\\\\\\\
  6368.                         if self.lastkey == keys.leftShift or self.lastkey == keys.rightShift then\\\\\\\
  6369.                             if tabs[i-1] then\\\\\\\
  6370.                                 self.focus = tabs[i-1]\\\\\\\
  6371.                             else\\\\\\\
  6372.                                 self.focus = tabs[#tabs]\\\\\\\
  6373.                             end\\\\\\\
  6374.                         else\\\\\\\
  6375.                             if tabs[i+1] then\\\\\\\
  6376.                                 self.focus = tabs[i+1]\\\\\\\
  6377.                             else\\\\\\\
  6378.                                 self.focus = tabs[1]\\\\\\\
  6379.                             end\\\\\\\
  6380.                         end\\\\\\\
  6381.                         found = true\\\\\\\
  6382.                         break\\\\\\\
  6383.                     end\\\\\\\
  6384.                 end\\\\\\\
  6385.                 if not found then\\\\\\\
  6386.                     self.focus = tabs[1]\\\\\\\
  6387.                 end\\\\\\\
  6388.                 if of ~= self.focus then\\\\\\\
  6389.                     if of then\\\\\\\
  6390.                         of:onUnFocus( )\\\\\\\
  6391.                     end\\\\\\\
  6392.                     if self.focus then\\\\\\\
  6393.                         self.focus:onFocus( )\\\\\\\
  6394.                     end\\\\\\\
  6395.                 end\\\\\\\
  6396.             end\\\\\\\
  6397.         else\\\\\\\
  6398.             if self.focus and ( self.focus.handlesKeys or ( self.focus.handlesEnter and event[2] == keys.enter ) ) then\\\\\\\
  6399.                 self.focus:onKeyPress( event[2], self.lastkey )\\\\\\\
  6400.             else\\\\\\\
  6401.                 local function find( c )\\\\\\\
  6402.                     for i = #c, 1, -1 do\\\\\\\
  6403.                         local ok, obj = find( c[i]:getChildren( ) )\\\\\\\
  6404.                         if ok then\\\\\\\
  6405.                             return true, obj\\\\\\\
  6406.                         end\\\\\\\
  6407.                         if c[i].active and c[i].handlesKeys then\\\\\\\
  6408.                             c[i]:onKeyPress( event[2], self.lastkey )\\\\\\\
  6409.                             return true, c[i]\\\\\\\
  6410.                         end\\\\\\\
  6411.                     end\\\\\\\
  6412.                 end\\\\\\\
  6413.                 find( self.children )\\\\\\\
  6414.             end\\\\\\\
  6415.         end\\\\\\\
  6416.     elseif event[1] == \\\\\\\"char\\\\\\\" then\\\\\\\
  6417.         if self.focus and self.focus.handlesKeys then\\\\\\\
  6418.             self.focus:onTextInput( event[2], self.lastkey )\\\\\\\
  6419.         else\\\\\\\
  6420.             local function find( c )\\\\\\\
  6421.                 for i = #c, 1, -1 do\\\\\\\
  6422.                     local ok, obj = find( c[i]:getChildren( ) )\\\\\\\
  6423.                     if ok then\\\\\\\
  6424.                         return true, obj\\\\\\\
  6425.                     end\\\\\\\
  6426.                     if c[i].active and c[i].handlesKeys then\\\\\\\
  6427.                         c[i]:onTextInput( event[2], self.lastkey )\\\\\\\
  6428.                         return true, c[i]\\\\\\\
  6429.                     end\\\\\\\
  6430.                 end\\\\\\\
  6431.             end\\\\\\\
  6432.             find( self.children )\\\\\\\
  6433.         end\\\\\\\
  6434.     end\\\\\\\
  6435.     if event[1] == \\\\\\\"key\\\\\\\" or ( event[1] == \\\\\\\"char\\\\\\\" and self.lastkey ) then\\\\\\\
  6436.         if event[1] == \\\\\\\"key\\\\\\\" then\\\\\\\
  6437.             self.lastkey = event[2]\\\\\\\
  6438.         end\\\\\\\
  6439.         self.lastkeytime = os.clock( )\\\\\\\
  6440.     else\\\\\\\
  6441.         self.lastkey = nil\\\\\\\
  6442.         self.lastkeytime = false\\\\\\\
  6443.     end\\\\\\\
  6444. end\\\\\\\
  6445. \\\\\\\
  6446. function UIHandler.public:newChild( child )\\\\\\\
  6447.     if class.typeOf( child, UIElement ) then\\\\\\\
  6448.         table.insert( self.children, child )\\\\\\\
  6449.         child.parent = self.public\\\\\\\
  6450.         return child\\\\\\\
  6451.     else\\\\\\\
  6452.         error( \\\\\\\"expected UIElement child\\\\\\\", 2 )\\\\\\\
  6453.     end\\\\\\\
  6454. end\\\\\\\
  6455. \\\\\\\
  6456. function UIHandler.public:removeChild( child )\\\\\\\
  6457.     local removed = false\\\\\\\
  6458.     for i = #self.children, 1, -1 do\\\\\\\
  6459.         if self.children[i] == child then\\\\\\\
  6460.             table.remove( self.children, i )\\\\\\\
  6461.             removed = true\\\\\\\
  6462.         end\\\\\\\
  6463.     end\\\\\\\
  6464.     return removed\\\\\\\
  6465. end\\\\\\\
  6466. \\\\\\\
  6467. function UIHandler.public:unFocus( )\\\\\\\
  6468.     if self.focus then\\\\\\\
  6469.         self.focus:onUnFocus( )\\\\\\\
  6470.         self.focus = false\\\\\\\
  6471.     end\\\\\\\
  6472. end\\\\\\\
  6473. \\\\\\\
  6474. function UIHandler.public:setFocus( child )\\\\\\\
  6475.     if class.typeOf( child, UIElement ) then\\\\\\\
  6476.         if self.focus ~= child then\\\\\\\
  6477.             if self.focus then\\\\\\\
  6478.                 self.focus:onUnFocus( )\\\\\\\
  6479.             end\\\\\\\
  6480.             child:onFocus( )\\\\\\\
  6481.             self.focus = child\\\\\\\
  6482.         end\\\\\\\
  6483.     else\\\\\\\
  6484.         error( \\\\\\\"expected UIElement child\\\\\\\", 3 )\\\\\\\
  6485.     end\\\\\\\
  6486. end\\\", meta={\\\
  6487.  type = \\\"class\\\",\\\
  6488. }};[\\\"main\\\"]={content=\\\"\\\\\\\
  6489. require \\\\\\\"buffer\\\\\\\"\\\\\\\
  6490. require \\\\\\\"clipboard\\\\\\\"\\\\\\\
  6491. require \\\\\\\"display\\\\\\\"\\\\\\\
  6492. require \\\\\\\"shader\\\\\\\"\\\\\\\
  6493. require \\\\\\\"stencil\\\\\\\"\\\\\\\
  6494. \\\\\\\
  6495. export \\\\\\\"buffer\\\\\\\"\\\\\\\
  6496. export \\\\\\\"clipboard\\\\\\\"\\\\\\\
  6497. export \\\\\\\"display\\\\\\\"\\\\\\\
  6498. export \\\\\\\"shader\\\\\\\"\\\\\\\
  6499. export \\\\\\\"stencil\\\\\\\"\\\\\\\
  6500. \\\\\\\
  6501. export \\\\\\\"Image\\\\\\\"\\\\\\\
  6502. export \\\\\\\"Thread\\\\\\\"\\\\\\\
  6503. export \\\\\\\"UIBuffer\\\\\\\"\\\\\\\
  6504. export \\\\\\\"UIButton\\\\\\\"\\\\\\\
  6505. export \\\\\\\"UICode\\\\\\\"\\\\\\\
  6506. export \\\\\\\"UIElement\\\\\\\"\\\\\\\
  6507. export \\\\\\\"UIFrame\\\\\\\"\\\\\\\
  6508. export \\\\\\\"UIHandler\\\\\\\"\\\\\\\
  6509. export \\\\\\\"UIImage\\\\\\\"\\\\\\\
  6510. export \\\\\\\"UIInput\\\\\\\"\\\\\\\
  6511. export \\\\\\\"UIKeyHandler\\\\\\\"\\\\\\\
  6512. export \\\\\\\"UIMenu\\\\\\\"\\\\\\\
  6513. export \\\\\\\"UIScrollBar\\\\\\\"\\\\\\\
  6514. export \\\\\\\"UIText\\\\\\\"\\\\\\\
  6515. export \\\\\\\"UITextbox\\\\\\\"\\\", meta={}};[\\\"UIScrollBar\\\"]={content=\\\"\\\\\\\
  6516. require \\\\\\\"UIElement\\\\\\\"\\\\\\\
  6517. \\\\\\\
  6518. UIScrollBar:extends( UIElement )\\\\\\\
  6519. \\\\\\\
  6520. UIScrollBar.public \\\\\\\"bc\\\\\\\"  \\\\\\\"number\\\\\\\"\\\\\\\
  6521. UIScrollBar.public \\\\\\\"tc\\\\\\\"  \\\\\\\"number\\\\\\\"\\\\\\\
  6522. \\\\\\\
  6523. UIScrollBar.handlesScroll = true\\\\\\\
  6524. \\\\\\\
  6525. function UIScrollBar:UIScrollBar( x, y, w, h, target, direction )\\\\\\\
  6526.     self:UIElement( x, y, w, h )\\\\\\\
  6527. \\\\\\\
  6528.     self.bc = colours.lightGrey\\\\\\\
  6529.     self.tc = colours.grey\\\\\\\
  6530.     self.direction = direction or \\\\\\\"vertical\\\\\\\"\\\\\\\
  6531. \\\\\\\
  6532.     if target then\\\\\\\
  6533.         if class.typeOf( target, UIElement ) then\\\\\\\
  6534.             if target.isScrollTarget then\\\\\\\
  6535.                 self.target = target\\\\\\\
  6536.             else\\\\\\\
  6537.                 error( \\\\\\\"cannot set \\\\\\\" .. target:type( ) .. \\\\\\\" as scroll target\\\\\\\", 3 )\\\\\\\
  6538.             end\\\\\\\
  6539.         else\\\\\\\
  6540.             error( \\\\\\\"expected UIElement target\\\\\\\", 3 )\\\\\\\
  6541.         end\\\\\\\
  6542.     end\\\\\\\
  6543.     return self.public\\\\\\\
  6544. end\\\\\\\
  6545. \\\\\\\
  6546. function UIScrollBar.public:setScrollTarget( target )\\\\\\\
  6547.     if class.typeOf( target, UIElement ) then\\\\\\\
  6548.         if target.isScrollTarget then\\\\\\\
  6549.             self.target = target\\\\\\\
  6550.         else\\\\\\\
  6551.             error( \\\\\\\"cannot set \\\\\\\" .. target:type( ) .. \\\\\\\" as scroll target\\\\\\\", 2 )\\\\\\\
  6552.         end\\\\\\\
  6553.     else\\\\\\\
  6554.         error( \\\\\\\"expected UIElement target\\\\\\\", 2 )\\\\\\\
  6555.     end\\\\\\\
  6556. end\\\\\\\
  6557. \\\\\\\
  6558. --[[\\\\\\\
  6559.     barsize/traysize = framesize/contentsize\\\\\\\
  6560.     barposition = traysize * contentscroll / contentsize\\\\\\\
  6561. ]]\\\\\\\
  6562. \\\\\\\
  6563. function UIScrollBar:getBar( )\\\\\\\
  6564.     if not self.target then\\\\\\\
  6565.         return 0, self.direction == \\\\\\\"vertical\\\\\\\" and self.h or self.w\\\\\\\
  6566.     end\\\\\\\
  6567.     local traysize\\\\\\\
  6568.     if self.direction == \\\\\\\"vertical\\\\\\\" then\\\\\\\
  6569.         traysize = self.h\\\\\\\
  6570.     else\\\\\\\
  6571.         traysize = self.w\\\\\\\
  6572.     end\\\\\\\
  6573.     local framesize, contentsize, contentscroll\\\\\\\
  6574.     if self.direction == \\\\\\\"vertical\\\\\\\" then\\\\\\\
  6575.         framesize = self.target:getDisplaySizeV( )\\\\\\\
  6576.         contentsize = self.target:getContentSizeV( )\\\\\\\
  6577.         contentscroll = self.target:getContentScrollV( )\\\\\\\
  6578.     else\\\\\\\
  6579.         framesize = self.target:getDisplaySizeH( )\\\\\\\
  6580.         contentsize = self.target:getContentSizeH( )\\\\\\\
  6581.         contentscroll = self.target:getContentScrollH( )\\\\\\\
  6582.     end\\\\\\\
  6583. \\\\\\\
  6584.     local barsize = math.max( math.floor( traysize * framesize / contentsize ), 1 )\\\\\\\
  6585. \\\\\\\
  6586.     local barpos = traysize * contentscroll / contentsize\\\\\\\
  6587.     return math.ceil( barpos ), barsize\\\\\\\
  6588. end\\\\\\\
  6589. \\\\\\\
  6590. function UIScrollBar.public:draw( x, y )\\\\\\\
  6591.     local layer = stencil.addLayer( x, y, self.w, self.h )\\\\\\\
  6592.     if self.target then\\\\\\\
  6593.         local pos, size = self:getBar( )\\\\\\\
  6594.         stencil.rectangle( x, y, self.w, self.h, self.tc, 1, \\\\\\\" \\\\\\\" )\\\\\\\
  6595.         if self.direction == \\\\\\\"vertical\\\\\\\" then\\\\\\\
  6596.             stencil.rectangle( x, y + pos, self.w, size, self.bc, 1, \\\\\\\" \\\\\\\" )\\\\\\\
  6597.         else\\\\\\\
  6598.             stencil.rectangle( x + pos, y, size, self.h, self.bc, 1, \\\\\\\" \\\\\\\" )\\\\\\\
  6599.         end\\\\\\\
  6600.     else\\\\\\\
  6601.         stencil.rectangle( x, y, self.w, self.h, self.bc, 1, \\\\\\\" \\\\\\\" )\\\\\\\
  6602.     end\\\\\\\
  6603.     local c = self.public:getChildren( )\\\\\\\
  6604.     for i, child in ipairs( c ) do\\\\\\\
  6605.         child:draw( x + child.x - 1 + self.cx, y + child.y - 1 + self.cy )\\\\\\\
  6606.     end\\\\\\\
  6607.     stencil.closeLayer( layer )\\\\\\\
  6608. end\\\\\\\
  6609. \\\\\\\
  6610. function UIScrollBar.public:onMouseClick( rx, ry, button )\\\\\\\
  6611.     if not self.target then return end\\\\\\\
  6612.     local pos, size = self:getBar( )\\\\\\\
  6613.     local traysize\\\\\\\
  6614.     if self.direction == \\\\\\\"vertical\\\\\\\" then\\\\\\\
  6615.         self.downoffset = ry - pos\\\\\\\
  6616.         if ry > pos and ry < pos + size - 1 then return end\\\\\\\
  6617.         pos = ry - 1\\\\\\\
  6618.         traysize = self.h\\\\\\\
  6619.     else\\\\\\\
  6620.         self.downoffset = rx - pos\\\\\\\
  6621.         if rx > pos and rx < pos + size - 1 then return end\\\\\\\
  6622.         pos = rx - 1\\\\\\\
  6623.         traysize = self.w\\\\\\\
  6624.     end\\\\\\\
  6625.     pos = math.max( math.min( pos, traysize - size), 0 )\\\\\\\
  6626.     if self.direction == \\\\\\\"vertical\\\\\\\" then\\\\\\\
  6627.         self.target:setContentScrollV( math.floor( pos / traysize * self.target:getContentSizeV( ) ) )\\\\\\\
  6628.     else\\\\\\\
  6629.         self.target:setContentScrollH( math.floor( pos / traysize * self.target:getContentSizeH( ) ) )\\\\\\\
  6630.     end\\\\\\\
  6631. end\\\\\\\
  6632. \\\\\\\
  6633. function UIScrollBar.public:onMouseDrag( rx, ry, cx, cy, button )\\\\\\\
  6634.     if not self.target then return end\\\\\\\
  6635.     local pos, size = self:getBar( )\\\\\\\
  6636.     local traysize\\\\\\\
  6637.     if self.direction == \\\\\\\"vertical\\\\\\\" then\\\\\\\
  6638.         pos = ry - self.downoffset\\\\\\\
  6639.         traysize = self.h\\\\\\\
  6640.     else\\\\\\\
  6641.         pos = rx - self.downoffset\\\\\\\
  6642.         traysize = self.w\\\\\\\
  6643.     end\\\\\\\
  6644.     pos = math.max( math.min( pos, traysize - size), 0 )\\\\\\\
  6645.     if self.direction == \\\\\\\"vertical\\\\\\\" then\\\\\\\
  6646.         self.target:setContentScrollV( math.floor( pos / traysize * self.target:getContentSizeV( ) ) )\\\\\\\
  6647.     else\\\\\\\
  6648.         self.target:setContentScrollH( math.floor( pos / traysize * self.target:getContentSizeH( ) ) )\\\\\\\
  6649.     end\\\\\\\
  6650. end\\\\\\\
  6651. \\\\\\\
  6652. function UIScrollBar.public:onMouseScroll( rx, ry, dir )\\\\\\\
  6653.     if not self.target then return end\\\\\\\
  6654.     local pos, size = self:getBar( )\\\\\\\
  6655.     local traysize\\\\\\\
  6656.     if self.direction == \\\\\\\"vertical\\\\\\\" then\\\\\\\
  6657.         traysize = self.h\\\\\\\
  6658.     else\\\\\\\
  6659.         traysize = self.w\\\\\\\
  6660.     end\\\\\\\
  6661.     pos = math.max( math.min( pos + dir, traysize - size), 0 )\\\\\\\
  6662.     if self.direction == \\\\\\\"vertical\\\\\\\" then\\\\\\\
  6663.         self.target:setContentScrollV( math.floor( pos / traysize * self.target:getContentSizeV( ) ) )\\\\\\\
  6664.     else\\\\\\\
  6665.         self.target:setContentScrollH( math.floor( pos / traysize * self.target:getContentSizeH( ) ) )\\\\\\\
  6666.     end\\\\\\\
  6667. end\\\", meta={\\\
  6668.  type = \\\"class\\\",\\\
  6669. }};[\\\"UIBuffer\\\"]={content=\\\"\\\\\\\
  6670. local colour = term.isColour( )\\\\\\\
  6671. \\\\\\\
  6672. require \\\\\\\"UIElement\\\\\\\"\\\\\\\
  6673. require \\\\\\\"Image\\\\\\\"\\\\\\\
  6674. UIBuffer:extends( UIElement )\\\\\\\
  6675. \\\\\\\
  6676. UIBuffer.public \\\\\\\"cx\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
  6677. UIBuffer.public \\\\\\\"cy\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
  6678. UIBuffer.public \\\\\\\"cb\\\\\\\" \\\\\\\"boolean\\\\\\\"\\\\\\\
  6679. UIBuffer.public \\\\\\\"bc\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
  6680. UIBuffer.public \\\\\\\"tc\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
  6681. UIBuffer.public \\\\\\\"image\\\\\\\"\\\\\\\
  6682. UIBuffer.public.image.write = false\\\\\\\
  6683. \\\\\\\
  6684. UIBuffer.handlesKeys = true\\\\\\\
  6685. UIBuffer.handlesScroll = true\\\\\\\
  6686. UIBuffer.handlesTab = true\\\\\\\
  6687. UIBuffer.handlesEnter = true\\\\\\\
  6688. \\\\\\\
  6689. local function termObject( canvas )\\\\\\\
  6690.     local t = { }\\\\\\\
  6691.     function t.write( str )\\\\\\\
  6692.         if type( str ) ~= \\\\\\\"string\\\\\\\" and type( str ) ~= \\\\\\\"number\\\\\\\" then return error \\\\\\\"expected string\\\\\\\" end\\\\\\\
  6693.         str = tostring( str )\\\\\\\
  6694.         for x = 1, #str do\\\\\\\
  6695.             canvas.image:pixel( canvas.cx, canvas.cy, canvas.bc, canvas.tc, str:sub( x, x ) )\\\\\\\
  6696.             canvas.cx = canvas.cx + 1\\\\\\\
  6697.         end\\\\\\\
  6698.     end\\\\\\\
  6699.     function t.clearLine( )\\\\\\\
  6700.         canvas.image:foreach( function( x, y, bc, tc, char )\\\\\\\
  6701.             if y == canvas.cy then\\\\\\\
  6702.                 return canvas.bc, canvas.tc, \\\\\\\" \\\\\\\"\\\\\\\
  6703.             end\\\\\\\
  6704.         end )\\\\\\\
  6705.     end\\\\\\\
  6706.     function t.clear( )\\\\\\\
  6707.         canvas.image:foreach( function( x, y, bc, tc, char )\\\\\\\
  6708.             return canvas.bc, canvas.tc, \\\\\\\" \\\\\\\"\\\\\\\
  6709.         end )\\\\\\\
  6710.     end\\\\\\\
  6711.     function t.setCursorPos( x, y )\\\\\\\
  6712.         if type( x ) ~= \\\\\\\"number\\\\\\\" or type( y ) ~= \\\\\\\"number\\\\\\\" then\\\\\\\
  6713.             return error \\\\\\\"expected number, number\\\\\\\"\\\\\\\
  6714.         end\\\\\\\
  6715.         canvas.cx = x\\\\\\\
  6716.         canvas.cy = y\\\\\\\
  6717.     end\\\\\\\
  6718.     function t.getCursorPos( )\\\\\\\
  6719.         return canvas.cx, canvas.cy\\\\\\\
  6720.     end\\\\\\\
  6721.     function t.setBackgroundColour( col )\\\\\\\
  6722.         if type( col ) ~= \\\\\\\"number\\\\\\\" then\\\\\\\
  6723.             return error \\\\\\\"expected number\\\\\\\"\\\\\\\
  6724.         end\\\\\\\
  6725.         canvas.bc = col\\\\\\\
  6726.     end\\\\\\\
  6727.     function t.setTextColour( col )\\\\\\\
  6728.         if type( col ) ~= \\\\\\\"number\\\\\\\" then\\\\\\\
  6729.             return error \\\\\\\"expected number\\\\\\\"\\\\\\\
  6730.         end\\\\\\\
  6731.         canvas.tc = col\\\\\\\
  6732.     end\\\\\\\
  6733.     function t.setBackgroundColor( col )\\\\\\\
  6734.         if type( col ) ~= \\\\\\\"number\\\\\\\" then\\\\\\\
  6735.             return error \\\\\\\"expected number\\\\\\\"\\\\\\\
  6736.         end\\\\\\\
  6737.         canvas.bc = col\\\\\\\
  6738.     end\\\\\\\
  6739.     function t.setTextColor( col )\\\\\\\
  6740.         if type( col ) ~= \\\\\\\"number\\\\\\\" then\\\\\\\
  6741.             return error \\\\\\\"expected number\\\\\\\"\\\\\\\
  6742.         end\\\\\\\
  6743.         canvas.tc = col\\\\\\\
  6744.     end\\\\\\\
  6745.     function t.scroll( dir )\\\\\\\
  6746.         canvas.image:foreach( function( x, y, bc, tc, char )\\\\\\\
  6747.             if canvas.image:getPixel( x, y + dir ) then\\\\\\\
  6748.                 return canvas.image:getPixel( x, y + dir )\\\\\\\
  6749.             else\\\\\\\
  6750.                 return canvas.bc, canvas.tc, \\\\\\\" \\\\\\\"\\\\\\\
  6751.             end\\\\\\\
  6752.         end )\\\\\\\
  6753.     end\\\\\\\
  6754.     function t.setCursorBlink( state )\\\\\\\
  6755.         canvas.cb = not not state\\\\\\\
  6756.     end\\\\\\\
  6757.     function t.isColour( )\\\\\\\
  6758.         return colour\\\\\\\
  6759.     end\\\\\\\
  6760.     function t.isColor( )\\\\\\\
  6761.         return colour\\\\\\\
  6762.     end\\\\\\\
  6763.     function t.getSize( )\\\\\\\
  6764.         return canvas.w, canvas.h\\\\\\\
  6765.     end\\\\\\\
  6766. \\\\\\\
  6767.     return t\\\\\\\
  6768. end\\\\\\\
  6769. \\\\\\\
  6770. local function luaEnvironment( canvas )\\\\\\\
  6771.     local env = { }\\\\\\\
  6772.     env.fs = fs\\\\\\\
  6773.     env.term = term\\\\\\\
  6774.     env._VERSION = _VERSION\\\\\\\
  6775.     env.pairs = pairs\\\\\\\
  6776.     env.ipairs = ipairs\\\\\\\
  6777.     env.select = select\\\\\\\
  6778.     env.unpack = unpack\\\\\\\
  6779.     env.setfenv = setfenv\\\\\\\
  6780.     env.getfenv = getfenv\\\\\\\
  6781.     env.setmetatable = setmetatable\\\\\\\
  6782.     env.getmetatable = getmetatable\\\\\\\
  6783.     env.next = next\\\\\\\
  6784.     env.rawset = rawset\\\\\\\
  6785.     env.rawget = rawget\\\\\\\
  6786.     env.rawequal = rawequal\\\\\\\
  6787.     env.type = type\\\\\\\
  6788.     env.tostring = tostring\\\\\\\
  6789.     env.tonumber = tonumber\\\\\\\
  6790.     env.pcall = pcall\\\\\\\
  6791.     env.xpcall = xpcall\\\\\\\
  6792.     env.loadstring = loadstring\\\\\\\
  6793.     env.assert = assert\\\\\\\
  6794.     env.error = error\\\\\\\
  6795.     env.sleep = sleep\\\\\\\
  6796.     env.__inext = __inext\\\\\\\
  6797.     env.math = math\\\\\\\
  6798.     env.string = string\\\\\\\
  6799.     env.table = table\\\\\\\
  6800.     env.coroutine = coroutine\\\\\\\
  6801.     env.keys = keys\\\\\\\
  6802.     env.colours = colours\\\\\\\
  6803.     env.colors = colors\\\\\\\
  6804.     env.vector = vector\\\\\\\
  6805.     env.bit = bit\\\\\\\
  6806.     env.http = http\\\\\\\
  6807.     env.write = write\\\\\\\
  6808.     env.print = print\\\\\\\
  6809.     env.printError = printError\\\\\\\
  6810.     env.read = read\\\\\\\
  6811.     env.rednet = rednet\\\\\\\
  6812.     local tAPIsLoading = { }\\\\\\\
  6813.     env.os = setmetatable( {\\\\\\\
  6814.         pullEventRaw = function( sFilter )\\\\\\\
  6815.             while true do\\\\\\\
  6816.                 local event = { coroutine.yield( ) }\\\\\\\
  6817.                 if not sFilter or sFilter == event[1] then\\\\\\\
  6818.                     return unpack( event )\\\\\\\
  6819.                 end\\\\\\\
  6820.             end\\\\\\\
  6821.         end;\\\\\\\
  6822.         pullEvent = function( sFilter )\\\\\\\
  6823.             while true do\\\\\\\
  6824.                 local event = { coroutine.yield( ) }\\\\\\\
  6825.                 if event[1] == \\\\\\\"terminate\\\\\\\" then\\\\\\\
  6826.                     error( \\\\\\\"Terminated\\\\\\\", 0 )\\\\\\\
  6827.                 end\\\\\\\
  6828.                 if not sFilter or sFilter == event[1] then\\\\\\\
  6829.                     return unpack( event )\\\\\\\
  6830.                 end\\\\\\\
  6831.             end\\\\\\\
  6832.         end;\\\\\\\
  6833.         run = function( _tEnv, _sPath, ... )\\\\\\\
  6834.             local tArgs = { ... }\\\\\\\
  6835.             local fnFile, err = env.loadfile( _sPath )\\\\\\\
  6836.             if fnFile then\\\\\\\
  6837.                 local tEnv = _tEnv\\\\\\\
  6838.                 --setmetatable( tEnv, { __index = function(t,k) return _G[k] end } )\\\\\\\
  6839.                 setmetatable( tEnv, { __index = env } )\\\\\\\
  6840.                 setfenv( fnFile, tEnv )\\\\\\\
  6841.                 local ok, err = pcall( function()\\\\\\\
  6842.                     fnFile( unpack( tArgs ) )\\\\\\\
  6843.                 end )\\\\\\\
  6844.                 if not ok then\\\\\\\
  6845.                     if err and err ~= \\\\\\\"\\\\\\\" then\\\\\\\
  6846.                         printError( err )\\\\\\\
  6847.                     end\\\\\\\
  6848.                     return false\\\\\\\
  6849.                 end\\\\\\\
  6850.                 return true\\\\\\\
  6851.             end\\\\\\\
  6852.             if err and err ~= \\\\\\\"\\\\\\\" then\\\\\\\
  6853.                 printError( err )\\\\\\\
  6854.             end\\\\\\\
  6855.             return false\\\\\\\
  6856.         end;\\\\\\\
  6857.         loadAPI = function( _sPath )\\\\\\\
  6858.             local sName = fs.getName( _sPath )\\\\\\\
  6859.             if tAPIsLoading[sName] == true then\\\\\\\
  6860.                 printError( \\\\\\\"API \\\\\\\"..sName..\\\\\\\" is already being loaded\\\\\\\" )\\\\\\\
  6861.                 return false\\\\\\\
  6862.             end\\\\\\\
  6863.             tAPIsLoading[sName] = true\\\\\\\
  6864.                 \\\\\\\
  6865.             local tEnv = {}\\\\\\\
  6866.             setmetatable( tEnv, { __index = env } )\\\\\\\
  6867.             local fnAPI, err = loadfile( _sPath )\\\\\\\
  6868.             if fnAPI then\\\\\\\
  6869.                 setfenv( fnAPI, tEnv )\\\\\\\
  6870.                 fnAPI()\\\\\\\
  6871.             else\\\\\\\
  6872.                 printError( err )\\\\\\\
  6873.                 tAPIsLoading[sName] = nil\\\\\\\
  6874.                 return false\\\\\\\
  6875.             end\\\\\\\
  6876.             \\\\\\\
  6877.             local tAPI = {}\\\\\\\
  6878.             for k,v in pairs( tEnv ) do\\\\\\\
  6879.                 tAPI[k] =  v\\\\\\\
  6880.             end\\\\\\\
  6881.             \\\\\\\
  6882.             env[sName] = tAPI    \\\\\\\
  6883.             tAPIsLoading[sName] = nil\\\\\\\
  6884.             return true\\\\\\\
  6885.         end;\\\\\\\
  6886.         unloadAPI = function( _sName )\\\\\\\
  6887.             if _sName ~= \\\\\\\"_G\\\\\\\" and type(env[_sName]) == \\\\\\\"table\\\\\\\" then\\\\\\\
  6888.                 env[_sName] = nil\\\\\\\
  6889.             end\\\\\\\
  6890.         end;\\\\\\\
  6891.     }, { __index = os } );\\\\\\\
  6892.     env.help = help\\\\\\\
  6893.     env.io = io\\\\\\\
  6894.     env.parallel = parallel\\\\\\\
  6895. \\\\\\\
  6896.     env.loadfile = function( _sFile )\\\\\\\
  6897.         local file = env.fs.open( _sFile, \\\\\\\"r\\\\\\\" )\\\\\\\
  6898.         if file then\\\\\\\
  6899.             local func, err = loadstring( file.readAll(), env.fs.getName( _sFile ) )\\\\\\\
  6900.             file.close()\\\\\\\
  6901.             return func, err\\\\\\\
  6902.         end\\\\\\\
  6903.         return nil, \\\\\\\"File not found\\\\\\\"\\\\\\\
  6904.     end\\\\\\\
  6905. \\\\\\\
  6906.     env.dofile = function( _sFile )\\\\\\\
  6907.         local fnFile, e = env.loadfile( _sFile )\\\\\\\
  6908.         if fnFile then\\\\\\\
  6909.             setfenv( fnFile, env )\\\\\\\
  6910.             return fnFile()\\\\\\\
  6911.         else\\\\\\\
  6912.             error( e, 2 )\\\\\\\
  6913.         end\\\\\\\
  6914.     end\\\\\\\
  6915. \\\\\\\
  6916.     env.shell = shell\\\\\\\
  6917.     env.multishell = multishell\\\\\\\
  6918.     env.redstone = redstone -- change in future, use device system\\\\\\\
  6919.     env.rs = rs -- change in future, use device system\\\\\\\
  6920.     env.gps = gps -- change in future, use Nova gps system\\\\\\\
  6921.     env.peripheral = peripheral -- change in future, use device system\\\\\\\
  6922.     env.disk = disk -- change in future, use filesystem\\\\\\\
  6923.     env.window = window -- oh crap...\\\\\\\
  6924.     env.textutils = textutils\\\\\\\
  6925.     env.paintutils = paintutils\\\\\\\
  6926.     env.term = term\\\\\\\
  6927. \\\\\\\
  6928.     env._G = env\\\\\\\
  6929. \\\\\\\
  6930.     return env\\\\\\\
  6931. end\\\\\\\
  6932. \\\\\\\
  6933. function UIBuffer:UIBuffer( x, y, w, h )\\\\\\\
  6934.     self:UIElement( x, y, w, h )\\\\\\\
  6935.     self.image = Image( w, h )\\\\\\\
  6936.     self.image:foreach( function( x, y, bc, tc, char )\\\\\\\
  6937.         return colours.black, colours.white, \\\\\\\" \\\\\\\"\\\\\\\
  6938.     end )\\\\\\\
  6939.     self.term = termObject( self.public )\\\\\\\
  6940.     self.running = false\\\\\\\
  6941.     self.co = false\\\\\\\
  6942.     self.cx = 1\\\\\\\
  6943.     self.cy = 1\\\\\\\
  6944.     self.bc = colours.black\\\\\\\
  6945.     self.tc = colours.white\\\\\\\
  6946.     self.cb = false\\\\\\\
  6947.     self.environment = luaEnvironment( )\\\\\\\
  6948.     return self.public\\\\\\\
  6949. end\\\\\\\
  6950. \\\\\\\
  6951. function UIBuffer.public:resize( w, h )\\\\\\\
  6952.     self.image:resize( w, h, self.bc, self.tc, \\\\\\\" \\\\\\\" )\\\\\\\
  6953.     self.w = w\\\\\\\
  6954.     self.h = h\\\\\\\
  6955. end\\\\\\\
  6956. \\\\\\\
  6957. function UIBuffer.public:setTask( func )\\\\\\\
  6958.     setfenv( func, self.environment )\\\\\\\
  6959.     self.co = coroutine.create( func )\\\\\\\
  6960.     self.running = true\\\\\\\
  6961. end\\\\\\\
  6962. \\\\\\\
  6963. function UIBuffer.public:passEvent( ... )\\\\\\\
  6964.     if not self.running then return end\\\\\\\
  6965.     local prev = term.redirect( self.term )\\\\\\\
  6966.     local ok, err = coroutine.resume( self.co, ... )\\\\\\\
  6967.     term.redirect( prev )\\\\\\\
  6968.     if not ok then\\\\\\\
  6969.         self.running = false\\\\\\\
  6970.         local prev = term.redirect( self.term )\\\\\\\
  6971.         self.environment.printError( err )\\\\\\\
  6972.         term.redirect( prev )\\\\\\\
  6973.     end\\\\\\\
  6974.     if coroutine.status( self.co ) == \\\\\\\"dead\\\\\\\" then\\\\\\\
  6975.         self.running = false\\\\\\\
  6976.     end\\\\\\\
  6977. end\\\\\\\
  6978. \\\\\\\
  6979. function UIBuffer.public:draw( x, y )\\\\\\\
  6980.     local layer = stencil.addLayer( x, y, self.w, self.h )\\\\\\\
  6981.     self.image:resize( self.w, self.h )\\\\\\\
  6982.     self.image:foreach( function( px, py, bc, tc, char )\\\\\\\
  6983.         stencil.pixel( x + px - 1, y + py - 1, bc, tc, char )\\\\\\\
  6984.         return bc, tc, char\\\\\\\
  6985.     end )\\\\\\\
  6986.     if self.cb then\\\\\\\
  6987.         stencil.setCursorBlink( x + self.cx - 1, y + self.cy - 1, self.tc )\\\\\\\
  6988.     end\\\\\\\
  6989.     local c = self.public:getChildren( )\\\\\\\
  6990.     for i, child in ipairs( c ) do\\\\\\\
  6991.         child:draw( x + child.x - 1 + self.cx, y + child.y - 1 + self.cy )\\\\\\\
  6992.     end\\\\\\\
  6993.     stencil.closeLayer( layer )\\\\\\\
  6994. end\\\\\\\
  6995. \\\\\\\
  6996. function UIBuffer.public:onMouseClick( x, y, button )\\\\\\\
  6997.     self.public:passEvent( \\\\\\\"mouse_click\\\\\\\", button, x, y )\\\\\\\
  6998. end\\\\\\\
  6999. \\\\\\\
  7000. function UIBuffer.public:onMouseDrag( x, y, cx, cy, button )\\\\\\\
  7001.     self.public:passEvent( \\\\\\\"mouse_drag\\\\\\\", button, x, y )\\\\\\\
  7002. end\\\\\\\
  7003. \\\\\\\
  7004. function UIBuffer.public:onMouseScroll( x, y, dir )\\\\\\\
  7005.     self.public:passEvent( \\\\\\\"mouse_scroll\\\\\\\", dir, x, y )\\\\\\\
  7006. end\\\\\\\
  7007. \\\\\\\
  7008. function UIBuffer.public:onKeyPress( key, lastkey )\\\\\\\
  7009.     self.public:passEvent( \\\\\\\"key\\\\\\\", key )\\\\\\\
  7010. end\\\\\\\
  7011. \\\\\\\
  7012. function UIBuffer.public:onTextInput( text, lastkey )\\\\\\\
  7013.     self.public:passEvent( \\\\\\\"char\\\\\\\", text )\\\\\\\
  7014. end\\\", meta={\\\
  7015.  type = \\\"class\\\",\\\
  7016. }};[\\\"parser\\\"]={content=\\\"\\\\\\\
  7017. local function split( str, pat )\\\\\\\
  7018.     local sopened = false\\\\\\\
  7019.     local opened = false\\\\\\\
  7020.     local parts = { }\\\\\\\
  7021.     local last = 1\\\\\\\
  7022.     for i = 1,#str do\\\\\\\
  7023.         if str:sub( i, i ) == \\\\\\\"\\\\\\\\\\\\\\\"\\\\\\\" then\\\\\\\
  7024.             local count = 0\\\\\\\
  7025.             for ii = i - 1, 1, -1 do\\\\\\\
  7026.                 if str:sub( ii, ii ) ~= \\\\\\\"\\\\\\\\\\\\\\\\\\\\\\\" then\\\\\\\
  7027.                     break\\\\\\\
  7028.                 end\\\\\\\
  7029.                 count = count + 1\\\\\\\
  7030.             end\\\\\\\
  7031.             if math.floor( count / 2 ) == count / 2 then -- an even number of \\\\\\\\s therefore a string opener\\\\\\\
  7032.                 if sopened and sopened == str:sub( i, i ) then\\\\\\\
  7033.                     sopened = false\\\\\\\
  7034.                 elseif not sopened then\\\\\\\
  7035.                     sopened = str:sub( i, i )\\\\\\\
  7036.                 end\\\\\\\
  7037.             end\\\\\\\
  7038.         elseif not sopened then\\\\\\\
  7039.             if str:sub( i, i + #pat - 1 ) == pat then\\\\\\\
  7040.                 table.insert( parts, str:sub( last, i - 1 ) )\\\\\\\
  7041.                 last = i + #pat\\\\\\\
  7042.             end\\\\\\\
  7043.         end\\\\\\\
  7044.     end\\\\\\\
  7045.     table.insert( parts, str:sub( last ) )\\\\\\\
  7046.     return parts\\\\\\\
  7047. end\\\\\\\
  7048. \\\\\\\
  7049. local function findTags( str )\\\\\\\
  7050.     local sopened = false\\\\\\\
  7051.     local opened = false\\\\\\\
  7052.     local tags = { }\\\\\\\
  7053.     for i = 1,#str do\\\\\\\
  7054.         if str:sub( i, i ) == \\\\\\\"\\\\\\\\\\\\\\\"\\\\\\\" then\\\\\\\
  7055.             local count = 0\\\\\\\
  7056.             for ii = i - 1, 1, -1 do\\\\\\\
  7057.                 if str:sub( ii, ii ) ~= \\\\\\\"\\\\\\\\\\\\\\\\\\\\\\\" then\\\\\\\
  7058.                     break\\\\\\\
  7059.                 end\\\\\\\
  7060.                 count = count + 1\\\\\\\
  7061.             end\\\\\\\
  7062.             if math.floor( count / 2 ) == count / 2 then -- an even number of \\\\\\\\s therefore a string opener\\\\\\\
  7063.                 if sopened and sopened == str:sub( i, i ) then\\\\\\\
  7064.                     sopened = false\\\\\\\
  7065.                 elseif not sopened then\\\\\\\
  7066.                     sopened = str:sub( i, i )\\\\\\\
  7067.                 end\\\\\\\
  7068.             end\\\\\\\
  7069.         elseif not sopened then\\\\\\\
  7070.             if str:sub( i, i ) == \\\\\\\"<\\\\\\\" then\\\\\\\
  7071.                 opened = i\\\\\\\
  7072.             elseif str:sub( i, i ) == \\\\\\\">\\\\\\\" then\\\\\\\
  7073.                 table.insert( tags, { start = opened, finish = i, content = str:sub( opened + 1, i - 1 ) } )\\\\\\\
  7074.             end\\\\\\\
  7075.         end\\\\\\\
  7076.     end\\\\\\\
  7077.     return tags\\\\\\\
  7078. end\\\\\\\
  7079. \\\\\\\
  7080. local function readTags( tags )\\\\\\\
  7081.     for i = 1,#tags do\\\\\\\
  7082.         local c = tags[i].content\\\\\\\
  7083.         tags[i].content = nil\\\\\\\
  7084.         if c:sub( 1, 1 ) == \\\\\\\"/\\\\\\\" then\\\\\\\
  7085.             tags[i].type = \\\\\\\"close\\\\\\\"\\\\\\\
  7086.             tags[i].name = c:sub( 2 )\\\\\\\
  7087.         else\\\\\\\
  7088.             local parts = split( c, \\\\\\\" \\\\\\\" )\\\\\\\
  7089.             tags[i].type = \\\\\\\"open\\\\\\\"\\\\\\\
  7090.             tags[i].name = parts[1]\\\\\\\
  7091.             tags[i].attributes = { }\\\\\\\
  7092.             for ii = 2,#parts do\\\\\\\
  7093.                 local s, f, name, value = parts[ii]:find( \\\\\\\"([%w_]-)[:=](.+)\\\\\\\" )\\\\\\\
  7094.                 if s then\\\\\\\
  7095.                     tags[i].attributes[name] = value\\\\\\\
  7096.                 end\\\\\\\
  7097.             end\\\\\\\
  7098.         end\\\\\\\
  7099.     end\\\\\\\
  7100.     return tags\\\\\\\
  7101. end\\\\\\\
  7102. \\\\\\\
  7103. local function loadAttributes( at )\\\\\\\
  7104.     local vals = { }\\\\\\\
  7105.     for k, v in pairs( at ) do\\\\\\\
  7106.         if v:sub( 1, 1 ) == \\\\\\\"\\\\\\\\\\\\\\\"\\\\\\\" and v:sub( #v ) == \\\\\\\"\\\\\\\\\\\\\\\"\\\\\\\" then\\\\\\\
  7107.             local str = loadstring( \\\\\\\"return \\\\\\\" .. v, \\\\\\\"string\\\\\\\" )\\\\\\\
  7108.             if str then\\\\\\\
  7109.                 vals[k] = str( )\\\\\\\
  7110.             end\\\\\\\
  7111.         else\\\\\\\
  7112.             if v == \\\\\\\"true\\\\\\\" or v == \\\\\\\"false\\\\\\\" then\\\\\\\
  7113.                 vals[k] = v == \\\\\\\"true\\\\\\\"\\\\\\\
  7114.             elseif tonumber( v ) then\\\\\\\
  7115.                 vals[k] = tonumber( v )\\\\\\\
  7116.             else\\\\\\\
  7117.                 vals[k] = v\\\\\\\
  7118.             end\\\\\\\
  7119.         end\\\\\\\
  7120.     end\\\\\\\
  7121.     return vals\\\\\\\
  7122. end\\\\\\\
  7123. \\\\\\\
  7124. local _tags = {\\\\\\\
  7125.     [\\\\\\\"script\\\\\\\"] = {\\\\\\\
  7126.         content = true;\\\\\\\
  7127.         children = false;\\\\\\\
  7128.         whenloaded = function( parent, attributes, content, scripts, ids, errors )\\\\\\\
  7129.             local name = attributes.name or \\\\\\\"script\\\\\\\"\\\\\\\
  7130.             local f, err = loadstring( content, name )\\\\\\\
  7131.             if f then\\\\\\\
  7132.                 table.insert( scripts, { run = true, script = f } )\\\\\\\
  7133.             else\\\\\\\
  7134.                 table.insert( errors, err )\\\\\\\
  7135.             end\\\\\\\
  7136.         end;\\\\\\\
  7137.     };\\\\\\\
  7138.     [\\\\\\\"style\\\\\\\"] = {\\\\\\\
  7139.         content = true;\\\\\\\
  7140.         children = false;\\\\\\\
  7141.         whenloaded = function( parent, attributes, content, scripts, ids )\\\\\\\
  7142.             local s = Style( content )\\\\\\\
  7143.             s:save( attributes.name or \\\\\\\"default\\\\\\\" )\\\\\\\
  7144.         end;\\\\\\\
  7145.     };\\\\\\\
  7146. }\\\\\\\
  7147. \\\\\\\
  7148. function registerTag( name, content, children, whenloaded, parents, childrenallowed )\\\\\\\
  7149.     _tags[name] = { content = content, children = children, whenloaded = whenloaded, parents = parents, childrenallowed = childrenallowed }\\\\\\\
  7150. end\\\\\\\
  7151. \\\\\\\
  7152. local loadString\\\\\\\
  7153. \\\\\\\
  7154. local function groupTags( tags, str, parenttype )\\\\\\\
  7155.     local t = { }\\\\\\\
  7156.     local i = 1\\\\\\\
  7157.     while i <= #tags do\\\\\\\
  7158.         local name = tags[i].name\\\\\\\
  7159.         if _tags[name] then\\\\\\\
  7160.             if tags[i].type == \\\\\\\"open\\\\\\\" then\\\\\\\
  7161.                 if _tags[name].parents then\\\\\\\
  7162.                     local ok = false\\\\\\\
  7163.                     for i = 1,#_tags[name].parents do\\\\\\\
  7164.                         if _tags[name].parents[i] == parenttype then\\\\\\\
  7165.                             ok = true\\\\\\\
  7166.                             break\\\\\\\
  7167.                         end\\\\\\\
  7168.                     end\\\\\\\
  7169.                     if not ok then\\\\\\\
  7170.                         error( \\\\\\\"cannot have \\\\\\\" .. parenttype .. \\\\\\\" as a parent to \\\\\\\" .. name, 0 )\\\\\\\
  7171.                     end\\\\\\\
  7172.                 end\\\\\\\
  7173.                 local tag = {\\\\\\\
  7174.                     name = name;\\\\\\\
  7175.                     attributes = loadAttributes( tags[i].attributes );\\\\\\\
  7176.                     whenloaded = _tags[name].whenloaded;\\\\\\\
  7177.                 }\\\\\\\
  7178.                 if _tags[name].content then\\\\\\\
  7179.                     local layer = 1\\\\\\\
  7180.                     local found = false\\\\\\\
  7181.                     for c = i + 1, #tags do\\\\\\\
  7182.                         if tags[c].name == name then\\\\\\\
  7183.                             if tags[c].type == \\\\\\\"open\\\\\\\" then\\\\\\\
  7184.                                 layer = layer + 1\\\\\\\
  7185.                             else\\\\\\\
  7186.                                 layer = layer - 1\\\\\\\
  7187.                                 if layer == 0 then\\\\\\\
  7188.                                     found = c\\\\\\\
  7189.                                     break\\\\\\\
  7190.                                 end\\\\\\\
  7191.                             end\\\\\\\
  7192.                         end\\\\\\\
  7193.                     end\\\\\\\
  7194.                     if found then\\\\\\\
  7195.                         tag.content = str:sub( tags[i].finish + 1, tags[found].start - 1 )\\\\\\\
  7196.                         i = found\\\\\\\
  7197.                     else\\\\\\\
  7198.                         error( \\\\\\\"ending tag expected for \\\\\\\" .. name, 0 )\\\\\\\
  7199.                     end\\\\\\\
  7200.                 end\\\\\\\
  7201.                 if tag.content and _tags[name].children then\\\\\\\
  7202.                     tag.children = loadString( tag.content, tag.name )\\\\\\\
  7203.                     tag.content = nil\\\\\\\
  7204.                     if _tags[name].childrenallowed then\\\\\\\
  7205.                         for i = 1,#tag.children do\\\\\\\
  7206.                             local ok = false\\\\\\\
  7207.                             for ii = 1,#_tags[name].childrenallowed do\\\\\\\
  7208.                                 if _tags[name].childrenallowed[ii] == tag.children[i].name then\\\\\\\
  7209.                                     ok = true\\\\\\\
  7210.                                     break\\\\\\\
  7211.                                 end\\\\\\\
  7212.                             end\\\\\\\
  7213.                             if not ok then\\\\\\\
  7214.                                 error( \\\\\\\"not allowed \\\\\\\" .. tag.children[i].name .. \\\\\\\" inside a \\\\\\\" .. tag.name, 0 )\\\\\\\
  7215.                             end\\\\\\\
  7216.                         end\\\\\\\
  7217.                     end\\\\\\\
  7218.                 end\\\\\\\
  7219.                 table.insert( t, tag )\\\\\\\
  7220.                 i = i + 1\\\\\\\
  7221.             else\\\\\\\
  7222.                 error( \\\\\\\"expected opening tag before closing tag for \\\\\\\" .. name, 0 )\\\\\\\
  7223.             end\\\\\\\
  7224.         else\\\\\\\
  7225.             error( \\\\\\\"unknown tag: \\\\\\\" .. name, 0 )\\\\\\\
  7226.         end\\\\\\\
  7227.     end\\\\\\\
  7228.     return t\\\\\\\
  7229. end\\\\\\\
  7230. \\\\\\\
  7231. function loadString( str, parent )\\\\\\\
  7232.     return groupTags( readTags( findTags( str ) ), str, parent or \\\\\\\"frame\\\\\\\" )\\\\\\\
  7233. end\\\\\\\
  7234. \\\\\\\
  7235. local loadElements\\\\\\\
  7236. function loadElements( children, parent, scripts, ids, errors )\\\\\\\
  7237.     if not scripts then\\\\\\\
  7238.         scripts = { }\\\\\\\
  7239.         ids = { }\\\\\\\
  7240.         errors = { }\\\\\\\
  7241.     end\\\\\\\
  7242.     for i = 1,#children do\\\\\\\
  7243.         local child = children[i].whenloaded( parent, children[i].attributes, children[i].content, scripts, ids, errors )\\\\\\\
  7244.         if children[i].children then\\\\\\\
  7245.             loadElements( children[i].children, child, scripts, ids, errors )\\\\\\\
  7246.         end\\\\\\\
  7247.     end\\\\\\\
  7248.     return parent, scripts, ids, errors\\\\\\\
  7249. end\\\\\\\
  7250. \\\\\\\
  7251. function load( str, frame )\\\\\\\
  7252.     local tags = loadString( str )\\\\\\\
  7253.     return loadElements( tags, frame )\\\\\\\
  7254. end\\\", meta={\\\
  7255.  type = \\\"lib\\\",\\\
  7256. }};[\\\"UICode\\\"]={content=\\\"\\\\\\\
  7257. require \\\\\\\"UIElement\\\\\\\"\\\\\\\
  7258. \\\\\\\
  7259. UICode:extends( UIElement )\\\\\\\
  7260. \\\\\\\
  7261. UICode.handlesKeys = true\\\\\\\
  7262. UICode.handlesScroll = true\\\\\\\
  7263. UICode.isScrollTarget = true\\\\\\\
  7264. UICode.handlesTab = true\\\\\\\
  7265. UICode.scrollDirection = \\\\\\\"vertical\\\\\\\"\\\\\\\
  7266. \\\\\\\
  7267. UICode.public \\\\\\\"showLines\\\\\\\" \\\\\\\"boolean\\\\\\\"\\\\\\\
  7268. UICode.public \\\\\\\"syntax\\\\\\\" \\\\\\\"table\\\\\\\"\\\\\\\
  7269. \\\\\\\
  7270. UICode.public \\\\\\\"onChange\\\\\\\" \\\\\\\"function\\\\\\\"\\\\\\\
  7271. UICode.public \\\\\\\"onCtrlKey\\\\\\\" \\\\\\\"function\\\\\\\"\\\\\\\
  7272. \\\\\\\
  7273. UICode.public \\\\\\\"selected\\\\\\\"\\\\\\\
  7274. UICode.public.selected.write = false\\\\\\\
  7275. function UICode.public.selected:read( )\\\\\\\
  7276.     return not not self.selection\\\\\\\
  7277. end\\\\\\\
  7278. \\\\\\\
  7279. function UICode:UICode( x, y, w, h, syntax )\\\\\\\
  7280.     self:UIElement( x, y, w, h )\\\\\\\
  7281. \\\\\\\
  7282.     self.showLines = true\\\\\\\
  7283.     self.lineWidth = 4\\\\\\\
  7284.     self.linechars = { }\\\\\\\
  7285. \\\\\\\
  7286.     self.selection = false\\\\\\\
  7287. \\\\\\\
  7288.     self.scrollx = 0\\\\\\\
  7289.     self.scrolly = 0\\\\\\\
  7290.     self.cursorx = 1\\\\\\\
  7291.     self.cursory = 1\\\\\\\
  7292. \\\\\\\
  7293.     self.lastclick = false\\\\\\\
  7294. \\\\\\\
  7295.     self.syntax = syntax or {\\\\\\\
  7296.         default = { bc = colours.white, tc = colours.black };\\\\\\\
  7297.         words = { };\\\\\\\
  7298.         blocks = { };\\\\\\\
  7299.         linen = { bc = colours.grey, tc = colours.white };\\\\\\\
  7300.         string = { tc = colours.lightGrey };\\\\\\\
  7301.         symbols = { };\\\\\\\
  7302.         selection = { bc = colours.blue, tc = colours.white };\\\\\\\
  7303.     }\\\\\\\
  7304. \\\\\\\
  7305.     self.lines = { \\\\\\\"\\\\\\\" }\\\\\\\
  7306.     self.characters = { }\\\\\\\
  7307. \\\\\\\
  7308.     return self.public\\\\\\\
  7309. end\\\\\\\
  7310. \\\\\\\
  7311. function UICode.public:updateCharacters( )\\\\\\\
  7312.     self.linechars = { [1] = 1 }\\\\\\\
  7313.     local str = table.concat( self.lines, \\\\\\\"\\\\\\\\n\\\\\\\" )\\\\\\\
  7314.     local characters = { }\\\\\\\
  7315.     local line = 1\\\\\\\
  7316.     local pos = 1\\\\\\\
  7317.     local done = true\\\\\\\
  7318.     local function nextC( )\\\\\\\
  7319.         local char = str:sub( pos, pos )\\\\\\\
  7320.         pos = pos + 1\\\\\\\
  7321.         done = false\\\\\\\
  7322.         return char\\\\\\\
  7323.     end\\\\\\\
  7324.     local function setC( colour, char, d )\\\\\\\
  7325.         if done and not d then return end\\\\\\\
  7326.         if char == \\\\\\\"\\\\\\\\n\\\\\\\" then\\\\\\\
  7327.             done = true\\\\\\\
  7328.             line = line + 1\\\\\\\
  7329.             -- self.linechars[line] = pos\\\\\\\
  7330.             return\\\\\\\
  7331.         end\\\\\\\
  7332.         if #char == 0 then return end\\\\\\\
  7333.         done = true\\\\\\\
  7334.         table.insert( characters, { colour = colour, char = char, line = line } )\\\\\\\
  7335.     end\\\\\\\
  7336.     while pos <= #str do\\\\\\\
  7337.         local c = nextC( )\\\\\\\
  7338.         if c == \\\\\\\"\\\\\\\\\\\\\\\"\\\\\\\" then\\\\\\\
  7339.             setC( self.syntax.string, c )\\\\\\\
  7340.             local escape = false\\\\\\\
  7341.             for i = pos, #str do\\\\\\\
  7342.                 c = nextC( )\\\\\\\
  7343.                 if escape then\\\\\\\
  7344.                     setC( self.syntax.string.escape or self.syntax.string, c )\\\\\\\
  7345.                     escape = false\\\\\\\
  7346.                 else\\\\\\\
  7347.                     if c == \\\\\\\"\\\\\\\\\\\\\\\\\\\\\\\" then\\\\\\\
  7348.                         setC( self.syntax.string.escape or self.syntax.string, c )\\\\\\\
  7349.                         escape = true\\\\\\\
  7350.                     elseif c == \\\\\\\"\\\\\\\\\\\\\\\"\\\\\\\" then\\\\\\\
  7351.                         setC( self.syntax.string, c )\\\\\\\
  7352.                         break\\\\\\\
  7353.                     else\\\\\\\
  7354.                         setC( self.syntax.string, c )\\\\\\\
  7355.                     end\\\\\\\
  7356.                 end\\\\\\\
  7357.             end\\\\\\\
  7358.         elseif c:find \\\\\\\"[a-zA-Z_]\\\\\\\" then\\\\\\\
  7359.             local word = c\\\\\\\
  7360.             c = nextC( )\\\\\\\
  7361.             while c:find \\\\\\\"[a-zA-Z0-9_]\\\\\\\" do\\\\\\\
  7362.                 word = word .. c\\\\\\\
  7363.                 c = nextC( )\\\\\\\
  7364.             end\\\\\\\
  7365.             for i = 1, #word do\\\\\\\
  7366.                 if self.syntax.words[word] then\\\\\\\
  7367.                     setC( self.syntax.words[word], word:sub( i, i ), true )\\\\\\\
  7368.                 else\\\\\\\
  7369.                     setC( self.syntax.default, word:sub( i, i ), true )\\\\\\\
  7370.                 end\\\\\\\
  7371.             end\\\\\\\
  7372.             done = false\\\\\\\
  7373.         elseif self.syntax.symbols[c] then\\\\\\\
  7374.             setC( self.syntax.symbols[c], c )\\\\\\\
  7375.         end\\\\\\\
  7376.         if not done then\\\\\\\
  7377.             for i = 1, #self.syntax.blocks do\\\\\\\
  7378.                 if c == self.syntax.blocks[i].start:sub( 1, 1 ) then\\\\\\\
  7379.                     if str:sub( pos, pos + #self.syntax.blocks[i].start - 2 ) == self.syntax.blocks[i].start:sub( 2 ) then\\\\\\\
  7380.                         local block = self.syntax.blocks[i]\\\\\\\
  7381.                         setC( block, c )\\\\\\\
  7382.                         for i = 1, #block.start - 1 do\\\\\\\
  7383.                             c = nextC( )\\\\\\\
  7384.                             setC( block, c )\\\\\\\
  7385.                         end\\\\\\\
  7386.                         while #c > 0 do\\\\\\\
  7387.                             if str:sub( pos, pos + #block.finish - 1 ) == block.finish then\\\\\\\
  7388.                                 for i = 1, #block.finish do\\\\\\\
  7389.                                     c = nextC( )\\\\\\\
  7390.                                     setC( block, c )\\\\\\\
  7391.                                 end\\\\\\\
  7392.                                 break\\\\\\\
  7393.                             end\\\\\\\
  7394.                             c = nextC( )\\\\\\\
  7395.                             setC( block, c )\\\\\\\
  7396.                         end\\\\\\\
  7397.                         break\\\\\\\
  7398.                     end\\\\\\\
  7399.                 end\\\\\\\
  7400.             end\\\\\\\
  7401.         end\\\\\\\
  7402.         setC( self.syntax.default, c )\\\\\\\
  7403.     end\\\\\\\
  7404.     self.characters = characters\\\\\\\
  7405.     if self.showLines then\\\\\\\
  7406.         self.lineWidth = math.floor( math.log( #self.lines ) / math.log( 10 ) ) + 4\\\\\\\
  7407.     else\\\\\\\
  7408.         self.lineWidth = 0\\\\\\\
  7409.     end\\\\\\\
  7410. end\\\\\\\
  7411. \\\\\\\
  7412. function UICode.public:update( dt )\\\\\\\
  7413.     if self.cursory > #self.lines then\\\\\\\
  7414.         self.cursory = #self.lines\\\\\\\
  7415.     end\\\\\\\
  7416.     if self.cursorx > #( self.lines[self.cursory] or \\\\\\\"\\\\\\\" ) + 1 then\\\\\\\
  7417.         self.cursorx = #( self.lines[self.cursory] or \\\\\\\"\\\\\\\" ) + 1\\\\\\\
  7418.     end\\\\\\\
  7419.     local c = self.public:getChildren( )\\\\\\\
  7420.     for i, child in ipairs( c ) do\\\\\\\
  7421.         child:update( dt )\\\\\\\
  7422.     end\\\\\\\
  7423. end\\\\\\\
  7424. \\\\\\\
  7425. local tabsize = 4\\\\\\\
  7426. \\\\\\\
  7427. function UICode.public:draw( x, y )\\\\\\\
  7428.     local layer = stencil.addLayer( x, y, self.w, self.h )\\\\\\\
  7429.     local characters = self.characters\\\\\\\
  7430.     stencil.rectangle( x, y, self.w, self.h, self.syntax.default.bc, self.syntax.default.tc, \\\\\\\" \\\\\\\" )\\\\\\\
  7431.     if self.showLines then\\\\\\\
  7432.         for i = 1, self.h do\\\\\\\
  7433.             if i + self.scrolly <= #self.lines then\\\\\\\
  7434.                 local n = tostring( i + self.scrolly )\\\\\\\
  7435.                 stencil.textLine( x, y + i - 1, self.syntax.linen.bc, self.syntax.linen.tc, (\\\\\\\" \\\\\\\"):rep( self.lineWidth - 2 - #n ) .. n .. \\\\\\\" \\\\\\\" )\\\\\\\
  7436.             else\\\\\\\
  7437.                 stencil.textLine( x, y + i - 1, self.syntax.linen.bc, 1, (\\\\\\\" \\\\\\\"):rep( self.lineWidth - 1 ) )\\\\\\\
  7438.             end\\\\\\\
  7439.         end\\\\\\\
  7440.     end\\\\\\\
  7441.     local xx, yy, cx = 1, 1, 1\\\\\\\
  7442.     local tabthisline = true\\\\\\\
  7443.     local i = self.linechars[self.scrolly + 1] or 1\\\\\\\
  7444.     while i <= #characters do\\\\\\\
  7445.         if characters[i].line > yy then\\\\\\\
  7446.             xx = 1\\\\\\\
  7447.             cx = 1\\\\\\\
  7448.             yy = characters[i].line\\\\\\\
  7449.             tabthisline = true\\\\\\\
  7450.         end\\\\\\\
  7451. \\\\\\\
  7452.         local bc, tc = characters[i].colour.bc or self.syntax.default.bc, characters[i].colour.tc or self.syntax.default.tc\\\\\\\
  7453.         if self.cursory == characters[i].line then\\\\\\\
  7454.             bc = characters[i].colour.lbc or characters[i].colour.bc or self.syntax.default.lbc or self.syntax.default.bc\\\\\\\
  7455.             tc = characters[i].colour.ltc or characters[i].colour.tc or self.syntax.default.ltc or self.syntax.default.tc\\\\\\\
  7456.         end\\\\\\\
  7457.         if self.selection then\\\\\\\
  7458.             local ssx, ssy, sex, sey\\\\\\\
  7459.             if self.selection.y < self.cursory then\\\\\\\
  7460.                 ssy = self.selection.y\\\\\\\
  7461.                 ssx = self.selection.x\\\\\\\
  7462.                 sey = self.cursory\\\\\\\
  7463.                 sex = self.cursorx\\\\\\\
  7464.             elseif self.selection.y > self.cursory then\\\\\\\
  7465.                 ssy = self.cursory\\\\\\\
  7466.                 ssx = self.cursorx\\\\\\\
  7467.                 sey = self.selection.y\\\\\\\
  7468.                 sex = self.selection.x\\\\\\\
  7469.             else\\\\\\\
  7470.                 ssy = self.selection.y\\\\\\\
  7471.                 ssx = math.min( self.selection.x, self.cursorx )\\\\\\\
  7472.                 sey = self.cursory\\\\\\\
  7473.                 sex = math.max( self.selection.x, self.cursorx )\\\\\\\
  7474.             end\\\\\\\
  7475.             if yy == ssy then\\\\\\\
  7476.                 if yy == sey then\\\\\\\
  7477.                     if cx >= ssx and cx <= sex then\\\\\\\
  7478.                         bc = self.syntax.selection.bc\\\\\\\
  7479.                         tc = self.syntax.selection.tc\\\\\\\
  7480.                     end\\\\\\\
  7481.                 else\\\\\\\
  7482.                     if cx >= ssx then\\\\\\\
  7483.                         bc = self.syntax.selection.bc\\\\\\\
  7484.                         tc = self.syntax.selection.tc\\\\\\\
  7485.                     end\\\\\\\
  7486.                 end\\\\\\\
  7487.             elseif yy == sey then\\\\\\\
  7488.                 if cx <= sex then\\\\\\\
  7489.                     bc = self.syntax.selection.bc\\\\\\\
  7490.                     tc = self.syntax.selection.tc\\\\\\\
  7491.                 end\\\\\\\
  7492.             elseif yy > ssy and yy < sey then\\\\\\\
  7493.                 bc = self.syntax.selection.bc\\\\\\\
  7494.                 tc = self.syntax.selection.tc\\\\\\\
  7495.             end\\\\\\\
  7496.         end\\\\\\\
  7497.         local rx = xx - self.scrollx\\\\\\\
  7498.         local ry = yy - self.scrolly\\\\\\\
  7499.         if rx >= 1 and rx <= self.w - self.lineWidth and ry >= 1 and ry <= self.h then\\\\\\\
  7500.             if characters[i].char ~= \\\\\\\"   \\\\\\\" then\\\\\\\
  7501.                 tabthisline = false\\\\\\\
  7502.                 stencil.pixel( x + rx - 1 + self.lineWidth, y + ry - 1, bc, tc, characters[i].char )\\\\\\\
  7503.             else\\\\\\\
  7504.                 for i = 1, ( tabsize - ( xx - 1 ) % tabsize ) - 1 do\\\\\\\
  7505.                     stencil.pixel( x + rx - 2 + self.lineWidth + i, y + ry - 1, bc, tc, \\\\\\\" \\\\\\\" )\\\\\\\
  7506.                 end\\\\\\\
  7507.                 if self.syntax.tab and tabthisline then\\\\\\\
  7508.                     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 \\\\\\\" \\\\\\\" )\\\\\\\
  7509.                 else\\\\\\\
  7510.                     stencil.pixel( x + rx - 2 + self.lineWidth + ( tabsize - ( xx - 1 ) % tabsize ), y + ry - 1, bc, tc, \\\\\\\" \\\\\\\" )\\\\\\\
  7511.                 end\\\\\\\
  7512.             end\\\\\\\
  7513.         end\\\\\\\
  7514.         if characters[i].char == \\\\\\\"   \\\\\\\" then\\\\\\\
  7515.             xx = xx + ( tabsize - ( xx - 1 ) % tabsize )\\\\\\\
  7516.         else\\\\\\\
  7517.             xx = xx + 1\\\\\\\
  7518.         end\\\\\\\
  7519.         if ry > self.h then\\\\\\\
  7520.             break\\\\\\\
  7521.         end\\\\\\\
  7522.         cx = cx + 1\\\\\\\
  7523.         i = i + 1\\\\\\\
  7524.     end\\\\\\\
  7525.     if self.focussed and not self.selection then\\\\\\\
  7526.         local xx, yy = self:getCursorPos( )\\\\\\\
  7527.         stencil.setCursorBlink( x + xx - 1 + self.lineWidth, y + yy - 1, self.syntax.default.tc )\\\\\\\
  7528.     end\\\\\\\
  7529.     local c = self.public:getChildren( )\\\\\\\
  7530.     for i, child in ipairs( c ) do\\\\\\\
  7531.         child:draw( x + child.x - 1 + self.cx, y + child.y - 1 + self.cy )\\\\\\\
  7532.     end\\\\\\\
  7533.     stencil.closeLayer( layer )\\\\\\\
  7534. end\\\\\\\
  7535. \\\\\\\
  7536. function UICode:setCursorPos( x, y )\\\\\\\
  7537.     self.cursorx = x\\\\\\\
  7538.     self.cursory = y\\\\\\\
  7539.     local ry = self.cursory\\\\\\\
  7540.     local l = self.lines[self.cursory]\\\\\\\
  7541.     local rx = 0\\\\\\\
  7542.     for i = 1, self.cursorx - 1 do\\\\\\\
  7543.         if l:sub( i, i ) == \\\\\\\"    \\\\\\\" then\\\\\\\
  7544.             rx = rx + ( tabsize - rx % tabsize )\\\\\\\
  7545.         else\\\\\\\
  7546.             rx = rx + 1\\\\\\\
  7547.         end\\\\\\\
  7548.     end\\\\\\\
  7549.     rx = rx + 1\\\\\\\
  7550.     if ry <= self.scrolly then\\\\\\\
  7551.         self.scrolly = ry - 1\\\\\\\
  7552.     elseif ry >= self.scrolly + self.h then\\\\\\\
  7553.         self.scrolly = ry - self.h\\\\\\\
  7554.     end\\\\\\\
  7555.     if rx <= self.scrollx then\\\\\\\
  7556.         self.scrollx = rx - 1\\\\\\\
  7557.     elseif rx >= self.scrollx + ( self.w - self.lineWidth ) then\\\\\\\
  7558.         self.scrollx = rx - ( self.w - self.lineWidth )\\\\\\\
  7559.     end\\\\\\\
  7560. end\\\\\\\
  7561. \\\\\\\
  7562. function UICode:getCursorPos( )\\\\\\\
  7563.     local y = self.cursory - self.scrolly\\\\\\\
  7564.     local l = self.lines[self.cursory] or \\\\\\\"\\\\\\\"\\\\\\\
  7565.     local x = 0\\\\\\\
  7566.     for i = 1, self.cursorx - 1 do\\\\\\\
  7567.         if l:sub( i, i ) == \\\\\\\"    \\\\\\\" then\\\\\\\
  7568.             x = x + ( tabsize - x % tabsize )\\\\\\\
  7569.         else\\\\\\\
  7570.             x = x + 1\\\\\\\
  7571.         end\\\\\\\
  7572.     end\\\\\\\
  7573.     return x + 1 - self.scrollx, y\\\\\\\
  7574. end\\\\\\\
  7575. \\\\\\\
  7576. function UICode:coordsToCursor( x, y )\\\\\\\
  7577.     if x <= self.lineWidth then\\\\\\\
  7578.         return\\\\\\\
  7579.     end\\\\\\\
  7580.     x = x + self.scrollx - self.lineWidth\\\\\\\
  7581.     if self.lines[y+self.scrolly] then\\\\\\\
  7582.         local l = self.lines[y + self.scrolly]\\\\\\\
  7583.         local cx = 0\\\\\\\
  7584.         for i = 1, #l do\\\\\\\
  7585.             if l:sub( i, i ) == \\\\\\\"    \\\\\\\" then\\\\\\\
  7586.                 cx = cx + ( tabsize - ( cx ) % tabsize )\\\\\\\
  7587.             else\\\\\\\
  7588.                 cx = cx + 1\\\\\\\
  7589.             end\\\\\\\
  7590.             if cx >= x then\\\\\\\
  7591.                 return i, y + self.scrolly\\\\\\\
  7592.             end\\\\\\\
  7593.         end\\\\\\\
  7594.         return #l + 1, y + self.scrolly\\\\\\\
  7595.     end\\\\\\\
  7596.     return #self.lines[#self.lines] + 1, #self.lines\\\\\\\
  7597. end\\\\\\\
  7598. \\\\\\\
  7599. function UICode:getSelectionBounds( )\\\\\\\
  7600.     local ssx, ssy, sex, sey\\\\\\\
  7601.     if self.selection.y < self.cursory then\\\\\\\
  7602.         ssy = self.selection.y\\\\\\\
  7603.         ssx = self.selection.x\\\\\\\
  7604.         sey = self.cursory\\\\\\\
  7605.         sex = self.cursorx\\\\\\\
  7606.     elseif self.selection.y > self.cursory then\\\\\\\
  7607.         ssy = self.cursory\\\\\\\
  7608.         ssx = self.cursorx\\\\\\\
  7609.         sey = self.selection.y\\\\\\\
  7610.         sex = self.selection.x\\\\\\\
  7611.     else\\\\\\\
  7612.         ssy = self.selection.y\\\\\\\
  7613.         ssx = math.min( self.selection.x, self.cursorx )\\\\\\\
  7614.         sey = self.cursory\\\\\\\
  7615.         sex = math.max( self.selection.x, self.cursorx )\\\\\\\
  7616.     end\\\\\\\
  7617.     return ssx, ssy, sex, sey\\\\\\\
  7618. end\\\\\\\
  7619. \\\\\\\
  7620. function UICode:write( str )\\\\\\\
  7621.     local cx, cy = self.cursorx, self.cursory\\\\\\\
  7622.     for i = 1, #str do\\\\\\\
  7623.         if str:sub( i, i ) == \\\\\\\"\\\\\\\\n\\\\\\\" then\\\\\\\
  7624.             cy = cy + 1\\\\\\\
  7625.             cx = 1\\\\\\\
  7626.             table.insert( self.lines, cy, \\\\\\\"\\\\\\\" )\\\\\\\
  7627.         else\\\\\\\
  7628.             self.lines[cy] = self.lines[cy]:sub( 1, cx - 1 ) .. str:sub( i, i ) .. self.lines[cy]:sub( cx )\\\\\\\
  7629.             cx = cx + 1\\\\\\\
  7630.         end\\\\\\\
  7631.     end\\\\\\\
  7632.     self:setCursorPos( cx, cy )\\\\\\\
  7633. end\\\\\\\
  7634. \\\\\\\
  7635. function UICode:setSelection( str )\\\\\\\
  7636.     local ssx, ssy, sex, sey = self:getSelectionBounds( )\\\\\\\
  7637.     local line_end = self.lines[sey]:sub( sex + 1 )\\\\\\\
  7638.     self.lines[ssy] = self.lines[ssy]:sub( 1, ssx - 1 )\\\\\\\
  7639.     self:setCursorPos( ssx, ssy )\\\\\\\
  7640.     for i = ssy + 1, sey do\\\\\\\
  7641.         table.remove( self.lines, ssy + 1 )\\\\\\\
  7642.     end\\\\\\\
  7643.     self:write( str .. line_end )\\\\\\\
  7644.     self:setCursorPos( self.cursorx - #line_end, self.cursory )\\\\\\\
  7645.     self.selection = false\\\\\\\
  7646. end\\\\\\\
  7647. \\\\\\\
  7648. function UICode.public:getSelection( str )\\\\\\\
  7649.     if self.selection then\\\\\\\
  7650.         local ssx, ssy, sex, sey = self:getSelectionBounds( )\\\\\\\
  7651.         if ssy == sey then\\\\\\\
  7652.             return self.lines[ssy]:sub( ssx, sex )\\\\\\\
  7653.         end\\\\\\\
  7654.         local selected = self.lines[ssy]:sub( ssx )\\\\\\\
  7655.         for i = ssy + 1, sey - 1 do\\\\\\\
  7656.             selected = selected .. \\\\\\\"\\\\\\\\n\\\\\\\" .. self.lines[i]\\\\\\\
  7657.         end\\\\\\\
  7658.         selected = selected .. \\\\\\\"\\\\\\\\n\\\\\\\" .. self.lines[sey]:sub( 1, sex )\\\\\\\
  7659.         return selected\\\\\\\
  7660.     end\\\\\\\
  7661. end\\\\\\\
  7662. \\\\\\\
  7663. function UICode.public:setSelection( str )\\\\\\\
  7664.     if self.selection then\\\\\\\
  7665.         self:setSelection( str )\\\\\\\
  7666.         self.public:updateCharacters( )\\\\\\\
  7667.     end\\\\\\\
  7668. end\\\\\\\
  7669. \\\\\\\
  7670. function UICode.public:write( str )\\\\\\\
  7671.     self:write( str )\\\\\\\
  7672.     self.public:updateCharacters( )\\\\\\\
  7673. end\\\\\\\
  7674. \\\\\\\
  7675. function UICode.public:onMouseClick( rx, ry, button )\\\\\\\
  7676.     if self.selection then\\\\\\\
  7677.         self.selection = false\\\\\\\
  7678.     end\\\\\\\
  7679.     local x, y = self:coordsToCursor( rx, ry )\\\\\\\
  7680.     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\\\\\\\
  7681.         local bounds = { min = x, max = x }\\\\\\\
  7682.         local line = self.lines[y]\\\\\\\
  7683.         local c = line:sub( x, x )\\\\\\\
  7684.         if c:find \\\\\\\"%s\\\\\\\" then\\\\\\\
  7685.             for i = x - 1, 1, -1 do\\\\\\\
  7686.                 if not line:sub( i, i ):find \\\\\\\"%s\\\\\\\" then\\\\\\\
  7687.                     break\\\\\\\
  7688.                 end\\\\\\\
  7689.                 bounds.min = i\\\\\\\
  7690.             end\\\\\\\
  7691.             for i = x + 1, #line do\\\\\\\
  7692.                 if not line:sub( i, i ):find \\\\\\\"%s\\\\\\\" then\\\\\\\
  7693.                     break\\\\\\\
  7694.                 end\\\\\\\
  7695.                 bounds.max = i\\\\\\\
  7696.             end\\\\\\\
  7697.         elseif c:find \\\\\\\"[%w_]\\\\\\\" then\\\\\\\
  7698.             for i = x - 1, 1, -1 do\\\\\\\
  7699.                 if not line:sub( i, i ):find \\\\\\\"[%w_]\\\\\\\" then\\\\\\\
  7700.                     break\\\\\\\
  7701.                 end\\\\\\\
  7702.                 bounds.min = i\\\\\\\
  7703.             end\\\\\\\
  7704.             for i = x + 1, #line do\\\\\\\
  7705.                 if not line:sub( i, i ):find \\\\\\\"[%w_]\\\\\\\" then\\\\\\\
  7706.                     break\\\\\\\
  7707.                 end\\\\\\\
  7708.                 bounds.max = i\\\\\\\
  7709.             end\\\\\\\
  7710.         else\\\\\\\
  7711.             for i = x - 1, 1, -1 do\\\\\\\
  7712.                 if line:sub( i, i ):find \\\\\\\"%s\\\\\\\" or line:sub( i, i ):find \\\\\\\"[%w_]\\\\\\\" then\\\\\\\
  7713.                     break\\\\\\\
  7714.                 end\\\\\\\
  7715.                 bounds.min = i\\\\\\\
  7716.             end\\\\\\\
  7717.             for i = x + 1, #line do\\\\\\\
  7718.                 if line:sub( i, i ):find \\\\\\\"%s\\\\\\\" or line:sub( i, i ):find \\\\\\\"[%w_]\\\\\\\" then\\\\\\\
  7719.                     break\\\\\\\
  7720.                 end\\\\\\\
  7721.                 bounds.max = i\\\\\\\
  7722.             end\\\\\\\
  7723.         end\\\\\\\
  7724.         self.selection = { x = bounds.min, y = y }\\\\\\\
  7725.         self.cursorx = bounds.max\\\\\\\
  7726.         self.cursory = y\\\\\\\
  7727.         self.lastclick = false\\\\\\\
  7728.         return\\\\\\\
  7729.     elseif button == 1 then\\\\\\\
  7730.         self.lastclick = { x = rx, y = ry, time = os.clock( ) }\\\\\\\
  7731.     end\\\\\\\
  7732.     if x then\\\\\\\
  7733.         self:setCursorPos( x, y )\\\\\\\
  7734.     end\\\\\\\
  7735. end\\\\\\\
  7736. \\\\\\\
  7737. function UICode.public:onMouseDrag( rx, ry, cx, cy, button )\\\\\\\
  7738.     local x, y = self:coordsToCursor( rx, ry )\\\\\\\
  7739.     if x then\\\\\\\
  7740.         if not self.selection then\\\\\\\
  7741.             self.selection = { x = self.cursorx, y = self.cursory }\\\\\\\
  7742.         end\\\\\\\
  7743.         self:setCursorPos( x, y )\\\\\\\
  7744.     end\\\\\\\
  7745. end\\\\\\\
  7746. \\\\\\\
  7747. function UICode.public:onMouseScroll( rx, ry, dir )\\\\\\\
  7748.     if self.scrolly > 0 and dir == -1 then\\\\\\\
  7749.         self.scrolly = self.scrolly - 1\\\\\\\
  7750.     elseif self.scrolly < #self.lines - self.h and dir == 1 then\\\\\\\
  7751.         self.scrolly = self.scrolly + 1\\\\\\\
  7752.     end\\\\\\\
  7753. end\\\\\\\
  7754. \\\\\\\
  7755. function UICode.public:onKeyPress( key, lastkey )\\\\\\\
  7756.     if not self.focussed then\\\\\\\
  7757.         return\\\\\\\
  7758.     end\\\\\\\
  7759.     if lastkey == 29 then\\\\\\\
  7760.         if key == keys.c or key == keys.x or key == keys.b then\\\\\\\
  7761.             if key == keys.c then\\\\\\\
  7762.                 if self.selection then\\\\\\\
  7763.                     clipboard.set( \\\\\\\"plaintext\\\\\\\", self.public:getSelection( ) )\\\\\\\
  7764.                 else\\\\\\\
  7765.                     clipboard.set( \\\\\\\"plaintext\\\\\\\", self.lines[self.cursory] or \\\\\\\"\\\\\\\" )\\\\\\\
  7766.                 end\\\\\\\
  7767.             elseif key == keys.x then\\\\\\\
  7768.                 if self.selection then\\\\\\\
  7769.                     clipboard.set( \\\\\\\"plaintext\\\\\\\", self.public:getSelection( ) )\\\\\\\
  7770.                     self:setSelection \\\\\\\"\\\\\\\"\\\\\\\
  7771.                     self.selection = false\\\\\\\
  7772.                 else\\\\\\\
  7773.                     clipboard.set( \\\\\\\"plaintext\\\\\\\", self.lines[self.cursory] or \\\\\\\"\\\\\\\" )\\\\\\\
  7774.                     table.remove( self.lines, self.cursory )\\\\\\\
  7775.                 end\\\\\\\
  7776.                 self.public:updateCharacters( )\\\\\\\
  7777.                 if self.onChange then\\\\\\\
  7778.                     self.onChange( self.public, \\\\\\\"cut\\\\\\\" )\\\\\\\
  7779.                 end\\\\\\\
  7780.             elseif key == keys.b then\\\\\\\
  7781.                 local mode, data = clipboard.get( )\\\\\\\
  7782.                 if mode == \\\\\\\"plaintext\\\\\\\" then\\\\\\\
  7783.                     if self.selection then\\\\\\\
  7784.                         self:setSelection( data )\\\\\\\
  7785.                         self.selection = false\\\\\\\
  7786.                     else\\\\\\\
  7787.                         self:write( data )\\\\\\\
  7788.                     end\\\\\\\
  7789.                     self.public:updateCharacters( )\\\\\\\
  7790.                 elseif mode == \\\\\\\"file\\\\\\\" then\\\\\\\
  7791.                     if self.selection then\\\\\\\
  7792.                         self:setSelection( \\\\\\\"file:\\\\\\\\\\\\\\\"\\\\\\\" .. data.name .. \\\\\\\"\\\\\\\\\\\\\\\"\\\\\\\" )\\\\\\\
  7793.                         self.selection = false\\\\\\\
  7794.                     else\\\\\\\
  7795.                         self:write( \\\\\\\"file:\\\\\\\\\\\\\\\"\\\\\\\" .. data.name .. \\\\\\\"\\\\\\\\\\\\\\\"\\\\\\\" )\\\\\\\
  7796.                     end\\\\\\\
  7797.                     self.public:updateCharacters( )\\\\\\\
  7798.                 elseif self.onCtrlKey then\\\\\\\
  7799.                     self.onCtrlKey( self.public, key )\\\\\\\
  7800.                 end\\\\\\\
  7801.                 if self.onChange then\\\\\\\
  7802.                     self.onChange( self.public, \\\\\\\"paste\\\\\\\" )\\\\\\\
  7803.                 end\\\\\\\
  7804.             end\\\\\\\
  7805.             return\\\\\\\
  7806.         end\\\\\\\
  7807.         if self.onCtrlKey then\\\\\\\
  7808.             self.onCtrlKey( self.public, key )\\\\\\\
  7809.         end\\\\\\\
  7810.         return\\\\\\\
  7811.     end\\\\\\\
  7812.     if key == keys.left then\\\\\\\
  7813.         if self.selection then\\\\\\\
  7814.             local ssx, ssy, sex, sey = self:getSelectionBounds( )\\\\\\\
  7815.             self:setCursorPos( ssx, ssy )\\\\\\\
  7816.             self.selection = false\\\\\\\
  7817.         else\\\\\\\
  7818.             if self.cursorx == 1 then\\\\\\\
  7819.                 if self.cursory > 1 then\\\\\\\
  7820.                     self:setCursorPos( #self.lines[self.cursory - 1] + 1, self.cursory - 1 )\\\\\\\
  7821.                 end\\\\\\\
  7822.             else\\\\\\\
  7823.                 self:setCursorPos( self.cursorx - 1, self.cursory )\\\\\\\
  7824.             end\\\\\\\
  7825.         end\\\\\\\
  7826.     elseif key == keys.right then\\\\\\\
  7827.         if self.selection then\\\\\\\
  7828.             local ssx, ssy, sex, sey = self:getSelectionBounds( )\\\\\\\
  7829.             self:setCursorPos( sex, sey )\\\\\\\
  7830.             self.selection = false\\\\\\\
  7831.         else\\\\\\\
  7832.             if self.cursorx >= #self.lines[self.cursory] + 1 then\\\\\\\
  7833.                 if self.cursory < #self.lines then\\\\\\\
  7834.                     self:setCursorPos( 1, self.cursory + 1 )\\\\\\\
  7835.                 else\\\\\\\
  7836.                     self.cursorx = #self.lines[self.cursory] + 1\\\\\\\
  7837.                 end\\\\\\\
  7838.             else\\\\\\\
  7839.                 self:setCursorPos( self.cursorx + 1, self.cursory )\\\\\\\
  7840.             end\\\\\\\
  7841.         end\\\\\\\
  7842.     elseif key == keys.up and self.cursory > 1 then\\\\\\\
  7843.         if self.selection then\\\\\\\
  7844.             local ssx, ssy, sex, sey = self:getSelectionBounds( )\\\\\\\
  7845.             self:setCursorPos( ssx, ssy )\\\\\\\
  7846.             self.selection = false\\\\\\\
  7847.         else\\\\\\\
  7848.             local x, y = self:getCursorPos( )\\\\\\\
  7849.             self:setCursorPos( self:coordsToCursor( x + self.lineWidth, y - 1 ) )\\\\\\\
  7850.         end\\\\\\\
  7851.     elseif key == keys.down and self.cursory < #self.lines then\\\\\\\
  7852.         if self.selection then\\\\\\\
  7853.             local ssx, ssy, sex, sey = self:getSelectionBounds( )\\\\\\\
  7854.             self:setCursorPos( sex, sey )\\\\\\\
  7855.             self.selection = false\\\\\\\
  7856.         else\\\\\\\
  7857.             local x, y = self:getCursorPos( )\\\\\\\
  7858.             self:setCursorPos( self:coordsToCursor( x + self.lineWidth, y + 1 ) )\\\\\\\
  7859.         end\\\\\\\
  7860.     elseif key == keys.enter then\\\\\\\
  7861.         if self.selection then\\\\\\\
  7862.             self:setSelection \\\\\\\"\\\\\\\\n\\\\\\\"\\\\\\\
  7863.         else\\\\\\\
  7864.             local whitespace = self.lines[self.cursory]:match \\\\\\\"^(%s*)\\\\\\\"\\\\\\\
  7865.             table.insert( self.lines, self.cursory + 1, whitespace .. self.lines[self.cursory]:sub( self.cursorx ) )\\\\\\\
  7866.             self.lines[self.cursory] = self.lines[self.cursory]:sub( 1, self.cursorx - 1 )\\\\\\\
  7867.             self:setCursorPos( #whitespace + 1, self.cursory + 1 )\\\\\\\
  7868.         end\\\\\\\
  7869.         self.public:updateCharacters( )\\\\\\\
  7870.         if self.onChange then\\\\\\\
  7871.             self.onChange( self.public, \\\\\\\"\\\\\\\\n\\\\\\\" )\\\\\\\
  7872.         end\\\\\\\
  7873.     elseif key == keys.tab then\\\\\\\
  7874.         if self.selection then\\\\\\\
  7875.             local ssx, ssy, sex, sey = self:getSelectionBounds( )\\\\\\\
  7876.             if ssy == sey then\\\\\\\
  7877.                 self:setSelection \\\\\\\"  \\\\\\\"\\\\\\\
  7878.             else\\\\\\\
  7879.                 for y = ssy, sey do\\\\\\\
  7880.                     if lastkey == keys.leftShift then\\\\\\\
  7881.                         self.lines[y] = self.lines[y]:gsub( \\\\\\\"^   \\\\\\\", \\\\\\\"\\\\\\\", 1 )\\\\\\\
  7882.                     else\\\\\\\
  7883.                         self.lines[y] = \\\\\\\"    \\\\\\\" .. self.lines[y]\\\\\\\
  7884.                     end\\\\\\\
  7885.                 end\\\\\\\
  7886.             end\\\\\\\
  7887.         else\\\\\\\
  7888.             self.lines[self.cursory] = self.lines[self.cursory]:sub( 1, self.cursorx - 1 ) .. \\\\\\\"  \\\\\\\" .. self.lines[self.cursory]:sub( self.cursorx )\\\\\\\
  7889.             self:setCursorPos( self.cursorx + 1, self.cursory )\\\\\\\
  7890.         end\\\\\\\
  7891.         self.public:updateCharacters( )\\\\\\\
  7892.         if self.onChange then\\\\\\\
  7893.             self.onChange( self.public, \\\\\\\"    \\\\\\\" )\\\\\\\
  7894.         end\\\\\\\
  7895.     elseif key == keys.delete then\\\\\\\
  7896.         if self.selection then\\\\\\\
  7897.             self:setSelection \\\\\\\"\\\\\\\"\\\\\\\
  7898.         else\\\\\\\
  7899.             if self.cursorx == #self.lines[self.cursory] + 1 then\\\\\\\
  7900.                 if self.lines[self.cursory + 1] then\\\\\\\
  7901.                     self.lines[self.cursory] = self.lines[self.cursory] .. self.lines[self.cursory + 1]\\\\\\\
  7902.                     table.remove( self.lines, self.cursory + 1 )\\\\\\\
  7903.                 end\\\\\\\
  7904.             else\\\\\\\
  7905.                 self.lines[self.cursory] = self.lines[self.cursory]:sub( 1, self.cursorx - 1 ) .. self.lines[self.cursory]:sub( self.cursorx + 1 )\\\\\\\
  7906.             end\\\\\\\
  7907.         end\\\\\\\
  7908.         self.public:updateCharacters( )\\\\\\\
  7909.         if self.onChange then\\\\\\\
  7910.             self.onChange( self.public, \\\\\\\"delete\\\\\\\" )\\\\\\\
  7911.         end\\\\\\\
  7912.     elseif key == keys.backspace then\\\\\\\
  7913.         if self.selection then\\\\\\\
  7914.             self:setSelection \\\\\\\"\\\\\\\"\\\\\\\
  7915.         else\\\\\\\
  7916.             if self.cursorx == 1 then\\\\\\\
  7917.                 if self.cursory > 1 then\\\\\\\
  7918.                     self:setCursorPos( #self.lines[self.cursory - 1] + 1, self.cursory - 1 )\\\\\\\
  7919.                     self.lines[self.cursory] = self.lines[self.cursory] .. self.lines[self.cursory + 1]\\\\\\\
  7920.                     table.remove( self.lines, self.cursory + 1 )\\\\\\\
  7921.                 end\\\\\\\
  7922.             else\\\\\\\
  7923.                 self.lines[self.cursory] = self.lines[self.cursory]:sub( 1, self.cursorx - 2 ) .. self.lines[self.cursory]:sub( self.cursorx )\\\\\\\
  7924.                 self:setCursorPos( self.cursorx - 1, self.cursory )\\\\\\\
  7925.             end\\\\\\\
  7926.         end\\\\\\\
  7927.         self.public:updateCharacters( )\\\\\\\
  7928.         if self.onChange then\\\\\\\
  7929.             self.onChange( self.public, \\\\\\\"backspace\\\\\\\" )\\\\\\\
  7930.         end\\\\\\\
  7931.     elseif key == 199 then -- home\\\\\\\
  7932.         local tabs = #( self.lines[self.cursory]:match \\\\\\\"^(   *)\\\\\\\" )\\\\\\\
  7933.         self:setCursorPos( tabs + 1, self.cursory )\\\\\\\
  7934.     elseif key == 207 then -- end\\\\\\\
  7935.         self:setCursorPos( #self.lines[self.cursory] + 1, self.cursory )\\\\\\\
  7936.     end\\\\\\\
  7937. end\\\\\\\
  7938. \\\\\\\
  7939. function UICode.public:onTextInput( text )\\\\\\\
  7940.     if not self.focussed then\\\\\\\
  7941.         return\\\\\\\
  7942.     end\\\\\\\
  7943.     if self.selection then\\\\\\\
  7944.         self:setSelection( text )\\\\\\\
  7945.     else\\\\\\\
  7946.         self.lines[self.cursory] = self.lines[self.cursory]:sub( 1, self.cursorx - 1 ) .. text .. self.lines[self.cursory]:sub( self.cursorx )\\\\\\\
  7947.         self:setCursorPos( self.cursorx + 1, self.cursory )\\\\\\\
  7948.     end\\\\\\\
  7949.     self.public:updateCharacters( )\\\\\\\
  7950.     if self.onChange then\\\\\\\
  7951.         self.onChange( self.public, text )\\\\\\\
  7952.     end\\\\\\\
  7953. end\\\\\\\
  7954. \\\\\\\
  7955. function UICode.public:setCode( code )\\\\\\\
  7956.     self.lines = { }\\\\\\\
  7957.     local last = 1\\\\\\\
  7958.     for i = 1, #code do\\\\\\\
  7959.         if code:sub( i, i ) == \\\\\\\"\\\\\\\\n\\\\\\\" then\\\\\\\
  7960.             table.insert( self.lines, code:sub( last, i - 1 ) )\\\\\\\
  7961.             last = i + 1\\\\\\\
  7962.         end\\\\\\\
  7963.     end\\\\\\\
  7964.     table.insert( self.lines, code:sub( last ) )\\\\\\\
  7965.     self.public:updateCharacters( )\\\\\\\
  7966. end\\\\\\\
  7967. \\\\\\\
  7968. function UICode.public:getCode( )\\\\\\\
  7969.     return table.concat( self.lines, \\\\\\\"\\\\\\\\n\\\\\\\" )\\\\\\\
  7970. end\\\\\\\
  7971. \\\\\\\
  7972. function UICode.public:onFocus( )\\\\\\\
  7973.     self.focussed = true\\\\\\\
  7974.     self.handlesKeys = true\\\\\\\
  7975.     if type( self.whenFocussed ) == \\\\\\\"function\\\\\\\" then\\\\\\\
  7976.         self.whenFocussed( self.public )\\\\\\\
  7977.     end\\\\\\\
  7978. end\\\\\\\
  7979. \\\\\\\
  7980. function UICode.public:onUnFocus( )\\\\\\\
  7981.     self.handlesKeys = false\\\\\\\
  7982.     self.focussed = false\\\\\\\
  7983.     if type( self.whenUnFocussed ) == \\\\\\\"function\\\\\\\" then\\\\\\\
  7984.         self.whenUnFocussed( self.public )\\\\\\\
  7985.     end\\\\\\\
  7986. end\\\\\\\
  7987. \\\\\\\
  7988. function UICode.public:focusOn( )\\\\\\\
  7989.     local handler = self.public:getHandler( )\\\\\\\
  7990.     handler:setFocus( self.public )\\\\\\\
  7991. end\\\\\\\
  7992. \\\\\\\
  7993. function UICode.public:getDisplaySizeH( )\\\\\\\
  7994.     return self.w - self.lineWidth\\\\\\\
  7995. end\\\\\\\
  7996. function UICode.public:getDisplaySizeV( )\\\\\\\
  7997.     return self.h\\\\\\\
  7998. end\\\\\\\
  7999. function UICode.public:getContentSizeH( )\\\\\\\
  8000.     local max = 1\\\\\\\
  8001.     for i = 1, #self.lines do\\\\\\\
  8002.         max = math.max( max, #self.lines[i] )\\\\\\\
  8003.     end\\\\\\\
  8004.     return max\\\\\\\
  8005. end\\\\\\\
  8006. function UICode.public:getContentSizeV( )\\\\\\\
  8007.     return #self.lines\\\\\\\
  8008. end\\\\\\\
  8009. function UICode.public:getContentScrollH( )\\\\\\\
  8010.     return self.scrollx\\\\\\\
  8011. end\\\\\\\
  8012. function UICode.public:getContentScrollV( )\\\\\\\
  8013.     return self.scrolly\\\\\\\
  8014. end\\\\\\\
  8015. function UICode.public:setContentScrollH( scroll )\\\\\\\
  8016.     self.scrollx = scroll\\\\\\\
  8017. end\\\\\\\
  8018. function UICode.public:setContentScrollV( scroll )\\\\\\\
  8019.     self.scrolly = scroll\\\\\\\
  8020. end\\\", meta={\\\
  8021.  type = \\\"class\\\",\\\
  8022. }};[\\\"Image\\\"]={content=\\\"\\\\\\\
  8023. local colourLookup = {\\\\\\\
  8024.     [\\\\\\\"0\\\\\\\"] = colours.white;\\\\\\\
  8025.     [\\\\\\\"1\\\\\\\"] = colours.orange;\\\\\\\
  8026.     [\\\\\\\"2\\\\\\\"] = colours.magenta;\\\\\\\
  8027.     [\\\\\\\"3\\\\\\\"] = colours.lightBlue;\\\\\\\
  8028.     [\\\\\\\"4\\\\\\\"] = colours.yellow;\\\\\\\
  8029.     [\\\\\\\"5\\\\\\\"] = colours.lime;\\\\\\\
  8030.     [\\\\\\\"6\\\\\\\"] = colours.pink;\\\\\\\
  8031.     [\\\\\\\"7\\\\\\\"] = colours.grey;\\\\\\\
  8032.     [\\\\\\\"8\\\\\\\"] = colours.lightGrey;\\\\\\\
  8033.     [\\\\\\\"9\\\\\\\"] = colours.cyan;\\\\\\\
  8034.     [\\\\\\\"A\\\\\\\"] = colours.purple;\\\\\\\
  8035.     [\\\\\\\"B\\\\\\\"] = colours.blue;\\\\\\\
  8036.     [\\\\\\\"C\\\\\\\"] = colours.brown;\\\\\\\
  8037.     [\\\\\\\"D\\\\\\\"] = colours.green;\\\\\\\
  8038.     [\\\\\\\"E\\\\\\\"] = colours.red;\\\\\\\
  8039.     [\\\\\\\"F\\\\\\\"] = colours.black;\\\\\\\
  8040.     [\\\\\\\" \\\\\\\"] = 0;\\\\\\\
  8041. }\\\\\\\
  8042. \\\\\\\
  8043. local colourSave = { }\\\\\\\
  8044. for k, v in pairs( colourLookup ) do\\\\\\\
  8045.     colourSave[v] = k\\\\\\\
  8046. end\\\\\\\
  8047. \\\\\\\
  8048. function Image:Image( w, h )\\\\\\\
  8049.     self.pixels = { }\\\\\\\
  8050. \\\\\\\
  8051.     for y = 1, h or 1 do\\\\\\\
  8052.         self.pixels[y] = { }\\\\\\\
  8053.         for x = 1, w or 1 do\\\\\\\
  8054.             self.pixels[y][x] = { bc = 0, tc = 0, char = \\\\\\\"\\\\\\\" }\\\\\\\
  8055.         end\\\\\\\
  8056.     end\\\\\\\
  8057. \\\\\\\
  8058.     return self.public\\\\\\\
  8059. end\\\\\\\
  8060. \\\\\\\
  8061. function Image.public:getSize( )\\\\\\\
  8062.     return #( self.pixels[1] or { } ), #self.pixels\\\\\\\
  8063. end\\\\\\\
  8064. \\\\\\\
  8065. function Image.public:foreach( f )\\\\\\\
  8066.     local w, h = #( self.pixels[1] or { } ), #self.pixels\\\\\\\
  8067.     local pixels = { }\\\\\\\
  8068.     for y = 1, h do\\\\\\\
  8069.         pixels[y] = { }\\\\\\\
  8070.         for x = 1, w do\\\\\\\
  8071.             pixels[y][x] = { }\\\\\\\
  8072.             local bc, tc, char = f( x, y, self.public:getPixel( x, y ) )\\\\\\\
  8073.             pixels[y][x].bc = bc or self.pixels[y][x].bc\\\\\\\
  8074.             pixels[y][x].tc = tc or self.pixels[y][x].tc\\\\\\\
  8075.             pixels[y][x].char = char or self.pixels[y][x].char\\\\\\\
  8076.         end\\\\\\\
  8077.     end\\\\\\\
  8078.     for y = 1, h do\\\\\\\
  8079.         for x = 1, w do\\\\\\\
  8080.             self.pixels[y][x].bc = pixels[y][x].bc\\\\\\\
  8081.             self.pixels[y][x].tc = pixels[y][x].tc\\\\\\\
  8082.             self.pixels[y][x].char = pixels[y][x].char\\\\\\\
  8083.         end\\\\\\\
  8084.     end\\\\\\\
  8085. end\\\\\\\
  8086. \\\\\\\
  8087. function Image.public:pixel( x, y, bc, tc, char )\\\\\\\
  8088.     if self.pixels[y] and self.pixels[y][x] then\\\\\\\
  8089.         --if bc == 0 then bc = pixels[y][x].bc end\\\\\\\
  8090.         --if tc == 0 then char = pixels[y][x].char tc = pixels[y][x].tc end\\\\\\\
  8091.         self.pixels[y][x] = { bc = bc, tc = tc, char = char }\\\\\\\
  8092.         return true\\\\\\\
  8093.     end\\\\\\\
  8094.     return false\\\\\\\
  8095. end\\\\\\\
  8096. \\\\\\\
  8097. function Image.public:getPixel( x, y )\\\\\\\
  8098.     if self.pixels[y] and self.pixels[y][x] then\\\\\\\
  8099.         return self.pixels[y][x].bc, self.pixels[y][x].tc, self.pixels[y][x].char\\\\\\\
  8100.     end\\\\\\\
  8101.     return false\\\\\\\
  8102. end\\\\\\\
  8103. \\\\\\\
  8104. function Image.public:savestr( str )\\\\\\\
  8105.     local w, h = #( self.pixels[1] or { } ), #self.pixels\\\\\\\
  8106.     local str = \\\\\\\"\\\\\\\"\\\\\\\
  8107.     for y = 1, h do\\\\\\\
  8108.         for x = 1, w do\\\\\\\
  8109.             local bc = colourSave[self.pixels[y][x].bc]\\\\\\\
  8110.             local tc = colourSave[self.pixels[y][x].tc]\\\\\\\
  8111.             local char = self.pixels[y][x].char\\\\\\\
  8112.             if #char == 0 then\\\\\\\
  8113.                 char = \\\\\\\" \\\\\\\"\\\\\\\
  8114.                 bc = colourSave[0]\\\\\\\
  8115.             end\\\\\\\
  8116.             str = str .. bc .. tc .. char\\\\\\\
  8117.         end\\\\\\\
  8118.         str = str .. \\\\\\\"\\\\\\\\n\\\\\\\"\\\\\\\
  8119.     end\\\\\\\
  8120.     return str:sub( 1, -2 )\\\\\\\
  8121. end\\\\\\\
  8122. \\\\\\\
  8123. function Image.public:loadstr( str )\\\\\\\
  8124.     local lines = { }\\\\\\\
  8125.     local last = 1\\\\\\\
  8126.     for i = 1, #str do\\\\\\\
  8127.         if str:sub( i, i ) == \\\\\\\"\\\\\\\\n\\\\\\\" then\\\\\\\
  8128.             table.insert( lines, str:sub( last, i - 1 ) )\\\\\\\
  8129.             last = i + 1\\\\\\\
  8130.         end\\\\\\\
  8131.     end\\\\\\\
  8132.     table.insert( lines, str:sub( last ) )\\\\\\\
  8133.     local width = #lines[1] / 3\\\\\\\
  8134.     local height = #lines\\\\\\\
  8135.     self.public:resize( width, height )\\\\\\\
  8136.     for i = 1, #lines do\\\\\\\
  8137.         local x = 1\\\\\\\
  8138.         for pixel in lines[i]:gmatch( \\\\\\\"[0123456789ABCDEF ][0123456789ABCDEF ].\\\\\\\" ) do\\\\\\\
  8139.             local bc = pixel:sub( 1, 1 )\\\\\\\
  8140.             local tc = pixel:sub( 2, 2 )\\\\\\\
  8141.             local ch = pixel:sub( 3, 3 )\\\\\\\
  8142.             self.public:pixel( x, i, colourLookup[bc], colourLookup[tc], ch )\\\\\\\
  8143.             x = x + 1\\\\\\\
  8144.         end\\\\\\\
  8145.     end\\\\\\\
  8146.     return self.public\\\\\\\
  8147. end\\\\\\\
  8148. \\\\\\\
  8149. function Image.public:resize( w, h, bc, tc, char )\\\\\\\
  8150.     while #self.pixels > h do\\\\\\\
  8151.         table.remove( self.pixels, #self.pixels )\\\\\\\
  8152.     end\\\\\\\
  8153.     while #self.pixels < h do\\\\\\\
  8154.         local t = { }\\\\\\\
  8155.         for i = 1, #( self.pixels[1] or { } ) do\\\\\\\
  8156.             t[i] = { bc = bc or 0, tc = tc or 0, char = char or \\\\\\\"\\\\\\\" }\\\\\\\
  8157.         end\\\\\\\
  8158.         table.insert( self.pixels, t )\\\\\\\
  8159.     end\\\\\\\
  8160.     if self.pixels[1] then\\\\\\\
  8161.         local cw = #self.pixels[1]\\\\\\\
  8162.         while cw > w do\\\\\\\
  8163.             for y = 1, h do\\\\\\\
  8164.                 table.remove( self.pixels[y], #self.pixels[y] )\\\\\\\
  8165.             end\\\\\\\
  8166.             cw = cw - 1\\\\\\\
  8167.         end\\\\\\\
  8168.         while cw < w do\\\\\\\
  8169.             for y = 1, h do\\\\\\\
  8170.                 table.insert( self.pixels[y], { bc = bc or 0, tc = tc or 0, char = char or \\\\\\\"\\\\\\\" } )\\\\\\\
  8171.             end\\\\\\\
  8172.             cw = cw + 1\\\\\\\
  8173.         end\\\\\\\
  8174.     end\\\\\\\
  8175. end\\\", meta={\\\
  8176.  type = \\\"class\\\",\\\
  8177. }};[\\\"markup\\\"]={content=\\\"\\\\\\\
  8178. require \\\\\\\"parser\\\\\\\"\\\\\\\
  8179. \\\\\\\
  8180. local function loadStandardAttributes( class, attributes, scripts, ids, errors, parent )\\\\\\\
  8181.     local x = type( attributes.x ) == \\\\\\\"number\\\\\\\" and attributes.x or 1\\\\\\\
  8182.     local y = type( attributes.y ) == \\\\\\\"number\\\\\\\" and attributes.y or 1\\\\\\\
  8183.     local w = type( attributes.width ) == \\\\\\\"number\\\\\\\" and attributes.width or 19\\\\\\\
  8184.     local h = type( attributes.height ) == \\\\\\\"number\\\\\\\" and attributes.height or 1\\\\\\\
  8185.     if not attributes.x or not attributes.y then\\\\\\\
  8186.         local children = parent:getChildren( )\\\\\\\
  8187.         if children[#children] then\\\\\\\
  8188.             if attributes.float == \\\\\\\"below\\\\\\\" then\\\\\\\
  8189.                 x = children[#children].x\\\\\\\
  8190.                 y = children[#children].y + children[#children].h\\\\\\\
  8191.             else\\\\\\\
  8192.                 x = children[#children].x + children[#children].w\\\\\\\
  8193.                 y = children[#children].y\\\\\\\
  8194.             end\\\\\\\
  8195.         end\\\\\\\
  8196.     end\\\\\\\
  8197.     if attributes.width == \\\\\\\"expand\\\\\\\" then\\\\\\\
  8198.         w = parent.w - x + 1\\\\\\\
  8199.     end\\\\\\\
  8200.     if attributes.height == \\\\\\\"expand\\\\\\\" then\\\\\\\
  8201.         h = parent.h - y + 1\\\\\\\
  8202.     end\\\\\\\
  8203.     if attributes.x == \\\\\\\"right\\\\\\\" then\\\\\\\
  8204.         x = parent.w - w + 1\\\\\\\
  8205.     end\\\\\\\
  8206.     if attributes.y == \\\\\\\"bottom\\\\\\\" then\\\\\\\
  8207.         y = parent.h - h + 1\\\\\\\
  8208.     end\\\\\\\
  8209.     if not ( attributes.x or attributes.y ) and ( attributes.float == \\\\\\\"right\\\\\\\" or not attributes.float ) and x + w > parent.w then\\\\\\\
  8210.         local c = parent:getChildren( )\\\\\\\
  8211.         if c[#c] then\\\\\\\
  8212.             x = c[#c].x\\\\\\\
  8213.             y = c[#c].y + c[#c].h\\\\\\\
  8214.         end\\\\\\\
  8215.     end\\\\\\\
  8216.     local ob = parent:newChild( class( x, y, w, h ) )\\\\\\\
  8217.     if attributes.x == \\\\\\\"centre\\\\\\\" then\\\\\\\
  8218.         ob:centreX( )\\\\\\\
  8219.     end\\\\\\\
  8220.     if attributes.y == \\\\\\\"centre\\\\\\\" then\\\\\\\
  8221.         ob:centreY( )\\\\\\\
  8222.     end\\\\\\\
  8223.     if attributes.id then\\\\\\\
  8224.         ids[attributes.id] = ob\\\\\\\
  8225.     end\\\\\\\
  8226.     if attributes.onFocus then\\\\\\\
  8227.         local f, err = loadstring( attributes.onFocus, \\\\\\\"onFocus\\\\\\\" )\\\\\\\
  8228.         if f then\\\\\\\
  8229.             ob.whenFocussed = f\\\\\\\
  8230.             table.insert( scripts, { script = f } )\\\\\\\
  8231.         else\\\\\\\
  8232.             table.insert( errors, err )\\\\\\\
  8233.         end\\\\\\\
  8234.     end\\\\\\\
  8235.     if attributes.onUnFocus then\\\\\\\
  8236.         local f, err = loadstring( attributes.onUnFocus, \\\\\\\"onUnFocus\\\\\\\" )\\\\\\\
  8237.         if f then\\\\\\\
  8238.             ob.whenUnFocussed = f\\\\\\\
  8239.             table.insert( scripts, { script = f } )\\\\\\\
  8240.         else\\\\\\\
  8241.             table.insert( errors, err )\\\\\\\
  8242.         end\\\\\\\
  8243.     end\\\\\\\
  8244.     if type( attributes.align ) == \\\\\\\"string\\\\\\\" then\\\\\\\
  8245.         local children = parent:getChildren( )\\\\\\\
  8246.         if #children > 1 then\\\\\\\
  8247.             ob:alignTo( attributes.align, children[#children-1], tonumber( attributes.spacing ) )\\\\\\\
  8248.             ob.w = w\\\\\\\
  8249.             ob.h = h\\\\\\\
  8250.         end\\\\\\\
  8251.     end\\\\\\\
  8252.     return ob\\\\\\\
  8253. end\\\\\\\
  8254. \\\\\\\
  8255. local function loadColours( ob, attributes )\\\\\\\
  8256.     local bc = type( attributes.bc ) == \\\\\\\"number\\\\\\\" and attributes.bc\\\\\\\
  8257.     local tc = type( attributes.tc ) == \\\\\\\"number\\\\\\\" and attributes.tc\\\\\\\
  8258.     if colours[attributes.bc] then bc = colours[attributes.bc] end\\\\\\\
  8259.     if attributes.bc == \\\\\\\"transparent\\\\\\\" then bc = 0 end\\\\\\\
  8260.     if colours[attributes.tc] then tc = colours[attributes.tc] end\\\\\\\
  8261.     if attributes.tc == \\\\\\\"transparent\\\\\\\" then tc = 0 end\\\\\\\
  8262.     if bc then\\\\\\\
  8263.         ob.bc = bc\\\\\\\
  8264.     end\\\\\\\
  8265.     if tc then\\\\\\\
  8266.         ob.tc = tc\\\\\\\
  8267.     end\\\\\\\
  8268. end\\\\\\\
  8269. \\\\\\\
  8270. parser.registerTag( \\\\\\\"text\\\\\\\", true, false, function( parent, attributes, content, scripts, ids, errors )\\\\\\\
  8271.     if attributes.width == \\\\\\\"auto\\\\\\\" or not attributes.width then\\\\\\\
  8272.         attributes.width = #content\\\\\\\
  8273.     end\\\\\\\
  8274.     local ob = loadStandardAttributes( UIText, attributes, scripts, ids, errors, parent )\\\\\\\
  8275.     loadColours( ob, attributes )\\\\\\\
  8276.     ob.text = content\\\\\\\
  8277.     return ob\\\\\\\
  8278. end, nil, nil )\\\\\\\
  8279. \\\\\\\
  8280. parser.registerTag( \\\\\\\"frame\\\\\\\", true, true, function( parent, attributes, content, scripts, ids, errors )\\\\\\\
  8281.     local ob = loadStandardAttributes( UIFrame, attributes, scripts, ids, errors, parent )\\\\\\\
  8282.     return ob\\\\\\\
  8283. end, nil, nil )\\\\\\\
  8284. \\\\\\\
  8285. parser.registerTag( \\\\\\\"button\\\\\\\", true, false, function( parent, attributes, content, scripts, ids, errors )\\\\\\\
  8286.     if attributes.width == \\\\\\\"auto\\\\\\\" or not attributes.width then\\\\\\\
  8287.         attributes.width = #content\\\\\\\
  8288.     end\\\\\\\
  8289.     local ob = loadStandardAttributes( UIButton, attributes, scripts, ids, errors, parent )\\\\\\\
  8290.     loadColours( ob, attributes )\\\\\\\
  8291.     ob.text = content\\\\\\\
  8292.     if attributes.onClick then\\\\\\\
  8293.         local f, err = loadstring( attributes.onClick, \\\\\\\"onClick\\\\\\\" )\\\\\\\
  8294.         if f then\\\\\\\
  8295.             ob.onClick = f\\\\\\\
  8296.             table.insert( scripts, { script = f } )\\\\\\\
  8297.         else\\\\\\\
  8298.             table.insert( errors, err )\\\\\\\
  8299.         end\\\\\\\
  8300.     end\\\\\\\
  8301.     return ob\\\\\\\
  8302. end, nil, nil )\\\\\\\
  8303. \\\\\\\
  8304. parser.registerTag( \\\\\\\"input\\\\\\\", false, false, function( parent, attributes, content, scripts, ids, errors )\\\\\\\
  8305.     local ob = loadStandardAttributes( UIInput, attributes, scripts, ids, errors, parent )\\\\\\\
  8306.     loadColours( ob, attributes )\\\\\\\
  8307.     local fbc = type( attributes.fbc ) == \\\\\\\"number\\\\\\\" and attributes.fbc\\\\\\\
  8308.     if colours[attributes.fbc] then fbc = colours[attributes.fbc] end\\\\\\\
  8309.     if attributes.fbc == \\\\\\\"transparent\\\\\\\" then fbc = 0 end\\\\\\\
  8310.     if fbc then\\\\\\\
  8311.         ob.fbc = fbc\\\\\\\
  8312.     end\\\\\\\
  8313.     if attributes.mask then\\\\\\\
  8314.         ob.mask = tostring( attributes.mask )\\\\\\\
  8315.     end\\\\\\\
  8316.     return ob\\\\\\\
  8317. end, nil, nil )\\\\\\\
  8318. \\\\\\\
  8319. parser.registerTag( \\\\\\\"image\\\\\\\", false, false, function( parent, attributes, content, scripts, ids, errors )\\\\\\\
  8320.     local ob = loadStandardAttributes( UIImage, attributes, scripts, ids, errors, parent )\\\\\\\
  8321.     if attributes.source then\\\\\\\
  8322.         local source = tostring( attributes.source )\\\\\\\
  8323.         local h = fs.open( source, \\\\\\\"r\\\\\\\" )\\\\\\\
  8324.         if h then\\\\\\\
  8325.             local content = h.readAll( )\\\\\\\
  8326.             h.close( )\\\\\\\
  8327.             ob.image = Image( )\\\\\\\
  8328.             ob.image:loadstr( content )\\\\\\\
  8329.             ob.w, ob.h = ob.image:getSize( )\\\\\\\
  8330.         else\\\\\\\
  8331.             ob:remove( )\\\\\\\
  8332.             ob = UIButton( ob.x, ob.y, ob.w, ob.h, attributes.alt or \\\\\\\"could not load image\\\\\\\" )\\\\\\\
  8333.             ob.tc = colours.lightGrey\\\\\\\
  8334.         end\\\\\\\
  8335.     else\\\\\\\
  8336.         ob.image = Image( ob.w, ob.h )\\\\\\\
  8337.         ob.image:foreach( function( )\\\\\\\
  8338.             return colours.white, 1, \\\\\\\" \\\\\\\"\\\\\\\
  8339.         end )\\\\\\\
  8340.     end\\\\\\\
  8341.     return ob\\\\\\\
  8342. end, nil, nil )\\\\\\\
  8343. \\\\\\\
  8344. function load( str, frame, penv )\\\\\\\
  8345.     local body, scripts, ids, errors = parser.load( str, frame or UIFrame( 1, 1, term.getSize( ) ) )\\\\\\\
  8346. \\\\\\\
  8347.     ids.body = body\\\\\\\
  8348. \\\\\\\
  8349.     local env = { }\\\\\\\
  8350.     env.document = { }\\\\\\\
  8351.     local meta = { __index = penv or { } }\\\\\\\
  8352.     if penv then meta.__newindex = penv end\\\\\\\
  8353.     setmetatable( env, meta )\\\\\\\
  8354. \\\\\\\
  8355.     function env.document.getElementById( id )\\\\\\\
  8356.         return ids[id]\\\\\\\
  8357.     end\\\\\\\
  8358.     function env.document.addContent( str, id )\\\\\\\
  8359.         if not id then id = \\\\\\\"body\\\\\\\" end\\\\\\\
  8360.         if ids[id] then\\\\\\\
  8361.             local _, errs = load( str, ids[id], penv )\\\\\\\
  8362.             for i = 1, #errs do\\\\\\\
  8363.                 table.insert( errors, errs[i] )\\\\\\\
  8364.             end\\\\\\\
  8365.             return true\\\\\\\
  8366.         else\\\\\\\
  8367.             return false, \\\\\\\"no such id\\\\\\\"\\\\\\\
  8368.         end\\\\\\\
  8369.     end\\\\\\\
  8370. \\\\\\\
  8371.     for i = 1,#scripts do\\\\\\\
  8372.         setfenv( scripts[i].script, env )\\\\\\\
  8373.         if scripts[i].run then\\\\\\\
  8374.             scripts[i].script( )\\\\\\\
  8375.         end\\\\\\\
  8376.     end\\\\\\\
  8377. \\\\\\\
  8378.     return frame, errors, env.document\\\\\\\
  8379. end\\\", meta={\\\
  8380.  type = \\\"lib\\\",\\\
  8381. }};[\\\"UIFrame\\\"]={content=\\\"\\\\\\\
  8382. require \\\\\\\"UIElement\\\\\\\"\\\\\\\
  8383. \\\\\\\
  8384. UIFrame:extends( UIElement )\\\\\\\
  8385. \\\\\\\
  8386. UIFrame.handlesMouse = false\\\\\\\
  8387. UIFrame.handlesScroll = true\\\\\\\
  8388. UIFrame.isScrollTarget = true\\\\\\\
  8389. UIFrame.scrollDirection = \\\\\\\"vertical\\\\\\\"\\\\\\\
  8390. \\\\\\\
  8391. UIFrame.scrolls = true\\\\\\\
  8392. UIFrame.public \\\\\\\"scrolls\\\\\\\" \\\\\\\"boolean\\\\\\\"\\\\\\\
  8393. \\\\\\\
  8394. function UIFrame:UIFrame( x, y, w, h, direction )\\\\\\\
  8395.     self:UIElement( x, y, w, h )\\\\\\\
  8396.     self.scrollDirection = direction or \\\\\\\"vertical\\\\\\\"\\\\\\\
  8397.     return self.public\\\\\\\
  8398. end\\\\\\\
  8399. \\\\\\\
  8400. function UIFrame.public:update( dt )\\\\\\\
  8401.     local max = self.scrollDirection == \\\\\\\"vertical\\\\\\\" and self.h or self.w\\\\\\\
  8402.     if self.scrollDirection == \\\\\\\"vertical\\\\\\\" then\\\\\\\
  8403.         for i = 1, #self.children do\\\\\\\
  8404.             max = math.max( max, self.children[i].y + self.children[i].h - 1 )\\\\\\\
  8405.         end\\\\\\\
  8406.     else\\\\\\\
  8407.         for i = 1, #self.children do\\\\\\\
  8408.             max = math.max( max, self.children[i].x + self.children[i].w - 1 )\\\\\\\
  8409.         end\\\\\\\
  8410.     end\\\\\\\
  8411.     if self.scrollDirection == \\\\\\\"vertical\\\\\\\" then\\\\\\\
  8412.         if max - self.h < -self.cy then\\\\\\\
  8413.             self.cy = -max + self.h\\\\\\\
  8414.         end\\\\\\\
  8415.     else\\\\\\\
  8416.         if max - self.w < -self.cx then\\\\\\\
  8417.             self.cx = -max + self.w\\\\\\\
  8418.         end\\\\\\\
  8419.     end\\\\\\\
  8420.     if max == ( self.scrollDirection == \\\\\\\"vertical\\\\\\\" and self.h or self.w ) or not self.scrolls then\\\\\\\
  8421.         self.handlesScroll = false\\\\\\\
  8422.     else\\\\\\\
  8423.         self.handlesScroll = true\\\\\\\
  8424.     end\\\\\\\
  8425.     local c = self.public:getChildren( )\\\\\\\
  8426.     for i, child in ipairs( c ) do\\\\\\\
  8427.         child:update( dt )\\\\\\\
  8428.     end\\\\\\\
  8429. end\\\\\\\
  8430. \\\\\\\
  8431. function UIFrame.public:onMouseScroll( rx, ry, dir )\\\\\\\
  8432.     if self.scrollDirection == \\\\\\\"vertical\\\\\\\" then\\\\\\\
  8433.         self.cy = self.cy - dir\\\\\\\
  8434.         local max = 0\\\\\\\
  8435.         for i = 1, #self.children do\\\\\\\
  8436.             max = math.max( max, self.children[i].y + self.children[i].h )\\\\\\\
  8437.         end\\\\\\\
  8438.         max = max - self.h - 1\\\\\\\
  8439.         if self.cy < -max then\\\\\\\
  8440.             self.cy = -max\\\\\\\
  8441.         end\\\\\\\
  8442.         if self.cy > 0 then self.cy = 0 end\\\\\\\
  8443.     else\\\\\\\
  8444.         self.cx = self.cx - dir\\\\\\\
  8445.         local max = 0\\\\\\\
  8446.         for i = 1, #self.children do\\\\\\\
  8447.             max = math.max( max, self.children[i].x + self.children[i].w )\\\\\\\
  8448.         end\\\\\\\
  8449.         max = max - self.w - 1\\\\\\\
  8450.         if self.cx < -max then\\\\\\\
  8451.             self.cx = -max\\\\\\\
  8452.         end\\\\\\\
  8453.         if self.cx > 0 then self.cx = 0 end\\\\\\\
  8454.     end\\\\\\\
  8455. end\\\\\\\
  8456. \\\\\\\
  8457. function UIFrame.public:getDisplaySizeH( )\\\\\\\
  8458.     return self.w\\\\\\\
  8459. end\\\\\\\
  8460. function UIFrame.public:getDisplaySizeV( )\\\\\\\
  8461.     return self.h\\\\\\\
  8462. end\\\\\\\
  8463. \\\\\\\
  8464. function UIFrame.public:getContentSizeH( )\\\\\\\
  8465.     local max = self.w\\\\\\\
  8466.     for i = 1, #self.children do\\\\\\\
  8467.         max = math.max( max, self.children[i].x + self.children[i].w - 1 )\\\\\\\
  8468.     end\\\\\\\
  8469.     return max\\\\\\\
  8470. end\\\\\\\
  8471. function UIFrame.public:getContentSizeV( )\\\\\\\
  8472.     local max = self.h\\\\\\\
  8473.     for i = 1, #self.children do\\\\\\\
  8474.         max = math.max( max, self.children[i].y + self.children[i].h - 1 )\\\\\\\
  8475.     end\\\\\\\
  8476.     return max\\\\\\\
  8477. end\\\\\\\
  8478. \\\\\\\
  8479. function UIFrame.public:getContentScrollH( )\\\\\\\
  8480.     return -self.cx\\\\\\\
  8481. end\\\\\\\
  8482. function UIFrame.public:getContentScrollV( )\\\\\\\
  8483.     return -self.cy\\\\\\\
  8484. end\\\\\\\
  8485. \\\\\\\
  8486. function UIFrame.public:setContentScrollH( scroll )\\\\\\\
  8487.     self.cx = -scroll\\\\\\\
  8488. end\\\\\\\
  8489. function UIFrame.public:setContentScrollV( scroll )\\\\\\\
  8490.     self.cy = -scroll\\\\\\\
  8491. end\\\", meta={\\\
  8492.  type = \\\"class\\\",\\\
  8493. }};[\\\"clipboard\\\"]={content=\\\"if NovaClipboard then\\\\\\\
  8494.     set, get = NovaClipboard.set, NovaClipboard.get\\\\\\\
  8495. else\\\\\\\
  8496.     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\\\\\\\
  8497.     export(\\\\\\\"NovaClipboard\\\\\\\",{set=set,get=get})\\\\\\\
  8498. end\\\", meta={\\\
  8499.  type = \\\"lib\\\",\\\
  8500. }};[\\\"UIText\\\"]={content=\\\"\\\\\\\
  8501. require \\\\\\\"UIElement\\\\\\\"\\\\\\\
  8502. \\\\\\\
  8503. UIText:extends( UIElement )\\\\\\\
  8504. \\\\\\\
  8505. UIText.text = \\\\\\\"\\\\\\\"\\\\\\\
  8506. UIText.public \\\\\\\"text\\\\\\\"\\\\\\\
  8507. \\\\\\\
  8508. UIText.public \\\\\\\"bc\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
  8509. UIText.public \\\\\\\"tc\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
  8510. \\\\\\\
  8511. function UIText:UIText( x, y, w, h, text )\\\\\\\
  8512.     self:UIElement( x, y, w, h )\\\\\\\
  8513.     self.text = text\\\\\\\
  8514.     self.bc = colours.white\\\\\\\
  8515.     self.tc = colours.grey\\\\\\\
  8516.     return self.public\\\\\\\
  8517. end\\\\\\\
  8518. \\\\\\\
  8519. function UIText.public:draw( x, y )\\\\\\\
  8520.     local layer = stencil.addLayer( x, y, self.w, self.h )\\\\\\\
  8521.     local text = tostring( self.text )\\\\\\\
  8522.     if type( self.text ) == \\\\\\\"function\\\\\\\" then\\\\\\\
  8523.         text = self.text( self.public )\\\\\\\
  8524.     end\\\\\\\
  8525.     stencil.text( x, y, self.w, self.h, self.bc, self.tc, text )\\\\\\\
  8526.     local c = self.public:getChildren( )\\\\\\\
  8527.     for i, child in ipairs( c ) do\\\\\\\
  8528.         child:draw( x + child.x - 1 + self.cx, y + child.y - 1 + self.cy )\\\\\\\
  8529.     end\\\\\\\
  8530.     stencil.closeLayer( layer )\\\\\\\
  8531. end\\\", meta={\\\
  8532.  type = \\\"class\\\",\\\
  8533. }};[\\\"stencil\\\"]={content=\\\"\\\\\\\
  8534. local function linewrap( str, w )\\\\\\\
  8535.     for i = 1, w + 1 do\\\\\\\
  8536.         if str:sub( i, i ) == \\\\\\\"\\\\\\\\n\\\\\\\" then\\\\\\\
  8537.             return str:sub( 1, i - 1 ), str:sub( i + 1 )\\\\\\\
  8538.         end\\\\\\\
  8539.     end\\\\\\\
  8540.     if #str <= w then\\\\\\\
  8541.         return str, \\\\\\\"\\\\\\\"\\\\\\\
  8542.     end\\\\\\\
  8543.     if str:sub( w + 1, w + 1 ):find \\\\\\\"%s\\\\\\\" then\\\\\\\
  8544.         return str:sub( 1, w ), str:sub( w + 1 ):gsub( \\\\\\\"^%s+\\\\\\\", \\\\\\\"\\\\\\\" )\\\\\\\
  8545.     end\\\\\\\
  8546.     for i = w, 1, -1 do\\\\\\\
  8547.         if str:sub( i, i ):find \\\\\\\"%s\\\\\\\" then\\\\\\\
  8548.             return str:sub( 1, i ), str:sub( i + 1 )\\\\\\\
  8549.         end\\\\\\\
  8550.     end\\\\\\\
  8551.     return str:sub( 1, w ), str:sub( w + 1 )\\\\\\\
  8552. end\\\\\\\
  8553. \\\\\\\
  8554. local function wordwrap( str, w, h )\\\\\\\
  8555.     if w < 1 then return { } end\\\\\\\
  8556.     local lines = { }\\\\\\\
  8557.     local line\\\\\\\
  8558.     while #str > 0 do\\\\\\\
  8559.         line, str = linewrap( str, w )\\\\\\\
  8560.         table.insert( lines, line )\\\\\\\
  8561.     end\\\\\\\
  8562.     while #lines > h do\\\\\\\
  8563.         lines[#lines] = nil\\\\\\\
  8564.     end\\\\\\\
  8565.     return lines\\\\\\\
  8566. end\\\\\\\
  8567. \\\\\\\
  8568. local function bbox( x1, y1, w1, h1, x2, y2, w2, h2 )\\\\\\\
  8569.     local x, y, w, h\\\\\\\
  8570.     if x1 + w1 <= x2 or y1 + h1 <= y2 or x2 + w2 <= x1 or y2 + h2 <= y1 then\\\\\\\
  8571.         return false\\\\\\\
  8572.     end\\\\\\\
  8573.     x = math.max( x1, x2 )\\\\\\\
  8574.     w = math.min( x1 + w1, x2 + w2 ) - x\\\\\\\
  8575.     y = math.max( y1, y2 )\\\\\\\
  8576.     h = math.min( y1 + h1, y2 + h2 ) - y\\\\\\\
  8577.     return x, y, w, h\\\\\\\
  8578. end\\\\\\\
  8579. \\\\\\\
  8580. local layers = { }\\\\\\\
  8581. local w, h = term.getSize( )\\\\\\\
  8582. local limit = { x = 1, y = 1, w = w, h = h }\\\\\\\
  8583. \\\\\\\
  8584. function pixel( x, y, bc, tc, char )\\\\\\\
  8585.     if limit and x >= limit.x and x < limit.x + limit.w and y >= limit.y and y < limit.y + limit.h then\\\\\\\
  8586.         buffer.setPixel( x, y, bc, tc, char )\\\\\\\
  8587.         return true\\\\\\\
  8588.     end\\\\\\\
  8589.     return false\\\\\\\
  8590. end\\\\\\\
  8591. \\\\\\\
  8592. function textLine( x, y, bc, tc, text )\\\\\\\
  8593.     for i = 1, #text do\\\\\\\
  8594.         pixel( x + i - 1, y, bc, tc, text:sub( i, i ) )\\\\\\\
  8595.     end\\\\\\\
  8596. end\\\\\\\
  8597. \\\\\\\
  8598. function rectangle( x, y, w, h, bc, tc, char )\\\\\\\
  8599.     for i = 0, h - 1 do\\\\\\\
  8600.         textLine( x, y + i, bc, tc, ( char or \\\\\\\" \\\\\\\" ):rep( w ) )\\\\\\\
  8601.     end\\\\\\\
  8602. end\\\\\\\
  8603. \\\\\\\
  8604. function text( x, y, w, h, bc, tc, text, pad )\\\\\\\
  8605.     local lines = wordwrap( text, w, h )\\\\\\\
  8606.     if pad then\\\\\\\
  8607.         pad( lines )\\\\\\\
  8608.     end\\\\\\\
  8609.     if bc ~= 0 then\\\\\\\
  8610.         while #lines < h do\\\\\\\
  8611.             table.insert( lines, string.rep( \\\\\\\" \\\\\\\", math.max( w, 0 ) ) )\\\\\\\
  8612.         end\\\\\\\
  8613.     end\\\\\\\
  8614.     for i = 1, #lines do\\\\\\\
  8615.         local line = lines[i]:sub( 1, w )\\\\\\\
  8616.         if bc ~= 0 then\\\\\\\
  8617.             line = line .. string.rep( \\\\\\\" \\\\\\\", math.max( w - #line, 0 ) )\\\\\\\
  8618.         end\\\\\\\
  8619.         textLine( x, y + i - 1, bc, tc, line )\\\\\\\
  8620.     end\\\\\\\
  8621. end\\\\\\\
  8622. \\\\\\\
  8623. function setCursorBlink( x, y, col )\\\\\\\
  8624.     if limit and x >= limit.x and x < limit.x + limit.w and y >= limit.y and y < limit.y + limit.h then\\\\\\\
  8625.         buffer.setCursorBlink( x, y, col )\\\\\\\
  8626.         return true\\\\\\\
  8627.     end\\\\\\\
  8628.     return false\\\\\\\
  8629. end\\\\\\\
  8630. \\\\\\\
  8631. function addLayer( x, y, w, h )\\\\\\\
  8632.     table.insert( layers, { x = x, y = y, w = w, h = h } )\\\\\\\
  8633.     if limit then\\\\\\\
  8634.         x, y, w, h = bbox( x, y, w, h, limit.x, limit.y, limit.w, limit.h )\\\\\\\
  8635.         if x then\\\\\\\
  8636.             limit = { x = x, y = y, w = w, h = h }\\\\\\\
  8637.         else\\\\\\\
  8638.             limit = false\\\\\\\
  8639.         end\\\\\\\
  8640.     end\\\\\\\
  8641.     return #layers\\\\\\\
  8642. end\\\\\\\
  8643. \\\\\\\
  8644. function closeLayer( layer )\\\\\\\
  8645.     while layers[layer] do\\\\\\\
  8646.         table.remove( layers, layer )\\\\\\\
  8647.     end\\\\\\\
  8648.     limit = { x = 1, y = 1, w = w, h = h }\\\\\\\
  8649.     for i, l in ipairs( layers ) do\\\\\\\
  8650.         local x, y, w, h = bbox( limit.x, limit.y, limit.w, limit.h, l.x, l.y, l.w, l.h )\\\\\\\
  8651.         if x then\\\\\\\
  8652.             limit = { x = x, y = y, w = w, h = h }\\\\\\\
  8653.         else\\\\\\\
  8654.             limit = false\\\\\\\
  8655.             break\\\\\\\
  8656.         end\\\\\\\
  8657.     end\\\\\\\
  8658. end\\\\\\\
  8659. \\\\\\\
  8660. function layerCount( )\\\\\\\
  8661.     return #layers\\\\\\\
  8662. end\\\\\\\
  8663. \\\\\\\
  8664. function getDrawArea( )\\\\\\\
  8665.     if limit then\\\\\\\
  8666.         return limit.x, limit.y, limit.w, limit.h\\\\\\\
  8667.     end\\\\\\\
  8668.     return 0, 0, 0, 0\\\\\\\
  8669. end\\\", meta={\\\
  8670.  type = \\\"lib\\\",\\\
  8671. }};[\\\"UIInput\\\"]={content=\\\"\\\\\\\
  8672. require \\\\\\\"UIElement\\\\\\\"\\\\\\\
  8673. \\\\\\\
  8674. UIInput:extends( UIElement )\\\\\\\
  8675. \\\\\\\
  8676. UIInput.handlesKeys = true\\\\\\\
  8677. UIInput.handlesScroll = true\\\\\\\
  8678. UIInput.tabIndex =  true\\\\\\\
  8679. \\\\\\\
  8680. UIInput.bc = colours.grey\\\\\\\
  8681. UIInput.fbc = colours.lightGrey\\\\\\\
  8682. UIInput.tc = colours.black\\\\\\\
  8683. UIInput.hbc = colours.blue\\\\\\\
  8684. UIInput.htc = colours.white\\\\\\\
  8685. \\\\\\\
  8686. UIInput.public \\\\\\\"bc\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
  8687. UIInput.public \\\\\\\"fbc\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
  8688. UIInput.public \\\\\\\"tc\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
  8689. UIInput.public \\\\\\\"hbc\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
  8690. UIInput.public \\\\\\\"htc\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
  8691. \\\\\\\
  8692. UIInput.public \\\\\\\"onEnter\\\\\\\" \\\\\\\"function\\\\\\\"\\\\\\\
  8693. UIInput.public \\\\\\\"onCtrlKey\\\\\\\" \\\\\\\"function\\\\\\\"\\\\\\\
  8694. UIInput.public \\\\\\\"onChange\\\\\\\" \\\\\\\"function\\\\\\\"\\\\\\\
  8695. \\\\\\\
  8696. UIInput.public \\\\\\\"text\\\\\\\" \\\\\\\"string\\\\\\\"\\\\\\\
  8697. UIInput.public \\\\\\\"mask\\\\\\\" \\\\\\\"string\\\\\\\"\\\\\\\
  8698. \\\\\\\
  8699. UIInput.public \\\\\\\"scroll\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
  8700. \\\\\\\
  8701. UIInput.public \\\\\\\"mask\\\\\\\"\\\\\\\
  8702. function UIInput.public.mask:write( value )\\\\\\\
  8703.     if not value or type( value ) == \\\\\\\"string\\\\\\\" then\\\\\\\
  8704.         self.mask = value\\\\\\\
  8705.     else\\\\\\\
  8706.         error( \\\\\\\"expected string mask\\\\\\\", 3 )\\\\\\\
  8707.     end\\\\\\\
  8708. end\\\\\\\
  8709. \\\\\\\
  8710. function UIInput:UIInput( x, y, w, h, mask )\\\\\\\
  8711.     self:UIElement( x, y, w, h )\\\\\\\
  8712.     self.mask = mask\\\\\\\
  8713.     self.cursor = 1\\\\\\\
  8714.     self.scroll = 0\\\\\\\
  8715.     self.text = \\\\\\\"\\\\\\\"\\\\\\\
  8716.     self.focussed = false\\\\\\\
  8717.     self.selection = false\\\\\\\
  8718.     return self.public\\\\\\\
  8719. end\\\\\\\
  8720. \\\\\\\
  8721. function UIInput.public:draw( x, y )\\\\\\\
  8722.     if self.cursor > #self.text + 1 then\\\\\\\
  8723.         self.cursor = #self.text + 1\\\\\\\
  8724.     end\\\\\\\
  8725.     local layer = stencil.addLayer( x, y, self.w, self.h )\\\\\\\
  8726.     local bc = self.bc\\\\\\\
  8727.     if self.focussed then\\\\\\\
  8728.         bc = self.fbc\\\\\\\
  8729.     end\\\\\\\
  8730.     local text = self.text\\\\\\\
  8731.     if self.mask then\\\\\\\
  8732.         text = text:gsub( \\\\\\\".\\\\\\\", self.mask:sub( 1, 1 ) )\\\\\\\
  8733.     end\\\\\\\
  8734.     if self.selection then\\\\\\\
  8735.         text = text .. string.rep( \\\\\\\" \\\\\\\", math.max( self.w - #text, 0 ) )\\\\\\\
  8736.         for i = 1, #text do\\\\\\\
  8737.             local bc = self.bc\\\\\\\
  8738.             local tc = self.tc\\\\\\\
  8739.             if i >= math.min( self.selection, self.cursor ) and i <= math.max( self.selection, self.cursor ) then\\\\\\\
  8740.                 bc = self.hbc\\\\\\\
  8741.                 tc = self.htc\\\\\\\
  8742.             elseif self.focussed then\\\\\\\
  8743.                 bc = self.fbc\\\\\\\
  8744.             end\\\\\\\
  8745.             stencil.pixel( x + i - self.scroll - 1, y, bc, tc, text:sub( i, i ) )\\\\\\\
  8746.         end\\\\\\\
  8747.     else\\\\\\\
  8748.         text = text:sub( self.scroll + 1 )\\\\\\\
  8749.         text = text:sub( 1, self.w )\\\\\\\
  8750.         text = text .. string.rep( \\\\\\\" \\\\\\\", self.w - #text )\\\\\\\
  8751.         stencil.textLine( x, y, bc, self.tc, text )\\\\\\\
  8752.         if self.focussed then\\\\\\\
  8753.             stencil.setCursorBlink( x + self.cursor - 1 - self.scroll, y, self.tc )\\\\\\\
  8754.         end\\\\\\\
  8755.     end\\\\\\\
  8756.     local c = self.public:getChildren( )\\\\\\\
  8757.     for i, child in ipairs( c ) do\\\\\\\
  8758.         child:draw( x + child.x - 1 + self.cx, y + child.y - 1 + self.cy )\\\\\\\
  8759.     end\\\\\\\
  8760.     stencil.closeLayer( layer )\\\\\\\
  8761. end\\\\\\\
  8762. \\\\\\\
  8763. function UIInput:setCursorPos( n )\\\\\\\
  8764.     self.cursor = math.max( math.min( n, #self.text + 1 ), 1 )\\\\\\\
  8765.     if self.cursor > self.scroll + self.w then\\\\\\\
  8766.         self.scroll = self.cursor - self.w\\\\\\\
  8767.     end\\\\\\\
  8768.     if self.cursor - 1 <= self.scroll then\\\\\\\
  8769.         self.scroll = math.max( self.cursor - 1, 1 ) - 1\\\\\\\
  8770.     end\\\\\\\
  8771. end\\\\\\\
  8772. \\\\\\\
  8773. function UIInput:write( str )\\\\\\\
  8774.     self.text = self.text:sub( 1, self.cursor - 1 ) .. str:gsub( \\\\\\\"\\\\\\\\n\\\\\\\", \\\\\\\" \\\\\\\" ) .. self.text:sub( self.cursor )\\\\\\\
  8775.     self:setCursorPos( self.cursor + #str )\\\\\\\
  8776. end\\\\\\\
  8777. \\\\\\\
  8778. function UIInput:setSelection( text )\\\\\\\
  8779.     self.text = self.text:sub( 1, math.min( self.selection, self.cursor ) - 1 ) .. self.text:sub( math.max( self.selection, self.cursor ) + 1 )\\\\\\\
  8780.     self.cursor = math.min( self.selection, self.cursor )\\\\\\\
  8781.     self:write( text )\\\\\\\
  8782. end\\\\\\\
  8783. \\\\\\\
  8784. function UIInput:getSelection( )\\\\\\\
  8785.     return self.text:sub( math.min( self.selection, self.cursor ), math.max( self.selection, self.cursor ) )\\\\\\\
  8786. end\\\\\\\
  8787. \\\\\\\
  8788. function UIInput.public:select( min, max )\\\\\\\
  8789.     self.selection = min\\\\\\\
  8790.     self.cursor = max\\\\\\\
  8791. end\\\\\\\
  8792. \\\\\\\
  8793. function UIInput.public:onMouseClick( rx, ry, button )\\\\\\\
  8794.     self.selection = false\\\\\\\
  8795.     self:setCursorPos( rx + self.scroll )\\\\\\\
  8796. end\\\\\\\
  8797. \\\\\\\
  8798. function UIInput.public:onMouseDrag( rx, ry )\\\\\\\
  8799.     if not self.selection then\\\\\\\
  8800.         self.selection = self.cursor\\\\\\\
  8801.     end\\\\\\\
  8802.     self:setCursorPos( rx + self.scroll )\\\\\\\
  8803. end\\\\\\\
  8804. \\\\\\\
  8805. function UIInput.public:onMouseScroll( rx, ry, dir )\\\\\\\
  8806.     self.scroll = math.max( math.min( self.scroll + dir, #self.text - self.w + 1 ), 0 )\\\\\\\
  8807. end\\\\\\\
  8808. \\\\\\\
  8809. function UIInput.public:onKeyPress( key, lastkey )\\\\\\\
  8810.     if not self.focussed then\\\\\\\
  8811.         return\\\\\\\
  8812.     end\\\\\\\
  8813.     if lastkey == 29 then\\\\\\\
  8814.         if ( ( key == keys.c or key == keys.x ) and not self.mask ) or key == keys.b then\\\\\\\
  8815.             if key == keys.c then\\\\\\\
  8816.                 if self.selection then\\\\\\\
  8817.                     clipboard.set( \\\\\\\"plaintext\\\\\\\", self:getSelection( ) )\\\\\\\
  8818.                 else\\\\\\\
  8819.                     clipboard.set( \\\\\\\"plaintext\\\\\\\", self.text )\\\\\\\
  8820.                 end\\\\\\\
  8821.                 if self.onChange then self.onChange( self.public, self.text ) end\\\\\\\
  8822.             elseif key == keys.x then\\\\\\\
  8823.                 if self.selection then\\\\\\\
  8824.                     clipboard.set( \\\\\\\"plaintext\\\\\\\", self:getSelection( ) )\\\\\\\
  8825.                     self:setSelection \\\\\\\"\\\\\\\"\\\\\\\
  8826.                     self.selection = false\\\\\\\
  8827.                 else\\\\\\\
  8828.                     clipboard.set( \\\\\\\"plaintext\\\\\\\", self.text )\\\\\\\
  8829.                     self.text = \\\\\\\"\\\\\\\"\\\\\\\
  8830.                 end\\\\\\\
  8831.                 if self.onChange then self.onChange( self.public, self.text ) end\\\\\\\
  8832.             elseif key == keys.b then\\\\\\\
  8833.                 local mode, data = clipboard.get( )\\\\\\\
  8834.                 if mode == \\\\\\\"plaintext\\\\\\\" then\\\\\\\
  8835.                     if self.selection then\\\\\\\
  8836.                         self:setSelection( data )\\\\\\\
  8837.                         self.selection = false\\\\\\\
  8838.                     else\\\\\\\
  8839.                         self:write( data )\\\\\\\
  8840.                     end\\\\\\\
  8841.                     if self.onChange then self.onChange( self.public, self.text ) end\\\\\\\
  8842.                 elseif mode == \\\\\\\"file\\\\\\\" then\\\\\\\
  8843.                     if self.selection then\\\\\\\
  8844.                         self:setSelection( \\\\\\\"file:\\\\\\\\\\\\\\\"\\\\\\\" .. data.name .. \\\\\\\"\\\\\\\\\\\\\\\"\\\\\\\" )\\\\\\\
  8845.                         self.selection = false\\\\\\\
  8846.                     else\\\\\\\
  8847.                         self:write( \\\\\\\"file:\\\\\\\\\\\\\\\"\\\\\\\" .. data.name .. \\\\\\\"\\\\\\\\\\\\\\\"\\\\\\\" )\\\\\\\
  8848.                     end\\\\\\\
  8849.                     if self.onChange then self.onChange( self.public, self.text ) end\\\\\\\
  8850.                 elseif self.onCtrlKey then\\\\\\\
  8851.                     self.onCtrlKey( self.public, key )\\\\\\\
  8852.                 end\\\\\\\
  8853.             end\\\\\\\
  8854.             return\\\\\\\
  8855.         end\\\\\\\
  8856.         if self.onCtrlKey then\\\\\\\
  8857.             self.onCtrlKey( self.public, key )\\\\\\\
  8858.         end\\\\\\\
  8859.         return\\\\\\\
  8860.     end\\\\\\\
  8861.     if key == keys.enter then\\\\\\\
  8862.         if type( self.onEnter ) == \\\\\\\"function\\\\\\\" then\\\\\\\
  8863.             self.onEnter( self.public )\\\\\\\
  8864.         end\\\\\\\
  8865.         local handler = self.public:getHandler( )\\\\\\\
  8866.         if handler and handler.focus == self.public then\\\\\\\
  8867.             handler:unFocus( )\\\\\\\
  8868.         end\\\\\\\
  8869.     elseif key == keys.left then\\\\\\\
  8870.         if self.selection then\\\\\\\
  8871.             self:setCursorPos( math.min( self.selection, self.cursor ) - 1 )\\\\\\\
  8872.             self.selection = false\\\\\\\
  8873.         elseif self.cursor > 1 then\\\\\\\
  8874.             self:setCursorPos( self.cursor - 1 )\\\\\\\
  8875.         end\\\\\\\
  8876.     elseif key == keys.right then\\\\\\\
  8877.         if self.selection then\\\\\\\
  8878.             self:setCursorPos( math.max( self.selection, self.cursor ) + 1 )\\\\\\\
  8879.             self.selection = false\\\\\\\
  8880.         else\\\\\\\
  8881.             self:setCursorPos( self.cursor + 1 )\\\\\\\
  8882.         end\\\\\\\
  8883.     elseif key == keys.backspace then\\\\\\\
  8884.         if self.selection then\\\\\\\
  8885.             self:setSelection \\\\\\\"\\\\\\\"\\\\\\\
  8886.             self.selection = false\\\\\\\
  8887.             if self.onChange then self.onChange( self.public, self.text ) end\\\\\\\
  8888.         elseif self.cursor > 1 then\\\\\\\
  8889.             self.text = self.text:sub( 1, self.cursor - 2 ) .. self.text:sub( self.cursor )\\\\\\\
  8890.             self:setCursorPos( self.cursor - 1 )\\\\\\\
  8891.             if self.onChange then self.onChange( self.public, self.text ) end\\\\\\\
  8892.         end\\\\\\\
  8893.     elseif key == keys.delete then\\\\\\\
  8894.         if self.selection then\\\\\\\
  8895.             self:setSelection \\\\\\\"\\\\\\\"\\\\\\\
  8896.             self.selection = false\\\\\\\
  8897.         else\\\\\\\
  8898.             self.text = self.text:sub( 1, self.cursor - 1 ) .. self.text:sub( self.cursor + 1 )\\\\\\\
  8899.         end\\\\\\\
  8900.         if self.onChange then self.onChange( self.public, self.text ) end\\\\\\\
  8901.     end\\\\\\\
  8902. end\\\\\\\
  8903. \\\\\\\
  8904. function UIInput.public:onTextInput( text, lastkey )\\\\\\\
  8905.     if not self.focussed then\\\\\\\
  8906.         return\\\\\\\
  8907.     end\\\\\\\
  8908.     if self.selection then\\\\\\\
  8909.         self:setSelection( text )\\\\\\\
  8910.         self.selection = false\\\\\\\
  8911.     else\\\\\\\
  8912.         self.text = self.text:sub( 1, self.cursor - 1 ) .. text .. self.text:sub( self.cursor )\\\\\\\
  8913.         self:setCursorPos( self.cursor + 1 )\\\\\\\
  8914.     end\\\\\\\
  8915.     if self.onChange then self.onChange( self.public, self.text ) end\\\\\\\
  8916. end\\\\\\\
  8917. \\\\\\\
  8918. function UIInput.public:onFocus( )\\\\\\\
  8919.     self.focussed = true\\\\\\\
  8920.     self.handlesKeys = true\\\\\\\
  8921.     if type( self.whenFocussed ) == \\\\\\\"function\\\\\\\" then\\\\\\\
  8922.         self.whenFocussed( self.public )\\\\\\\
  8923.     end\\\\\\\
  8924. end\\\\\\\
  8925. \\\\\\\
  8926. function UIInput.public:onUnFocus( )\\\\\\\
  8927.     self.focussed = false\\\\\\\
  8928.     self.handlesKeys = false\\\\\\\
  8929.     self.selection = false\\\\\\\
  8930.     if type( self.whenUnFocussed ) == \\\\\\\"function\\\\\\\" then\\\\\\\
  8931.         self.whenUnFocussed( self.public )\\\\\\\
  8932.     end\\\\\\\
  8933. end\\\\\\\
  8934. \\\\\\\
  8935. function UIInput.public:focusOn( )\\\\\\\
  8936.     local handler = self.public:getHandler( )\\\\\\\
  8937.     if handler then\\\\\\\
  8938.         handler:setFocus( self.public )\\\\\\\
  8939.     end\\\\\\\
  8940. end\\\", meta={\\\
  8941.  type = \\\"class\\\",\\\
  8942. }};[\\\"buffer\\\"]={content=\\\"\\\\\\\
  8943. local screen = { }\\\\\\\
  8944. local last = { }\\\\\\\
  8945. local w, h = term.getSize( )\\\\\\\
  8946. local bgc = colours.white\\\\\\\
  8947. local cb\\\\\\\
  8948. \\\\\\\
  8949. for y = 1, h do\\\\\\\
  8950.     screen[y] = { }\\\\\\\
  8951.     last[y] = { }\\\\\\\
  8952.     for x = 1, w do\\\\\\\
  8953.         screen[y][x] = { bc = 1, tc = 32768, char = \\\\\\\" \\\\\\\" }\\\\\\\
  8954.         last[y][x] = { }\\\\\\\
  8955.     end\\\\\\\
  8956. end\\\\\\\
  8957. \\\\\\\
  8958. function setPixel( x, y, bc, tc, char )\\\\\\\
  8959.     if screen[y] and screen[y][x] then\\\\\\\
  8960.         if bc ~= 0 then\\\\\\\
  8961.             if type( bc ) == \\\\\\\"function\\\\\\\" then\\\\\\\
  8962.                 bc = bc( screen[y][x].bc )\\\\\\\
  8963.             end\\\\\\\
  8964.             screen[y][x].bc = bc\\\\\\\
  8965.         end\\\\\\\
  8966.         if tc == 0 then\\\\\\\
  8967.             char = \\\\\\\"\\\\\\\"\\\\\\\
  8968.         else\\\\\\\
  8969.             if type( tc ) == \\\\\\\"function\\\\\\\" then\\\\\\\
  8970.                 tc = tc( screen[y][x].tc )\\\\\\\
  8971.             end\\\\\\\
  8972.             screen[y][x].tc = tc\\\\\\\
  8973.         end\\\\\\\
  8974.         if char ~= \\\\\\\"\\\\\\\" then\\\\\\\
  8975.             if type( char ) == \\\\\\\"function\\\\\\\" then\\\\\\\
  8976.                 char = char( screen[y][x].char )\\\\\\\
  8977.             end\\\\\\\
  8978.             screen[y][x].char = char\\\\\\\
  8979.         end\\\\\\\
  8980.         return true\\\\\\\
  8981.     end\\\\\\\
  8982.     return false\\\\\\\
  8983. end\\\\\\\
  8984. \\\\\\\
  8985. function getPixel( x, y )\\\\\\\
  8986.     if last[y] and last[y][x] then\\\\\\\
  8987.         return last[y][x].bc, last[y][x].tc, last[y][x].char\\\\\\\
  8988.     end\\\\\\\
  8989.     return false\\\\\\\
  8990. end\\\\\\\
  8991. \\\\\\\
  8992. function hasChanged( x, y )\\\\\\\
  8993.     if screen[y] and screen[y][x] then\\\\\\\
  8994.         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\\\\\\\
  8995.     end\\\\\\\
  8996.     return false\\\\\\\
  8997. end\\\\\\\
  8998. \\\\\\\
  8999. function setCursorBlink( x, y, col )\\\\\\\
  9000.     cb = { x = x, y = y, col = col }\\\\\\\
  9001. end\\\\\\\
  9002. \\\\\\\
  9003. function drawChanges( )\\\\\\\
  9004.     local lasts = last\\\\\\\
  9005.     for y = 1, h do\\\\\\\
  9006.         local last\\\\\\\
  9007.         for x = 1, w do\\\\\\\
  9008.             if hasChanged( x, y ) then\\\\\\\
  9009.                 if last then\\\\\\\
  9010.                     if last.bc == screen[y][x].bc and ( last.tc == screen[y][x].tc or screen[y][x].char == \\\\\\\" \\\\\\\" ) then\\\\\\\
  9011.                         last.char = last.char .. screen[y][x].char\\\\\\\
  9012.                     else\\\\\\\
  9013.                         term.setCursorPos( last.x, last.y )\\\\\\\
  9014.                         term.setBackgroundColour( last.bc )\\\\\\\
  9015.                         term.setTextColour( last.tc )\\\\\\\
  9016.                         term.write( last.char )\\\\\\\
  9017.                         last = { x = x, y = y, bc = screen[y][x].bc, tc = screen[y][x].tc, char = screen[y][x].char }\\\\\\\
  9018.                     end\\\\\\\
  9019.                 else\\\\\\\
  9020.                     last = { x = x, y = y, bc = screen[y][x].bc, tc = screen[y][x].tc, char = screen[y][x].char }\\\\\\\
  9021.                 end\\\\\\\
  9022.                 lasts[y][x] = { bc = screen[y][x].bc, tc = screen[y][x].tc, char = screen[y][x].char }\\\\\\\
  9023.             elseif last then\\\\\\\
  9024.                 term.setCursorPos( last.x, last.y )\\\\\\\
  9025.                 term.setBackgroundColour( last.bc )\\\\\\\
  9026.                 term.setTextColour( last.tc )\\\\\\\
  9027.                 term.write( last.char )\\\\\\\
  9028.                 last = nil\\\\\\\
  9029.             end\\\\\\\
  9030.         end\\\\\\\
  9031.         if last then\\\\\\\
  9032.             term.setCursorPos( last.x, last.y )\\\\\\\
  9033.             term.setBackgroundColour( last.bc )\\\\\\\
  9034.             term.setTextColour( last.tc )\\\\\\\
  9035.             term.write( last.char )\\\\\\\
  9036.         end\\\\\\\
  9037.     end\\\\\\\
  9038.     if cb then\\\\\\\
  9039.         if cb.col then\\\\\\\
  9040.             term.setTextColour( cb.col )\\\\\\\
  9041.         end\\\\\\\
  9042.         term.setCursorPos( cb.x, cb.y )\\\\\\\
  9043.         term.setCursorBlink( true )\\\\\\\
  9044.     else\\\\\\\
  9045.         term.setCursorBlink( false )\\\\\\\
  9046.     end\\\\\\\
  9047. end\\\\\\\
  9048. \\\\\\\
  9049. function clear( )\\\\\\\
  9050.     for x = 1, w do\\\\\\\
  9051.         for y = 1, h do\\\\\\\
  9052.             screen[y][x].bc = bgc\\\\\\\
  9053.             screen[y][x].char = \\\\\\\" \\\\\\\"\\\\\\\
  9054.         end\\\\\\\
  9055.     end\\\\\\\
  9056.     cb = false\\\\\\\
  9057. end\\\\\\\
  9058. \\\\\\\
  9059. function setBackgroundColour( col )\\\\\\\
  9060.     bgc = col\\\\\\\
  9061. end\\\\\\\
  9062. \\\\\\\
  9063. function reset( )\\\\\\\
  9064.     for y = 1, h do\\\\\\\
  9065.         last[y] = { }\\\\\\\
  9066.         for x = 1, w do\\\\\\\
  9067.             last[y][x] = { }\\\\\\\
  9068.         end\\\\\\\
  9069.     end\\\\\\\
  9070. end\\\", meta={\\\
  9071.  type = \\\"lib\\\",\\\
  9072. }};[\\\"UIKeyHandler\\\"]={content=\\\"\\\\\\\
  9073. require \\\\\\\"UIElement\\\\\\\"\\\\\\\
  9074. UIKeyHandler:extends( UIElement )\\\\\\\
  9075. \\\\\\\
  9076. UIKeyHandler.handlesMouse = false\\\\\\\
  9077. UIKeyHandler.handlesKeys = true\\\\\\\
  9078. \\\\\\\
  9079. UIKeyHandler.onKey = false\\\\\\\
  9080. UIKeyHandler.public \\\\\\\"onKey\\\\\\\" \\\\\\\"function\\\\\\\"\\\\\\\
  9081. UIKeyHandler.onChar = false\\\\\\\
  9082. UIKeyHandler.public \\\\\\\"onChar\\\\\\\" \\\\\\\"function\\\\\\\"\\\\\\\
  9083. \\\\\\\
  9084. function UIKeyHandler:UIKeyHandler( )\\\\\\\
  9085.     self:UIElement( 0, 0, 0, 0 )\\\\\\\
  9086.     return self.public\\\\\\\
  9087. end\\\\\\\
  9088. \\\\\\\
  9089. function UIKeyHandler.public:onKeyPress( key, lastkey )\\\\\\\
  9090.     if self.onKey then\\\\\\\
  9091.         self.onKey( self.public, key, lastkey )\\\\\\\
  9092.     end\\\\\\\
  9093. end\\\\\\\
  9094. function UIKeyHandler.public:onTextInput( char, lastkey )\\\\\\\
  9095.     if self.onChar then\\\\\\\
  9096.         self.onChar( self.public, char, lastkey )\\\\\\\
  9097.     end\\\\\\\
  9098. end\\\", meta={\\\
  9099.  type = \\\"class\\\",\\\
  9100. }};[\\\"UITextbox\\\"]={content=\\\"\\\\\\\
  9101. local function linewrap( str, w )\\\\\\\
  9102.     for i = 1, w + 1 do\\\\\\\
  9103.         if str:sub( i, i ) == \\\\\\\"\\\\\\\\n\\\\\\\" then\\\\\\\
  9104.             return str:sub( 1, i - 1 ), str:sub( i + 1 )\\\\\\\
  9105.         end\\\\\\\
  9106.     end\\\\\\\
  9107.     if #str <= w then\\\\\\\
  9108.         return str, \\\\\\\"\\\\\\\"\\\\\\\
  9109.     end\\\\\\\
  9110.     if str:sub( w + 1, w + 1 ):find \\\\\\\"%s\\\\\\\" then\\\\\\\
  9111.         return str:sub( 1, w ), str:sub( w + 1 ):gsub( \\\\\\\"^%s+\\\\\\\", \\\\\\\"\\\\\\\" )\\\\\\\
  9112.     end\\\\\\\
  9113.     for i = w, 1, -1 do\\\\\\\
  9114.         if str:sub( i, i ):find \\\\\\\"%s\\\\\\\" then\\\\\\\
  9115.             return str:sub( 1, i ), str:sub( i + 1 )\\\\\\\
  9116.         end\\\\\\\
  9117.     end\\\\\\\
  9118.     return str:sub( 1, w ), str:sub( w + 1 )\\\\\\\
  9119. end\\\\\\\
  9120. \\\\\\\
  9121. local function wordwrap( str, w, h )\\\\\\\
  9122.     local lines = { }\\\\\\\
  9123.     local line\\\\\\\
  9124.     while #str > 0 do\\\\\\\
  9125.         line, str = linewrap( str, w )\\\\\\\
  9126.         table.insert( lines, line )\\\\\\\
  9127.     end\\\\\\\
  9128.     if h then\\\\\\\
  9129.         while #lines > h do\\\\\\\
  9130.             lines[#lines] = nil\\\\\\\
  9131.         end\\\\\\\
  9132.     end\\\\\\\
  9133.     return lines\\\\\\\
  9134. end\\\\\\\
  9135. \\\\\\\
  9136. require \\\\\\\"UIElement\\\\\\\"\\\\\\\
  9137. UITextbox:extends( UIElement )\\\\\\\
  9138. \\\\\\\
  9139. UITextbox.handlesKeys = true\\\\\\\
  9140. UITextbox.tabIndex =  true\\\\\\\
  9141. \\\\\\\
  9142. UITextbox.public \\\\\\\"text\\\\\\\"\\\\\\\
  9143. \\\\\\\
  9144. UITextbox.public \\\\\\\"onChange\\\\\\\" \\\\\\\"function\\\\\\\"\\\\\\\
  9145. function UITextbox.public.text:write( value )\\\\\\\
  9146.     local t = tostring( value )\\\\\\\
  9147.     t = t:sub( 1, self.w * self.h )\\\\\\\
  9148.     while true do\\\\\\\
  9149.         local lines = wordwrap( t, self.w )\\\\\\\
  9150.         if #lines <= self.h then\\\\\\\
  9151.             break\\\\\\\
  9152.         end\\\\\\\
  9153.         t = t:sub( 1, -2 )\\\\\\\
  9154.     end\\\\\\\
  9155.     self.text = t\\\\\\\
  9156. end\\\\\\\
  9157. \\\\\\\
  9158. UITextbox.bc = colours.lightGrey\\\\\\\
  9159. UITextbox.fbc = colours.white\\\\\\\
  9160. UITextbox.tc = colours.black\\\\\\\
  9161. \\\\\\\
  9162. UITextbox.public \\\\\\\"bc\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
  9163. UITextbox.public \\\\\\\"fbc\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
  9164. UITextbox.public \\\\\\\"tc\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
  9165. \\\\\\\
  9166. UITextbox.onEnter = false\\\\\\\
  9167. UITextbox.public \\\\\\\"onEnter\\\\\\\" \\\\\\\"function\\\\\\\"\\\\\\\
  9168. \\\\\\\
  9169. function UITextbox:UITextbox( x, y, w, h, text, session )\\\\\\\
  9170.     self:UIElement( x, y, w, h )\\\\\\\
  9171. \\\\\\\
  9172.     self.public.text = text\\\\\\\
  9173.     self.cursor = cursor\\\\\\\
  9174. \\\\\\\
  9175.     return self.public\\\\\\\
  9176. end\\\\\\\
  9177. \\\\\\\
  9178. function UITextbox:getCursorXY( )\\\\\\\
  9179.     local lines = wordwrap( self.text, self.w, self.h )\\\\\\\
  9180.     local c = self.cursor\\\\\\\
  9181.     for i = 1, #lines do\\\\\\\
  9182.         if #lines[i] <= c then\\\\\\\
  9183.             c = c - #lines[i]\\\\\\\
  9184.         else\\\\\\\
  9185.             return c, i\\\\\\\
  9186.         end\\\\\\\
  9187.     end\\\\\\\
  9188.     if #lines == 0 then\\\\\\\
  9189.         return 1, 1\\\\\\\
  9190.     elseif self.text:sub( -1, -1 ) == \\\\\\\"\\\\\\\\n\\\\\\\" then\\\\\\\
  9191.         return 1, #lines + 1\\\\\\\
  9192.     else\\\\\\\
  9193.         return #lines[#lines] + 1, #lines\\\\\\\
  9194.     end\\\\\\\
  9195. end\\\\\\\
  9196. \\\\\\\
  9197. function UITextbox:getCursorPos( x, y )\\\\\\\
  9198.     local lines = wordwrap( self.text, self.w, self.h )\\\\\\\
  9199.     if lines[y] then\\\\\\\
  9200.         local pos = 0\\\\\\\
  9201.         for i = 1, y - 1 do\\\\\\\
  9202.             pos = pos + #lines[i]\\\\\\\
  9203.         end\\\\\\\
  9204.         if #lines[y] >= x then\\\\\\\
  9205.             pos = pos + x\\\\\\\
  9206.         else\\\\\\\
  9207.             pos = pos + #lines[y] + 1\\\\\\\
  9208.         end\\\\\\\
  9209.         return pos\\\\\\\
  9210.     else\\\\\\\
  9211.         return #self.text + 1\\\\\\\
  9212.     end\\\\\\\
  9213. end\\\\\\\
  9214. \\\\\\\
  9215. function UITextbox.public:draw( x, y )\\\\\\\
  9216.     local layer = stencil.addLayer( x, y, self.w, self.h )\\\\\\\
  9217.     local text = tostring( self.text )\\\\\\\
  9218.     local bc = self.bc\\\\\\\
  9219.     if self.focussed then\\\\\\\
  9220.         bc = self.fbc\\\\\\\
  9221.     end\\\\\\\
  9222.     stencil.text( x, y, self.w, self.h, bc, self.tc, self.text )\\\\\\\
  9223.     if self.focussed then\\\\\\\
  9224.         local cx, cy = self:getCursorXY( )\\\\\\\
  9225.         stencil.setCursorBlink( x + cx - 1, y + cy - 1 )\\\\\\\
  9226.     end\\\\\\\
  9227.     local c = self.public:getChildren( )\\\\\\\
  9228.     for i, child in ipairs( c ) do\\\\\\\
  9229.         child:draw( x + child.x - 1 + self.cx, y + child.y - 1 + self.cy )\\\\\\\
  9230.     end\\\\\\\
  9231.     stencil.closeLayer( layer )\\\\\\\
  9232. end\\\\\\\
  9233. \\\\\\\
  9234. function UITextbox.public:onMouseClick( rx, ry, button )\\\\\\\
  9235.     local pos = self:getCursorPos( rx, ry )\\\\\\\
  9236.     self.cursor = pos\\\\\\\
  9237. end\\\\\\\
  9238. \\\\\\\
  9239. function UITextbox.public:onKeyPress( key, lastkey )\\\\\\\
  9240.     if not self.focussed then\\\\\\\
  9241.         return\\\\\\\
  9242.     end\\\\\\\
  9243.     if key == keys.enter then\\\\\\\
  9244.         local c = self.cursor\\\\\\\
  9245.         local t = self.text\\\\\\\
  9246.         self.text = self.text:sub( 1, self.cursor - 1 ) .. \\\\\\\"\\\\\\\\n\\\\\\\" .. self.text:sub( self.cursor )\\\\\\\
  9247.         self.cursor = self.cursor + 1\\\\\\\
  9248.         if type( self.onChange ) == \\\\\\\"function\\\\\\\" then\\\\\\\
  9249.             self.onChange( self.public )\\\\\\\
  9250.         end\\\\\\\
  9251.         local lines = wordwrap( self.text, self.w )\\\\\\\
  9252.         if #lines > self.h then\\\\\\\
  9253.             self.text = t\\\\\\\
  9254.             self.cursor = c\\\\\\\
  9255.         end\\\\\\\
  9256.     elseif key == keys.left and self.cursor > 1 then\\\\\\\
  9257.         self.cursor = self.cursor - 1\\\\\\\
  9258.     elseif key == keys.right and self.cursor <= #self.text then\\\\\\\
  9259.         self.cursor = self.cursor + 1\\\\\\\
  9260.     elseif key == keys.backspace and self.cursor > 1 then\\\\\\\
  9261.         self.text = self.text:sub( 1, self.cursor - 2 ) .. self.text:sub( self.cursor )\\\\\\\
  9262.         self.cursor = self.cursor - 1\\\\\\\
  9263.     elseif key == keys.delete then\\\\\\\
  9264.         self.text = self.text:sub( 1, self.cursor - 1 ) .. self.text:sub( self.cursor + 1 )\\\\\\\
  9265.     end\\\\\\\
  9266. end\\\\\\\
  9267. \\\\\\\
  9268. function UITextbox.public:onTextInput( text, lastkey )\\\\\\\
  9269.     if not self.focussed then\\\\\\\
  9270.         return\\\\\\\
  9271.     end\\\\\\\
  9272.     if lastkey ~= keys.leftCtrl then\\\\\\\
  9273.         local c = self.cursor\\\\\\\
  9274.         local t = self.text\\\\\\\
  9275.         self.text = self.text:sub( 1, self.cursor - 1 ) .. text .. self.text:sub( self.cursor )\\\\\\\
  9276.         self.cursor = self.cursor + 1\\\\\\\
  9277.         if type( self.onChange ) == \\\\\\\"function\\\\\\\" then\\\\\\\
  9278.             self.onChange( self.public )\\\\\\\
  9279.         end\\\\\\\
  9280.         local lines = wordwrap( self.text, self.w )\\\\\\\
  9281.         if #lines > self.h then\\\\\\\
  9282.             self.text = t\\\\\\\
  9283.             self.cursor = c\\\\\\\
  9284.         end\\\\\\\
  9285.     end\\\\\\\
  9286. end\\\\\\\
  9287. \\\\\\\
  9288. function UITextbox.public:onFocus( )\\\\\\\
  9289.     self.focussed = true\\\\\\\
  9290.     self.handlesKeys = true\\\\\\\
  9291.     if type( self.whenFocussed ) == \\\\\\\"function\\\\\\\" then\\\\\\\
  9292.         self.whenFocussed( self.public )\\\\\\\
  9293.     end\\\\\\\
  9294. end\\\\\\\
  9295. \\\\\\\
  9296. function UITextbox.public:onUnFocus( )\\\\\\\
  9297.     self.focussed = false\\\\\\\
  9298.     self.handlesKeys = false\\\\\\\
  9299.     if type( self.whenUnFocussed ) == \\\\\\\"function\\\\\\\" then\\\\\\\
  9300.         self.whenUnFocussed( self.public )\\\\\\\
  9301.     end\\\\\\\
  9302. end\\\\\\\
  9303. \\\\\\\
  9304. function UITextbox.public:focusOn( )\\\\\\\
  9305.     local handler = self.public:getHandler( )\\\\\\\
  9306.     handler:setFocus( self.public )\\\\\\\
  9307. end\\\", meta={\\\
  9308.  type = \\\"class\\\",\\\
  9309. }};[\\\"UIMenu\\\"]={content=\\\"\\\\\\\
  9310. require \\\\\\\"UIElement\\\\\\\"\\\\\\\
  9311. \\\\\\\
  9312. UIMenu:extends( UIElement )\\\\\\\
  9313. \\\\\\\
  9314. UIMenu.handlesMouse = false\\\\\\\
  9315. UIMenu.handlesScroll = true\\\\\\\
  9316. UIMenu.isScrollTarget = true\\\\\\\
  9317. UIMenu.scrollDirection = \\\\\\\"vertical\\\\\\\"\\\\\\\
  9318. UIMenu.public \\\\\\\"padding\\\\\\\" \\\\\\\"number\\\\\\\"\\\\\\\
  9319. \\\\\\\
  9320. function UIMenu:UIMenu( x, y, w, h, direction )\\\\\\\
  9321.     self:UIElement( x, y, w, h )\\\\\\\
  9322.     self.scrollDirection = direction or \\\\\\\"vertical\\\\\\\"\\\\\\\
  9323.     self.padding = 0\\\\\\\
  9324.     return self.public\\\\\\\
  9325. end\\\\\\\
  9326. \\\\\\\
  9327. function UIMenu.public:update( dt )\\\\\\\
  9328.     local max = self.scrollDirection == \\\\\\\"vertical\\\\\\\" and self.h or self.w\\\\\\\
  9329.     if self.scrollDirection == \\\\\\\"vertical\\\\\\\" then\\\\\\\
  9330.         for i = 1, #self.children do\\\\\\\
  9331.             max = math.max( max, self.children[i].y + self.children[i].h - 1 )\\\\\\\
  9332.         end\\\\\\\
  9333.     else\\\\\\\
  9334.         for i = 1, #self.children do\\\\\\\
  9335.             max = math.max( max, self.children[i].x + self.children[i].w - 1 )\\\\\\\
  9336.         end\\\\\\\
  9337.     end\\\\\\\
  9338.     if self.scrollDirection == \\\\\\\"vertical\\\\\\\" then\\\\\\\
  9339.         if max - self.h < -self.cy then\\\\\\\
  9340.             self.cy = -max + self.h\\\\\\\
  9341.         end\\\\\\\
  9342.     else\\\\\\\
  9343.         if max - self.w < -self.cx then\\\\\\\
  9344.             self.cx = -max + self.w\\\\\\\
  9345.         end\\\\\\\
  9346.     end\\\\\\\
  9347.     if max == ( self.scrollDirection == \\\\\\\"vertical\\\\\\\" and self.h or self.w ) then\\\\\\\
  9348.         self.handlesScroll = false\\\\\\\
  9349.     else\\\\\\\
  9350.         self.handlesScroll = true\\\\\\\
  9351.     end\\\\\\\
  9352.     local c = self.public:getChildren( )\\\\\\\
  9353.     for i, child in ipairs( c ) do\\\\\\\
  9354.         child:update( dt )\\\\\\\
  9355.     end\\\\\\\
  9356.     local x, y = 1, 1\\\\\\\
  9357.     for i, child in ipairs( self.children ) do\\\\\\\
  9358.         child.x = x\\\\\\\
  9359.         child.y = y\\\\\\\
  9360.         if self.scrollDirection == \\\\\\\"vertical\\\\\\\" then\\\\\\\
  9361.             child.w = self.w\\\\\\\
  9362.             y = y + child.h + self.padding\\\\\\\
  9363.         else\\\\\\\
  9364.             child.h = self.h\\\\\\\
  9365.             x = x + child.w + self.padding\\\\\\\
  9366.         end\\\\\\\
  9367.     end\\\\\\\
  9368. end\\\\\\\
  9369. \\\\\\\
  9370. function UIMenu.public:onMouseScroll( rx, ry, dir )\\\\\\\
  9371.     if self.scrollDirection == \\\\\\\"vertical\\\\\\\" then\\\\\\\
  9372.         self.cy = self.cy - dir\\\\\\\
  9373.         local max = 0\\\\\\\
  9374.         for i = 1, #self.children do\\\\\\\
  9375.             max = math.max( max, self.children[i].y + self.children[i].h )\\\\\\\
  9376.         end\\\\\\\
  9377.         max = max - self.h - 1\\\\\\\
  9378.         if self.cy < -max then\\\\\\\
  9379.             self.cy = -max\\\\\\\
  9380.         end\\\\\\\
  9381.         if self.cy > 0 then self.cy = 0 end\\\\\\\
  9382.     else\\\\\\\
  9383.         self.cx = self.cx - dir\\\\\\\
  9384.         local max = 0\\\\\\\
  9385.         for i = 1, #self.children do\\\\\\\
  9386.             max = math.max( max, self.children[i].x + self.children[i].w )\\\\\\\
  9387.         end\\\\\\\
  9388.         max = max - self.w - 1\\\\\\\
  9389.         if self.cx < -max then\\\\\\\
  9390.             self.cx = -max\\\\\\\
  9391.         end\\\\\\\
  9392.         if self.cx > 0 then self.cx = 0 end\\\\\\\
  9393.     end\\\\\\\
  9394. end\\\\\\\
  9395. \\\\\\\
  9396. function UIMenu.public:getDisplaySizeH( )\\\\\\\
  9397.     return self.w\\\\\\\
  9398. end\\\\\\\
  9399. function UIMenu.public:getDisplaySizeV( )\\\\\\\
  9400.     return self.h\\\\\\\
  9401. end\\\\\\\
  9402. \\\\\\\
  9403. function UIMenu.public:getContentSizeH( )\\\\\\\
  9404.     local max = self.w\\\\\\\
  9405.     for i = 1, #self.children do\\\\\\\
  9406.         max = math.max( max, self.children[i].x + self.children[i].w - 1 )\\\\\\\
  9407.     end\\\\\\\
  9408.     return max\\\\\\\
  9409. end\\\\\\\
  9410. function UIMenu.public:getContentSizeV( )\\\\\\\
  9411.     local max = self.h\\\\\\\
  9412.     for i = 1, #self.children do\\\\\\\
  9413.         max = math.max( max, self.children[i].y + self.children[i].h - 1 )\\\\\\\
  9414.     end\\\\\\\
  9415.     return max\\\\\\\
  9416. end\\\\\\\
  9417. \\\\\\\
  9418. function UIMenu.public:getContentScrollH( )\\\\\\\
  9419.     return -self.cx\\\\\\\
  9420. end\\\\\\\
  9421. function UIMenu.public:getContentScrollV( )\\\\\\\
  9422.     return -self.cy\\\\\\\
  9423. end\\\\\\\
  9424. \\\\\\\
  9425. function UIMenu.public:setContentScrollH( scroll )\\\\\\\
  9426.     self.cx = -scroll\\\\\\\
  9427. end\\\\\\\
  9428. function UIMenu.public:setContentScrollV( scroll )\\\\\\\
  9429.     self.cy = -scroll\\\\\\\
  9430. end\\\", meta={\\\
  9431.  type = \\\"class\\\",\\\
  9432. }};}\\\
  9433. --/end of files\\\
  9434. local autorun = {\\\"UIButton\\\";\\\"UIImage\\\";\\\"UIElement\\\";\\\"UIHandler\\\";\\\"UIScrollBar\\\";\\\"UIBuffer\\\";\\\"UICode\\\";\\\"Image\\\";\\\"UIFrame\\\";\\\"UIText\\\";\\\"UIInput\\\";\\\"UIKeyHandler\\\";\\\"UITextbox\\\";\\\"UIMenu\\\";\\\"main\\\"}\\\
  9435. --@meta[type]:\\\"class\\\"\\\
  9436. \\\
  9437. -- Swift Class Library\\\
  9438. -- Made by awsumben13\\\
  9439. -- Feel free to use this in any of your projects but keep this at the top and give credit where necessary\\\
  9440. \\\
  9441. --[[\\\
  9442.     This class library offers:\\\
  9443.         Inheritance \\\"Class:extends( other class )\\\"\\\
  9444.         Initialiser methods \\\"function ClassName:ClassName( )\\\"\\\
  9445.         Public / private variables with customisable read/write access \\\"Class.public.x.write = false\\\"\\\
  9446.         Static variables ( accessed from the class object ) \\\"Class.static.x = 5\\\"\\\
  9447.         Custom metamethods including __index and __newindex \\\"Class.meta.index = { }\\\"\\\
  9448. ]]\\\
  9449. \\\
  9450. local class = { }\\\
  9451. \\\
  9452. function class.new( name )\\\
  9453. \\\
  9454.     local object = { }\\\
  9455.     local public = { }\\\
  9456. \\\
  9457.     object.public = { }\\\
  9458.     object.private = { }\\\
  9459.     object.static = { }\\\
  9460.     object.name = name\\\
  9461.     object.extends = false\\\
  9462.     object.class = public\\\
  9463. \\\
  9464.     public.name = name\\\
  9465. \\\
  9466.     local customindex = false\\\
  9467.     local customnewindex = false\\\
  9468.     local objectmeta = { }\\\
  9469. \\\
  9470.     function public:new( ... )\\\
  9471. \\\
  9472.         local ob = { }\\\
  9473.         local pb = { }\\\
  9474. \\\
  9475.         ob.class = public\\\
  9476.         ob.public = pb\\\
  9477. \\\
  9478.         setmetatable( ob, {\\\
  9479.             __index = object.private;\\\
  9480.         } )\\\
  9481. \\\
  9482.         function pb:type( full )\\\
  9483.             return ob.class:type( full )\\\
  9484.         end\\\
  9485. \\\
  9486.         function pb:typeOf( class )\\\
  9487.             return ob.class:typeOf( class )\\\
  9488.         end\\\
  9489. \\\
  9490.         local obmeta = { }\\\
  9491.         obmeta.__index = function( _, k )\\\
  9492.             if object.public[k] then\\\
  9493.                 if object.public[k].read then\\\
  9494.                     local val\\\
  9495.                     if customindex then\\\
  9496.                         if type( customindex ) == \\\"function\\\" then\\\
  9497.                             return customindex( ob, k )\\\
  9498.                         else\\\
  9499.                             return customindex[k]\\\
  9500.                         end\\\
  9501.                     elseif type( object.public[k].read ) == \\\"function\\\" then\\\
  9502.                         val = object.public[k].read( ob )\\\
  9503.                     elseif object.public[k].value ~= nil then\\\
  9504.                         val = object.public[k].value\\\
  9505.                     else\\\
  9506.                         val = ob[k]\\\
  9507.                     end\\\
  9508.                     if type( val ) == \\\"function\\\" then\\\
  9509.                         return function( self, ... )\\\
  9510.                             if self == pb then\\\
  9511.                                 return val( ob, ... )\\\
  9512.                             end\\\
  9513.                             return val( self, ... )\\\
  9514.                         end\\\
  9515.                     end\\\
  9516.                     return val\\\
  9517.                 else\\\
  9518.                     error( \\\"variable has no read access\\\", 2 )\\\
  9519.                 end\\\
  9520.             elseif customindex then\\\
  9521.                 if type( customindex ) == \\\"function\\\" then\\\
  9522.                     return customindex( ob, k )\\\
  9523.                 else\\\
  9524.                     return customindex[k]\\\
  9525.                 end\\\
  9526.             else\\\
  9527.                 error( \\\"no such variable \\\\\\\"\\\" .. tostring( k ) .. \\\"\\\\\\\"\\\", 2 )\\\
  9528.             end\\\
  9529.         end;\\\
  9530.         obmeta.__newindex = function( _, k, v )\\\
  9531.             if object.public[k] then\\\
  9532.                 if object.public[k].write then\\\
  9533.                     if customnewindex then\\\
  9534.                         if type( customnewindex ) == \\\"function\\\" then\\\
  9535.                             return customnewindex( ob, k, v )\\\
  9536.                         else\\\
  9537.                             customnewindex[k] = v\\\
  9538.                         end\\\
  9539.                     elseif type( object.public[k].write ) == \\\"function\\\" then\\\
  9540.                         object.public[k].write( ob, v )\\\
  9541.                     else\\\
  9542.                         ob[k] = v\\\
  9543.                     end\\\
  9544.                 else\\\
  9545.                     error( \\\"variable has no write access\\\", 2 )\\\
  9546.                 end\\\
  9547.             else\\\
  9548.                 error( \\\"no such variable \\\\\\\"\\\" .. tostring( k ) .. \\\"\\\\\\\"\\\", 2 )\\\
  9549.             end\\\
  9550.         end;\\\
  9551.         obmeta.__tostring = function( )\\\
  9552.             return object.name\\\
  9553.         end;\\\
  9554.         obmeta.__metatable = \\\"SwiftClassObject\\\";\\\
  9555. \\\
  9556.         for k, v in pairs( objectmeta ) do\\\
  9557.             obmeta[\\\"__\\\" .. tostring( k )] = v\\\
  9558.         end\\\
  9559. \\\
  9560.         setmetatable( pb, obmeta )\\\
  9561. \\\
  9562.         local c = object\\\
  9563.         while true do\\\
  9564.             if type( c.private[c.name] ) == \\\"function\\\" then\\\
  9565.                 return c.private[c.name]( ob, ... )\\\
  9566.             end\\\
  9567.             if c.extends then\\\
  9568.                 c = c.extends\\\
  9569.             else\\\
  9570.                 break\\\
  9571.             end\\\
  9572.         end\\\
  9573. \\\
  9574.         return pb\\\
  9575.     end\\\
  9576. \\\
  9577.     function public:type( full )\\\
  9578.         local str = \\\"\\\"\\\
  9579.         if full then\\\
  9580.             local c = object.extends\\\
  9581.             while c do\\\
  9582.                 str = c.name .. \\\".\\\" .. str\\\
  9583.                 c = c.extends\\\
  9584.             end\\\
  9585.         end\\\
  9586.         return str .. object.name\\\
  9587.     end\\\
  9588. \\\
  9589.     function public:typeOf( other )\\\
  9590.         if type( other ) == \\\"table\\\" then\\\
  9591.             if getmetatable( other ) == \\\"SwiftClass\\\" then\\\
  9592.                 local ob = object\\\
  9593.                 while ob do\\\
  9594.                     if ob.class == other then\\\
  9595.                         return true\\\
  9596.                     end\\\
  9597.                     ob = ob.extends\\\
  9598.                 end\\\
  9599.             end\\\
  9600.         end\\\
  9601.         return false\\\
  9602.     end\\\
  9603. \\\
  9604.     function public:extends( ob )\\\
  9605.         ob:extend( object )\\\
  9606.     end\\\
  9607. \\\
  9608.     function public:extend( ob )\\\
  9609.         setmetatable( ob.static, { __index = object.static } )\\\
  9610.         setmetatable( ob.public, { __index = object.public } )\\\
  9611.         setmetatable( ob.private, { __index = object.private } )\\\
  9612.         ob.extends = object\\\
  9613.     end\\\
  9614. \\\
  9615.     local meta = { }\\\
  9616.     meta.__index = function( _, k )\\\
  9617.         if k == \\\"static\\\" then\\\
  9618.             return setmetatable( { }, {\\\
  9619.                 __newindex = function( _, k, v )\\\
  9620.                     object.static[k] = v\\\
  9621.                 end;\\\
  9622.                 __metatable = { };\\\
  9623.             } )\\\
  9624.         elseif k == \\\"public\\\" then\\\
  9625.             return setmetatable( { }, {\\\
  9626.                 __newindex = function( _, k, v )\\\
  9627.                     object.public[k] = {\\\
  9628.                         read = true;\\\
  9629.                         write = false;\\\
  9630.                         value = v;\\\
  9631.                     }\\\
  9632.                 end;\\\
  9633.                 __call = function( _, k )\\\
  9634.                     object.public[k] = {\\\
  9635.                         read = true;\\\
  9636.                         write = true;\\\
  9637.                         value = nil;\\\
  9638.                     }\\\
  9639.                     return function( _type )\\\
  9640.                         local types = { _type }\\\
  9641.                         object.public[k].write = function( ob, value )\\\
  9642.                             for i = 1, #types do\\\
  9643.                                 if class.typeOf( value, types[i] ) then\\\
  9644.                                     ob[k] = value\\\
  9645.                                     return\\\
  9646.                                 end\\\
  9647.                             end\\\
  9648.                             if class.type( types[1] ) == \\\"Class\\\" then\\\
  9649.                                 error( \\\"expected \\\" .. types[1]:type( ) .. \\\" \\\" .. k, 3 )\\\
  9650.                             else\\\
  9651.                                 error( \\\"expected \\\" .. types[1] .. \\\" \\\" .. k, 3 )\\\
  9652.                             end\\\
  9653.                         end\\\
  9654.                         return function( _type )\\\
  9655.                             table.insert( types, _type )\\\
  9656.                         end\\\
  9657.                     end\\\
  9658.                 end;\\\
  9659.                 __index = function( _, k )\\\
  9660.                     if object.public[k] then\\\
  9661.                         return setmetatable( { }, {\\\
  9662.                             __newindex = function( _, name, v )\\\
  9663.                                 if name == \\\"read\\\" then\\\
  9664.                                     if type( v ) == \\\"boolean\\\" or type( v ) == \\\"function\\\" then\\\
  9665.                                         object.public[k].read = v\\\
  9666.                                     else\\\
  9667.                                         error( \\\"invalid modifier value\\\", 2 )\\\
  9668.                                     end\\\
  9669.                                 elseif name == \\\"write\\\" then\\\
  9670.                                     if type( v ) == \\\"boolean\\\" or type( v ) == \\\"function\\\" then\\\
  9671.                                         object.public[k].write = v\\\
  9672.                                     else\\\
  9673.                                         error( \\\"invalid modifier value\\\", 2 )\\\
  9674.                                     end\\\
  9675.                                 else\\\
  9676.                                     error( \\\"invalid modifier name\\\", 2 )\\\
  9677.                                 end\\\
  9678.                             end;\\\
  9679.                             __metatable = { };\\\
  9680.                         } )\\\
  9681.                     else\\\
  9682.                         error( \\\"public index \\\" .. tostring( k ) .. \\\" not found\\\", 2 )\\\
  9683.                     end\\\
  9684.                 end;\\\
  9685.                 __metatable = { };\\\
  9686.             } )\\\
  9687.         elseif k == \\\"meta\\\" then\\\
  9688.             return setmetatable( { }, {\\\
  9689.                 __index = function( _, k )\\\
  9690.                     if k == \\\"index\\\" then\\\
  9691.                         return customindex\\\
  9692.                     elseif k == \\\"newindex\\\" then\\\
  9693.                         return customnewindex\\\
  9694.                     else\\\
  9695.                         return objectmeta[k]\\\
  9696.                     end\\\
  9697.                 end;\\\
  9698.                 __newindex = function( _, k, v )\\\
  9699.                     if k == \\\"metatable\\\" then\\\
  9700.                         error( \\\"cannot change this metamethod\\\", 2 )\\\
  9701.                     elseif k == \\\"index\\\" then\\\
  9702.                         if type( v ) == \\\"function\\\" or type( v ) == \\\"table\\\" or v == nil then\\\
  9703.                             customindex = v\\\
  9704.                         else\\\
  9705.                             error( \\\"cannot use type \\\" .. type( v ) .. \\\" for index metamethod\\\", 2 )\\\
  9706.                         end\\\
  9707.                     elseif k == \\\"newindex\\\" then\\\
  9708.                         if type( v ) == \\\"function\\\" or type( v ) == \\\"table\\\" or v == nil then\\\
  9709.                             customnewindex = v\\\
  9710.                         else\\\
  9711.                             error( \\\"cannot use type \\\" .. type( v ) .. \\\" for newindex metamethod\\\", 2 )\\\
  9712.                         end\\\
  9713.                     else\\\
  9714.                         objectmeta[k] = v\\\
  9715.                     end\\\
  9716.                 end;\\\
  9717.                 __metatable = { };\\\
  9718.             } )\\\
  9719.         else\\\
  9720.             return object.static[k]\\\
  9721.         end\\\
  9722.     end\\\
  9723.     meta.__newindex = function( _, k, v )\\\
  9724.         object.private[k] = v\\\
  9725.     end;\\\
  9726.     meta.__call = function( self, ... )\\\
  9727.         return self:new( ... )\\\
  9728.     end;\\\
  9729.     meta.__totring = function( )\\\
  9730.         return \\\"Class\\\"\\\
  9731.     end;\\\
  9732.     meta.__metatable = \\\"SwiftClass\\\"\\\
  9733. \\\
  9734.     setmetatable( public, meta )\\\
  9735. \\\
  9736.     return public\\\
  9737. end\\\
  9738. \\\
  9739. function class.public( name )\\\
  9740.     local object = class.new( name )\\\
  9741.     getfenv( )[name] = object\\\
  9742. end\\\
  9743. \\\
  9744. function class.type( object )\\\
  9745.     if type( object ) == \\\"table\\\" then\\\
  9746.         if getmetatable( object ) == \\\"SwiftClass\\\" then\\\
  9747.             return \\\"Class\\\"\\\
  9748.         end\\\
  9749.         if getmetatable( object ) == \\\"SwiftClassObject\\\" then\\\
  9750.             return object:type( )\\\
  9751.         end\\\
  9752.     end\\\
  9753.     return type( object )\\\
  9754. end\\\
  9755. \\\
  9756. function class.typeOf( object, ... )\\\
  9757.     local types = { ... }\\\
  9758.     for i = 1, #types do\\\
  9759.         if type( object ) == \\\"table\\\" and getmetatable( object ) == \\\"SwiftClassObject\\\" then\\\
  9760.             if object:typeOf( types[i] ) then\\\
  9761.                 return types[i]\\\
  9762.             end\\\
  9763.         elseif type( object ) == \\\"table\\\" and getmetatable( object ) == \\\"SwiftClass\\\" then\\\
  9764.             if types[i] == \\\"Class\\\" then\\\
  9765.                 return \\\"Class\\\"\\\
  9766.             end\\\
  9767.         else\\\
  9768.             if type( object ) == types[i] then\\\
  9769.                 return types[i]\\\
  9770.             end\\\
  9771.         end\\\
  9772.     end\\\
  9773.     return false\\\
  9774. end\\\
  9775. \\\
  9776. setmetatable( class, { __call = function( _, ... ) return class.new( ... ) end } )\\\
  9777. local args = { ... }\\\
  9778. local global = getfenv( )\\\
  9779. local custom = setmetatable( { class = class, ARGS = args }, { __index = global } )\\\
  9780. function custom.export( variable, value )\\\
  9781.     if custom[variable] ~= nil or value then\\\
  9782.         global[variable] = custom[variable] == nil and value or custom[variable]\\\
  9783.     end\\\
  9784. end\\\
  9785. local required = { }\\\
  9786. local function require( file, ... )\\\
  9787.     if not required[file] and files[file] then\\\
  9788.         required[file] = true\\\
  9789.         if files[file].meta.type == \\\"class\\\" then\\\
  9790.             custom[file] = class( file )\\\
  9791.         end\\\
  9792.         local fenv = setmetatable( { shared = custom }, { __index = custom } )\\\
  9793.         local f, err = loadstring( files[file].content, file .. \\\".lua\\\" )\\\
  9794.         if f then\\\
  9795.             setfenv( f, fenv )\\\
  9796.             local ok, data = pcall( f )\\\
  9797.             if ok then\\\
  9798.                 if files[file].meta.type == \\\"lib\\\" then\\\
  9799.                     if data ~= nil then\\\
  9800.                         custom[file] = data\\\
  9801.                         return data\\\
  9802.                     else\\\
  9803.                         local t = { }\\\
  9804.                         for k, v in pairs( fenv ) do\\\
  9805.                             t[k] = v\\\
  9806.                         end\\\
  9807.                         custom[file] = t\\\
  9808.                         return t\\\
  9809.                     end\\\
  9810.                 end\\\
  9811.                 return data\\\
  9812.             else\\\
  9813.                 error( data, 0 )\\\
  9814.             end\\\
  9815.         else\\\
  9816.             error( err, 0 )\\\
  9817.         end\\\
  9818.     end\\\
  9819. end\\\
  9820. custom.require = require\\\
  9821. \\\
  9822. for i = 1, #autorun do\\\
  9823.     require( autorun[i] )\\\
  9824. end\",\
  9825. [\"NovaNet.lua\"]=\"--[[type=\\\"executable_package\\\", name=\\\"NovaNet\\\"]]\\\
  9826. local files = {[\\\"handshake\\\"]={content=\\\"-- Handshake\\\\\\\
  9827. -- Used with permission from 1lann\\\\\\\
  9828. -- Compressed using Lua Minifier (https://mothereff.in/lua-minifier)\\\\\\\
  9829. 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={\\\
  9830.  type = \\\"lib\\\",\\\
  9831. }};[\\\"main\\\"]={content=\\\"\\\\\\\
  9832. require \\\\\\\"com\\\\\\\"\\\\\\\
  9833. require \\\\\\\"encryption\\\\\\\"\\\\\\\
  9834. require \\\\\\\"handshake\\\\\\\"\\\\\\\
  9835. require \\\\\\\"network\\\\\\\"\\\\\\\
  9836. \\\\\\\
  9837. for k, v in pairs( network ) do\\\\\\\
  9838.     export( k, v )\\\\\\\
  9839. end\\\\\\\
  9840. \\\\\\\
  9841. export \\\\\\\"com\\\\\\\"\\\", meta={}};[\\\"encryption\\\"]={content=\\\"\\\\\\\
  9842. local function sum( n, ... ) -- number n, number ...additions\\\\\\\
  9843.     local t = { ... }\\\\\\\
  9844.     for i = 1, #t do\\\\\\\
  9845.         n = n + t[i]\\\\\\\
  9846.     end\\\\\\\
  9847.     return n\\\\\\\
  9848. \\\\\\\
  9849.     -- number sum\\\\\\\
  9850. end\\\\\\\
  9851. \\\\\\\
  9852. local function loop( n, lim ) -- number n, number limit\\\\\\\
  9853.     while n > lim do\\\\\\\
  9854.         n = n - lim\\\\\\\
  9855.     end\\\\\\\
  9856.     while n < 1 do\\\\\\\
  9857.         n = n + lim\\\\\\\
  9858.     end\\\\\\\
  9859.     return n\\\\\\\
  9860.     -- number limited\\\\\\\
  9861. end\\\\\\\
  9862. \\\\\\\
  9863. local function shift( str, count )\\\\\\\
  9864.     local str = { str:byte( 1, #str ) }\\\\\\\
  9865.     for i = 1, #str do\\\\\\\
  9866.         str[i] = loop( str[i] + count, 255 )\\\\\\\
  9867.     end\\\\\\\
  9868.     for i = 1, #str do\\\\\\\
  9869.         str[i] = string.char( str[i] )\\\\\\\
  9870.     end\\\\\\\
  9871.     return table.concat( str, \\\\\\\"\\\\\\\" )\\\\\\\
  9872. end\\\\\\\
  9873. \\\\\\\
  9874. local function tohex( b ) -- string[4] bits\\\\\\\
  9875.     local n = 0\\\\\\\
  9876.     for i = 1, 4 do\\\\\\\
  9877.         n = n * 2\\\\\\\
  9878.         n = n + tonumber( b:sub( i, i ) )\\\\\\\
  9879.     end\\\\\\\
  9880.     if n >= 10 then\\\\\\\
  9881.         local hexes = { \\\\\\\"A\\\\\\\", \\\\\\\"B\\\\\\\", \\\\\\\"C\\\\\\\", \\\\\\\"D\\\\\\\", \\\\\\\"E\\\\\\\", \\\\\\\"F\\\\\\\" }\\\\\\\
  9882.         return hexes[n - 9]\\\\\\\
  9883.     end\\\\\\\
  9884.     return tostring( n )\\\\\\\
  9885. \\\\\\\
  9886.     -- string[1] hex\\\\\\\
  9887. end\\\\\\\
  9888. \\\\\\\
  9889. local function fromhex( h ) -- string[1] hex\\\\\\\
  9890.     local n = tonumber( h )\\\\\\\
  9891.     if not n then\\\\\\\
  9892.         local hexes = { [\\\\\\\"A\\\\\\\"] = 10, [\\\\\\\"B\\\\\\\"] = 11, [\\\\\\\"C\\\\\\\"] = 12, [\\\\\\\"D\\\\\\\"] = 13, [\\\\\\\"E\\\\\\\"] = 14, [\\\\\\\"F\\\\\\\"] = 15 }\\\\\\\
  9893.         n = hexes[h]\\\\\\\
  9894.     end\\\\\\\
  9895.     local str = \\\\\\\"\\\\\\\"\\\\\\\
  9896.     for i = 1, 4 do\\\\\\\
  9897.         str = str .. n % 2\\\\\\\
  9898.         n = math.floor( n / 2 )\\\\\\\
  9899.     end\\\\\\\
  9900.     return str:reverse( )\\\\\\\
  9901. \\\\\\\
  9902.     -- string[4] bits\\\\\\\
  9903. end\\\\\\\
  9904. \\\\\\\
  9905. local function xor( n1, n2 )\\\\\\\
  9906.     if n1 > 255 or n2 > 255 or n1 < 0 or n2 < 0 then\\\\\\\
  9907.         return error \\\\\\\"expected numbers between 0 and 255\\\\\\\"\\\\\\\
  9908.     end\\\\\\\
  9909.     local bit1, bit2 = { }, { }\\\\\\\
  9910.     for i = 1, 8 do\\\\\\\
  9911.         bit1[9-i] = n1 % 2 == 1\\\\\\\
  9912.         n1 = math.floor( n1 / 2 )\\\\\\\
  9913.     end\\\\\\\
  9914.     for i = 1, 8 do\\\\\\\
  9915.         bit2[9-i] = n2 % 2 == 1\\\\\\\
  9916.         n2 = math.floor( n2 / 2 )\\\\\\\
  9917.     end\\\\\\\
  9918.     local bits = { }\\\\\\\
  9919.     for i = 1, 8 do\\\\\\\
  9920.         bits[i] = ( bit1[i] and not bit2[i] ) or ( not bit1[i] and bit2[i] )\\\\\\\
  9921.     end\\\\\\\
  9922.     local n = 0\\\\\\\
  9923.     for i = 1, 8 do\\\\\\\
  9924.         n = n * 2\\\\\\\
  9925.         n = n + ( bits[i] and 1 or 0 )\\\\\\\
  9926.     end\\\\\\\
  9927.     return n\\\\\\\
  9928. end\\\\\\\
  9929. \\\\\\\
  9930. local function nand( n1, n2 )\\\\\\\
  9931.     if n1 > 255 or n2 > 255 or n1 < 0 or n2 < 0 then\\\\\\\
  9932.         return error \\\\\\\"expected numbers between 0 and 255\\\\\\\"\\\\\\\
  9933.     end\\\\\\\
  9934.     local bit1, bit2 = { }, { }\\\\\\\
  9935.     for i = 1, 8 do\\\\\\\
  9936.         bit1[9-i] = n1 % 2 == 1\\\\\\\
  9937.         n1 = math.floor( n1 / 2 )\\\\\\\
  9938.     end\\\\\\\
  9939.     for i = 1, 8 do\\\\\\\
  9940.         bit2[9-i] = n2 % 2 == 1\\\\\\\
  9941.         n2 = math.floor( n2 / 2 )\\\\\\\
  9942.     end\\\\\\\
  9943.     local bits = { }\\\\\\\
  9944.     for i = 1, 8 do\\\\\\\
  9945.         bits[i] = not bit1[i] == bit2[i]\\\\\\\
  9946.     end\\\\\\\
  9947.     local n = 0\\\\\\\
  9948.     for i = 1, 8 do\\\\\\\
  9949.         n = n * 2\\\\\\\
  9950.         n = n + ( bits[i] and 1 or 0 )\\\\\\\
  9951.     end\\\\\\\
  9952.     return n\\\\\\\
  9953. end\\\\\\\
  9954. \\\\\\\
  9955. local function tobits( n )\\\\\\\
  9956.     local str = \\\\\\\"\\\\\\\"\\\\\\\
  9957.     for i = 1, 8 do\\\\\\\
  9958.         str = str .. n % 2\\\\\\\
  9959.         n = math.floor( n / 2 )\\\\\\\
  9960.     end\\\\\\\
  9961.     return str:reverse( )\\\\\\\
  9962. end\\\\\\\
  9963. \\\\\\\
  9964. local function frombits( b )\\\\\\\
  9965.     local n = 0\\\\\\\
  9966.     for i = 1, 8 do\\\\\\\
  9967.         n = n * 2\\\\\\\
  9968.         n = n + tonumber( b:sub( i, i ) )\\\\\\\
  9969.     end\\\\\\\
  9970.     return n\\\\\\\
  9971. end\\\\\\\
  9972. \\\\\\\
  9973. local t = os.clock( )\\\\\\\
  9974. local function start( )\\\\\\\
  9975.     t = os.clock( )\\\\\\\
  9976. end\\\\\\\
  9977. local function yield( )\\\\\\\
  9978.     if os.clock( ) - t > .1 then\\\\\\\
  9979.         coroutine.yield( )\\\\\\\
  9980.         start( )\\\\\\\
  9981.     end\\\\\\\
  9982. end\\\\\\\
  9983. \\\\\\\
  9984. function encrypt( str, key ) -- string text, string key\\\\\\\
  9985.     local enc = \\\\\\\"\\\\\\\"\\\\\\\
  9986.     start( )\\\\\\\
  9987.     for i = 1, #str do\\\\\\\
  9988.         math.randomseed( sum( key:byte( 1, #key ) ) )\\\\\\\
  9989.         key = shift( key, math.random( 1, 100 ) )\\\\\\\
  9990.         local ki = loop( i, #key )\\\\\\\
  9991.         local a = str:sub( i, i ):byte( )\\\\\\\
  9992.         local b = key:sub( ki, ki ):byte( )\\\\\\\
  9993.         enc = enc .. tobits( xor( a, b ) )\\\\\\\
  9994.         yield( )\\\\\\\
  9995.     end\\\\\\\
  9996.     local enc2 = \\\\\\\"\\\\\\\"\\\\\\\
  9997.     for i = 1, #enc / 4 do\\\\\\\
  9998.         enc2 = enc2 .. tohex( enc:sub( i * 4 - 3, i * 4 ) )\\\\\\\
  9999.         yield( )\\\\\\\
  10000.     end\\\\\\\
  10001.     return enc2\\\\\\\
  10002. \\\\\\\
  10003.     -- string cipher\\\\\\\
  10004. end\\\\\\\
  10005. \\\\\\\
  10006. function decrypt( str, key ) -- string cipher, string key\\\\\\\
  10007.     start( )\\\\\\\
  10008.     local dec2 = \\\\\\\"\\\\\\\"\\\\\\\
  10009.     for i = 1, #str do\\\\\\\
  10010.         dec2 = dec2 .. fromhex( str:sub( i, i ) )\\\\\\\
  10011.         yield( )\\\\\\\
  10012.     end\\\\\\\
  10013.     str = dec2\\\\\\\
  10014.     local dec = \\\\\\\"\\\\\\\"\\\\\\\
  10015.     local keys = { }\\\\\\\
  10016.     for i = 1, #str / 8 do\\\\\\\
  10017.         math.randomseed( sum( key:byte( 1, #key ) ) )\\\\\\\
  10018.         keys[i] = shift( key, math.random( 1, 100 ) )\\\\\\\
  10019.         key = keys[i]\\\\\\\
  10020.         yield( )\\\\\\\
  10021.     end\\\\\\\
  10022.     for i = 1, #str / 8 do\\\\\\\
  10023.         local ki = loop( i, #key )\\\\\\\
  10024.         local a = frombits( str:sub( ( i - 1 ) * 8 + 1, i * 8 ) )\\\\\\\
  10025.         local b = string.byte( keys[i]:sub( ki, ki ) )\\\\\\\
  10026.         dec = dec .. string.char( nand( a, b ) )\\\\\\\
  10027.         yield( )\\\\\\\
  10028.     end\\\\\\\
  10029.     return dec\\\\\\\
  10030. \\\\\\\
  10031.     -- string text\\\\\\\
  10032. end\\\", meta={\\\
  10033.  type = \\\"lib\\\",\\\
  10034. }};[\\\"network\\\"]={content=\\\"\\\\\\\
  10035. require \\\\\\\"com\\\\\\\"\\\\\\\
  10036. require \\\\\\\"handshake\\\\\\\"\\\\\\\
  10037. \\\\\\\
  10038. local buffers = {\\\\\\\
  10039.     established = { }; -- .listen()\\\\\\\
  10040. }\\\\\\\
  10041. local channels = { }\\\\\\\
  10042. local opening = false\\\\\\\
  10043. local channelID = 0\\\\\\\
  10044. \\\\\\\
  10045. function genkey( seed )\\\\\\\
  10046.     math.randomseed( seed )\\\\\\\
  10047.     local s = \\\\\\\"\\\\\\\"\\\\\\\
  10048.     for i = 1, 64 do\\\\\\\
  10049.         s = s .. string.char( math.random( 0, 255 ) )\\\\\\\
  10050.     end\\\\\\\
  10051.     return s\\\\\\\
  10052. end\\\\\\\
  10053. \\\\\\\
  10054. function open( peer, protocol, insecure ) -- opens a secure channel with peer with [protocol]\\\\\\\
  10055.     if peer == os.getComputerID( ) then\\\\\\\
  10056.         local key = math.random( 1, 2^16 )\\\\\\\
  10057.         local t = { }\\\\\\\
  10058.         if insecure then\\\\\\\
  10059.             channels[t] = { peer = peer }\\\\\\\
  10060.         else\\\\\\\
  10061.             channels[t] = { peer = peer, key = genkey( key ), rawkey = key }\\\\\\\
  10062.         end\\\\\\\
  10063.         table.insert( buffers.established, { protocol = protocol, id = t } )\\\\\\\
  10064.         return t\\\\\\\
  10065.     end\\\\\\\
  10066.     local ok, err = com.send( peer, {\\\\\\\
  10067.         request = \\\\\\\"connection\\\\\\\";\\\\\\\
  10068.         protocol = type( protocol ) == \\\\\\\"string\\\\\\\" and protocol or \\\\\\\"none\\\\\\\";\\\\\\\
  10069.         handshake = handshake.generateInitiatorData( );\\\\\\\
  10070.         insecure = insecure\\\\\\\
  10071.     } )\\\\\\\
  10072.     if not ok then return false, err end\\\\\\\
  10073.     local ok, message = com.receive( peer, nil, 1 )\\\\\\\
  10074.     if ok then\\\\\\\
  10075.         local key = handshake.generateResponseData( message )\\\\\\\
  10076.         local t = { }\\\\\\\
  10077.         if insecure then\\\\\\\
  10078.             channels[t] = { peer = peer }\\\\\\\
  10079.         else\\\\\\\
  10080.             channels[t] = { peer = peer, key = genkey( key ), rawkey = key }\\\\\\\
  10081.         end\\\\\\\
  10082.         if insecure then\\\\\\\
  10083.             core.log( \\\\\\\"Network\\\\\\\", \\\\\\\"Insecure connection established with \\\\\\\" .. tostring( peer ) )\\\\\\\
  10084.         else\\\\\\\
  10085.             core.log( \\\\\\\"Network\\\\\\\", \\\\\\\"Secure connection established with \\\\\\\" .. tostring( peer ) )\\\\\\\
  10086.         end\\\\\\\
  10087.         return t\\\\\\\
  10088.     end\\\\\\\
  10089.     return false, message\\\\\\\
  10090. end\\\\\\\
  10091. \\\\\\\
  10092. function close( channel, reason ) -- closes an open channel with [reason]\\\\\\\
  10093.     if not channels[channel] then\\\\\\\
  10094.         return false, \\\\\\\"channel not open\\\\\\\"\\\\\\\
  10095.     end\\\\\\\
  10096.     local ok, data = com.send( channels[channel].peer, {\\\\\\\
  10097.         request = \\\\\\\"ConnectionClose\\\\\\\";\\\\\\\
  10098.         reason = type( reason ) == \\\\\\\"string\\\\\\\" and reason or \\\\\\\"unknown\\\\\\\";\\\\\\\
  10099.     }, channels[channel].key, channels[channel].rawkey )\\\\\\\
  10100.     if ok then\\\\\\\
  10101.         channels[channel] = nil\\\\\\\
  10102.     end\\\\\\\
  10103.     return ok, data\\\\\\\
  10104. end\\\\\\\
  10105. \\\\\\\
  10106. function send( channel, data ) -- sends a message over an open channel\\\\\\\
  10107.     if not channels[channel] then\\\\\\\
  10108.         return false, \\\\\\\"channel not open\\\\\\\"\\\\\\\
  10109.     end\\\\\\\
  10110.     return com.send( channels[channel].peer, data, channels[channel].key, channels[channel].rawkey )\\\\\\\
  10111. end\\\\\\\
  10112. \\\\\\\
  10113. function receive( channel, timeout ) -- waits for messages on an open channel for [timeout] seconds\\\\\\\
  10114.     if not channels[channel] then\\\\\\\
  10115.         return false, \\\\\\\"channel not open\\\\\\\"\\\\\\\
  10116.     end\\\\\\\
  10117.     local ok, data = com.receive( channels[channel].peer, channels[channel].key, timeout, channels[channel].rawkey )\\\\\\\
  10118.     if not ok then\\\\\\\
  10119.         return false, data\\\\\\\
  10120.     end\\\\\\\
  10121.     if type( data ) == \\\\\\\"table\\\\\\\" and data.request == \\\\\\\"ConnectionClose\\\\\\\" then\\\\\\\
  10122.         return false, \\\\\\\"closed\\\\\\\", data.reason\\\\\\\
  10123.     end\\\\\\\
  10124.     return true, data\\\\\\\
  10125. end\\\\\\\
  10126. \\\\\\\
  10127. function listen( protocol, timeout ) -- waits for a channel to be opened with [protocol] for [timeout] seconds\\\\\\\
  10128.     local time = os.clock( )\\\\\\\
  10129.     while not timeout or os.clock( ) - time <= timeout do\\\\\\\
  10130.         for i = #buffers.established, 1, -1 do\\\\\\\
  10131.             local channel = buffers.established[i]\\\\\\\
  10132.             if channel.protocol == protocol then\\\\\\\
  10133.                 table.remove( buffers.established, i )\\\\\\\
  10134.                 return channel.id\\\\\\\
  10135.             end\\\\\\\
  10136.         end\\\\\\\
  10137.         coroutine.yield( )\\\\\\\
  10138.     end\\\\\\\
  10139.     return false\\\\\\\
  10140. end\\\\\\\
  10141. \\\\\\\
  10142. -- internal functions\\\\\\\
  10143. \\\\\\\
  10144. function addToBuffer( buffer, data )\\\\\\\
  10145.     buffers[buffer] = buffers[buffer] or { }\\\\\\\
  10146.     table.insert( buffers[buffer], data )\\\\\\\
  10147. end\\\\\\\
  10148. \\\\\\\
  10149. function addChannel( peer, protocol, key, rawkey )\\\\\\\
  10150.     local t = { }\\\\\\\
  10151.     channels[t] = {\\\\\\\
  10152.         peer = peer;\\\\\\\
  10153.         key = key;\\\\\\\
  10154.         rawkey = rawkey;\\\\\\\
  10155.     }\\\\\\\
  10156.     addToBuffer( \\\\\\\"established\\\\\\\", { protocol = protocol, id = t } )\\\\\\\
  10157. end\\\\\\\
  10158. \\\\\\\
  10159. function clearBuffers( )\\\\\\\
  10160.     for key in pairs( buffers ) do\\\\\\\
  10161.         buffers[key] = { }\\\\\\\
  10162.     end\\\\\\\
  10163. end\\\", meta={\\\
  10164.  type = \\\"lib\\\",\\\
  10165. }};[\\\"com\\\"]={content=\\\"\\\\\\\
  10166. require \\\\\\\"encryption\\\\\\\"\\\\\\\
  10167. require \\\\\\\"network\\\\\\\"\\\\\\\
  10168. \\\\\\\
  10169. local modem\\\\\\\
  10170. local mside\\\\\\\
  10171. local messages = { }\\\\\\\
  10172. \\\\\\\
  10173. function setModem( side )\\\\\\\
  10174.     if mside ~= side then\\\\\\\
  10175.         modem = peripheral.wrap( side )\\\\\\\
  10176.         modem.open( 2514 ) -- messages\\\\\\\
  10177.         modem.open( 2515 ) -- pings\\\\\\\
  10178.         mside = side\\\\\\\
  10179.     end\\\\\\\
  10180. end\\\\\\\
  10181. \\\\\\\
  10182. function updateModems( )\\\\\\\
  10183.     for _, side in pairs( peripheral.getNames( ) ) do\\\\\\\
  10184.         if peripheral.getType( side ) == \\\\\\\"modem\\\\\\\" then\\\\\\\
  10185.             setModem( side )\\\\\\\
  10186.             return true\\\\\\\
  10187.         end\\\\\\\
  10188.     end\\\\\\\
  10189. end\\\\\\\
  10190. \\\\\\\
  10191. function send( id, data, key, seed )\\\\\\\
  10192.     local channel = \\\\\\\"public\\\\\\\"\\\\\\\
  10193.     if key then\\\\\\\
  10194.         data = encryption.encrypt( textutils.serialize( data ), key )\\\\\\\
  10195.         math.randomseed( seed )\\\\\\\
  10196.         channel = math.random( 1, 32768 )\\\\\\\
  10197.     end\\\\\\\
  10198.     local t = {\\\\\\\
  10199.         target = id;\\\\\\\
  10200.         sender = os.getComputerID( );\\\\\\\
  10201.         data = data;\\\\\\\
  10202.         id = os.time( ) .. \\\\\\\":\\\\\\\" .. os.getComputerID( );\\\\\\\
  10203.         channel = channel;\\\\\\\
  10204.     }\\\\\\\
  10205.     if id == os.getComputerID( ) then\\\\\\\
  10206.         os.queueEvent( \\\\\\\"modem_message\\\\\\\", \\\\\\\"top\\\\\\\", 2514, 2514, t, 0 )\\\\\\\
  10207.         messages[t.id] = os.clock( )\\\\\\\
  10208.         return true\\\\\\\
  10209.     end\\\\\\\
  10210.     if not modem then\\\\\\\
  10211.         if not updateModems( ) then\\\\\\\
  10212.             return false, \\\\\\\"no modem\\\\\\\"\\\\\\\
  10213.         end\\\\\\\
  10214.     end\\\\\\\
  10215.     modem.transmit( 2514, 2514, t )\\\\\\\
  10216.     messages[t.id] = os.clock( )\\\\\\\
  10217.     return true\\\\\\\
  10218. end\\\\\\\
  10219. \\\\\\\
  10220. function receive( id, key, timeout, seed )\\\\\\\
  10221.     if not modem then\\\\\\\
  10222.         updateModems( )\\\\\\\
  10223.     end\\\\\\\
  10224.     local timer = timeout and os.startTimer( timeout )\\\\\\\
  10225.     while true do\\\\\\\
  10226.         local ev = { coroutine.yield( ) }\\\\\\\
  10227.         if ev[1] == \\\\\\\"timer\\\\\\\" and ev[2] == timer then\\\\\\\
  10228.             return false, \\\\\\\"timeout\\\\\\\"\\\\\\\
  10229.         end\\\\\\\
  10230.         if ev[1] == \\\\\\\"modem_message\\\\\\\" and ev[3] == 2514 and ev[4] == 2514 and type( ev[5] ) == \\\\\\\"table\\\\\\\" then -- a normal message\\\\\\\
  10231.             if ev[5].target == os.getComputerID( ) then\\\\\\\
  10232.                 if id == ev[5].sender then\\\\\\\
  10233.                     local data = ev[5].data\\\\\\\
  10234.                     if key then\\\\\\\
  10235.                         data = textutils.unserialize( encryption.decrypt( data, key ) )\\\\\\\
  10236.                         math.randomseed( seed )\\\\\\\
  10237.                         local channel = math.random( 1, 32768 )\\\\\\\
  10238.                         if data.channel == channel then\\\\\\\
  10239.                             return true, data\\\\\\\
  10240.                         end\\\\\\\
  10241.                     else\\\\\\\
  10242.                         return true, data\\\\\\\
  10243.                     end\\\\\\\
  10244.                 end\\\\\\\
  10245.             elseif not messages[ev[5].id] then\\\\\\\
  10246.                 modem.transmit( 2514, 2514, ev[5] )\\\\\\\
  10247.             end\\\\\\\
  10248.             messages[ev[5].id] = os.clock( )\\\\\\\
  10249.         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\\\\\\\
  10250.             local mode, id = ev[5]:match \\\\\\\":(.-):\\\\\\\", ev[5]:match \\\\\\\":.-:(%d+)\\\\\\\"\\\\\\\
  10251.             if mode and id then\\\\\\\
  10252.                 if not messages[ev[5]] and tonumber( id ) ~= os.getComputerID( ) then\\\\\\\
  10253.                     if mode == \\\\\\\"Request\\\\\\\" then\\\\\\\
  10254.                         modem.transmit( 2515, 2515, \\\\\\\"NovaNetworkPing:Response:\\\\\\\" .. id .. \\\\\\\":\\\\\\\" .. os.getComputerID( ) )\\\\\\\
  10255.                     else\\\\\\\
  10256.                         modem.transmit( 2515, 2515, ev[5] )\\\\\\\
  10257.                     end\\\\\\\
  10258.                     messages[ev[5]] = os.clock( )\\\\\\\
  10259.                 end\\\\\\\
  10260.             end\\\\\\\
  10261.         end\\\\\\\
  10262.     end\\\\\\\
  10263. end\\\\\\\
  10264. \\\\\\\
  10265. function ping( )\\\\\\\
  10266.     if not modem then\\\\\\\
  10267.         if not updateModems( ) then\\\\\\\
  10268.             return false, \\\\\\\"no modem\\\\\\\"\\\\\\\
  10269.         end\\\\\\\
  10270.     end\\\\\\\
  10271.     modem.transmit( 2515, 2515, \\\\\\\"NovaNetworkPing:Request:\\\\\\\" .. os.getComputerID( ) )\\\\\\\
  10272.     local time = os.clock( )\\\\\\\
  10273.     local devices = { }\\\\\\\
  10274.     while os.clock( ) - time < 1 do\\\\\\\
  10275.         local ev = { coroutine.yield( ) }\\\\\\\
  10276.         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\\\\\\\
  10277.             local mode, id, sender = ev[5]:match \\\\\\\":(.-):\\\\\\\", ev[5]:match \\\\\\\":.-:(%d+)\\\\\\\", ev[5]:match \\\\\\\":.-:%d+:(%d+)\\\\\\\"\\\\\\\
  10278.             if mode and id and sender then\\\\\\\
  10279.                 if tonumber( id ) == os.getComputerID( ) then\\\\\\\
  10280.                     if mode == \\\\\\\"Response\\\\\\\" then\\\\\\\
  10281.                         table.insert( devices, tonumber( sender ) )\\\\\\\
  10282.                     end\\\\\\\
  10283.                 end\\\\\\\
  10284.             end\\\\\\\
  10285.         end\\\\\\\
  10286.     end\\\\\\\
  10287. end\\\\\\\
  10288. \\\\\\\
  10289. function listen( event ) -- waiting for connections to establish, not pings or normal messages...\\\\\\\
  10290.     if not modem then\\\\\\\
  10291.         updateModems( )\\\\\\\
  10292.     end\\\\\\\
  10293.     if event[1] == \\\\\\\"modem_message\\\\\\\" and event[3] == 2514 and event[4] == 2514 and type( event[5] ) == \\\\\\\"table\\\\\\\" then\\\\\\\
  10294.         if event[5].target == os.getComputerID( ) then\\\\\\\
  10295.             local data = event[5].data\\\\\\\
  10296.             if type( data ) == \\\\\\\"table\\\\\\\" and data.request == \\\\\\\"connection\\\\\\\" then\\\\\\\
  10297.                 local response, key = handshake.generateResponseData( data.handshake )\\\\\\\
  10298.                 if data.insecure then\\\\\\\
  10299.                     network.addChannel( event[5].sender, data.protocol )\\\\\\\
  10300.                 else\\\\\\\
  10301.                     network.addChannel( event[5].sender, data.protocol, network.genkey( key ), key )\\\\\\\
  10302.                 end\\\\\\\
  10303.                 send( event[5].sender, response )\\\\\\\
  10304.                 if data.insecure then\\\\\\\
  10305.                     core.log( \\\\\\\"Network\\\\\\\", \\\\\\\"Insecure connection established with \\\\\\\" .. tostring( event[5].sender ) )\\\\\\\
  10306.                 else\\\\\\\
  10307.                     core.log( \\\\\\\"Network\\\\\\\", \\\\\\\"Secure connection established with \\\\\\\" .. tostring( event[5].sender ) )\\\\\\\
  10308.                 end\\\\\\\
  10309.             end\\\\\\\
  10310.         elseif not messages[event[5].id] then\\\\\\\
  10311.             modem.transmit( 2514, 2514, event[5] )\\\\\\\
  10312.         end\\\\\\\
  10313.         messages[event[5].id] = os.clock( )\\\\\\\
  10314.     end\\\\\\\
  10315. end\\\\\\\
  10316. \\\\\\\
  10317. function clear( )\\\\\\\
  10318.     local time = os.clock( )\\\\\\\
  10319.     local r = { }\\\\\\\
  10320.     for k, v in pairs( messages ) do\\\\\\\
  10321.         if time - v > 5 then\\\\\\\
  10322.             r[#r+1] = k\\\\\\\
  10323.         end\\\\\\\
  10324.     end\\\\\\\
  10325.     for i = 1, #r do\\\\\\\
  10326.         messages[r[i]] = nil\\\\\\\
  10327.     end\\\\\\\
  10328. end\\\", meta={\\\
  10329.  type = \\\"lib\\\",\\\
  10330. }};}\\\
  10331. --/end of files\\\
  10332. local autorun = {\\\"main\\\"}\\\
  10333. \\\
  10334. local args = { ... }\\\
  10335. local global = getfenv( )\\\
  10336. local custom = setmetatable( { class = class, ARGS = args }, { __index = global } )\\\
  10337. function custom.export( variable, value )\\\
  10338.     if custom[variable] ~= nil or value then\\\
  10339.         global[variable] = custom[variable] == nil and value or custom[variable]\\\
  10340.     end\\\
  10341. end\\\
  10342. local required = { }\\\
  10343. local function require( file, ... )\\\
  10344.     if not required[file] and files[file] then\\\
  10345.         required[file] = true\\\
  10346.         if files[file].meta.type == \\\"class\\\" then\\\
  10347.             custom[file] = class( file )\\\
  10348.         end\\\
  10349.         local fenv = setmetatable( { shared = custom }, { __index = custom } )\\\
  10350.         local f, err = loadstring( files[file].content, file .. \\\".lua\\\" )\\\
  10351.         if f then\\\
  10352.             setfenv( f, fenv )\\\
  10353.             local ok, data = pcall( f )\\\
  10354.             if ok then\\\
  10355.                 if files[file].meta.type == \\\"lib\\\" then\\\
  10356.                     if data ~= nil then\\\
  10357.                         custom[file] = data\\\
  10358.                         return data\\\
  10359.                     else\\\
  10360.                         local t = { }\\\
  10361.                         for k, v in pairs( fenv ) do\\\
  10362.                             t[k] = v\\\
  10363.                         end\\\
  10364.                         custom[file] = t\\\
  10365.                         return t\\\
  10366.                     end\\\
  10367.                 end\\\
  10368.                 return data\\\
  10369.             else\\\
  10370.                 error( data, 0 )\\\
  10371.             end\\\
  10372.         else\\\
  10373.             error( err, 0 )\\\
  10374.         end\\\
  10375.     end\\\
  10376. end\\\
  10377. custom.require = require\\\
  10378. \\\
  10379. for i = 1, #autorun do\\\
  10380.     require( autorun[i] )\\\
  10381. end\",\
  10382. }\
  10383. \
  10384. function ttf( table, dir )\
  10385.     if not fs.isDir( dir ) then\
  10386.         fs.makeDir( dir )\
  10387.     end\
  10388.     for k, v in pairs( table ) do\
  10389.         if type( v ) == \"table\" then\
  10390.             ttf( v, dir..\"/\"..k )\
  10391.         elseif type( v ) == \"string\" then\
  10392.             local f = fs.open( dir..\"/\"..k, \"w\" )\
  10393.             f.write( v )\
  10394.             f.close( )\
  10395.         end\
  10396.     end\
  10397. end\
  10398. return function( path )\
  10399.     ttf( pack, path )\
  10400. end", meta={
  10401.   type = "lib",
  10402. }};}
  10403. --/end of files
  10404. local autorun = {"loader";"DefaultAppInstance";"Process";"WindowManager";"ProcessAppInstance";"AppManager";"Account";"App";"Session";"Window";"main"}
  10405. --@meta[type]:"class"
  10406.  
  10407. -- Swift Class Library
  10408. -- Made by awsumben13
  10409. -- Feel free to use this in any of your projects but keep this at the top and give credit where necessary
  10410.  
  10411. --[[
  10412.     This class library offers:
  10413.         Inheritance "Class:extends( other class )"
  10414.         Initialiser methods "function ClassName:ClassName( )"
  10415.         Public / private variables with customisable read/write access "Class.public.x.write = false"
  10416.         Static variables ( accessed from the class object ) "Class.static.x = 5"
  10417.         Custom metamethods including __index and __newindex "Class.meta.index = { }"
  10418. ]]
  10419.  
  10420. local class = { }
  10421.  
  10422. function class.new( name )
  10423.  
  10424.     local object = { }
  10425.     local public = { }
  10426.  
  10427.     object.public = { }
  10428.     object.private = { }
  10429.     object.static = { }
  10430.     object.name = name
  10431.     object.extends = false
  10432.     object.class = public
  10433.  
  10434.     public.name = name
  10435.  
  10436.     local customindex = false
  10437.     local customnewindex = false
  10438.     local objectmeta = { }
  10439.  
  10440.     function public:new( ... )
  10441.  
  10442.         local ob = { }
  10443.         local pb = { }
  10444.  
  10445.         ob.class = public
  10446.         ob.public = pb
  10447.  
  10448.         setmetatable( ob, {
  10449.             __index = object.private;
  10450.         } )
  10451.  
  10452.         function pb:type( full )
  10453.             return ob.class:type( full )
  10454.         end
  10455.  
  10456.         function pb:typeOf( class )
  10457.             return ob.class:typeOf( class )
  10458.         end
  10459.  
  10460.         local obmeta = { }
  10461.         obmeta.__index = function( _, k )
  10462.             if object.public[k] then
  10463.                 if object.public[k].read then
  10464.                     local val
  10465.                     if customindex then
  10466.                         if type( customindex ) == "function" then
  10467.                             return customindex( ob, k )
  10468.                         else
  10469.                             return customindex[k]
  10470.                         end
  10471.                     elseif type( object.public[k].read ) == "function" then
  10472.                         val = object.public[k].read( ob )
  10473.                     elseif object.public[k].value ~= nil then
  10474.                         val = object.public[k].value
  10475.                     else
  10476.                         val = ob[k]
  10477.                     end
  10478.                     if type( val ) == "function" then
  10479.                         return function( self, ... )
  10480.                             if self == pb then
  10481.                                 return val( ob, ... )
  10482.                             end
  10483.                             return val( self, ... )
  10484.                         end
  10485.                     end
  10486.                     return val
  10487.                 else
  10488.                     error( "variable has no read access", 2 )
  10489.                 end
  10490.             elseif customindex then
  10491.                 if type( customindex ) == "function" then
  10492.                     return customindex( ob, k )
  10493.                 else
  10494.                     return customindex[k]
  10495.                 end
  10496.             else
  10497.                 error( "no such variable \"" .. tostring( k ) .. "\"", 2 )
  10498.             end
  10499.         end;
  10500.         obmeta.__newindex = function( _, k, v )
  10501.             if object.public[k] then
  10502.                 if object.public[k].write then
  10503.                     if customnewindex then
  10504.                         if type( customnewindex ) == "function" then
  10505.                             return customnewindex( ob, k, v )
  10506.                         else
  10507.                             customnewindex[k] = v
  10508.                         end
  10509.                     elseif type( object.public[k].write ) == "function" then
  10510.                         object.public[k].write( ob, v )
  10511.                     else
  10512.                         ob[k] = v
  10513.                     end
  10514.                 else
  10515.                     error( "variable has no write access", 2 )
  10516.                 end
  10517.             else
  10518.                 error( "no such variable \"" .. tostring( k ) .. "\"", 2 )
  10519.             end
  10520.         end;
  10521.         obmeta.__tostring = function( )
  10522.             return object.name
  10523.         end;
  10524.         obmeta.__metatable = "SwiftClassObject";
  10525.  
  10526.         for k, v in pairs( objectmeta ) do
  10527.             obmeta["__" .. tostring( k )] = v
  10528.         end
  10529.  
  10530.         setmetatable( pb, obmeta )
  10531.  
  10532.         local c = object
  10533.         while true do
  10534.             if type( c.private[c.name] ) == "function" then
  10535.                 return c.private[c.name]( ob, ... )
  10536.             end
  10537.             if c.extends then
  10538.                 c = c.extends
  10539.             else
  10540.                 break
  10541.             end
  10542.         end
  10543.  
  10544.         return pb
  10545.     end
  10546.  
  10547.     function public:type( full )
  10548.         local str = ""
  10549.         if full then
  10550.             local c = object.extends
  10551.             while c do
  10552.                 str = c.name .. "." .. str
  10553.                 c = c.extends
  10554.             end
  10555.         end
  10556.         return str .. object.name
  10557.     end
  10558.  
  10559.     function public:typeOf( other )
  10560.         if type( other ) == "table" then
  10561.             if getmetatable( other ) == "SwiftClass" then
  10562.                 local ob = object
  10563.                 while ob do
  10564.                     if ob.class == other then
  10565.                         return true
  10566.                     end
  10567.                     ob = ob.extends
  10568.                 end
  10569.             end
  10570.         end
  10571.         return false
  10572.     end
  10573.  
  10574.     function public:extends( ob )
  10575.         ob:extend( object )
  10576.     end
  10577.  
  10578.     function public:extend( ob )
  10579.         setmetatable( ob.static, { __index = object.static } )
  10580.         setmetatable( ob.public, { __index = object.public } )
  10581.         setmetatable( ob.private, { __index = object.private } )
  10582.         ob.extends = object
  10583.     end
  10584.  
  10585.     local meta = { }
  10586.     meta.__index = function( _, k )
  10587.         if k == "static" then
  10588.             return setmetatable( { }, {
  10589.                 __newindex = function( _, k, v )
  10590.                     object.static[k] = v
  10591.                 end;
  10592.                 __metatable = { };
  10593.             } )
  10594.         elseif k == "public" then
  10595.             return setmetatable( { }, {
  10596.                 __newindex = function( _, k, v )
  10597.                     object.public[k] = {
  10598.                         read = true;
  10599.                         write = false;
  10600.                         value = v;
  10601.                     }
  10602.                 end;
  10603.                 __call = function( _, k )
  10604.                     object.public[k] = {
  10605.                         read = true;
  10606.                         write = true;
  10607.                         value = nil;
  10608.                     }
  10609.                     return function( _type )
  10610.                         local types = { _type }
  10611.                         object.public[k].write = function( ob, value )
  10612.                             for i = 1, #types do
  10613.                                 if class.typeOf( value, types[i] ) then
  10614.                                     ob[k] = value
  10615.                                     return
  10616.                                 end
  10617.                             end
  10618.                             if class.type( types[1] ) == "Class" then
  10619.                                 error( "expected " .. types[1]:type( ) .. " " .. k, 3 )
  10620.                             else
  10621.                                 error( "expected " .. types[1] .. " " .. k, 3 )
  10622.                             end
  10623.                         end
  10624.                         return function( _type )
  10625.                             table.insert( types, _type )
  10626.                         end
  10627.                     end
  10628.                 end;
  10629.                 __index = function( _, k )
  10630.                     if object.public[k] then
  10631.                         return setmetatable( { }, {
  10632.                             __newindex = function( _, name, v )
  10633.                                 if name == "read" then
  10634.                                     if type( v ) == "boolean" or type( v ) == "function" then
  10635.                                         object.public[k].read = v
  10636.                                     else
  10637.                                         error( "invalid modifier value", 2 )
  10638.                                     end
  10639.                                 elseif name == "write" then
  10640.                                     if type( v ) == "boolean" or type( v ) == "function" then
  10641.                                         object.public[k].write = v
  10642.                                     else
  10643.                                         error( "invalid modifier value", 2 )
  10644.                                     end
  10645.                                 else
  10646.                                     error( "invalid modifier name", 2 )
  10647.                                 end
  10648.                             end;
  10649.                             __metatable = { };
  10650.                         } )
  10651.                     else
  10652.                         error( "public index " .. tostring( k ) .. " not found", 2 )
  10653.                     end
  10654.                 end;
  10655.                 __metatable = { };
  10656.             } )
  10657.         elseif k == "meta" then
  10658.             return setmetatable( { }, {
  10659.                 __index = function( _, k )
  10660.                     if k == "index" then
  10661.                         return customindex
  10662.                     elseif k == "newindex" then
  10663.                         return customnewindex
  10664.                     else
  10665.                         return objectmeta[k]
  10666.                     end
  10667.                 end;
  10668.                 __newindex = function( _, k, v )
  10669.                     if k == "metatable" then
  10670.                         error( "cannot change this metamethod", 2 )
  10671.                     elseif k == "index" then
  10672.                         if type( v ) == "function" or type( v ) == "table" or v == nil then
  10673.                             customindex = v
  10674.                         else
  10675.                             error( "cannot use type " .. type( v ) .. " for index metamethod", 2 )
  10676.                         end
  10677.                     elseif k == "newindex" then
  10678.                         if type( v ) == "function" or type( v ) == "table" or v == nil then
  10679.                             customnewindex = v
  10680.                         else
  10681.                             error( "cannot use type " .. type( v ) .. " for newindex metamethod", 2 )
  10682.                         end
  10683.                     else
  10684.                         objectmeta[k] = v
  10685.                     end
  10686.                 end;
  10687.                 __metatable = { };
  10688.             } )
  10689.         else
  10690.             return object.static[k]
  10691.         end
  10692.     end
  10693.     meta.__newindex = function( _, k, v )
  10694.         object.private[k] = v
  10695.     end;
  10696.     meta.__call = function( self, ... )
  10697.         return self:new( ... )
  10698.     end;
  10699.     meta.__totring = function( )
  10700.         return "Class"
  10701.     end;
  10702.     meta.__metatable = "SwiftClass"
  10703.  
  10704.     setmetatable( public, meta )
  10705.  
  10706.     return public
  10707. end
  10708.  
  10709. function class.public( name )
  10710.     local object = class.new( name )
  10711.     getfenv( )[name] = object
  10712. end
  10713.  
  10714. function class.type( object )
  10715.     if type( object ) == "table" then
  10716.         if getmetatable( object ) == "SwiftClass" then
  10717.             return "Class"
  10718.         end
  10719.         if getmetatable( object ) == "SwiftClassObject" then
  10720.             return object:type( )
  10721.         end
  10722.     end
  10723.     return type( object )
  10724. end
  10725.  
  10726. function class.typeOf( object, ... )
  10727.     local types = { ... }
  10728.     for i = 1, #types do
  10729.         if type( object ) == "table" and getmetatable( object ) == "SwiftClassObject" then
  10730.             if object:typeOf( types[i] ) then
  10731.                 return types[i]
  10732.             end
  10733.         elseif type( object ) == "table" and getmetatable( object ) == "SwiftClass" then
  10734.             if types[i] == "Class" then
  10735.                 return "Class"
  10736.             end
  10737.         else
  10738.             if type( object ) == types[i] then
  10739.                 return types[i]
  10740.             end
  10741.         end
  10742.     end
  10743.     return false
  10744. end
  10745.  
  10746. setmetatable( class, { __call = function( _, ... ) return class.new( ... ) end } )
  10747. local args = { ... }
  10748. local global = getfenv( )
  10749. local custom = setmetatable( { class = class, ARGS = args }, { __index = global } )
  10750. function custom.export( variable, value )
  10751.     if custom[variable] ~= nil or value then
  10752.         global[variable] = custom[variable] == nil and value or custom[variable]
  10753.     end
  10754. end
  10755. local required = { }
  10756. local function require( file, ... )
  10757.     if not required[file] and files[file] then
  10758.         required[file] = true
  10759.         if files[file].meta.type == "class" then
  10760.             custom[file] = class( file )
  10761.         end
  10762.         local fenv = setmetatable( { shared = custom }, { __index = custom } )
  10763.         local f, err = loadstring( files[file].content, file .. ".lua" )
  10764.         if f then
  10765.             setfenv( f, fenv )
  10766.             local ok, data = pcall( f )
  10767.             if ok then
  10768.                 if files[file].meta.type == "lib" then
  10769.                     if data ~= nil then
  10770.                         custom[file] = data
  10771.                         return data
  10772.                     else
  10773.                         local t = { }
  10774.                         for k, v in pairs( fenv ) do
  10775.                             t[k] = v
  10776.                         end
  10777.                         custom[file] = t
  10778.                         return t
  10779.                     end
  10780.                 end
  10781.                 return data
  10782.             else
  10783.                 error( data, 0 )
  10784.             end
  10785.         else
  10786.             error( err, 0 )
  10787.         end
  10788.     end
  10789. end
  10790. custom.require = require
  10791.  
  10792. for i = 1, #autorun do
  10793.     require( autorun[i] )
  10794. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement