Advertisement
Oeed

oeedPay Server

Nov 8th, 2014
375
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 118.28 KB | None | 0 0
  1. --  Hideously Smashed Together by Compilr, a Hideous Smash-Stuff-Togetherer, (c) 2014 oeed  --
  2.  
  3. --  This file REALLLLLLLY isn't suitable to be used for anything other than being executed --
  4.  
  5. --  To extract all the files, run: "<filename> --extract" in the Shell --
  6. local files = {
  7.   [ ".oeedPay.settings" ] = "{\
  8.  [ \"408166006\" ] = {\
  9.    Hash = \"Ld.Gv,#AF@]qL<)=:sYW?DhR),YJ2'2x^rvU}_noPZ>'<dtZ2K;l#n'.uc0{_-t \",\
  10.    Balance = 100,\
  11.    Number = 408166006,\
  12.    Name = \"FD\",\
  13.  },\
  14.  [ \"898597157\" ] = {\
  15.    Name = \"E\",\
  16.    Balance = 0,\
  17.    Number = 898597157,\
  18.    Hash = \"\\\\A5P,Y'!~|}e<')*zdg}>yS~I<)raU!?AkVsk^5Y8|f0PHkLxA]#oV}xvh}E<>p \",\
  19.  },\
  20.  [ \"442376689\" ] = {\
  21.    Name = \"F\",\
  22.    Balance = 100,\
  23.    Number = 442376689,\
  24.    Hash = \"~^FKyuanXkoxrZ(}-%MUYP*ST{EpjP+w>m\\\"Kg!c<:07JwRHm+Y&L~iiJq>MQT`GE\",\
  25.  },\
  26.  [ \"639544655\" ] = {\
  27.    Hash = \"^we*ka*Sd3,A@qjDFYu&Dj=b%\\\"3[tYW`B.,OBmX$TOT29Ed2`n=WEvPWM)llhi0-\",\
  28.    Balance = 100,\
  29.    Number = 639544655,\
  30.    Name = \"E\",\
  31.  },\
  32.  [ \"275232556\" ] = {\
  33.    Name = \"L\",\
  34.    Balance = 100,\
  35.    Number = 275232556,\
  36.    Hash = \"TLO7zFO<7hQ\\\\/:)7cn\\\\Fy]_'!UK-AoV|%0-Q0YS6d:si7J9,:Q;NE`pMr^y,?_#=\",\
  37.  },\
  38.  [ \"378914233\" ] = {\
  39.    Hash = \"H!19UC=go1d}kOnITG[Kw}l&xP)&EQE`re7c;M`zX2cJOD#sc,:UG w=;Y*77 aA\",\
  40.    Balance = 100,\
  41.    Number = 378914233,\
  42.    Name = \"FRED\",\
  43.  },\
  44.  [ \"123456789\" ] = {\
  45.    Hash = \"abcdef\",\
  46.    Balance = 960,\
  47.    Number = 123456789,\
  48.    Name = \"oeed\",\
  49.  },\
  50.  [ \"342528443\" ] = {\
  51.    Hash = \"EAprHV'DGE 2MkkYj0U5Gp$yjz1-\\\\SZCXQQ<yE:&tmJq6a\\\"`Q7nq/>5 A,%5Z[4w\",\
  52.    Balance = 90,\
  53.    Number = 342528443,\
  54.    Name = \"L\",\
  55.  },\
  56.  [ \"119792362\" ] = {\
  57.    Name = \"D\",\
  58.    Balance = 100,\
  59.    Number = 119792362,\
  60.    Hash = \"4uLWjWwyK/|K=q+x+/D>+}K=kD)w5iS+(f;Am[5jTX}jj]\\\\$xr<F63=5>qu[}/?s\",\
  61.  },\
  62.  [ \"166260086\" ] = {\
  63.    Name = \"LEV\",\
  64.    Balance = 100,\
  65.    Number = 166260086,\
  66.    Hash = \"hQ*c~I1<,~; JYTZVWVzs\\\"T0g8eMq2h5g&7|@NEldMvHO<ib&RZ([Pv/>;`N&x&p\",\
  67.  },\
  68.  [ \"822901662\" ] = {\
  69.    Name = \"G\",\
  70.    Balance = 100,\
  71.    Number = 822901662,\
  72.    Hash = \">OOK^BfqT%BiRE(y[K~hLn?YXJ_8_v5pzPC7-s&)Gu87X64\\\\ry=CJzHg:!UYsNG?\",\
  73.  },\
  74.  [ \"911184650\" ] = {\
  75.    Hash = \"/].^SN?q]\\\"=7P>[^lBkCCtfJZj,F=w#B!jntK3k6L6VOaB_cqzf9fF\\\\vcs/BM]8S\",\
  76.    Balance = 100,\
  77.    Number = 911184650,\
  78.    Name = \"GOD\",\
  79.  },\
  80.  [ \"480902998\" ] = {\
  81.    Hash = \"LMBP0pt_]JE3ZEQFkC}jn1E4c5dLQ9CU';%QZb-6fyD&DcqSehM][dc(R?0|BFsC\",\
  82.    Balance = 100,\
  83.    Number = 480902998,\
  84.    Name = \"F\",\
  85.  },\
  86.  [ \"127083607\" ] = {\
  87.    Name = \"EF\",\
  88.    Balance = 100,\
  89.    Number = 127083607,\
  90.    Hash = \"J'&KV*q4*P6v./VA4k|O^gvij|.V':5f5T|2<%yHtdiN?M8L,\\\"!-cHWDaTi\\\\3Jc4\",\
  91.  },\
  92.  [ \"987654321\" ] = {\
  93.    Hash = \"abcdef\",\
  94.    Balance = 273,\
  95.    Number = 987654321,\
  96.    Name = \"smith\",\
  97.  },\
  98. }",
  99.   disk = {
  100.     [ ".oeedPay.settings" ] = "{\
  101.  [ \"119792362\" ] = {\
  102.    Hash = \"4uLWjWwyK/|K=q+x+/D>+}K=kD)w5iS+(f;Am[5jTX}jj]\\\\$xr<F63=5>qu[}/?s\",\
  103.    Balance = 100,\
  104.    Number = 119792362,\
  105.    Name = \"D\",\
  106.  },\
  107.  [ \"987654321\" ] = {\
  108.    Name = \"smith\",\
  109.    Balance = 123,\
  110.    Number = 987654321,\
  111.    Hash = \"abcdef\",\
  112.  },\
  113.  [ \"898597157\" ] = {\
  114.    Hash = \"\\\\A5P,Y'!~|}e<')*zdg}>yS~I<)raU!?AkVsk^5Y8|f0PHkLxA]#oV}xvh}E<>p \",\
  115.    Balance = 100,\
  116.    Number = 898597157,\
  117.    Name = \"E\",\
  118.  },\
  119.  [ \"127083607\" ] = {\
  120.    Hash = \"J'&KV*q4*P6v./VA4k|O^gvij|.V':5f5T|2<%yHtdiN?M8L,\\\"!-cHWDaTi\\\\3Jc4\",\
  121.    Balance = 100,\
  122.    Number = 127083607,\
  123.    Name = \"EF\",\
  124.  },\
  125.  [ \"822901662\" ] = {\
  126.    Hash = \">OOK^BfqT%BiRE(y[K~hLn?YXJ_8_v5pzPC7-s&)Gu87X64\\\\ry=CJzHg:!UYsNG?\",\
  127.    Balance = 100,\
  128.    Number = 822901662,\
  129.    Name = \"G\",\
  130.  },\
  131.  [ \"639544655\" ] = {\
  132.    Name = \"E\",\
  133.    Balance = 100,\
  134.    Number = 639544655,\
  135.    Hash = \"^we*ka*Sd3,A@qjDFYu&Dj=b%\\\"3[tYW`B.,OBmX$TOT29Ed2`n=WEvPWM)llhi0-\",\
  136.  },\
  137.  [ \"123456789\" ] = {\
  138.    Name = \"oeed\",\
  139.    Balance = -90,\
  140.    Number = 123456789,\
  141.    Hash = \"abcdef\",\
  142.  },\
  143.  [ \"275232556\" ] = {\
  144.    Hash = \"TLO7zFO<7hQ\\\\/:)7cn\\\\Fy]_'!UK-AoV|%0-Q0YS6d:si7J9,:Q;NE`pMr^y,?_#=\",\
  145.    Balance = 100,\
  146.    Number = 275232556,\
  147.    Name = \"L\",\
  148.  },\
  149.  [ \"442376689\" ] = {\
  150.    Hash = \"~^FKyuanXkoxrZ(}-%MUYP*ST{EpjP+w>m\\\"Kg!c<:07JwRHm+Y&L~iiJq>MQT`GE\",\
  151.    Balance = 100,\
  152.    Number = 442376689,\
  153.    Name = \"F\",\
  154.  },\
  155. }",
  156.   },
  157.   [ "oeedPay Server.log" ] = "[Info] Loading accounts.\
  158. [Info] Starting server...\
  159. [Info] Checking for other oeedPay servers...\
  160. [Success] No other servers found!\
  161. [Info] Starting server...\
  162. [Success] Server started!\
  163. [Info] Validated payment of: $10 from: 123456789 to: 987654321.\
  164. [Info] Saving accounts.\
  165. [Success] Making payment of: $10 from: 123456789 to: 987654321. New balance (debitor): 980\
  166. [Info] Sent balance check for account: 123456789\
  167. [Info] Sent balance check for account: 123456789\
  168. [Info] Sent name for account: 987654321\
  169. [Info] Sent name for account: 987654321\
  170. [Info] Validated payment of: $10 from: 123456789 to: 987654321.\
  171. [Info] Saving accounts.\
  172. [Success] Making payment of: $10 from: 123456789 to: 987654321. New balance (debitor): 970\
  173. [Info] Sent balance check for account: 123456789\
  174. [Info] Sent name for account: 987654321\
  175. [Info] Validated payment of: $10 from: 123456789 to: 987654321.\
  176. [Info] Saving accounts.\
  177. [Success] Making payment of: $10 from: 123456789 to: 987654321. New balance (debitor): 960\
  178. [Info] Sent balance check for account: 123456789\
  179. [Info] Sent balance check for account: 123456789\
  180. [Info] Created new account: 166260086\
  181. [Info] Saving accounts.\
  182. [Info] Sent balance check for account: 166260086\
  183. [Info] Sent balance check for account: 123456789\
  184. [Info] Sent balance check for account: 166260086\
  185. [Info] Sent balance check for account: 123456789\
  186. [Info] Sent balance check for account: 166260086\
  187. [Info] Sent balance check for account: 166260086\
  188. [Info] Sent balance check for account: 123456789\
  189. [Info] Sent balance check for account: 166260086\
  190. [Info] Sent balance check for account: 166260086\
  191. [Info] Sent name for account: 987654321\
  192. [Info] Validated payment of: $10 from: 166260086 to: 987654321.\
  193. [Error] Failed payment of: $10 from: 166260086\
  194. [Info] Sent balance check for account: 166260086\
  195. [Info] Validated payment of: $10 from: 166260086 to: 987654321.\
  196. [Error] Failed payment of: $10 from: 166260086\
  197. [Info] Sent balance check for account: 166260086\
  198. [Info] Validated payment of: $10 from: 166260086 to: 987654321.\
  199. [Error] Failed payment of: $10 from: 166260086\
  200. [Info] Sent balance check for account: 166260086\
  201. [Info] Sent balance check for account: 166260086\
  202. [Info] Sent balance check for account: 166260086\
  203. [Info] Sent balance check for account: 166260086\
  204. [Info] Sent balance check for account: 166260086\
  205. [Info] Sent balance check for account: 166260086\
  206. [Info] Sent balance check for account: 166260086\
  207. [Info] Sent balance check for account: 166260086\
  208. [Info] Sent balance check for account: 166260086\
  209. [Info] Sent balance check for account: 166260086",
  210.   [ "QuestServer.log" ] = "[Info] Loading accounts.\
  211. [Info] Starting server...\
  212. [Info] Checking for other oeedPay servers...\
  213. [Success] No other servers found!\
  214. [Info] Starting server...\
  215. [Success] Server started!",
  216.   startup = "local defaultBalance = 100\
  217. \
  218. local bedrockPath='' if OneOS then OneOS.LoadAPI('/System/API/Bedrock.lua', false)elseif fs.exists(bedrockPath..'/Bedrock')then os.loadAPI(bedrockPath..'/Bedrock')else if http then print('Downloading Bedrock...')local h=http.get('http://pastebin.com/raw.php?i=0MgKNqpN')if h then local f=fs.open(bedrockPath..'/Bedrock','w')f.write(h.readAll())f.close()h.close()os.loadAPI(bedrockPath..'/Bedrock')else error('Failed to download Bedrock. Is your internet working?') end else error('This program needs to download Bedrock to work. Please enable HTTP.') end end if Bedrock then Bedrock.BasePath = bedrockPath Bedrock.ProgramPath = shell.getRunningProgram() end\
  219. \
  220. local program = Bedrock:Initialise()\
  221. \
  222. os.loadAPI(program.ProgramPath .. '/APIs/Peripheral')\
  223. os.loadAPI(program.ProgramPath .. '/APIs/Wireless')\
  224. os.loadAPI(program.ProgramPath .. '/APIs/hash')\
  225. \
  226. local serverRunning = false\
  227. \
  228. local messageLevel = {\
  229.     Info    = 'Info',\
  230.     Success = 'Success',\
  231.     Warning = 'Warning',\
  232.     Error   = 'Error',\
  233. }\
  234. \
  235. local function logMsg(msg, level)\
  236.     level = level or messageLevel.Info\
  237.     program:GetObject('LogView'):AddItem('[' .. level .. '] '..msg, level)\
  238. end\
  239. \
  240. function comma_value(amount)\
  241.  local formatted = amount\
  242.  while true do  \
  243.    formatted, k = string.gsub(formatted, \"^(-?%d+)(%d%d%d)\", '%1,%2')\
  244.    if (k==0) then\
  245.      break\
  246.    end\
  247.  end\
  248.  return formatted\
  249. end\
  250. \
  251. function round(val, decimal)\
  252.  if (decimal) then\
  253.    return math.floor( (val * 10^decimal) + 0.5) / (10^decimal)\
  254.  else\
  255.    return math.floor(val+0.5)\
  256.  end\
  257. end\
  258. \
  259. function formatCurrency(amount, decimal, prefix, neg_prefix)\
  260.  local str_amount,  formatted, famount, remain\
  261.  decimal = decimal or 2  -- default 2 decimal places\
  262.  neg_prefix = neg_prefix or \"-\" -- default negative sign\
  263.  famount = math.abs(round(amount,decimal))\
  264.  famount = math.floor(famount)\
  265. \
  266.  remain = round(math.abs(amount) - famount, decimal)\
  267.  formatted = comma_value(famount)\
  268.  if (decimal > 0) then\
  269.    remain = string.sub(tostring(remain),3)\
  270.    formatted = formatted .. \".\" .. remain ..\
  271.                string.rep(\"0\", decimal - string.len(remain))\
  272.  end\
  273.  formatted = (prefix or \"\") .. formatted \
  274.  if (amount<0) then\
  275.    if (neg_prefix==\"()\") then\
  276.      formatted = \"(\"..formatted ..\")\"\
  277.    else\
  278.      formatted = neg_prefix .. formatted \
  279.    end\
  280.  end\
  281. \
  282.  return formatted\
  283. end\
  284. \
  285. local Accounts = {}\
  286. \
  287. local function saveAccounts()\
  288.     logMsg('Saving accounts.')\
  289.     local f = fs.open('.oeedPay.settings', 'w')\
  290.     if f then\
  291.         f.write(textutils.serialize(Accounts))\
  292.         f.close()\
  293.     end\
  294. end\
  295. \
  296. local function loadAccounts()\
  297.     logMsg('Loading accounts.')\
  298.     local f = fs.open('.oeedPay.settings', 'r')\
  299.     if f then\
  300.         Accounts = textutils.unserialize(f.readAll())\
  301.         f.close()\
  302.     else\
  303.         logMsg('No accounts file, using empty.', messageLevel.Warning)\
  304.         Accounts = Accounts\
  305.         saveAccounts()\
  306.     end\
  307. end\
  308. \
  309. local function stretch(str, len)\
  310.     if #str == len then\
  311.     elseif #str > len then\
  312.         str = string.sub(str, len)\
  313.     else\
  314.         local add = len - #str\
  315.         for i = 1, add do\
  316.             str = str .. ' '\
  317.         end\
  318.     end\
  319.     return str\
  320. end\
  321. \
  322. local selectedAccount\
  323. \
  324. local function switchView(name)\
  325.     local viewNames = {\
  326.         'Accounts',\
  327.         'Log',\
  328.     }\
  329. \
  330.     for i, v in ipairs(viewNames) do\
  331.         if name == v then\
  332.             program:GetObject(v .. 'View').Visible = true\
  333.             program:GetObject(v .. 'Button').Toggle = true\
  334.         else\
  335.             program:GetObject(v .. 'View').Visible = false\
  336.             program:GetObject(v .. 'Button').Toggle = false\
  337.         end\
  338.     end\
  339. \
  340.     selectedAccount = nil\
  341.     if name == 'Accounts' then\
  342.         local items = {}\
  343.         for n, account in pairs(Accounts) do\
  344.             local text = stretch(account.Name, 15) .. ' ' .. stretch(tostring(account.Number), 21) .. ' ' .. formatCurrency(account.Balance, 2, '$')\
  345.             table.insert(items, {Text = text, AccountNumber = n})\
  346.         end\
  347.         program:GetObject('ListView').Items = items\
  348.         program:GetObject('ListView').OnSelect = function(self, text)\
  349.             for i, v in ipairs(items) do\
  350.                 if v.Text == text then\
  351.                     selectedAccount = v.AccountNumber\
  352. \
  353.                 end\
  354.             end\
  355.         end\
  356.         program:GetObject('DeleteButton').OnClick = function(self)\
  357.             if selectedAccount then\
  358.                 program:DisplayAlertWindow(\"Delete Account?\", 'Are you sure you want to delete account: '..selectedAccount, {'Delete', 'Cancel'}, function(value)\
  359.                     if value == 'Delete' then\
  360.                         Accounts[tostring(selectedAccount)] = nil\
  361.                         saveAccounts()\
  362.                         switchView('Accounts')\
  363.                     end\
  364.                 end)\
  365.             end\
  366.         end\
  367.         program:GetObject('EditButton').OnClick = function(self)\
  368.             if selectedAccount then\
  369.                 program:DisplayTextBoxWindow('Edit Balance', 'Enter the new balance for account: '..selectedAccount, function(ok, value)\
  370.                     if ok then\
  371.                         local n = tonumber(value)\
  372.                         if n then\
  373.                             Accounts[tostring(selectedAccount)].Balance = n\
  374.                             saveAccounts()\
  375.                             switchView('Accounts')\
  376.                         end\
  377.                     end\
  378.                 end)\
  379.             end\
  380.         end\
  381.         program:GetObject('BackupButton').OnClick = function(self)\
  382.             logMsg('Backing up accounts.')\
  383.             saveAccounts()\
  384.             if fs.exists('disk') then\
  385.                 local f = fs.open('disk/.oeedPay.settings', 'w')\
  386.                 if f then\
  387.                     f.write(textutils.serialize(Accounts))\
  388.                     f.close()\
  389.                     self.Text = 'Done'\
  390.                 else\
  391.                     self.Text = 'Error'\
  392.                 end\
  393.             else\
  394.                 self.Text = 'No Disk'\
  395.             end\
  396.         end\
  397.     end\
  398.     program:SetActiveObject()\
  399. end\
  400. \
  401. local availableTimer = nil\
  402. \
  403. local startServer = nil\
  404. local stopServer = nil\
  405. \
  406. local function checkServerAvailable(name)\
  407.     logMsg('Checking for other oeedPay servers...')\
  408.     Wireless.SendMessage(Wireless.Channels.oeedPayServerAvailable, 'AVIALABLE?')\
  409.     availableTimer = program:StartTimer(function()\
  410.         if availableTimer then\
  411.             logMsg('No other servers found!', messageLevel.Success)\
  412.             availableTimer = nil\
  413.             startServer(true)\
  414.         end\
  415.     end, 1)\
  416. end\
  417. \
  418. function stopServer(reason)\
  419.     logMsg('Stopping server: ' .. reason or 'Stopped', messageLevel.Warning)\
  420.     serverRunning = false\
  421.     program:GetObject('StatusLabel').Text = reason or 'Stopped'\
  422. end\
  423. \
  424. function startServer(available)\
  425.     logMsg('Starting server...')\
  426.     if available then\
  427.         logMsg('Server started!', messageLevel.Success)\
  428.         serverRunning = true\
  429.         program:GetObject('StatusLabel').Text = 'Running'\
  430.     else\
  431.         program:GetObject('StatusLabel').Text = 'Checking Name'\
  432.         checkServerAvailable()\
  433.     end\
  434. end\
  435. \
  436. program.OnKeyChar = function(self, event, keychar)\
  437.     if keychar == '\\\\' then\
  438.         os.reboot()\
  439.     end\
  440. end\
  441. \
  442. local modems = {}\
  443. for i, side in ipairs(peripheral.getNames()) do\
  444.     local _type = peripheral.getType(side)\
  445.     if _type == 'modem' then\
  446.         local isWireless = false\
  447.         if not pcall(function()isWireless = peripheral.call(side, 'isWireless') end) then\
  448.             isWireless = true\
  449.         end     \
  450.         if not isWireless then\
  451.             local m = peripheral.wrap(side)\
  452.             m.open(Wireless.Channels.oeedPayNewAccount)\
  453.             m.open(Wireless.Channels.oeedPayNewAccountReply)\
  454.             table.insert(modems, m)\
  455.         end\
  456.     end\
  457. end\
  458. \
  459. local function transmit(channel, reply, message)\
  460.     for i, m in ipairs(modems) do\
  461.         m.transmit(channel, reply, message)\
  462.     end\
  463. end\
  464. \
  465. local function generateChallenge()\
  466.    local str = \"\"\
  467.    for _ = 1, 64 do\
  468.        local char = math.random(32, 126)\
  469.        str = str .. string.char(char)\
  470.    end\
  471.    return str\
  472. end\
  473. \
  474. local function registerNewAccount(name)\
  475.     local newAccount = {\
  476.         Name    = name,\
  477.         Number  = math.random(100000000, 999999999),\
  478.         Hash    = generateChallenge(),\
  479.         Balance = defaultBalance\
  480.     }\
  481.     Accounts[tostring(newAccount.Number)] = newAccount\
  482.     logMsg('Created new account: '..newAccount.Number)\
  483.     saveAccounts()\
  484.     return newAccount\
  485. end\
  486. \
  487. program:RegisterEvent('modem_message', function(self, event, side, channel, replyChannel, message, distance)\
  488.     if channel == Wireless.Channels.oeedPayNewAccount then\
  489.         local newAccount = registerNewAccount(message)\
  490.         transmit(replyChannel, channel, textutils.serialize(newAccount))\
  491.     else\
  492.         Wireless.HandleMessage(event, side, channel, replyChannel, message, distance)\
  493.     end\
  494. end)\
  495. \
  496. Wireless.Responder = function(event, side, channel, replyChannel, message, distance)\
  497.     if channel == Wireless.Channels.oeedPayValidatePayment and serverRunning then\
  498.         local reason\
  499.         if not type(message.content) == 'table' or not message.content.AccountNumber or not message.content.Creditor or message.content.Value <= 0 then\
  500.             reason = 'Invalid Request'\
  501.         elseif not Accounts[tostring(message.content.AccountNumber)] then\
  502.             reason = 'Invalid Debitor'\
  503.         elseif not Accounts[tostring(message.content.Creditor)] then\
  504.             reason = 'Invalid Creditor'\
  505.         elseif Accounts[tostring(message.content.AccountNumber)].Balance < message.content.Value then\
  506.             reason = 'Insufficient Funds'\
  507.         else\
  508.             logMsg('Validated payment of: $'..message.content.Value..' from: '..message.content.AccountNumber..' to: '..message.content.Creditor..'.', messageLevel.Info)\
  509.             Wireless.SendMessage(replyChannel, {status = 'VALIDATED', challenge=generateChallenge()}, nil, message.messageID)\
  510.             return          \
  511.         end\
  512.         logMsg('Rejected payment of: $'..message.content.Value..' from: '..message.content.AccountNumber..' to: '..message.content.Creditor..'.', messageLevel.Warning)\
  513.         Wireless.SendMessage(replyChannel, {status='REJECTED', reason=reason}, nil, message.messageID)\
  514. \
  515.     elseif channel == Wireless.Channels.oeedPayPocketPayChallengeAnswer and serverRunning then\
  516.         if type(message.content) == 'table' and message.content.AccountNumber and message.content.Creditor and Accounts[tostring(message.content.AccountNumber)] and Accounts[tostring(message.content.Creditor)] and  Accounts[tostring(message.content.AccountNumber)].Balance >= message.content.Value then\
  517.             local account = Accounts[tostring(message.content.AccountNumber)]\
  518.             local realAnswer = hash.sha256(account.Hash .. message.content.Challenge .. message.content.Value)\
  519.             if realAnswer == message.content.ChallengeAnswer then\
  520.                 Accounts[tostring(message.content.AccountNumber)].Balance = Accounts[tostring(message.content.AccountNumber)].Balance - message.content.Value\
  521.                 Accounts[tostring(message.content.Creditor)].Balance = Accounts[tostring(message.content.Creditor)].Balance + message.content.Value\
  522.                 saveAccounts()\
  523.                 logMsg('Making payment of: $'..message.content.Value..' from: '..message.content.AccountNumber..' to: '..message.content.Creditor..'. New balance (debitor): '..Accounts[tostring(message.content.AccountNumber)].Balance, messageLevel.Success)\
  524.                 Wireless.SendMessage(replyChannel, 'SUCCESS', nil, message.messageID)\
  525.                 return\
  526.             end\
  527.         end\
  528.         logMsg('Failed payment of: $'..message.content.Value..' from: '..message.content.AccountNumber, messageLevel.Error)\
  529.         Wireless.SendMessage(replyChannel, 'FAILED', nil, message.messageID)\
  530.     elseif channel == Wireless.Channels.oeedPayBalanceCheck and serverRunning then\
  531.         if message.content and Accounts[tostring(message.content)] then\
  532.             Wireless.SendMessage(replyChannel, Accounts[tostring(message.content)].Balance, nil, message.messageID)\
  533.             logMsg('Sent balance check for account: '..message.content, messageLevel.Info)\
  534.         end\
  535.     elseif channel == Wireless.Channels.oeedPayGetAccountName and serverRunning then\
  536.         if message.content and Accounts[tostring(message.content)] then\
  537.             Wireless.SendMessage(replyChannel, Accounts[tostring(message.content)].Name, nil, message.messageID)\
  538.             logMsg('Sent name for account: '..message.content, messageLevel.Info)\
  539.         end\
  540.     elseif channel == Wireless.Channels.oeedPayServerAvailable and serverRunning then\
  541.         logMsg('Someone tried to start another oeedPay server.', messageLevel.Warning)\
  542.         Wireless.SendMessage(replyChannel, 'IN_USE', nil, message.messageID)\
  543.     elseif channel == Wireless.Channels.oeedPayServerAvailableReply then\
  544.         availableTimer = nil\
  545.         logMsg('Another oeedPay server is running!', messageLevel.Error)\
  546.         stopServer('Another Server is Running')\
  547.     end\
  548. end\
  549. \
  550. local debounce = nil\
  551. \
  552. program.OnTimer = function(self, event, timer)\
  553.     if timer == debounce then\
  554.         saveAccounts()\
  555.     end\
  556. end\
  557. \
  558. program:Run(function()\
  559.     if Wireless.Present() then\
  560.         program:LoadView('main')\
  561. \
  562.         loadAccounts()\
  563.         Wireless.Initialise()\
  564. \
  565.         switchView('Accounts')\
  566.         startServer()\
  567. \
  568.         program:GetObject('AccountsButton').OnClick = function(self, event, side, x, y)\
  569.             switchView('Accounts')\
  570.         end\
  571. \
  572.         program:GetObject('LogButton').OnClick = function(self, event, side, x, y)\
  573.             switchView('Log')\
  574.         end\
  575.     else\
  576.         program:LoadView('nomodem')\
  577.     end\
  578. \
  579.     program:GetObject('QuitButton').OnClick = function(self, event, side, x, y)\
  580.         term.setBackgroundColour(colours.black)\
  581.         term.setTextColor(colours.white)\
  582.         term.clear()\
  583.         term.setCursorPos(1, 1)\
  584.         print('Thanks for using oeedPay Server')\
  585.         program:Quit()\
  586.     end\
  587. end)",
  588.   Objects = {
  589.     [ "LogView.lua" ] = "Inherit = 'ScrollView'\
  590. Log = nil\
  591. \
  592. SaveLog = function(self)\
  593.     local str = table.concat(self.Log, '\\n')\
  594.     local f = fs.open('oeedPay Server.log', 'w')\
  595.     if f then\
  596.         f.write(str)\
  597.         f.close()\
  598.     end\
  599. end\
  600. \
  601. AddItem = function(self, str, level)\
  602.     local messageColours = {\
  603.         Info    = colours.blue,\
  604.         Success = colours.green,\
  605.         Warning = colours.orange,\
  606.         Error   = colours.red,\
  607.     }\
  608.     table.insert(self.Log, str)\
  609. \
  610.     local y = 1\
  611. \
  612.     for i, v in ipairs(self.Children) do\
  613.         y = y + v.Height\
  614.     end\
  615. \
  616.     local bar = self:GetObject('ScrollViewScrollBar')\
  617.     local shouldScroll\
  618.     if bar then\
  619.         shouldScroll = bar.Scroll == bar.MaxScroll\
  620.     end\
  621. \
  622.     self:AddObject({\
  623.         X = 1,\
  624.         Y = y,\
  625.         Width = \"100%\",\
  626.         Type = 'Label',\
  627.         Text = str,\
  628.         TextColour = messageColours[level]\
  629.     })\
  630.     self:UpdateScroll()\
  631.     if shouldScroll or (not bar and self:GetObject('ScrollViewScrollBar')) then\
  632.         bar = self:GetObject('ScrollViewScrollBar')\
  633.         bar.Scroll = bar.MaxScroll\
  634.         if bar.OnChange then\
  635.             bar:OnChange()\
  636.         end\
  637.     end\
  638.     \
  639.     self:SaveLog()\
  640. end\
  641. \
  642. OnLoad = function(self)\
  643.     self.Log = {}\
  644.     if not self.ChildOffset or not self.ChildOffset.X or not self.ChildOffset.Y then\
  645.         self.ChildOffset = {X = 0, Y = 0}\
  646.     end\
  647. end",
  648.   },
  649.   Bedrock = "--Bedrock Build: 263\
  650. --This code is squished down in to one, rather hard to read file.\
  651. --As such it is not much good for anything other than being loaded as an API.\
  652. --If you want to look at the code to learn from it, copy parts or just take a look,\
  653. --you should go to the GitHub repo. http://github.com/oeed/Bedrock/\
  654. \
  655. --\
  656. --      Bedrock is the core program framework used by all OneOS and OneCode programs.\
  657. --                          Inspired by Apple's Cocoa framework.\
  658. --                                     (c) oeed 2014\
  659. --\
  660. --        For documentation see the Bedrock wiki, github.com/oeed/Bedrock/wiki/\
  661. --\
  662. \
  663. local apis = {\
  664. [\"Drawing\"] = [[\
  665. local round = function(num, idp)\
  666.     local mult = 10^(idp or 0)\
  667.     return math.floor(num * mult + 0.5) / mult\
  668. end\
  669. \
  670. local _w, _h = term.getSize()\
  671. local copyBuffer = nil\
  672. \
  673. Screen = {\
  674.     Width = _w,\
  675.     Height = _h\
  676. }\
  677. \
  678. Constraints = {\
  679.     \
  680. }\
  681. \
  682. CurrentConstraint = {1,1,_w,_h}\
  683. IgnoreConstraint = false\
  684. \
  685. function AddConstraint(x, y, width, height)\
  686.     local x2 = x + width - 1\
  687.     local y2 = y + height - 1\
  688.     table.insert(Drawing.Constraints, {x, y, x2, y2})\
  689.     Drawing.GetConstraint()\
  690. end\
  691. \
  692. function RemoveConstraint()\
  693.     --table.remove(Drawing.Constraints, #Drawing.Constraints)\
  694.     Drawing.Constraints[#Drawing.Constraints] = nil\
  695.     Drawing.GetConstraint()\
  696. end\
  697. \
  698. function GetConstraint()\
  699.     local x = 1\
  700.     local y = 1\
  701.     local x2 = Drawing.Screen.Width\
  702.     local y2 = Drawing.Screen.Height\
  703.     for i, c in ipairs(Drawing.Constraints) do\
  704.         if x < c[1] then\
  705.             x = c[1]\
  706.         end\
  707.         if y < c[2] then\
  708.             y = c[2]\
  709.         end\
  710.         if x2 > c[3] then\
  711.             x2 = c[3]\
  712.         end\
  713.         if y2 > c[4] then\
  714.             y2 = c[4]\
  715.         end\
  716.     end\
  717.     Drawing.CurrentConstraint = {x, y, x2, y2}\
  718. end\
  719. \
  720. function WithinContraint(x, y)\
  721.     return Drawing.IgnoreConstraint or\
  722.           (x >= Drawing.CurrentConstraint[1] and\
  723.            y >= Drawing.CurrentConstraint[2] and\
  724.            x <= Drawing.CurrentConstraint[3] and\
  725.            y <= Drawing.CurrentConstraint[4])\
  726. end\
  727. \
  728. colours.transparent = 0\
  729. colors.transparent = 0\
  730. \
  731. DrawCharacters = function (x, y, characters, textColour, bgColour)\
  732.     Drawing.WriteStringToBuffer(x, y, tostring(characters), textColour, bgColour)\
  733. end\
  734. \
  735. DrawBlankArea = function (x, y, w, h, colour)\
  736.     if colour ~= colours.transparent then\
  737.         Drawing.DrawArea (x, y, w, h, \" \", 1, colour)\
  738.     end\
  739. end\
  740. \
  741. DrawArea = function (x, y, w, h, character, textColour, bgColour)\
  742.     --width must be greater than 1, otherwise we get problems\
  743.     if w < 0 then\
  744.         w = w * -1\
  745.     elseif w == 0 then\
  746.         w = 1\
  747.     end\
  748. \
  749.     for ix = 1, w do\
  750.         local currX = x + ix - 1\
  751.         for iy = 1, h do\
  752.             local currY = y + iy - 1\
  753.             Drawing.WriteToBuffer(currX, currY, character, textColour, bgColour)\
  754.         end\
  755.     end\
  756. end\
  757. \
  758. DrawImage = function(_x,_y,tImage, w, h)\
  759.     if tImage then\
  760.         for y = 1, h do\
  761.             if not tImage[y] then\
  762.                 break\
  763.             end\
  764.             for x = 1, w do\
  765.                 if not tImage[y][x] then\
  766.                     break\
  767.                 end\
  768.                 local bgColour = tImage[y][x]\
  769.                 local textColour = tImage.textcol[y][x] or colours.white\
  770.                 local char = tImage.text[y][x]\
  771.                 Drawing.WriteToBuffer(x+_x-1, y+_y-1, char, textColour, bgColour)\
  772.             end\
  773.         end\
  774.     elseif w and h then\
  775.         Drawing.DrawBlankArea(_x, _y, w, h, colours.lightGrey)\
  776.     end\
  777. end\
  778. \
  779. --using .nft\
  780. LoadImage = function(path, global)\
  781.     local image = {\
  782.         text = {},\
  783.         textcol = {}\
  784.     }\
  785.     if fs.exists(path) then\
  786.         local _io = io\
  787.         if OneOS and global then\
  788.             _io = OneOS.IO\
  789.         end\
  790.        local file = _io.open(path, \"r\")\
  791.        if not file then\
  792.             error('Error Occured. _io:'..tostring(_io)..' OneOS: '..tostring(OneOS)..' OneOS.IO'..tostring(OneOS.IO)..' io: '..tostring(io))\
  793.        end\
  794.        local sLine = file:read()\
  795.        local num = 1\
  796.        while sLine do  \
  797.            table.insert(image, num, {})\
  798.            table.insert(image.text, num, {})\
  799.            table.insert(image.textcol, num, {})\
  800.                                        \
  801.            --As we're no longer 1-1, we keep track of what index to write to\
  802.            local writeIndex = 1\
  803.            --Tells us if we've hit a 30 or 31 (BG and FG respectively)- next char specifies the curr colour\
  804.            local bgNext, fgNext = false, false\
  805.            --The current background and foreground colours\
  806.            local currBG, currFG = nil,nil\
  807.            for i=1,#sLine do\
  808.                    local nextChar = string.sub(sLine, i, i)\
  809.                    if nextChar:byte() == 30 then\
  810.                            bgNext = true\
  811.                    elseif nextChar:byte() == 31 then\
  812.                            fgNext = true\
  813.                    elseif bgNext then\
  814.                            currBG = Drawing.GetColour(nextChar)\
  815.                             if currBG == nil then\
  816.                                 currBG = colours.transparent\
  817.                             end\
  818.                            bgNext = false\
  819.                    elseif fgNext then\
  820.                            currFG = Drawing.GetColour(nextChar)\
  821.                             if currFG == nil or currFG == colours.transparent then\
  822.                                 currFG = colours.white\
  823.                             end\
  824.                            fgNext = false\
  825.                    else\
  826.                            if nextChar ~= \" \" and currFG == nil then\
  827.                                    currFG = colours.white\
  828.                            end\
  829.                            image[num][writeIndex] = currBG\
  830.                            image.textcol[num][writeIndex] = currFG\
  831.                            image.text[num][writeIndex] = nextChar\
  832.                            writeIndex = writeIndex + 1\
  833.                    end\
  834.            end\
  835.            num = num+1\
  836.            sLine = file:read()\
  837.        end\
  838.        file:close()\
  839.    else\
  840.         return nil\
  841.     end\
  842.     return image\
  843. end\
  844. \
  845. DrawCharactersCenter = function(x, y, w, h, characters, textColour,bgColour)\
  846.     w = w or Drawing.Screen.Width\
  847.     h = h or Drawing.Screen.Height\
  848.     x = x or 0\
  849.     y = y or 0\
  850.     x = math.floor((w - #characters) / 2) + x\
  851.     y = math.floor(h / 2) + y\
  852. \
  853.     Drawing.DrawCharacters(x, y, characters, textColour, bgColour)\
  854. end\
  855. \
  856. GetColour = function(hex)\
  857.     if hex == ' ' then\
  858.         return colours.transparent\
  859.     end\
  860.    local value = tonumber(hex, 16)\
  861.    if not value then return nil end\
  862.    value = math.pow(2,value)\
  863.    return value\
  864. end\
  865. \
  866. Clear = function (_colour)\
  867.     _colour = _colour or colours.black\
  868.     Drawing.DrawBlankArea(1, 1, Drawing.Screen.Width, Drawing.Screen.Height, _colour)\
  869. end\
  870. \
  871. Buffer = {}\
  872. BackBuffer = {}\
  873. \
  874. TryRestore = false\
  875. \
  876. \
  877. --TODO: make this quicker\
  878. -- maybe sort the pixels in order of colour so it doesn't have to set the colour each time\
  879. DrawBuffer = function()\
  880.     if TryRestore and Restore then\
  881.         Restore()\
  882.     end\
  883. \
  884.     for y,row in pairs(Drawing.Buffer) do\
  885.         for x,pixel in pairs(row) do\
  886.             local shouldDraw = true\
  887.             local hasBackBuffer = true\
  888.             if Drawing.BackBuffer[y] == nil or Drawing.BackBuffer[y][x] == nil or #Drawing.BackBuffer[y][x] ~= 3 then\
  889.                 hasBackBuffer = false\
  890.             end\
  891.             if hasBackBuffer and Drawing.BackBuffer[y][x][1] == Drawing.Buffer[y][x][1] and Drawing.BackBuffer[y][x][2] == Drawing.Buffer[y][x][2] and Drawing.BackBuffer[y][x][3] == Drawing.Buffer[y][x][3] then\
  892.                 shouldDraw = false\
  893.             end\
  894.             if shouldDraw then\
  895.                 term.setBackgroundColour(pixel[3])\
  896.                 term.setTextColour(pixel[2])\
  897.                 term.setCursorPos(x, y)\
  898.                 term.write(pixel[1])\
  899.             end\
  900.         end\
  901.     end\
  902.     Drawing.BackBuffer = Drawing.Buffer\
  903.     Drawing.Buffer = {}\
  904. end\
  905. \
  906. ClearBuffer = function()\
  907.     Drawing.Buffer = {}\
  908. end\
  909. \
  910. WriteStringToBuffer = function (x, y, characters, textColour,bgColour)\
  911.     for i = 1, #characters do\
  912.         local character = characters:sub(i,i)\
  913.         Drawing.WriteToBuffer(x + i - 1, y, character, textColour, bgColour)\
  914.     end\
  915. end\
  916. \
  917. WriteToBuffer = function(x, y, character, textColour,bgColour, cached)\
  918.     if not cached and not Drawing.WithinContraint(x, y) then\
  919.         return\
  920.     end\
  921.     x = round(x)\
  922.     y = round(y)\
  923. \
  924.     if textColour == colours.transparent then\
  925.         character = ' '\
  926.     end\
  927. \
  928.     if bgColour == colours.transparent then\
  929.         Drawing.Buffer[y] = Drawing.Buffer[y] or {}\
  930.         Drawing.Buffer[y][x] = Drawing.Buffer[y][x] or {\"\", colours.white, colours.black}\
  931.         Drawing.Buffer[y][x][1] = character\
  932.         Drawing.Buffer[y][x][2] = textColour\
  933.     else\
  934.         Drawing.Buffer[y] = Drawing.Buffer[y] or {}\
  935.         Drawing.Buffer[y][x] = {character, textColour, bgColour}\
  936.     end\
  937. \
  938.     if copyBuffer then\
  939.         copyBuffer[y] = copyBuffer[y] or {}\
  940.         copyBuffer[y][x] = {character, textColour, bgColour}        \
  941.     end\
  942. end\
  943. \
  944. DrawCachedBuffer = function(buffer)\
  945.     for y, row in pairs(buffer) do\
  946.         for x, pixel in pairs(row) do\
  947.             WriteToBuffer(x, y, pixel[1], pixel[2], pixel[3], true)\
  948.         end\
  949.     end\
  950. end\
  951. \
  952. StartCopyBuffer = function()\
  953.     copyBuffer = {}\
  954. end\
  955. \
  956. EndCopyBuffer = function()\
  957.     local tmpCopy = copyBuffer\
  958.     copyBuffer = nil\
  959.     return tmpCopy\
  960. end\
  961. ]],\
  962. [\"Helpers\"] = [[\
  963. LongestString = function(input, key, isKey)\
  964.     local length = 0\
  965.     if isKey then\
  966.         for k, v in pairs(input) do\
  967.             local titleLength = string.len(k)\
  968.             if titleLength > length then\
  969.                 length = titleLength\
  970.             end\
  971.         end\
  972.     else\
  973.         for i = 1, #input do\
  974.             local value = input[i]\
  975.             if key then\
  976.                 if value[key] then\
  977.                     value = value[key]\
  978.                 else\
  979.                     value = ''\
  980.                 end\
  981.             end\
  982.             local titleLength = string.len(value)\
  983.             if titleLength > length then\
  984.                 length = titleLength\
  985.             end\
  986.         end\
  987.     end\
  988.     return length\
  989. end\
  990. \
  991. Split = function(str,sep)\
  992.    sep=sep or'/'\
  993.    return str:match(\"(.*\"..sep..\")\")\
  994. end\
  995. \
  996. Extension = function(path, addDot)\
  997.     if not path then\
  998.         return nil\
  999.     elseif not string.find(fs.getName(path), '%.') then\
  1000.         if not addDot then\
  1001.             return fs.getName(path)\
  1002.         else\
  1003.             return ''\
  1004.         end\
  1005.     else\
  1006.         local _path = path\
  1007.         if path:sub(#path) == '/' then\
  1008.             _path = path:sub(1,#path-1)\
  1009.         end\
  1010.         local extension = _path:gmatch('%.[0-9a-z]+$')()\
  1011.         if extension then\
  1012.             extension = extension:sub(2)\
  1013.         else\
  1014.             --extension = nil\
  1015.             return ''\
  1016.         end\
  1017.         if addDot then\
  1018.             extension = '.'..extension\
  1019.         end\
  1020.         return extension:lower()\
  1021.     end\
  1022. end\
  1023. \
  1024. RemoveExtension = function(path)\
  1025. --local name = string.match(fs.getName(path), '(%a+)%.?.-')\
  1026.     if path:sub(1,1) == '.' then\
  1027.         return path\
  1028.     end\
  1029.     local extension = Helpers.Extension(path)\
  1030.     if extension == path then\
  1031.         return fs.getName(path)\
  1032.     end\
  1033.     return string.gsub(path, extension, ''):sub(1, -2)\
  1034. end\
  1035. \
  1036. RemoveFileName = function(path)\
  1037.     if string.sub(path, -1) == '/' then\
  1038.         path = string.sub(path, 1, -2)\
  1039.     end\
  1040.     local v = string.match(path, \"(.-)([^\\\\/]-%.?([^%.\\\\/]*))$\")\
  1041.     if type(v) == 'string' then\
  1042.         return v\
  1043.     end\
  1044.     return v[1]\
  1045. end\
  1046. \
  1047. TruncateString = function(sString, maxLength)\
  1048.     if #sString > maxLength then\
  1049.         sString = sString:sub(1,maxLength-3)\
  1050.         if sString:sub(-1) == ' ' then\
  1051.             sString = sString:sub(1,maxLength-4)\
  1052.         end\
  1053.         sString = sString  .. '...'\
  1054.     end\
  1055.     return sString\
  1056. end\
  1057. \
  1058. TruncateStringStart = function(sString, maxLength)\
  1059.     local len = #sString\
  1060.     if #sString > maxLength then\
  1061.         sString = sString:sub(len - maxLength, len - 3)\
  1062.         if sString:sub(-1) == ' ' then\
  1063.             sString = sString:sub(len - maxLength, len - 4)\
  1064.         end\
  1065.         sString = '...' .. sString\
  1066.     end\
  1067.     return sString\
  1068. end\
  1069. \
  1070. WrapText = function(text, maxWidth)\
  1071.     local lines = {''}\
  1072.    for word, space in text:gmatch('(%S+)(%s*)') do\
  1073.            local temp = lines[#lines] .. word .. space:gsub('\\n','')\
  1074.            if #temp > maxWidth then\
  1075.                    table.insert(lines, '')\
  1076.            end\
  1077.            if space:find('\\n') then\
  1078.                    lines[#lines] = lines[#lines] .. word\
  1079.                    \
  1080.                    space = space:gsub('\\n', function()\
  1081.                            table.insert(lines, '')\
  1082.                            return ''\
  1083.                    end)\
  1084.            else\
  1085.                    lines[#lines] = lines[#lines] .. word .. space\
  1086.            end\
  1087.    end\
  1088.     return lines\
  1089. end\
  1090. \
  1091. TidyPath = function(path)\
  1092.     path = '/'..path\
  1093.     if fs.exists(path) and fs.isDir(path) then\
  1094.         path = path .. '/'\
  1095.     end\
  1096. \
  1097.     path, n = path:gsub(\"//\", \"/\")\
  1098.     while n > 0 do\
  1099.         path, n = path:gsub(\"//\", \"/\")\
  1100.     end\
  1101.     return path\
  1102. end\
  1103. \
  1104. Capitalise = function(str)\
  1105.     return str:sub(1, 1):upper() .. str:sub(2, -1)\
  1106. end\
  1107. \
  1108. Round = function(num, idp)\
  1109.     local mult = 10^(idp or 0)\
  1110.     return math.floor(num * mult + 0.5) / mult\
  1111. end\
  1112. ]],\
  1113. [\"Object\"] = [[\
  1114. X = 1\
  1115. Y = 1\
  1116. Width = 1\
  1117. Height = 1\
  1118. Parent = nil\
  1119. OnClick = nil\
  1120. Visible = true\
  1121. IgnoreClick = false\
  1122. Name = nil \
  1123. ClipDrawing = true\
  1124. UpdateDrawBlacklist = {}\
  1125. Fixed = false\
  1126. \
  1127. DrawCache = {}\
  1128. \
  1129. NeedsDraw = function(self)\
  1130.     if not self.Visible then\
  1131.         return false\
  1132.     end\
  1133.     \
  1134.     if not self.DrawCache.Buffer or self.DrawCache.AlwaysDraw or self.DrawCache.NeedsDraw then\
  1135.         return true\
  1136.     end\
  1137. \
  1138.     if self.OnNeedsUpdate then\
  1139.         if self.OnNeedsUpdate() then\
  1140.             return true\
  1141.         end\
  1142.     end\
  1143. \
  1144.     if self.Children then\
  1145.         for i, v in ipairs(self.Children) do\
  1146.             if v:NeedsDraw() then\
  1147.                 return true\
  1148.             end\
  1149.         end\
  1150.     end\
  1151. end\
  1152. \
  1153. GetPosition = function(self)\
  1154.     return self.Bedrock:GetAbsolutePosition(self)\
  1155. end\
  1156. \
  1157. GetOffsetPosition = function(self)\
  1158.     if not self.Parent then\
  1159.         return {X = 1, Y = 1}\
  1160.     end\
  1161. \
  1162.     local offset = {X = 0, Y = 0}\
  1163.     if not self.Fixed and self.Parent.ChildOffset then\
  1164.         offset = self.Parent.ChildOffset\
  1165.     end\
  1166. \
  1167.     return {X = self.X + offset.X, Y = self.Y + offset.Y}\
  1168. end\
  1169. \
  1170. Draw = function(self)\
  1171.     if not self.Visible then\
  1172.         return\
  1173.     end\
  1174. \
  1175.     self.DrawCache.NeedsDraw = false\
  1176.     local pos = self:GetPosition()\
  1177.     Drawing.StartCopyBuffer()\
  1178. \
  1179.     if self.ClipDrawing then\
  1180.         Drawing.AddConstraint(pos.X, pos.Y, self.Width, self.Height)\
  1181.     end\
  1182. \
  1183.     if self.OnDraw then\
  1184.         self:OnDraw(pos.X, pos.Y)\
  1185.     end\
  1186. \
  1187.     self.DrawCache.Buffer = Drawing.EndCopyBuffer()\
  1188.     \
  1189.     if self.Children then\
  1190.         for i, child in ipairs(self.Children) do\
  1191.             local pos = child:GetOffsetPosition()\
  1192.             if pos.Y + self.Height > 1 and pos.Y <= self.Height and pos.X + self.Width > 1 and pos.X <= self.Width then\
  1193.                 child:Draw()\
  1194.             end\
  1195.         end\
  1196.     end\
  1197. \
  1198.     if self.ClipDrawing then\
  1199.         Drawing.RemoveConstraint()\
  1200.     end \
  1201. end\
  1202. \
  1203. ForceDraw = function(self, ignoreChildren, ignoreParent, ignoreBedrock)\
  1204.     if not ignoreBedrock and self.Bedrock then\
  1205.         self.Bedrock:ForceDraw()\
  1206.     end\
  1207.     self.DrawCache.NeedsDraw = true\
  1208.     if not ignoreParent and self.Parent then\
  1209.         self.Parent:ForceDraw(true, nil, true)\
  1210.     end\
  1211.     if not ignoreChildren and self.Children then\
  1212.         for i, child in ipairs(self.Children) do\
  1213.             child:ForceDraw(nil, true, true)\
  1214.         end\
  1215.     end\
  1216. end\
  1217. \
  1218. OnRemove = function(self)\
  1219.     if self == self.Bedrock:GetActiveObject() then\
  1220.         self.Bedrock:SetActiveObject()\
  1221.     end\
  1222. end\
  1223. \
  1224. local function ParseColour(value)\
  1225.     if type(value) == 'string' then\
  1226.         if colours[value] and type(colours[value]) == 'number' then\
  1227.             return colours[value]\
  1228.         elseif colors[value] and type(colors[value]) == 'number' then\
  1229.             return colors[value]\
  1230.         end\
  1231.     elseif type(value) == 'number' and (value == colours.transparent or (value >= colours.white and value <= colours.black)) then\
  1232.         return value\
  1233.     end\
  1234.     error('Invalid colour: \"'..tostring(value)..'\"')\
  1235. end\
  1236. \
  1237. Initialise = function(self, values)\
  1238.     local _new = values    -- the new instance\
  1239.     _new.DrawCache = {\
  1240.         NeedsDraw = true,\
  1241.         AlwaysDraw = false,\
  1242.         Buffer = nil\
  1243.     }\
  1244.     setmetatable(_new, {__index = self} )\
  1245. \
  1246.     local new = {} -- the proxy\
  1247.     setmetatable(new, {\
  1248.         __index = function(t, k)\
  1249.             if k:find('Color') then\
  1250.                 k = k:gsub('Color', 'Colour')\
  1251.             end\
  1252. \
  1253.             if k:find('Colour') and type(_new[k]) ~= 'table' then\
  1254.                 if _new[k] then\
  1255.                     return ParseColour(_new[k])\
  1256.                 end\
  1257.             elseif _new[k] ~= nil then\
  1258.                 return _new[k]\
  1259.             end\
  1260.         end,\
  1261. \
  1262.         __newindex = function (t,k,v)\
  1263.             if k:find('Color') then\
  1264.                 k = k:gsub('Color', 'Colour')\
  1265.             end\
  1266. \
  1267.             if k == 'Width' or k == 'X' or k == 'Height' or k == 'Y' then\
  1268.                 v = new.Bedrock:ParseStringSize(new.Parent, k, v)\
  1269.             end\
  1270. \
  1271.             if v ~= _new[k] then\
  1272.                 _new[k] = v\
  1273.                 if t.OnUpdate then\
  1274.                     t:OnUpdate(k)\
  1275.                 end\
  1276. \
  1277.                 if t.UpdateDrawBlacklist[k] == nil then\
  1278.                     t:ForceDraw()\
  1279.                 end\
  1280.             end\
  1281.         end\
  1282.     })\
  1283.     if new.OnInitialise then\
  1284.         new:OnInitialise()\
  1285.     end\
  1286. \
  1287.     return new\
  1288. end\
  1289. \
  1290. Click = function(self, event, side, x, y)\
  1291.     if self.Visible and not self.IgnoreClick then\
  1292.         if event == 'mouse_click' and self.OnClick and self:OnClick(event, side, x, y) ~= false then\
  1293.             return true\
  1294.         elseif event == 'mouse_drag' and self.OnDrag and self:OnDrag(event, side, x, y) ~= false then\
  1295.             return true\
  1296.         elseif event == 'mouse_scroll' and self.OnScroll and self:OnScroll(event, side, x, y) ~= false then\
  1297.             return true\
  1298.         else\
  1299.             return false\
  1300.         end\
  1301.     else\
  1302.         return false\
  1303.     end\
  1304. \
  1305. end\
  1306. \
  1307. ToggleMenu = function(self, name, x, y)\
  1308.     return self.Bedrock:ToggleMenu(name, self, x, y)\
  1309. end\
  1310. \
  1311. function OnUpdate(self, value)\
  1312.     if value == 'Z' then\
  1313.         self.Bedrock:ReorderObjects()\
  1314.     end\
  1315. end\
  1316. ]],\
  1317. }\
  1318. local objects = {\
  1319. [\"Button\"] = [[\
  1320. BackgroundColour = colours.lightGrey\
  1321. ActiveBackgroundColour = colours.blue\
  1322. ActiveTextColour = colours.white\
  1323. TextColour = colours.black\
  1324. DisabledTextColour = colours.lightGrey\
  1325. Text = \"\"\
  1326. Toggle = nil\
  1327. Momentary = true\
  1328. AutoWidthAutoWidth = true\
  1329. Align = 'Center'\
  1330. Enabled = true\
  1331. \
  1332. OnUpdate = function(self, value)\
  1333.     if value == 'Text' and self.AutoWidth then\
  1334.         self.Width = #self.Text + 2\
  1335.     end\
  1336. end\
  1337. \
  1338. OnDraw = function(self, x, y)\
  1339.     local bg = self.BackgroundColour\
  1340. \
  1341.     if self.Toggle then\
  1342.         bg = self.ActiveBackgroundColour\
  1343.     end\
  1344. \
  1345.     local txt = self.TextColour\
  1346.     if self.Toggle then\
  1347.         txt = self.ActiveTextColour\
  1348.     end\
  1349.     if not self.Enabled then\
  1350.         txt = self.DisabledTextColour\
  1351.     end\
  1352.     Drawing.DrawBlankArea(x, y, self.Width, self.Height, bg)\
  1353. \
  1354.     local _x = 1\
  1355.    if self.Align == 'Right' then\
  1356.        _x = self.Width - #self.Text - 1\
  1357.    elseif self.Align == 'Center' then\
  1358.        _x = math.floor((self.Width - #self.Text) / 2)\
  1359.    end\
  1360.     --Drawing.DrawCharacters(x + _x, y, self.Text, txt, bg)\
  1361.     Drawing.DrawCharacters(x + _x, y-1+math.ceil(self.Height/2), self.Text, txt, bg)\
  1362. end\
  1363. \
  1364. OnLoad = function(self)\
  1365.     if self.Toggle ~= nil then\
  1366.         self.Momentary = false\
  1367.     end\
  1368. end\
  1369. \
  1370. Click = function(self, event, side, x, y)\
  1371.     if self.Visible and not self.IgnoreClick and self.Enabled and event ~= 'mouse_scroll' then\
  1372.         if self.OnClick then\
  1373.             if self.Momentary then\
  1374.                 self.Toggle = true\
  1375.                 self.Bedrock:StartTimer(function()self.Toggle = false end,0.25)\
  1376.             elseif self.Toggle ~= nil then\
  1377.                 self.Toggle = not self.Toggle\
  1378.             end\
  1379. \
  1380.             self:OnClick(event, side, x, y, self.Toggle)\
  1381.         else\
  1382.             self.Toggle = not self.Toggle\
  1383.         end\
  1384.         return true\
  1385.     else\
  1386.         return false\
  1387.     end\
  1388. end\
  1389. ]],\
  1390. [\"CollectionView\"] = [[\
  1391. Inherit = 'ScrollView'\
  1392. UpdateDrawBlacklist = {['NeedsItemUpdate']=true}\
  1393. \
  1394. TextColour = colours.black\
  1395. BackgroundColour = colours.white\
  1396. Items = false\
  1397. NeedsItemUpdate = false\
  1398. SpacingX = 2\
  1399. SpacingY = 1\
  1400. \
  1401. OnDraw = function(self, x, y)\
  1402.     if self.NeedsItemUpdate then\
  1403.         self:UpdateItems()\
  1404.         self.NeedsItemUpdate = false\
  1405.     end\
  1406.     Drawing.DrawBlankArea(x, y, self.Width, self.Height, self.BackgroundColour)\
  1407. end\
  1408. \
  1409. local function MaxIcons(self, obj)\
  1410.     local x, y = 2, 1\
  1411.     if not obj.Height or not obj.Width then\
  1412.         error('You must provide each object\\'s height when adding to a CollectionView.')\
  1413.     end\
  1414.     local slotHeight = obj.Height + self.SpacingY\
  1415.     local slotWidth = obj.Width + self.SpacingX\
  1416.     local maxX = math.floor((self.Width - 2) / slotWidth)\
  1417.     return x, y, maxX, slotWidth, slotHeight\
  1418. end\
  1419. \
  1420. local function IconLocation(self, obj, i)\
  1421.     local x, y, maxX, slotWidth, slotHeight = MaxIcons(self, obj)\
  1422.     local rowPos = ((i - 1) % maxX)\
  1423.     local colPos = math.ceil(i / maxX) - 1\
  1424.     x = x + (slotWidth * rowPos)\
  1425.     y = y + colPos * slotHeight\
  1426.     return x, y\
  1427. end\
  1428. \
  1429. local function AddItem(self, v, i)\
  1430.     local toggle = false\
  1431.     if not self.CanSelect then\
  1432.         toggle = nil\
  1433.     end\
  1434.     local x, y = IconLocation(self, v, i)\
  1435.     local item = {\
  1436.         [\"X\"]=x,\
  1437.         [\"Y\"]=y,\
  1438.         [\"Name\"]=\"CollectionViewItem\",\
  1439.         [\"Type\"]=\"View\",\
  1440.         [\"TextColour\"]=self.TextColour,\
  1441.         [\"BackgroundColour\"]=0F,\
  1442.         OnClick = function(itm)\
  1443.             if self.CanSelect then\
  1444.                 for i2, _v in ipairs(self.Children) do\
  1445.                     _v.Toggle = false\
  1446.                 end\
  1447.                 self.Selected = itm\
  1448.             end\
  1449.         end\
  1450.    }\
  1451.     for k, _v in pairs(v) do\
  1452.         item[k] = _v\
  1453.     end\
  1454.     self:AddObject(item)\
  1455. end\
  1456. \
  1457. \
  1458. UpdateItems = function(self)\
  1459.     self:RemoveAllObjects()\
  1460.     local groupMode = false\
  1461.     for k, v in pairs(self.Items) do\
  1462.         if type(k) == 'string' then\
  1463.             groupMode = true\
  1464.             break\
  1465.         end\
  1466.     end\
  1467. \
  1468.     for i, v in ipairs(self.Items) do\
  1469.         AddItem(self, v, i)\
  1470.     end\
  1471.     self:UpdateScroll()\
  1472. end\
  1473. \
  1474. OnUpdate = function(self, value)\
  1475.     if value == 'Items' then\
  1476.         self.NeedsItemUpdate = true\
  1477.     end\
  1478. end\
  1479. ]],\
  1480. [\"ImageView\"] = [[\
  1481. Image = false\
  1482. \
  1483. OnDraw = function(self, x, y)\
  1484.     Drawing.DrawImage(x, y, self.Image, self.Width, self.Height)\
  1485. end\
  1486. \
  1487. OnLoad = function(self)\
  1488.     if self.Path and fs.exists(self.Path) then\
  1489.         self.Image = Drawing.LoadImage(self.Path)\
  1490.     end\
  1491. end\
  1492. \
  1493. OnUpdate = function(self, value)\
  1494.     if value == 'Path' then\
  1495.         if self.Path and fs.exists(self.Path) then\
  1496.             self.Image = Drawing.LoadImage(self.Path)\
  1497.         end\
  1498.     end\
  1499. end\
  1500. ]],\
  1501. [\"Label\"] = [[\
  1502. TextColour = colours.black\
  1503. BackgroundColour = colours.transparent\
  1504. Text = \"\"\
  1505. AutoWidth = false\
  1506. Align = 'Left'\
  1507. \
  1508. local wrapText = function(text, maxWidth)\
  1509.    local lines = {''}\
  1510.    for word, space in text:gmatch('(%S+)(%s*)') do\
  1511.        local temp = lines[#lines] .. word .. space:gsub('\\n','')\
  1512.        if #temp > maxWidth then\
  1513.            table.insert(lines, '')\
  1514.        end\
  1515.        if space:find('\\n') then\
  1516.            lines[#lines] = lines[#lines] .. word\
  1517.            \
  1518.            space = space:gsub('\\n', function()\
  1519.                    table.insert(lines, '')\
  1520.                    return ''\
  1521.            end)\
  1522.        else\
  1523.            lines[#lines] = lines[#lines] .. word .. space\
  1524.        end\
  1525.    end\
  1526.    if #lines[1] == 0 then\
  1527.        table.remove(lines,1)\
  1528.    end\
  1529.    return lines\
  1530. end\
  1531. \
  1532. OnUpdate = function(self, value)\
  1533.    if value == 'Text' then\
  1534.        if self.AutoWidth then\
  1535.            self.Width = #self.Text\
  1536.        else\
  1537.            self.Height = #wrapText(self.Text, self.Width)\
  1538.        end\
  1539.    end\
  1540. end\
  1541. \
  1542. OnDraw = function(self, x, y)\
  1543.     for i, v in ipairs(wrapText(self.Text, self.Width)) do\
  1544.        local _x = 0\
  1545.        if self.Align == 'Right' then\
  1546.            _x = self.Width - #v\
  1547.        elseif self.Align == 'Center' then\
  1548.            _x = math.floor((self.Width - #v) / 2)\
  1549.        end\
  1550.         Drawing.DrawCharacters(x + _x, y + i - 1, v, self.TextColour, self.BackgroundColour)\
  1551.     end\
  1552. end\
  1553. ]],\
  1554. [\"ListView\"] = [[\
  1555. Inherit = 'ScrollView'\
  1556. UpdateDrawBlacklist = {['NeedsItemUpdate']=true}\
  1557. \
  1558. TextColour = colours.black\
  1559. BackgroundColour = colours.white\
  1560. HeadingColour = colours.lightGrey\
  1561. SelectionBackgroundColour = colours.blue\
  1562. SelectionTextColour = colours.white\
  1563. Items = false\
  1564. CanSelect = false\
  1565. Selected = nil\
  1566. NeedsItemUpdate = false\
  1567. ItemMargin = 1\
  1568. HeadingMargin = 0\
  1569. TopMargin = 0\
  1570. \
  1571. OnDraw = function(self, x, y)\
  1572.     if self.NeedsItemUpdate then\
  1573.         self:UpdateItems()\
  1574.     end\
  1575.     Drawing.DrawBlankArea(x, y, self.Width, self.Height, self.BackgroundColour)\
  1576. end\
  1577. \
  1578. local function AddItem(self, v, x, y, group)\
  1579.     local toggle = false\
  1580.     if not self.CanSelect then\
  1581.         toggle = nil\
  1582.     elseif v.Selected then\
  1583.         toggle = true\
  1584.     end\
  1585.     local item = {\
  1586.         [\"Width\"]=self.Width,\
  1587.         [\"X\"]=x,\
  1588.         [\"Y\"]=y,\
  1589.         [\"Name\"]=\"ListViewItem\",\
  1590.         [\"Type\"]=\"Button\",\
  1591.         [\"TextColour\"]=self.TextColour,\
  1592.         [\"BackgroundColour\"]=0,\
  1593.         [\"ActiveTextColour\"]=self.SelectionTextColour,\
  1594.         [\"ActiveBackgroundColour\"]=self.SelectionBackgroundColour,\
  1595.         [\"Align\"]='Left',\
  1596.         [\"Toggle\"]=toggle,\
  1597.         [\"Group\"]=group,\
  1598.         OnClick = function(itm)\
  1599.             if self.CanSelect then\
  1600.                 self:SelectItem(itm)\
  1601.             elseif self.OnSelect then\
  1602.                 self:OnSelect(itm.Text)\
  1603.             end\
  1604.         end\
  1605.    }\
  1606.    if type(v) == 'table' then\
  1607.         for k, _v in pairs(v) do\
  1608.             item[k] = _v\
  1609.         end\
  1610.    else\
  1611.         item.Text = v\
  1612.    end\
  1613.     \
  1614.     local itm = self:AddObject(item)\
  1615.     if v.Selected then\
  1616.         self:SelectItem(itm)\
  1617.     end\
  1618. end\
  1619. \
  1620. UpdateItems = function(self)\
  1621.     if not self.Items or type(self.Items) ~= 'table' then\
  1622.         self.Items = {}\
  1623.     end\
  1624.     self.Selected = nil\
  1625.     self:RemoveAllObjects()\
  1626.     local groupMode = false\
  1627.     for k, v in pairs(self.Items) do\
  1628.         if type(k) == 'string' then\
  1629.             groupMode = true\
  1630.             break\
  1631.         end\
  1632.     end\
  1633. \
  1634.     if not groupMode then\
  1635.         for i, v in ipairs(self.Items) do\
  1636.             AddItem(self, v, self.ItemMargin, i)\
  1637.         end\
  1638.     else\
  1639.         local y = self.TopMargin\
  1640.         for k, v in pairs(self.Items) do\
  1641.             y = y + 1\
  1642.             AddItem(self, {Text = k, TextColour = self.HeadingColour, IgnoreClick = true}, self.HeadingMargin, y)\
  1643.             for i, _v in ipairs(v) do\
  1644.                 y = y + 1\
  1645.                 AddItem(self, _v, 1, y, k)\
  1646.             end\
  1647.             y = y + 1\
  1648.         end\
  1649.     end\
  1650.     self:UpdateScroll()\
  1651.     self.NeedsItemUpdate = false\
  1652. end\
  1653. \
  1654. OnKeyChar = function(self, event, keychar)\
  1655.     if keychar == keys.up or keychar == keys.down then\
  1656.         local n = self:GetIndex(self.Selected)\
  1657.         if keychar == keys.up then\
  1658.             n = n - 1\
  1659.         else\
  1660.             n = n + 1\
  1661.         end\
  1662.         local new = self:GetNth(n)\
  1663.         if new then\
  1664.             self:SelectItem(new)\
  1665.         end\
  1666.     elseif keychar == keys.enter and self.Selected then\
  1667.         self.Selected:Click('mouse_click', 1, 1, 1)\
  1668.     end\
  1669. end\
  1670. \
  1671. --returns the index/'n' of the given item\
  1672. GetIndex = function(self, obj)\
  1673.     local n = 1\
  1674.     for i, v in ipairs(self.Children) do\
  1675.         if not v.IgnoreClick then\
  1676.             if obj == v then\
  1677.                 return n\
  1678.             end\
  1679.             n = n + 1\
  1680.         end\
  1681.     end\
  1682. end\
  1683. \
  1684. --gets the 'nth' list item (does not include headings)\
  1685. GetNth = function(self, n)\
  1686.     local _n = 1\
  1687.     for i, v in ipairs(self.Children) do\
  1688.         if not v.IgnoreClick then\
  1689.             if n == _n then\
  1690.                 return v\
  1691.             end\
  1692.             _n = _n + 1\
  1693.         end\
  1694.     end\
  1695. end\
  1696. \
  1697. SelectItem = function(self, item)\
  1698.     for i, v in ipairs(self.Children) do\
  1699.         v.Toggle = false\
  1700.     end\
  1701.     self.Selected = item\
  1702.     item.Toggle = true\
  1703.     if self.OnSelect then\
  1704.         self:OnSelect(item.Text)\
  1705.     end\
  1706. end\
  1707. \
  1708. OnUpdate = function(self, value)\
  1709.     if value == 'Items' then\
  1710.         self.NeedsItemUpdate = true\
  1711.     end\
  1712. end\
  1713. ]],\
  1714. [\"Menu\"] = [[\
  1715. Inherit = 'View'\
  1716. \
  1717. TextColour = colours.black\
  1718. BackgroundColour = colours.white\
  1719. HideTop = false\
  1720. \
  1721. OnDraw = function(self, x, y)\
  1722.     Drawing.IgnoreConstraint = true\
  1723.     Drawing.DrawBlankArea(x + 1, y + (self.HideTop and 0 or 1), self.Width, self.Height + (self.HideTop and 1 or 0), colours.grey)\
  1724.     Drawing.IgnoreConstraint = false\
  1725.     Drawing.DrawBlankArea(x, y, self.Width, self.Height, self.BackgroundColour)\
  1726. end\
  1727. \
  1728. OnLoad = function(self)\
  1729.     local owner = self.Owner\
  1730.     if type(owner) == 'string' then\
  1731.         owner = self.Bedrock:GetObject(self.Owner)\
  1732.     end\
  1733. \
  1734.     if owner then\
  1735.         if self.X == 0 and self.Y == 0 then\
  1736.             local pos = owner:GetPosition()\
  1737.             self.X = pos.X\
  1738.             self.Y = pos.Y + owner.Height\
  1739.         end\
  1740.         self.Owner = owner\
  1741.     else\
  1742.         self.Owner = nil\
  1743.     end\
  1744. end\
  1745. \
  1746. OnUpdate = function(self, value)\
  1747.     if value == 'Children' then\
  1748.         self.Width = self.Bedrock.Helpers.LongestString(self.Children, 'Text') + 2\
  1749.         self.Height = #self.Children + 1 + (self.HideTop and 0 or 1)\
  1750.         if not self.BaseY then\
  1751.             self.BaseY = self.Y\
  1752.         end\
  1753. \
  1754.         for i, v in ipairs(self.Children) do\
  1755.             if v.TextColour then\
  1756.                 v.TextColour = self.TextColour\
  1757.             end\
  1758.             if v.BackgroundColour then\
  1759.                 v.BackgroundColour = colours.transparent\
  1760.             end\
  1761.             if v.Colour then\
  1762.                 v.Colour = colours.lightGrey\
  1763.             end\
  1764.             v.Align = 'Left'\
  1765.             v.X = 1\
  1766.             v.Y = i + (self.HideTop and 0 or 1)\
  1767.             v.Width = self.Width\
  1768.             v.Height = 1\
  1769.         end\
  1770. \
  1771.         self.Y = self.BaseY\
  1772.         local pos = self:GetPosition()\
  1773.         if pos.Y + self.Height + 1 > Drawing.Screen.Height then\
  1774.             self.Y = self.BaseY - ((self.Height +  pos.Y) - Drawing.Screen.Height)\
  1775.         end\
  1776.         \
  1777.         if pos.X + self.Width > Drawing.Screen.Width then\
  1778.             self.X = Drawing.Screen.Width - self.Width\
  1779.         end\
  1780.     end\
  1781. end\
  1782. \
  1783. Close = function(self, isBedrockCall)\
  1784.     self.Bedrock.Menu = nil\
  1785.     self.Parent:RemoveObject(self)\
  1786.     if self.Owner and self.Owner.Toggle then\
  1787.         self.Owner.Toggle = false\
  1788.     end\
  1789.     self.Parent:ForceDraw()\
  1790.     self = nil\
  1791. end\
  1792. \
  1793. OnChildClick = function(self, child, event, side, x, y)\
  1794.     self:Close()\
  1795. end\
  1796. ]],\
  1797. [\"ProgressBar\"] = [[\
  1798. BackgroundColour = colours.lightGrey\
  1799. BarColour = colours.blue\
  1800. TextColour = colours.white\
  1801. ShowText = false\
  1802. Value = 0\
  1803. Maximum = 1\
  1804. Indeterminate = false\
  1805. AnimationStep = 0\
  1806. \
  1807. OnDraw = function(self, x, y)\
  1808.     Drawing.DrawBlankArea(x, y, self.Width, self.Height, self.BackgroundColour)\
  1809. \
  1810.     -- if self.Indeterminate then\
  1811.     --  for i = 1, self.Width do\
  1812.     --      local s = x + i - 1 + self.AnimationStep\
  1813.     --      if s % 4 == 1 or s % 4 == 2 then\
  1814.     --          Drawing.DrawBlankArea(s, y, 1, self.Height, self.BarColour)\
  1815.     --      end\
  1816.     --  end\
  1817.     --  self.AnimationStep = self.AnimationStep + 1\
  1818.     --  if self.AnimationStep >= 4 then\
  1819.     --      self.AnimationStep = 0\
  1820.     --  end\
  1821.     --  self.Bedrock:StartTimer(function()\
  1822.     --      self:Draw()\
  1823.     --  end, 0.25)\
  1824.     -- else\
  1825.         local values = self.Value\
  1826.         local barColours = self.BarColour\
  1827.         if type(values) == 'number' then\
  1828.             values = {values}\
  1829.         end\
  1830.         if type(barColours) == 'number' then\
  1831.             barColours = {barColours}\
  1832.         end\
  1833.         local total = 0\
  1834.         local _x = x\
  1835.         for i, v in ipairs(values) do\
  1836.             local width = self.Bedrock.Helpers.Round((v / self.Maximum) * self.Width)\
  1837.             total = total + v\
  1838.             Drawing.DrawBlankArea(_x, y, width, self.Height, barColours[((i-1)%#barColours)+1])\
  1839.             _x = _x + width\
  1840.         end\
  1841. \
  1842.         if self.ShowText then\
  1843.             local text = self.Bedrock.Helpers.Round((total / self.Maximum) * 100) .. '%'\
  1844.             Drawing.DrawCharactersCenter(x, y, self.Width, self.Height, text, self.TextColour, colours.transparent)\
  1845.         end\
  1846.     -- end\
  1847. end\
  1848. ]],\
  1849. [\"ScrollBar\"] = [[\
  1850. BackgroundColour = colours.lightGrey\
  1851. BarColour = colours.lightBlue\
  1852. Scroll = 0\
  1853. MaxScroll = 0\
  1854. ClickPoint = nil\
  1855. Fixed = true\
  1856. \
  1857. OnUpdate = function(self, value)\
  1858.     if value == 'Text' and self.AutoWidth then\
  1859.         self.Width = #self.Text + 2\
  1860.     end\
  1861. end\
  1862. \
  1863. OnDraw = function(self, x, y)\
  1864.     local barHeight = self.Height * (self.Height / (self.Height + self.MaxScroll))\
  1865.    if barHeight < 3 then\
  1866.      barHeight = 3\
  1867.    end\
  1868.    local percentage = (self.Scroll/self.MaxScroll)\
  1869. \
  1870.    Drawing.DrawBlankArea(x, y, self.Width, self.Height, self.BackgroundColour)\
  1871.    Drawing.DrawBlankArea(x, y + math.ceil(self.Height*percentage - barHeight*percentage), self.Width, barHeight, self.BarColour)\
  1872. end\
  1873. \
  1874. OnScroll = function(self, event, direction, x, y)\
  1875.     if event == 'mouse_scroll' then\
  1876.         direction = self.Bedrock.Helpers.Round(direction * 3)\
  1877.     end\
  1878.     if self.Scroll < 0 or self.Scroll > self.MaxScroll then\
  1879.         return false\
  1880.     end\
  1881.     local old = self.Scroll\
  1882.     self.Scroll = self.Bedrock.Helpers.Round(self.Scroll + direction)\
  1883.     if self.Scroll < 0 then\
  1884.         self.Scroll = 0\
  1885.     elseif self.Scroll > self.MaxScroll then\
  1886.         self.Scroll = self.MaxScroll\
  1887.     end\
  1888. \
  1889.     if self.Scroll ~= old and self.OnChange then\
  1890.         self:OnChange()\
  1891.     end\
  1892. end\
  1893. \
  1894. OnClick = function(self, event, side, x, y)\
  1895.     if event == 'mouse_click' then\
  1896.         self.ClickPoint = y\
  1897.     else\
  1898.         local gapHeight = self.Height - (self.Height * (self.Height / (self.Height + self.MaxScroll)))\
  1899.         local barHeight = self.Height * (self.Height / (self.Height + self.MaxScroll))\
  1900.         --local delta = (self.Height + self.MaxScroll) * ((y - self.ClickPoint) / barHeight)\
  1901.         local delta = ((y - self.ClickPoint)/gapHeight)*self.MaxScroll\
  1902.         --l(((y - self.ClickPoint)/gapHeight))\
  1903.         --l(delta)\
  1904.         self.Scroll = self.Bedrock.Helpers.Round(delta)\
  1905.         --l(self.Scroll)\
  1906.         --l('----')\
  1907.         if self.Scroll < 0 then\
  1908.             self.Scroll = 0\
  1909.         elseif self.Scroll > self.MaxScroll then\
  1910.             self.Scroll = self.MaxScroll\
  1911.         end\
  1912.         if self.OnChange then\
  1913.             self:OnChange()\
  1914.         end\
  1915.     end\
  1916. \
  1917.     local relScroll = self.MaxScroll * ((y-1)/self.Height)\
  1918.     if y == self.Height then\
  1919.         relScroll = self.MaxScroll\
  1920.     end\
  1921.     self.Scroll = self.Bedrock.Helpers.Round(relScroll)\
  1922. \
  1923. \
  1924. end\
  1925. \
  1926. OnDrag = OnClick\
  1927. ]],\
  1928. [\"ScrollView\"] = [[\
  1929. Inherit = 'View'\
  1930. ChildOffset = false\
  1931. ContentWidth = 0\
  1932. ContentHeight = 0\
  1933. ScrollBarBackgroundColour = colours.lightGrey\
  1934. ScrollBarColour = colours.lightBlue\
  1935. \
  1936. CalculateContentSize = function(self)\
  1937.     local function calculateObject(obj)\
  1938.         local pos = obj:GetPosition()\
  1939.         local x2 = pos.X + obj.Width - 1\
  1940.         local y2 = pos.Y + obj.Height - 1\
  1941.         if obj.Children then\
  1942.             for i, child in ipairs(obj.Children) do\
  1943.                 local _x2, _y2 = calculateObject(child)\
  1944.                 if _x2 > x2 then\
  1945.                     x2 = _x2\
  1946.                 end\
  1947.                 if _y2 > y2 then\
  1948.                     y2 = _y2\
  1949.                 end\
  1950.             end\
  1951.         end\
  1952.         return x2, y2\
  1953.     end\
  1954. \
  1955.     local pos = self:GetPosition()\
  1956.     local x2, y2 = calculateObject(self)\
  1957.     self.ContentWidth = x2 - pos.X + 1\
  1958.     self.ContentHeight = y2 - pos.Y + 1\
  1959. end\
  1960. \
  1961. UpdateScroll = function(self)\
  1962.     self.ChildOffset.Y = 0\
  1963.     self:CalculateContentSize()\
  1964.     if self.ContentHeight > self.Height then\
  1965.         if not self:GetObject('ScrollViewScrollBar') then\
  1966.             local _scrollBar = self:AddObject({\
  1967.                 [\"Name\"] = 'ScrollViewScrollBar',\
  1968.                 [\"Type\"] = 'ScrollBar',\
  1969.                 [\"X\"] = self.Width,\
  1970.                 [\"Y\"] = 1,\
  1971.                 [\"Width\"] = 1,\
  1972.                 [\"Height\"] = self.Height,\
  1973.                 [\"BackgroundColour\"] = self.ScrollBarBackgroundColour,\
  1974.                 [\"BarColour\"] = self.ScrollBarColour,\
  1975.                 [\"Z\"]=999\
  1976.             })\
  1977. \
  1978.             _scrollBar.OnChange = function(scrollBar)\
  1979.                 self.ChildOffset.Y = -scrollBar.Scroll\
  1980.                 for i, child in ipairs(self.Children) do\
  1981.                     child:ForceDraw()\
  1982.                 end\
  1983.             end\
  1984.         end\
  1985.         self:GetObject('ScrollViewScrollBar').MaxScroll = self.ContentHeight - self.Height\
  1986.     else\
  1987.         self:RemoveObject('ScrollViewScrollBar')\
  1988.     end\
  1989. end\
  1990. \
  1991. OnScroll = function(self, event, direction, x, y)\
  1992.     if self:GetObject('ScrollViewScrollBar') then\
  1993.         self:GetObject('ScrollViewScrollBar'):OnScroll(event, direction, x, y)\
  1994.     end\
  1995. end\
  1996. \
  1997. OnLoad = function(self)\
  1998.     if not self.ChildOffset or not self.ChildOffset.X or not self.ChildOffset.Y then\
  1999.         self.ChildOffset = {X = 0, Y = 0}\
  2000.     end\
  2001. end\
  2002. ]],\
  2003. [\"SecureTextBox\"] = [[\
  2004. Inherit = 'TextBox'\
  2005. MaskCharacter = '*'\
  2006. \
  2007. OnDraw = function(self, x, y)\
  2008.     Drawing.DrawBlankArea(x, y, self.Width, self.Height, self.BackgroundColour)\
  2009.     if self.CursorPos > #self.Text then\
  2010.         self.CursorPos = #self.Text\
  2011.     elseif self.CursorPos < 0 then\
  2012.         self.CursorPos = 0\
  2013.     end\
  2014.     local text = ''\
  2015. \
  2016.     for i = 1, #self.Text do\
  2017.         text = text .. self.MaskCharacter\
  2018.     end\
  2019. \
  2020.     if self.Bedrock:GetActiveObject() == self then\
  2021.         if #text > (self.Width - 2) then\
  2022.             text = text:sub(#text-(self.Width - 3))\
  2023.             self.Bedrock.CursorPos = {x + 1 + self.Width-2, y}\
  2024.         else\
  2025.             self.Bedrock.CursorPos = {x + 1 + self.CursorPos, y}\
  2026.         end\
  2027.         self.Bedrock.CursorColour = self.TextColour\
  2028.     end\
  2029. \
  2030.     if #tostring(text) == 0 then\
  2031.         Drawing.DrawCharacters(x + 1, y, self.Placeholder, self.PlaceholderTextColour, self.BackgroundColour)\
  2032.     else\
  2033.         if not self.Selected then\
  2034.             Drawing.DrawCharacters(x + 1, y, text, self.TextColour, self.BackgroundColour)\
  2035.         else\
  2036.             for i = 1, #text do\
  2037.                 local char = text:sub(i, i)\
  2038.                 local textColour = self.TextColour\
  2039.                 local backgroundColour = self.BackgroundColour\
  2040.                 if i > self.DragStart and i - 1 <= self.CursorPos then\
  2041.                     textColour = self.SelectedTextColour\
  2042.                     backgroundColour = self.SelectedBackgroundColour\
  2043.                 end\
  2044.                 Drawing.DrawCharacters(x + i, y, char, textColour, backgroundColour)\
  2045.             end\
  2046.         end\
  2047.     end\
  2048. end\
  2049. ]],\
  2050. [\"Separator\"] = [[\
  2051. Colour = colours.grey\
  2052. \
  2053. OnDraw = function(self, x, y)\
  2054.     local char = \"|\"\
  2055.     if self.Width > self.Height then\
  2056.         char = '-'\
  2057.     end\
  2058.     Drawing.DrawArea(x, y, self.Width, self.Height, char, self.Colour, colours.transparent)\
  2059. end\
  2060. ]],\
  2061. [\"TextBox\"] = [[\
  2062. BackgroundColour = colours.lightGrey\
  2063. SelectedBackgroundColour = colours.blue\
  2064. SelectedTextColour = colours.white\
  2065. TextColour = colours.black\
  2066. PlaceholderTextColour = colours.grey\
  2067. Placeholder = ''\
  2068. AutoWidth = false\
  2069. Text = \"\"\
  2070. CursorPos = nil\
  2071. Numerical = false\
  2072. DragStart = nil\
  2073. Selected = false\
  2074. SelectOnClick = false\
  2075. ActualDragStart = nil\
  2076. \
  2077. OnDraw = function(self, x, y)\
  2078.     Drawing.DrawBlankArea(x, y, self.Width, self.Height, self.BackgroundColour)\
  2079.     if self.CursorPos > #self.Text then\
  2080.         self.CursorPos = #self.Text\
  2081.     elseif self.CursorPos < 0 then\
  2082.         self.CursorPos = 0\
  2083.     end\
  2084.     local text = self.Text\
  2085.     local offset = self:TextOffset()\
  2086.     if #text > (self.Width - 2) then\
  2087.         text = text:sub(offset+1, offset + self.Width - 2)\
  2088.         -- self.Bedrock.CursorPos = {x + 1 + self.Width-2, y}\
  2089.     -- else\
  2090.     end\
  2091.     if self.Bedrock:GetActiveObject() == self then\
  2092.         self.Bedrock.CursorPos = {x + 1 + self.CursorPos - offset, y}\
  2093.         self.Bedrock.CursorColour = self.TextColour\
  2094.     else\
  2095.         self.Selected = false\
  2096.     end\
  2097. \
  2098.     if #tostring(text) == 0 then\
  2099.         Drawing.DrawCharacters(x + 1, y, self.Placeholder, self.PlaceholderTextColour, self.BackgroundColour)\
  2100.     else\
  2101.         if not self.Selected then\
  2102.             Drawing.DrawCharacters(x + 1, y, text, self.TextColour, self.BackgroundColour)\
  2103.         else\
  2104.             local startPos = self.DragStart - offset\
  2105.             local endPos = self.CursorPos - offset\
  2106.             if startPos > endPos then\
  2107.                 startPos = self.CursorPos - offset\
  2108.                 endPos = self.DragStart - offset\
  2109.             end\
  2110.             for i = 1, #text do\
  2111.                 local char = text:sub(i, i)\
  2112.                 local textColour = self.TextColour\
  2113.                 local backgroundColour = self.BackgroundColour\
  2114. \
  2115.                 if i > startPos and i - 1 <= endPos then\
  2116.                     textColour = self.SelectedTextColour\
  2117.                     backgroundColour = self.SelectedBackgroundColour\
  2118.                 end\
  2119.                 Drawing.DrawCharacters(x + i, y, char, textColour, backgroundColour)\
  2120.             end\
  2121.         end\
  2122.     end\
  2123. end\
  2124. \
  2125. TextOffset = function(self)\
  2126.     if #self.Text < (self.Width - 2) then\
  2127.         return 0\
  2128.     elseif self.Bedrock:GetActiveObject() ~= self then\
  2129.         return 0\
  2130.     else\
  2131.         local textWidth = (self.Width - 2)\
  2132.         local offset = self.CursorPos - textWidth\
  2133.         if offset < 0 then\
  2134.             offset = 0\
  2135.         end\
  2136.         return offset\
  2137.     end\
  2138. end\
  2139. \
  2140. OnLoad = function(self)\
  2141.     if not self.CursorPos then\
  2142.         self.CursorPos = #self.Text\
  2143.     end\
  2144. end\
  2145. \
  2146. OnClick = function(self, event, side, x, y)\
  2147.     if self.Bedrock:GetActiveObject() ~= self and self.SelectOnClick then\
  2148.         self.CursorPos = #self.Text - 1\
  2149.         self.DragStart = 0\
  2150.         self.ActualDragStart = x - 2 + self:TextOffset()\
  2151.         self.Selected = true\
  2152.     else\
  2153.         self.CursorPos = x - 2 + self:TextOffset()\
  2154.         self.DragStart = self.CursorPos\
  2155.         self.Selected = false\
  2156.     end\
  2157.     self.Bedrock:SetActiveObject(self)\
  2158. end\
  2159. \
  2160. OnDrag = function(self, event, side, x, y)\
  2161.     self.CursorPos = x - 2 + self:TextOffset()\
  2162.     if self.ActualDragStart then\
  2163.         self.DragStart = self.ActualDragStart\
  2164.         self.ActualDragStart = nil\
  2165.     end\
  2166.     if self.DragStart then\
  2167.         self.Selected = true\
  2168.     end\
  2169. end\
  2170. \
  2171. OnKeyChar = function(self, event, keychar)\
  2172.     local deleteSelected = function()\
  2173.         if self.Selected then\
  2174.             local startPos = self.DragStart\
  2175.             local endPos = self.CursorPos\
  2176.             if startPos > endPos then\
  2177.                 startPos = self.CursorPos\
  2178.                 endPos = self.DragStart\
  2179.             end\
  2180.             self.Text = self.Text:sub(1, startPos) .. self.Text:sub(endPos + 2)\
  2181.             self.CursorPos = startPos\
  2182.             self.DragStart = nil\
  2183.             self.Selected = false\
  2184.             return true\
  2185.         end\
  2186.     end\
  2187. \
  2188.     if event == 'char' then\
  2189.         deleteSelected()\
  2190.         if self.Numerical then\
  2191.             keychar = tostring(tonumber(keychar))\
  2192.         end\
  2193.         if keychar == 'nil' then\
  2194.             return\
  2195.         end\
  2196.         self.Text = string.sub(self.Text, 1, self.CursorPos ) .. keychar .. string.sub( self.Text, self.CursorPos + 1 )\
  2197.         if self.Numerical then\
  2198.             self.Text = tostring(tonumber(self.Text))\
  2199.             if self.Text == 'nil' then\
  2200.                 self.Text = '1'\
  2201.             end\
  2202.         end\
  2203.         \
  2204.         self.CursorPos = self.CursorPos + 1\
  2205.         if self.OnChange then\
  2206.             self:OnChange(event, keychar)\
  2207.         end\
  2208.         return false\
  2209.     elseif event == 'key' then\
  2210.         if keychar == keys.enter then\
  2211.             if self.OnChange then\
  2212.                 self:OnChange(event, keychar)\
  2213.             end\
  2214.         elseif keychar == keys.left then\
  2215.             -- Left\
  2216.             if self.CursorPos > 0 then\
  2217.                 if self.Selected then\
  2218.                     self.CursorPos = self.DragStart\
  2219.                     self.DragStart = nil\
  2220.                     self.Selected = false\
  2221.                 else\
  2222.                     self.CursorPos = self.CursorPos - 1\
  2223.                 end\
  2224.                 if self.OnChange then\
  2225.                     self:OnChange(event, keychar)\
  2226.                 end\
  2227.             end\
  2228.             \
  2229.         elseif keychar == keys.right then\
  2230.             -- Right                \
  2231.             if self.CursorPos < string.len(self.Text) then\
  2232.                 if self.Selected then\
  2233.                     self.CursorPos = self.CursorPos\
  2234.                     self.DragStart = nil\
  2235.                     self.Selected = false\
  2236.                 else\
  2237.                     self.CursorPos = self.CursorPos + 1\
  2238.                 end\
  2239.                 if self.OnChange then\
  2240.                     self:OnChange(event, keychar)\
  2241.                 end\
  2242.             end\
  2243.         \
  2244.         elseif keychar == keys.backspace then\
  2245.             -- Backspace\
  2246.             if not deleteSelected() and self.CursorPos > 0 then\
  2247.                 self.Text = string.sub( self.Text, 1, self.CursorPos - 1 ) .. string.sub( self.Text, self.CursorPos + 1 )\
  2248.                 self.CursorPos = self.CursorPos - 1                 \
  2249.                 if self.Numerical then\
  2250.                     self.Text = tostring(tonumber(self.Text))\
  2251.                     if self.Text == 'nil' then\
  2252.                         self.Text = '1'\
  2253.                     end\
  2254.                 end\
  2255.                 if self.OnChange then\
  2256.                     self:OnChange(event, keychar)\
  2257.                 end\
  2258.             end\
  2259.         elseif keychar == keys.home then\
  2260.             -- Home\
  2261.             self.CursorPos = 0\
  2262.             if self.OnChange then\
  2263.                 self:OnChange(event, keychar)\
  2264.             end\
  2265.         elseif keychar == keys.delete then\
  2266.             if not deleteSelected() and self.CursorPos < string.len(self.Text) then\
  2267.                 self.Text = string.sub( self.Text, 1, self.CursorPos ) .. string.sub( self.Text, self.CursorPos + 2 )       \
  2268.                 if self.Numerical then\
  2269.                     self.Text = tostring(tonumber(self.Text))\
  2270.                     if self.Text == 'nil' then\
  2271.                         self.Text = '1'\
  2272.                     end\
  2273.                 end\
  2274.                 if self.OnChange then\
  2275.                     self:OnChange(keychar)\
  2276.                 end\
  2277.             end\
  2278.         elseif keychar == keys[\"end\"] then\
  2279.             -- End\
  2280.             self.CursorPos = string.len(self.Text)\
  2281.         else\
  2282.             if self.OnChange then\
  2283.                 self:OnChange(event, keychar)\
  2284.             end\
  2285.             return false\
  2286.         end\
  2287.     end\
  2288. end\
  2289. ]],\
  2290. [\"View\"] = [[\
  2291. BackgroundColour = colours.transparent\
  2292. Children = {}\
  2293. \
  2294. OnDraw = function(self, x, y)\
  2295.     if self.BackgroundColour then\
  2296.         Drawing.DrawBlankArea(x, y, self.Width, self.Height, self.BackgroundColour)\
  2297.     end\
  2298. end\
  2299. \
  2300. OnInitialise = function(self)\
  2301.     self.Children = {}\
  2302. end\
  2303. \
  2304. InitialiseFile = function(self, bedrock, file, name)\
  2305.     local _new = {}\
  2306.     _new.X = 1\
  2307.     _new.Y = 1\
  2308.     _new.Width = Drawing.Screen.Width\
  2309.     _new.Height = Drawing.Screen.Height\
  2310.     _new.BackgroundColour = file.BackgroundColour\
  2311.     _new.Name = name\
  2312.     _new.Children = {}\
  2313.     _new.Bedrock = bedrock\
  2314.     local new = self:Initialise(_new)\
  2315.     for i, obj in ipairs(file.Children) do\
  2316.         local view = bedrock:ObjectFromFile(obj, new)\
  2317.         if not view.Z then\
  2318.             view.Z = i\
  2319.         end\
  2320.         view.Parent = new\
  2321.         table.insert(new.Children, view)\
  2322.     end\
  2323.     return new\
  2324. end\
  2325. \
  2326. function CheckClick(self, object, x, y)\
  2327.     local offset = {X = 0, Y = 0}\
  2328.     if not object.Fixed and self.ChildOffset then\
  2329.         offset = self.ChildOffset\
  2330.     end\
  2331.     if object.X + offset.X <= x and object.Y + offset.Y <= y and  object.X + offset.X + object.Width > x and object.Y + offset.Y + object.Height > y then\
  2332.         return true\
  2333.     end\
  2334. end\
  2335. \
  2336. function DoClick(self, object, event, side, x, y)\
  2337.     if object then\
  2338.         if self:CheckClick(object, x, y) then\
  2339.             local offset = {X = 0, Y = 0}\
  2340.             if not object.Fixed and self.ChildOffset then\
  2341.                 offset = self.ChildOffset\
  2342.             end\
  2343.             return object:Click(event, side, x - object.X - offset.X + 1, y - object.Y + 1 - offset.Y)\
  2344.         end\
  2345.     end \
  2346. end\
  2347. \
  2348. Click = function(self, event, side, x, y, z)\
  2349.     if self.Visible and not self.IgnoreClick then\
  2350.         for i = #self.Children, 1, -1 do --children are ordered from smallest Z to highest, so this is done in reverse\
  2351.             local child = self.Children[i]\
  2352.             if self:DoClick(child, event, side, x, y) then\
  2353.                 if self.OnChildClick then\
  2354.                     self:OnChildClick(child, event, side, x, y)\
  2355.                 end\
  2356.                 return true\
  2357.             end\
  2358.         end\
  2359.         if event == 'mouse_click' and self.OnClick and self:OnClick(event, side, x, y) ~= false then\
  2360.             return true\
  2361.         elseif event == 'mouse_drag' and self.OnDrag and self:OnDrag(event, side, x, y) ~= false then\
  2362.             return true\
  2363.         elseif event == 'mouse_scroll' and self.OnScroll and self:OnScroll(event, side, x, y) ~= false then\
  2364.             return true\
  2365.         else\
  2366.             return false\
  2367.         end\
  2368.     else\
  2369.         return false\
  2370.     end\
  2371. end\
  2372. \
  2373. OnRemove = function(self)\
  2374.     if self == self.Bedrock:GetActiveObject() then\
  2375.         self.Bedrock:SetActiveObject()\
  2376.     end\
  2377.     for i, child in ipairs(self.Children) do\
  2378.         child:OnRemove()\
  2379.     end\
  2380. end\
  2381. \
  2382. local function findObjectNamed(view, name, minI)\
  2383.     local minI = minI or 0\
  2384.     if view and view.Children then\
  2385.         for i, child in ipairs(view.Children) do\
  2386.             if child.Name == name or child == name then\
  2387.                 return child, i, view\
  2388.             elseif child.Children then\
  2389.                 local found, index, foundView = findObjectNamed(child, name)\
  2390.                 if found and minI <= index then\
  2391.                     return found, index, foundView\
  2392.                 end\
  2393.             end\
  2394.         end\
  2395.     end\
  2396. end\
  2397. \
  2398. function AddObject(self, info, extra)\
  2399.     if type(info) == 'string' then\
  2400.         local h = fs.open(self.Bedrock.ViewPath..info..'.view', 'r')\
  2401.         if h then\
  2402.             info = textutils.unserialize(h.readAll())\
  2403.             h.close()\
  2404.         else\
  2405.             error('Error in opening object: '..info)\
  2406.         end\
  2407.     end\
  2408. \
  2409.     if extra then\
  2410.         for k, v in pairs(extra) do\
  2411.             if v then\
  2412.                 info[k] = v\
  2413.             end\
  2414.         end\
  2415.     end\
  2416. \
  2417.     local view = self.Bedrock:ObjectFromFile(info, self)\
  2418.     if not view.Z then\
  2419.         view.Z = #self.Children + 1\
  2420.     end\
  2421.     \
  2422.     table.insert(self.Children, view)\
  2423.     if self.Bedrock.View then\
  2424.         self.Bedrock:ReorderObjects()\
  2425.     end\
  2426.     self:ForceDraw()\
  2427.     return view\
  2428. end\
  2429. \
  2430. function GetObject(self, name)\
  2431.     return findObjectNamed(self, name)\
  2432. end\
  2433. \
  2434. local function findObjects(view, name)\
  2435.     local objects = {}\
  2436.     if view and view.Children then\
  2437.         for i, child in ipairs(view.Children) do\
  2438.             if child.Name == name or child == name then\
  2439.                 table.insert(objects, child)\
  2440.             elseif child.Children then\
  2441.                 local objs = findObjects(child, name)\
  2442.                 if objs then\
  2443.                     for i2, v in ipairs(objs) do\
  2444.                         table.insert(objects, v)\
  2445.                     end\
  2446.                 end\
  2447.             end\
  2448.         end\
  2449.     end\
  2450.     return objects\
  2451. end\
  2452. \
  2453. function GetObjects(self, name)\
  2454.     return findObjects(self, name)\
  2455. end\
  2456. \
  2457. function RemoveObject(self, name)\
  2458.     local obj, index, view = findObjectNamed(self, name, minI)\
  2459.     if index then\
  2460.         view.Children[index]:OnRemove()\
  2461.         table.remove(view.Children, index)\
  2462.         if view.OnUpdate then\
  2463.             view:OnUpdate('Children')\
  2464.         end\
  2465.         return true\
  2466.     end\
  2467.     return false\
  2468. end\
  2469. \
  2470. function RemoveObjects(self, name)\
  2471.     local i = 1\
  2472.     while self:RemoveObject(name) and i < 100 do\
  2473.         i = i + 1\
  2474.     end\
  2475.     \
  2476. end\
  2477. \
  2478. function RemoveAllObjects(self)\
  2479.     for i, child in ipairs(self.Children) do\
  2480.         child:OnRemove()\
  2481.         self.Children[i] = nil\
  2482.     end\
  2483.     self:ForceDraw()\
  2484. end\
  2485. ]],\
  2486. [\"Window\"] = [[\
  2487. Inherit = 'View'\
  2488. \
  2489. ToolBarColour = colours.lightGrey\
  2490. ToolBarTextColour = colours.black\
  2491. ShadowColour = colours.grey\
  2492. Title = ''\
  2493. Flashing = false\
  2494. CanClose = true\
  2495. OnCloseButton = nil\
  2496. OldActiveObject = nil\
  2497. \
  2498. OnLoad = function(self)\
  2499.     --self:GetObject('View') = self.Bedrock:ObjectFromFile({Type = 'View',Width = 10, Height = 5, BackgroundColour = colours.red}, self)\
  2500. end\
  2501. \
  2502. LoadView = function(self)\
  2503.     local view = self:GetObject('View')\
  2504.     if view.ToolBarColour then\
  2505.         window.ToolBarColour = view.ToolBarColour\
  2506.     end\
  2507.     if view.ToolBarTextColour then\
  2508.         window.ToolBarTextColour = view.ToolBarTextColour\
  2509.     end\
  2510.     view.X = 1\
  2511.     view.Y = 2\
  2512. \
  2513.     view:ForceDraw()\
  2514.     self:OnUpdate('View')\
  2515.     if self.OnViewLoad then\
  2516.         self.OnViewLoad(view)\
  2517.     end\
  2518.     self.OldActiveObject = self.Bedrock:GetActiveObject()\
  2519.     self.Bedrock:SetActiveObject(view)\
  2520. end\
  2521. \
  2522. SetView = function(self, view)\
  2523.     self:RemoveObject('View')\
  2524.     table.insert(self.Children, view)\
  2525.     view.Parent = self\
  2526.     self:LoadView()\
  2527. end\
  2528. \
  2529. Flash = function(self)\
  2530.     self.Flashing = true\
  2531.     self:ForceDraw()\
  2532.     self.Bedrock:StartTimer(function()self.Flashing = false end, 0.4)\
  2533. end\
  2534. \
  2535. OnDraw = function(self, x, y)\
  2536.     local toolBarColour = (self.Flashing and colours.white or self.ToolBarColour)\
  2537.     local toolBarTextColour = (self.Flashing and colours.black or self.ToolBarTextColour)\
  2538.     if toolBarColour then\
  2539.         Drawing.DrawBlankArea(x, y, self.Width, 1, toolBarColour)\
  2540.     end\
  2541.     if toolBarTextColour then\
  2542.         local title = self.Bedrock.Helpers.TruncateString(self.Title, self.Width - 2)\
  2543.         Drawing.DrawCharactersCenter(self.X, self.Y, self.Width, 1, title, toolBarTextColour, toolBarColour)\
  2544.     end\
  2545.     Drawing.IgnoreConstraint = true\
  2546.     Drawing.DrawBlankArea(x + 1, y + 1, self.Width, self.Height, self.ShadowColour)\
  2547.     Drawing.IgnoreConstraint = false\
  2548. end\
  2549. \
  2550. Close = function(self)\
  2551.     self.Bedrock:SetActiveObject(self.OldActiveObject)\
  2552.     self.Bedrock.Window = nil\
  2553.     self.Bedrock:RemoveObject(self)\
  2554.     if self.OnClose then\
  2555.         self:OnClose()\
  2556.     end\
  2557.     self = nil\
  2558. end\
  2559. \
  2560. OnUpdate = function(self, value)\
  2561.     if value == 'View' and self:GetObject('View') then\
  2562.         self.Width = self:GetObject('View').Width\
  2563.         self.Height = self:GetObject('View').Height + 1\
  2564.         self.X = math.ceil((Drawing.Screen.Width - self.Width) / 2)\
  2565.         self.Y = math.ceil((Drawing.Screen.Height - self.Height) / 2)\
  2566.     elseif value == 'CanClose' then\
  2567.         self:RemoveObject('CloseButton')\
  2568.         if self.CanClose then\
  2569.             local button = self:AddObject({X = 1, Y = 1, Width = 1, Height = 1, Type = 'Button', BackgroundColour = colours.red, TextColour = colours.white, Text = 'x', Name = 'CloseButton'})\
  2570.             button.OnClick = function(btn)\
  2571.                 if self.OnCloseButton then\
  2572.                     self:OnCloseButton()\
  2573.                 end\
  2574.                 self:Close()\
  2575.             end\
  2576.         end\
  2577.     end\
  2578. end\
  2579. ]],\
  2580. }\
  2581. \
  2582. BasePath = ''\
  2583. ProgramPath = nil\
  2584. \
  2585. -- Program functions...\
  2586. \
  2587. local function main(...)\
  2588.     -- Code here...\
  2589. end\
  2590. \
  2591. -- Run\
  2592. local args = {...}\
  2593. local _, err = pcall(function() main(unpack(args)) end)\
  2594. if err then\
  2595.     -- Make a nice error handling screen here...\
  2596.     term.setBackgroundColor(colors.black)\
  2597.     term.setTextColor(colors.white)\
  2598.     term.clear()\
  2599.     term.setCursorPos(1, 3)\
  2600.     print(\" An Error Has Occured! D:\\n\\n\")\
  2601.     print(\" \" .. tostring(err) .. \"\\n\\n\")\
  2602.     print(\" Press any key to exit...\")\
  2603.     os.pullEvent(\"key\")\
  2604. end\
  2605. \
  2606. \
  2607. \
  2608. function LoadAPIs(self)\
  2609.     local function loadAPI(name, content)\
  2610.         local env = setmetatable({}, { __index = getfenv() })\
  2611.         local func, err = loadstring(content, name..' (Bedrock API)')\
  2612.         if not func then\
  2613.             return false, printError(err)\
  2614.         end\
  2615.         setfenv(func, env)\
  2616.         func()\
  2617.         local api = {}\
  2618.         for k,v in pairs(env) do\
  2619.             api[k] = v\
  2620.         end\
  2621.         _G[name] = api\
  2622.         return true\
  2623.     end\
  2624. \
  2625.     local env = getfenv()\
  2626.     local function loadObject(name, content)\
  2627.         loadAPI(name, content)\
  2628.         if env[name].Inherit then\
  2629.             if not getfenv()[env[name].Inherit] then    \
  2630.                 if objects[env[name].Inherit] then\
  2631.                     loadObject(env[name].Inherit, objects[env[name].Inherit])\
  2632.                 elseif fs.exists(self.ProgramPath..'/Objects/'..env[name].Inherit..'.lua') then\
  2633.                 end\
  2634.             end\
  2635.             env[name].__index = getfenv()[env[name].Inherit]\
  2636.         else\
  2637.             env[name].__index = Object\
  2638.         end\
  2639.         setmetatable(env[name], env[name])\
  2640.     end\
  2641. \
  2642.     for k, v in pairs(apis) do\
  2643.         loadAPI(k, v)\
  2644.         if k == 'Helpers' then\
  2645.             self.Helpers = Helpers\
  2646.         end\
  2647.     end\
  2648. \
  2649.     for k, v in pairs(objects) do\
  2650.         loadObject(k, v)\
  2651.     end\
  2652.     \
  2653.     local privateObjPath = self.ProgramPath..'/Objects/'\
  2654.     if fs.exists(privateObjPath) and fs.isDir(privateObjPath) then\
  2655.         for i, v in ipairs(fs.list(privateObjPath)) do\
  2656.             if v ~= '.DS_Store' then\
  2657.                 local name = string.match(v, '(%a+)%.?.-')\
  2658.                 local h = fs.open(privateObjPath..v, 'r')\
  2659.                 loadObject(name, h.readAll())\
  2660.                 h.close()\
  2661.             end\
  2662.         end\
  2663.     end\
  2664. end\
  2665. \
  2666. AllowTerminate = true\
  2667. \
  2668. View = nil\
  2669. Menu = nil\
  2670. \
  2671. ActiveObject = nil\
  2672. \
  2673. DrawTimer = nil\
  2674. DrawTimerExpiry = 0\
  2675. \
  2676. IsDrawing = false\
  2677. \
  2678. Running = true\
  2679. \
  2680. DefaultView = 'main'\
  2681. \
  2682. EventHandlers = {\
  2683.     \
  2684. }\
  2685. \
  2686. ObjectClickHandlers = {\
  2687.     \
  2688. }\
  2689. \
  2690. ObjectUpdateHandlers = {\
  2691.     \
  2692. }\
  2693. \
  2694. Timers = {\
  2695.     \
  2696. }\
  2697. \
  2698. function Initialise(self, programPath)\
  2699.     self.ProgramPath = programPath or self.ProgramPath\
  2700.     if not programPath then\
  2701.         if self.ProgramPath then\
  2702.             local prgPath = self.ProgramPath\
  2703.             local prgName = fs.getName(prgPath)\
  2704.             if prgPath:find('/') then \
  2705.                 self.ProgramPath = prgPath:sub(1, #prgPath-#prgName-1)\
  2706.                 self.ProgramPath = prgPath:sub(1, #prgPath-#prgName-1) \
  2707.             else \
  2708.                 self.ProgramPath = '' \
  2709.             end\
  2710.         else\
  2711.             self.ProgramPath = ''\
  2712.         end\
  2713.     end\
  2714.     self:LoadAPIs()\
  2715.     self.ViewPath = self.ProgramPath .. '/Views/'\
  2716.     --first, check that the barebones APIs are available\
  2717.     local requiredApis = {\
  2718.         'Drawing',\
  2719.         'View'\
  2720.     }\
  2721.     local env = getfenv()\
  2722.     for i,v in ipairs(requiredApis) do\
  2723.         if not env[v] then\
  2724.             error('The API: '..v..' is not loaded. Please make sure you load it to use Bedrock.')\
  2725.         end\
  2726.     end\
  2727. \
  2728.     local copy = { }\
  2729.     for k, v in pairs(self) do\
  2730.         if k ~= 'Initialise' then\
  2731.             copy[k] = v\
  2732.         end\
  2733.     end\
  2734.     return setmetatable(copy, getmetatable(self))\
  2735. end\
  2736. \
  2737. function HandleClick(self, event, side, x, y)\
  2738.     if self.Window then\
  2739.         if not self.View:CheckClick(self.Window, x, y) then\
  2740.             self.Window:Flash()\
  2741.         else\
  2742.             self.View:DoClick(self.Window, event, side, x, y)\
  2743.         end\
  2744.     elseif self.Menu then\
  2745.         if not self.View:DoClick(self.Menu, event, side, x, y) then\
  2746.             self.Menu:Close()\
  2747.         end\
  2748.     elseif self.View then\
  2749.         if self.View:Click(event, side, x, y) ~= false then\
  2750.         end     \
  2751.     end\
  2752. end\
  2753. \
  2754. function HandleKeyChar(self, event, keychar)\
  2755.     if self:GetActiveObject() then\
  2756.         local activeObject = self:GetActiveObject()\
  2757.         if activeObject.OnKeyChar then\
  2758.             if activeObject:OnKeyChar(event, keychar) ~= false then\
  2759.                 --self:Draw()\
  2760.             end\
  2761.         end\
  2762.     end\
  2763. end\
  2764. \
  2765. function ToggleMenu(self, name, owner, x, y)\
  2766.     if self.Menu then\
  2767.         self.Menu:Close()\
  2768.         return false\
  2769.     else\
  2770.         self:SetMenu(name, owner, x, y)\
  2771.         return true\
  2772.     end\
  2773. end\
  2774. \
  2775. function SetMenu(self, menu, owner, x, y)\
  2776.     x = x or 1\
  2777.     y = y or 1\
  2778.     if self.Menu then\
  2779.         self.Menu:Close()\
  2780.     end \
  2781.     if menu then\
  2782.         local pos = owner:GetPosition()\
  2783.         self.Menu = self:AddObject(menu, {Type = 'Menu', Owner = owner, X = pos.X + x - 1, Y = pos.Y + y})\
  2784.     end\
  2785. end\
  2786. \
  2787. function ObjectClick(self, name, func)\
  2788.     self.ObjectClickHandlers[name] = func\
  2789. end\
  2790. \
  2791. function ClickObject(self, object, event, side, x, y)\
  2792.     if self.ObjectClickHandlers[object.Name] then\
  2793.         return self.ObjectClickHandlers[object.Name](object, event, side, x, y)\
  2794.     end\
  2795.     return false\
  2796. end\
  2797. \
  2798. function ObjectUpdate(self, name, func)\
  2799.     self.ObjectUpdateHandlers[name] = func\
  2800. end\
  2801. \
  2802. function UpdateObject(self, object, ...)\
  2803.     if self.ObjectUpdateHandlers[object.Name] then\
  2804.         self.ObjectUpdateHandlers[object.Name](object, ...)\
  2805.         --self:Draw()\
  2806.     end\
  2807. end\
  2808. \
  2809. function GetAbsolutePosition(self, obj)\
  2810.     if not obj.Parent then\
  2811.         return {X = obj.X, Y = obj.Y}\
  2812.     else\
  2813.         local pos = self:GetAbsolutePosition(obj.Parent)\
  2814.         local x = pos.X + obj.X - 1\
  2815.         local y = pos.Y + obj.Y - 1\
  2816.         if not obj.Fixed and obj.Parent.ChildOffset then\
  2817.             x = x + obj.Parent.ChildOffset.X\
  2818.             y = y + obj.Parent.ChildOffset.Y\
  2819.         end\
  2820.         return {X = x, Y = y}\
  2821.     end\
  2822. end\
  2823. \
  2824. function LoadView(self, name, draw)\
  2825.     if self.View and self.OnViewClose then\
  2826.         self.OnViewClose(self.View.Name)\
  2827.     end\
  2828.     if self.View then\
  2829.         self.View:OnRemove()\
  2830.     end\
  2831.     local success = false\
  2832. \
  2833.     if not fs.exists(self.ViewPath..name..'.view') then\
  2834.         error('The view: '..name..'.view does not exist.')\
  2835.     end\
  2836. \
  2837.     local h = fs.open(self.ViewPath..name..'.view', 'r')\
  2838.     if h then\
  2839.         local view = textutils.unserialize(h.readAll())\
  2840.         h.close()\
  2841.         if view then\
  2842.             self.View = View:InitialiseFile(self, view, name)\
  2843.             self:ReorderObjects()\
  2844. \
  2845.             if OneOS and view.ToolBarColour then\
  2846.                 OneOS.ToolBarColour = view.ToolBarColour\
  2847.             end\
  2848.             if OneOS and view.ToolBarTextColour then\
  2849.                 OneOS.ToolBarTextColour = view.ToolBarTextColour\
  2850.             end\
  2851.             if not self:GetActiveObject() then\
  2852.                 self:SetActiveObject()\
  2853.             end\
  2854.             success = true\
  2855.         end\
  2856.     end\
  2857. \
  2858.     if success and self.OnViewLoad then\
  2859.         self.OnViewLoad(name)\
  2860.     end\
  2861. \
  2862.     if draw ~= false then\
  2863.         self:Draw()\
  2864.     end\
  2865. \
  2866.     if not success then\
  2867.         error('Failed to load view: '..name..'. It probably isn\\'t formatted correctly. Did you forget a } or ,?')\
  2868.     end\
  2869. \
  2870.     return success\
  2871. end\
  2872. \
  2873. function InheritFile(self, file, name)\
  2874.     local h = fs.open(self.ViewPath..name..'.view', 'r')\
  2875.     if h then\
  2876.         local super = textutils.unserialize(h.readAll())\
  2877.         if super then\
  2878.             if type(super) ~= 'table' then\
  2879.                 error('View: \"'..name..'.view\" is not formatted correctly.')\
  2880.             end\
  2881. \
  2882.             for k, v in pairs(super) do\
  2883.                 if not file[k] then\
  2884.                     file[k] = v\
  2885.                 end\
  2886.             end\
  2887.             return file\
  2888.         end\
  2889.     end\
  2890.     return file\
  2891. end\
  2892. \
  2893. function ParseStringSize(self, parent, k, v)\
  2894.         local parentSize = parent.Width\
  2895.         if k == 'Height' or k == 'Y' then\
  2896.             parentSize = parent.Height\
  2897.         end\
  2898.         local parts = {v}\
  2899.         if type(v) == 'string' and string.find(v, ',') then\
  2900.             parts = {}\
  2901.             for word in string.gmatch(v, '([^,]+)') do\
  2902.                 table.insert(parts, word)\
  2903.             end\
  2904.         end\
  2905. \
  2906.         v = 0\
  2907.         for i2, part in ipairs(parts) do\
  2908.             if type(part) == 'string' and part:sub(#part) == '%' then\
  2909.                 v = v + math.ceil(parentSize * (tonumber(part:sub(1, #part-1)) / 100))\
  2910.             else\
  2911.                 v = v + tonumber(part)\
  2912.             end\
  2913.         end\
  2914.         return v\
  2915. end\
  2916. \
  2917. function ObjectFromFile(self, file, view)\
  2918.     local env = getfenv()\
  2919.     if env[file.Type] then\
  2920.         if not env[file.Type].Initialise then\
  2921.             error('Malformed Object: '..file.Type)\
  2922.         end\
  2923.         local object = {}\
  2924. \
  2925.         if file.InheritView then\
  2926.             file = self:InheritFile(file, file.InheritView)\
  2927.         end\
  2928.         \
  2929.         object.AutoWidth = true\
  2930.         for k, v in pairs(file) do\
  2931.             if k == 'Width' or k == 'X' or k == 'Height' or k == 'Y' then\
  2932.                 v = self:ParseStringSize(view, k, v)\
  2933.             end\
  2934. \
  2935.             if k == 'Width' then\
  2936.                 object.AutoWidth = false\
  2937.             end\
  2938.             if k ~= 'Children' then\
  2939.                 object[k] = v\
  2940.             else\
  2941.                 object[k] = {}\
  2942.             end\
  2943.         end\
  2944. \
  2945.         object.Parent = view\
  2946.         object.Bedrock = self\
  2947.         if not object.Name then\
  2948.             object.Name = file.Type\
  2949.         end\
  2950. \
  2951.         object = env[file.Type]:Initialise(object)\
  2952. \
  2953.         if file.Children then\
  2954.             for i, obj in ipairs(file.Children) do\
  2955.                 local _view = self:ObjectFromFile(obj, object)\
  2956.                 if not _view.Z then\
  2957.                     _view.Z = i\
  2958.                 end\
  2959.                 _view.Parent = object\
  2960.                 table.insert(object.Children, _view)\
  2961.             end\
  2962.         end\
  2963. \
  2964.         if not object.OnClick then\
  2965.             object.OnClick = function(...) return self:ClickObject(...) end\
  2966.         end\
  2967.         --object.OnUpdate = function(...) self:UpdateObject(...) end\
  2968. \
  2969.         if object.OnUpdate then\
  2970.             for k, v in pairs(env[file.Type]) do\
  2971.                 object:OnUpdate(k)\
  2972.             end\
  2973. \
  2974.             for k, v in pairs(object.__index) do\
  2975.                 object:OnUpdate(k)\
  2976.             end\
  2977.         end\
  2978. \
  2979.         if object.Active then\
  2980.             object.Bedrock:SetActiveObject(object)\
  2981.         end\
  2982.         if object.OnLoad then\
  2983.             object:OnLoad()\
  2984.         end\
  2985.         return object\
  2986.     elseif not file.Type then\
  2987.         error('No object type specified. (e.g. Type = \"Button\")')\
  2988.     else\
  2989.         error('No Object: '..file.Type..'. The API probably isn\\'t loaded')\
  2990.     end\
  2991. end\
  2992. \
  2993. function ReorderObjects(self)\
  2994.     if self.View and self.View.Children then\
  2995.         table.sort(self.View.Children, function(a,b)\
  2996.             return a.Z < b.Z \
  2997.         end)\
  2998.     end\
  2999. end\
  3000. \
  3001. function AddObject(self, info, extra)\
  3002.     return self.View:AddObject(info, extra)\
  3003. end\
  3004. \
  3005. function GetObject(self, name)\
  3006.     return self.View:GetObject(name)\
  3007. end\
  3008. \
  3009. function GetObjects(self, name)\
  3010.     return self.View:GetObjects(name)\
  3011. end\
  3012. \
  3013. function RemoveObject(self, name)\
  3014.     return self.View:RemoveObject(name)\
  3015. end\
  3016. \
  3017. function RemoveObjects(self, name)\
  3018.     return self.View:RemoveObjects(name)\
  3019. end\
  3020. \
  3021. function ForceDraw(self)\
  3022.     if not self.DrawTimer or self.DrawTimerExpiry <= os.clock() then\
  3023.         self.DrawTimer = self:StartTimer(function()\
  3024.             self.DrawTimer = nil\
  3025.             self:Draw()\
  3026.         end, 0.05)\
  3027.         self.DrawTimerExpiry = os.clock() + 0.1\
  3028.     end\
  3029. end\
  3030. \
  3031. function DisplayWindow(self, _view, title, canClose)\
  3032.     if canClose == nil then\
  3033.         canClose = true\
  3034.     end\
  3035.     if type(_view) == 'string' then\
  3036.         local h = fs.open(self.ViewPath.._view..'.view', 'r')\
  3037.         if h then\
  3038.             _view = textutils.unserialize(h.readAll())\
  3039.             h.close()\
  3040.         end\
  3041.     end\
  3042. \
  3043.     self.Window = self:AddObject({Type = 'Window', Z = 999, Title = title, CanClose = canClose})\
  3044.     _view.Type = 'View'\
  3045.     _view.Name = 'View'\
  3046.     _view.BackgroundColour = _view.BackgroundColour or colours.white\
  3047.     self.Window:SetView(self:ObjectFromFile(_view, self.Window))\
  3048. end\
  3049. \
  3050. function DisplayAlertWindow(self, title, text, buttons, callback)\
  3051.     local func = function(btn)\
  3052.         self.Window:Close()\
  3053.         if callback then\
  3054.             callback(btn.Text)\
  3055.         end\
  3056.     end\
  3057.     local children = {}\
  3058.     local usedX = -1\
  3059.     if buttons then\
  3060.         for i, text in ipairs(buttons) do\
  3061.             usedX = usedX + 3 + #text\
  3062.             table.insert(children, {\
  3063.                 [\"Y\"]=\"100%,-1\",\
  3064.                 [\"X\"]=\"100%,-\"..usedX,\
  3065.                 [\"Name\"]=text..\"Button\",\
  3066.                 [\"Type\"]=\"Button\",\
  3067.                 [\"Text\"]=text,\
  3068.                 OnClick = func\
  3069.             })\
  3070.         end\
  3071.     end\
  3072. \
  3073.     local width = usedX + 2\
  3074.     if width < 28 then\
  3075.         width = 28\
  3076.     end\
  3077. \
  3078.     local canClose = true\
  3079.     if buttons and #buttons~=0 then\
  3080.         canClose = false\
  3081.     end\
  3082. \
  3083.     local height = 0\
  3084.     if text then\
  3085.         height = #Helpers.WrapText(text, width - 2)\
  3086.         table.insert(children, {\
  3087.             [\"Y\"]=2,\
  3088.             [\"X\"]=2,\
  3089.             [\"Width\"]=\"100%,-2\",\
  3090.             [\"Height\"]=height,\
  3091.             [\"Name\"]=\"Label\",\
  3092.             [\"Type\"]=\"Label\",\
  3093.             [\"Text\"]=text\
  3094.         })\
  3095.     end\
  3096.     local view = {\
  3097.         Children = children,\
  3098.         Width=width,\
  3099.         Height=3+height+(canClose and 0 or 1),\
  3100.         OnKeyChar = function(_view, keychar)\
  3101.             func({Text=buttons[1]})\
  3102.         end\
  3103.     }\
  3104.     self:DisplayWindow(view, title, canClose)\
  3105. end\
  3106. \
  3107. function DisplayTextBoxWindow(self, title, text, callback, textboxText, cursorAtEnd)\
  3108.     textboxText = textboxText or ''\
  3109.     local func = function(btn)\
  3110.         self.Window:Close()\
  3111.         if callback then\
  3112.             callback(btn.Text)\
  3113.         end\
  3114.     end\
  3115.     local children = {\
  3116.         {\
  3117.             [\"Y\"]=\"100%,-1\",\
  3118.             [\"X\"]=\"100%,-4\",\
  3119.             [\"Name\"]=\"OkButton\",\
  3120.             [\"Type\"]=\"Button\",\
  3121.             [\"Text\"]=\"Ok\",\
  3122.             OnClick = function()\
  3123.                 local text = self.Window:GetObject('TextBox').Text\
  3124.                 self.Window:Close()\
  3125.                 callback(true, text)\
  3126.             end\
  3127.         },\
  3128.         {\
  3129.             [\"Y\"]=\"100%,-1\",\
  3130.             [\"X\"]=\"100%,-13\",\
  3131.             [\"Name\"]=\"CancelButton\",\
  3132.             [\"Type\"]=\"Button\",\
  3133.             [\"Text\"]=\"Cancel\",\
  3134.             OnClick = function()\
  3135.                 self.Window:Close()\
  3136.                 callback(false)\
  3137.             end\
  3138.         }\
  3139.     }\
  3140. \
  3141.     local height = -1\
  3142.     if text and #text ~= 0 then\
  3143.         height = #Helpers.WrapText(text, 26)\
  3144.         table.insert(children, {\
  3145.             [\"Y\"]=2,\
  3146.             [\"X\"]=2,\
  3147.             [\"Width\"]=\"100%,-2\",\
  3148.             [\"Height\"]=height,\
  3149.             [\"Name\"]=\"Label\",\
  3150.             [\"Type\"]=\"Label\",\
  3151.             [\"Text\"]=text\
  3152.         })\
  3153.     end\
  3154.     table.insert(children,\
  3155.         {\
  3156.             [\"Y\"]=3+height,\
  3157.             [\"X\"]=2,\
  3158.             [\"Width\"]=\"100%,-2\",\
  3159.             [\"Name\"]=\"TextBox\",\
  3160.             [\"Type\"]=\"TextBox\",\
  3161.             [\"Text\"]=textboxText,\
  3162.             [\"CursorPos\"]=(cursorAtEnd and 0 or nil)\
  3163.         })\
  3164.     local view = {\
  3165.         Children = children,\
  3166.         Width=28,\
  3167.         Height=5+height+(canClose and 0 or 1),\
  3168.     }\
  3169.     self:DisplayWindow(view, title)\
  3170.     self.Window:GetObject('TextBox').OnUpdate = function(txtbox, keychar)\
  3171.         if keychar == keys.enter then\
  3172.             self.Window:Close()\
  3173.             callback(true, txtbox.Text)\
  3174.         end\
  3175.     end\
  3176.     self:SetActiveObject(self.Window:GetObject('TextBox'))\
  3177.     self.Window.OnCloseButton = function()callback(false)end\
  3178. end\
  3179. \
  3180. function DisplayOpenFileWindow(self, title, callback)\
  3181.     title = title or 'Open File'\
  3182.     local func = function(btn)\
  3183.         self.Window:Close()\
  3184.         if callback then\
  3185.             callback(btn.Text)\
  3186.         end\
  3187.     end\
  3188. \
  3189.     local sidebarItems = {}\
  3190. \
  3191.     --this is a really, really super bad way of doing it\
  3192.     local separator = '                               !'\
  3193. \
  3194.     local function addFolder(path, level)\
  3195.         for i, v in ipairs(fs.list(path)) do\
  3196.             local fPath = path .. '/' .. v\
  3197.             if fPath ~= '/rom' and fs.isDir(fPath) then\
  3198.                 table.insert(sidebarItems, level .. v..separator..fPath)\
  3199.                 addFolder(fPath, level .. '  ')\
  3200.             end\
  3201.         end\
  3202.     end\
  3203.     addFolder('','')\
  3204. \
  3205.     local currentFolder = ''\
  3206.     local selectedPath = nil\
  3207. \
  3208.     local goToFolder = nil\
  3209. \
  3210.     local children = {\
  3211.         {\
  3212.             [\"Y\"]=\"100%,-2\",\
  3213.             [\"X\"]=1,\
  3214.             [\"Height\"]=3,\
  3215.             [\"Width\"]=\"100%\",\
  3216.             [\"BackgroundColour\"]=colours.lightGrey,\
  3217.             [\"Name\"]=\"SidebarListView\",\
  3218.             [\"Type\"]=\"View\"\
  3219.         },\
  3220.         {\
  3221.             [\"Y\"]=\"100%,-1\",\
  3222.             [\"X\"]=\"100%,-4\",\
  3223.             [\"Name\"]=\"OkButton\",\
  3224.             [\"Type\"]=\"Button\",\
  3225.             [\"Text\"]=\"Ok\",\
  3226.             [\"BackgroundColour\"]=colours.white,\
  3227.             [\"Enabled\"]=false,\
  3228.             OnClick = function()\
  3229.                 if selectedPath then\
  3230.                     self.Window:Close()\
  3231.                     callback(true, Helpers.TidyPath(selectedPath))\
  3232.                 end\
  3233.             end\
  3234.         },\
  3235.         {\
  3236.             [\"Y\"]=\"100%,-1\",\
  3237.             [\"X\"]=\"100%,-13\",\
  3238.             [\"Name\"]=\"CancelButton\",\
  3239.             [\"Type\"]=\"Button\",\
  3240.             [\"Text\"]=\"Cancel\",\
  3241.             [\"BackgroundColour\"]=colours.white,\
  3242.             OnClick = function()\
  3243.                 self.Window:Close()\
  3244.                 callback(false)\
  3245.             end\
  3246.         },\
  3247.         {\
  3248.             [\"Y\"]=1,\
  3249.             [\"X\"]=1,\
  3250.             [\"Height\"]=\"100%,-3\",\
  3251.             [\"Width\"]=\"40%,-1\",\
  3252.             [\"Name\"]=\"SidebarListView\",\
  3253.             [\"Type\"]=\"ListView\",\
  3254.             [\"CanSelect\"]=true,\
  3255.             [\"Items\"]={\
  3256.                 [\"Computer\"] = sidebarItems\
  3257.             },\
  3258.             OnSelect = function(listView, text)\
  3259.                 local _,s = text:find(separator)\
  3260.                 if s then\
  3261.                     local path = text:sub(s + 1)\
  3262.                     goToFolder(path)\
  3263.                 end\
  3264.             end,\
  3265.             OnClick = function(listView, event, side, x, y)\
  3266.                 if y == 1 then\
  3267.                     goToFolder('/')\
  3268.                 end\
  3269.             end\
  3270.         },\
  3271.         {\
  3272.             [\"Y\"]=1,\
  3273.             [\"X\"]=\"40%\",\
  3274.             [\"Height\"]=\"100%,-3\",\
  3275.             [\"Width\"]=1,\
  3276.             [\"Type\"]=\"Separator\"\
  3277.         },\
  3278.         {\
  3279.             [\"Y\"]=1,\
  3280.             [\"X\"]=\"40%,2\",\
  3281.             [\"Width\"]=\"65%,-3\",\
  3282.             [\"Height\"]=1,\
  3283.             [\"Type\"]=\"Label\",\
  3284.             [\"Name\"]=\"PathLabel\",\
  3285.             [\"TextColour\"]=colours.lightGrey,\
  3286.             [\"Text\"]='/hello/there'\
  3287.         },\
  3288.         {\
  3289.             [\"Y\"]=2,\
  3290.             [\"X\"]=\"40%,1\",\
  3291.             [\"Height\"]=\"100%,-4\",\
  3292.             [\"Width\"]=\"65%,-1\",\
  3293.             [\"Name\"]=\"FilesListView\",\
  3294.             [\"Type\"]=\"ListView\",\
  3295.             [\"CanSelect\"]=true,\
  3296.             [\"Items\"]={},\
  3297.             OnSelect = function(listView, text)\
  3298.                 selectedPath = Helpers.TidyPath(currentFolder .. '/' .. text)\
  3299.                 self.Window:GetObject('OkButton').Enabled = true\
  3300.             end,\
  3301.             OnClick = function(listView, event, side, x, y)\
  3302.                 if y == 1 then\
  3303.                     goToFolder('/')\
  3304.                 end\
  3305.             end\
  3306.         },\
  3307.     }\
  3308.     local view = {\
  3309.         Children = children,\
  3310.         Width=40,\
  3311.         Height= Drawing.Screen.Height - 4\
  3312.     }\
  3313.     self:DisplayWindow(view, title)\
  3314. \
  3315.     goToFolder = function(path)\
  3316.         path = Helpers.TidyPath(path)\
  3317.         self.Window:GetObject('PathLabel').Text = path\
  3318.         currentFolder = path\
  3319. \
  3320.         local filesListItems = {}\
  3321.         for i, v in ipairs(fs.list(path)) do\
  3322.             if not fs.isDir(currentFolder .. v) then\
  3323.                 table.insert(filesListItems, v)\
  3324.             end\
  3325.         end\
  3326.         self.Window:GetObject('OkButton').Enabled = false\
  3327.         selectedPath = nil\
  3328.         self.Window:GetObject('FilesListView').Items = filesListItems\
  3329. \
  3330.     end\
  3331. \
  3332.     goToFolder('')\
  3333. \
  3334.     self.Window.OnCloseButton = function()callback(false)end\
  3335. end\
  3336. \
  3337. function RegisterEvent(self, event, func)\
  3338.     if not self.EventHandlers[event] then\
  3339.         self.EventHandlers[event] = {}\
  3340.     end\
  3341.     table.insert(self.EventHandlers[event], func)\
  3342. end\
  3343. \
  3344. function StartRepeatingTimer(self, func, interval)\
  3345.     local int = interval\
  3346.     if type(int) == 'function' then\
  3347.         int = int()\
  3348.     end\
  3349.     if not int or int <= 0 then\
  3350.         return\
  3351.     end\
  3352.     local timer = os.startTimer(int)\
  3353. \
  3354.     self.Timers[timer] = {func, true, interval}\
  3355.     return timer\
  3356. end\
  3357. \
  3358. function StartTimer(self, func, delay)\
  3359.     local timer = os.startTimer(delay)\
  3360.     self.Timers[timer] = {func, false}\
  3361.     return timer\
  3362. end\
  3363. \
  3364. function StopTimer(self, timer)\
  3365.     if self.Timers[timer] then\
  3366.         self.Timers[timer] = nil\
  3367.     end\
  3368. end\
  3369. \
  3370. function HandleTimer(self, event, timer)\
  3371.     if self.Timers[timer] then\
  3372.         local oldTimer = self.Timers[timer]\
  3373.         self.Timers[timer] = nil\
  3374.         local new = nil\
  3375.         if oldTimer[2] then\
  3376.             new = self:StartRepeatingTimer(oldTimer[1], oldTimer[3])\
  3377.         end\
  3378.         if oldTimer and oldTimer[1] then\
  3379.             oldTimer[1](new)\
  3380.         end\
  3381.     elseif self.OnTimer then\
  3382.         self.OnTimer(self, event, timer)\
  3383.     end\
  3384. end\
  3385. \
  3386. function SetActiveObject(self, object)\
  3387.     if object then\
  3388.         if object ~= self.ActiveObject then\
  3389.             self.ActiveObject = object\
  3390.             object:ForceDraw()\
  3391.         end\
  3392.     elseif self.ActiveObject ~= nil then\
  3393.         self.ActiveObject = nil\
  3394.         self.CursorPos = nil\
  3395.         self.View:ForceDraw()\
  3396.     end\
  3397. end\
  3398. \
  3399. function GetActiveObject(self)\
  3400.     return self.ActiveObject\
  3401. end\
  3402. \
  3403. OnTimer = nil\
  3404. OnClick = nil\
  3405. OnKeyChar = nil\
  3406. OnDrag = nil\
  3407. OnScroll = nil\
  3408. OnViewLoad = nil\
  3409. OnViewClose = nil\
  3410. OnDraw = nil\
  3411. OnQuit = nil\
  3412. \
  3413. local eventFuncs = {\
  3414.     OnClick = {'mouse_click'},\
  3415.     OnKeyChar = {'key', 'char'},\
  3416.     OnDrag = {'mouse_drag'},\
  3417.     OnScroll = {'mouse_scroll'},\
  3418.     HandleClick = {'mouse_click', 'mouse_drag', 'mouse_scroll'},\
  3419.     HandleKeyChar = {'key', 'char'},\
  3420.     HandleTimer = {'timer'}\
  3421. }\
  3422. \
  3423. local drawCalls = 0\
  3424. local ignored = 0\
  3425. function Draw(self)\
  3426.     self.IsDrawing = true\
  3427.     if self.OnDraw then\
  3428.         self:OnDraw()\
  3429.     end\
  3430. \
  3431.     if self.View and self.View:NeedsDraw() then\
  3432.         self.View:Draw()\
  3433.         Drawing.DrawBuffer()\
  3434.         if isDebug then\
  3435.             drawCalls = drawCalls + 1\
  3436.         end\
  3437.     elseif not self.View then\
  3438.         print('No loaded view. You need to do program:LoadView first.')\
  3439.     end \
  3440. \
  3441.     if self:GetActiveObject() and self.CursorPos and type(self.CursorPos[1]) == 'number' and type(self.CursorPos[2]) == 'number' then\
  3442.         term.setCursorPos(self.CursorPos[1], self.CursorPos[2])\
  3443.         term.setTextColour(self.CursorColour)\
  3444.         term.setCursorBlink(true)\
  3445.     else\
  3446.         term.setCursorBlink(false)\
  3447.     end\
  3448. \
  3449.     self.IsDrawing = false\
  3450. end\
  3451. \
  3452. function EventHandler(self)\
  3453.     local event = { os.pullEventRaw() }\
  3454.     \
  3455.     if self.EventHandlers[event[1]] then\
  3456.         for i, e in ipairs(self.EventHandlers[event[1]]) do\
  3457.             e(self, unpack(event))\
  3458.         end\
  3459.     end\
  3460. end\
  3461. \
  3462. function Quit(self)\
  3463.     self.Running = false\
  3464.     if self.OnQuit then\
  3465.         self:OnQuit()\
  3466.     end\
  3467.     if OneOS then\
  3468.         OneOS.Close()\
  3469.     end\
  3470. end\
  3471. \
  3472. function Run(self, ready)\
  3473.     for name, events in pairs(eventFuncs) do\
  3474.         if self[name] then\
  3475.             for i, event in ipairs(events) do\
  3476.                 self:RegisterEvent(event, self[name])\
  3477.             end\
  3478.         end\
  3479.     end\
  3480. \
  3481.     if self.AllowTerminate then\
  3482.         self:RegisterEvent('terminate', function()error('Terminated', 0) end)\
  3483.     end\
  3484. \
  3485.     if self.DefaultView and self.DefaultView ~= '' and fs.exists(self.ViewPath..self.DefaultView..'.view') then\
  3486.         self:LoadView(self.DefaultView)\
  3487.     end\
  3488. \
  3489.     if ready then\
  3490.         ready()\
  3491.     end\
  3492.     \
  3493.     self:Draw()\
  3494. \
  3495.     while self.Running do\
  3496.         self:EventHandler()\
  3497.     end\
  3498. end",
  3499.   Views = {
  3500.     [ "toolbar.view" ] = "{\
  3501.  [\"Width\"]=\"100%\",\
  3502.  [\"Height\"]=3,\
  3503.  [\"Type\"]=\"View\",\
  3504.  [\"BackgroundColour\"]=128,\
  3505.  [\"Children\"]={\
  3506.    [1]={\
  3507.      [\"Y\"]=2,\
  3508.      [\"X\"]=\"100%,-23\",\
  3509.      [\"Name\"]=\"LogButton\",\
  3510.      [\"Type\"]=\"Button\",\
  3511.      [\"Text\"]=\"Log\",\
  3512.      [\"BackgroundColour\"]=1,\
  3513.      [\"TextColour\"]=128,\
  3514.      [\"Toggle\"]=false\
  3515.    },\
  3516.    [2]={\
  3517.      [\"Y\"]=2,\
  3518.      [\"X\"]=\"100%,-17\",\
  3519.      [\"Name\"]=\"AccountsButton\",\
  3520.      [\"Type\"]=\"Button\",\
  3521.      [\"Text\"]=\"Accounts\",\
  3522.      [\"BackgroundColour\"]=1,\
  3523.      [\"TextColour\"]=128,\
  3524.      [\"Toggle\"]=false\
  3525.    },\
  3526.    [3]={\
  3527.      [\"Y\"]=2,\
  3528.      [\"X\"]=\"100%,-6\",\
  3529.      [\"Name\"]=\"QuitButton\",\
  3530.      [\"Type\"]=\"Button\",\
  3531.      [\"Text\"]=\"Quit\",\
  3532.      [\"BackgroundColour\"]=1,\
  3533.      [\"TextColour\"]=128\
  3534.    },\
  3535.    [4]={\
  3536.      [\"Y\"]=2,\
  3537.      [\"X\"]=2,\
  3538.      [\"Name\"]=\"StatusLabel\",\
  3539.      [\"Type\"]=\"Label\",\
  3540.      [\"Text\"]=\"Another Server is Running\",\
  3541.      [\"TextColour\"]=1\
  3542.    },\
  3543.  },\
  3544. }",
  3545.     [ "main.view" ] = "{\
  3546.  [\"Children\"]={\
  3547.    [1]={\
  3548.      [\"Y\"]=1,\
  3549.      [\"X\"]=1,\
  3550.      [\"Name\"]=\"Toolbar\",\
  3551.      [\"Type\"]=\"View\",\
  3552.      [\"InheritView\"]=\"toolbar\"\
  3553.    },\
  3554.    [2]={\
  3555.      [\"X\"]=1,\
  3556.      [\"Y\"]=4,\
  3557.      [\"Name\"]=\"AccountsView\",\
  3558.      [\"Type\"]=\"View\",\
  3559.      [\"Width\"]=\"100%\",\
  3560.      [\"Height\"]=\"100%,-3\",\
  3561.      [\"Visible\"]=false,\
  3562.      [\"InheritView\"]=\"accounts\"\
  3563.    },\
  3564.    [3]={\
  3565.      [\"X\"]=1,\
  3566.      [\"Y\"]=4,\
  3567.      [\"Name\"]=\"LogView\",\
  3568.      [\"Type\"]=\"LogView\",\
  3569.      [\"Width\"]=\"100%\",\
  3570.      [\"Height\"]=\"100%,-3\",\
  3571.      [\"Visible\"]=false\
  3572.    },\
  3573.  },\
  3574.  [\"BackgroundColour\"]=1,\
  3575.  [\"ToolBarColour\"]=128,\
  3576.  [\"ToolBarTextColour\"]=1\
  3577. }",
  3578.     [ "accounts.view" ] = "{\
  3579.  [\"Children\"]={\
  3580.    [1]={\
  3581.      [\"Y\"]=1,\
  3582.      [\"X\"]=1,\
  3583.      [\"Width\"]=\"100%\",\
  3584.      [\"Height\"]=3,\
  3585.      [\"Type\"]=\"View\",\
  3586.      [\"BackgroundColour\"]=256\
  3587.    },\
  3588.    [2]={\
  3589.      [\"Y\"]=2,\
  3590.      [\"X\"]=2,\
  3591.      [\"Type\"]=\"Button\",\
  3592.      [\"Text\"]=\"Add\",\
  3593.      [\"Name\"]=\"AddButton\",\
  3594.      [\"BackgroundColour\"]=1\
  3595.    },\
  3596.    [3]={\
  3597.      [\"Y\"]=2,\
  3598.      [\"X\"]=8,\
  3599.      [\"Type\"]=\"Button\",\
  3600.      [\"Text\"]=\"Edit\",\
  3601.      [\"Name\"]=\"EditButton\",\
  3602.      [\"BackgroundColour\"]=1\
  3603.    },\
  3604.    [4]={\
  3605.      [\"Y\"]=2,\
  3606.      [\"X\"]=15,\
  3607.      [\"Type\"]=\"Button\",\
  3608.      [\"Name\"]=\"DeleteButton\",\
  3609.      [\"Text\"]=\"Delete\",\
  3610.      [\"BackgroundColour\"]=16384,\
  3611.      [\"TextColour\"]=1\
  3612.    },\
  3613.    [5]={\
  3614.      [\"Y\"]=2,\
  3615.      [\"X\"]=24,\
  3616.      [\"Type\"]=\"Button\",\
  3617.      [\"Text\"]=\"Backup\",\
  3618.      [\"Name\"]=\"BackupButton\",\
  3619.      [\"BackgroundColour\"]=1\
  3620.    },\
  3621.    [6]={\
  3622.      [\"Y\"]=4,\
  3623.      [\"X\"]=2,\
  3624.      [\"Type\"]=\"Label\",\
  3625.      [\"Text\"]=\"Name\",\
  3626.      [\"TextColour\"]=256\
  3627.    },\
  3628.    [7]={\
  3629.      [\"Y\"]=4,\
  3630.      [\"X\"]=18,\
  3631.      [\"Type\"]=\"Label\",\
  3632.      [\"Text\"]=\"Account Number\",\
  3633.      [\"TextColour\"]=256\
  3634.    },\
  3635.    [8]={\
  3636.      [\"Y\"]=4,\
  3637.      [\"X\"]=40,\
  3638.      [\"Type\"]=\"Label\",\
  3639.      [\"Text\"]=\"Balance\",\
  3640.      [\"TextColour\"]=256\
  3641.    },\
  3642.    [9]={\
  3643.      [\"Y\"]=5,\
  3644.      [\"X\"]=1,\
  3645.      [\"Width\"]=\"100%\",\
  3646.      [\"Height\"]=\"100%,-4\",\
  3647.      [\"Type\"]=\"ListView\",\
  3648.      [\"CanSelect\"]=true\
  3649.    },\
  3650.  },\
  3651. }",
  3652.     [ "nomodem.view" ] = "{\
  3653.  [\"Children\"]={\
  3654.    [1]={\
  3655.      [\"Y\"]=\"50%,-2\",\
  3656.      [\"X\"]=1,\
  3657.      [\"Width\"]=\"100%\",\
  3658.      [\"Type\"]=\"Label\",\
  3659.      [\"Text\"]=\"No Modem Attached!\",\
  3660.      [\"TextColour\"]=16384,\
  3661.      [\"Align\"]=\"Center\"\
  3662.    },\
  3663.    [2]={\
  3664.      [\"Y\"]=\"50%\",\
  3665.      [\"X\"]=\"10%\",\
  3666.      [\"Width\"]=\"80%\",\
  3667.      [\"Type\"]=\"Label\",\
  3668.      [\"Text\"]=\"Please attach a wireless modem to use oeedPay Server.\",\
  3669.      [\"Align\"]=\"Center\"\
  3670.    },\
  3671.    [3]={\
  3672.      [\"Y\"]=\"100%,-1\",\
  3673.      [\"X\"]=\"100%,-6\",\
  3674.      [\"Name\"]=\"QuitButton\",\
  3675.      [\"Type\"]=\"Button\",\
  3676.      [\"Text\"]=\"Quit\",\
  3677.    },\
  3678.  },\
  3679.  [\"BackgroundColour\"]=1,\
  3680.  [\"ToolBarColour\"]=128,\
  3681.  [\"ToolBarTextColour\"]=1\
  3682. }",
  3683.   },
  3684.   APIs = {
  3685.     hash = "--\
  3686. --  Thanks to GravityScore for this!\
  3687. --  http://www.computercraft.info/forums2/index.php?/topic/8169-sha-256-in-pure-lua/\
  3688. --\
  3689. --  This is used to hash passwords sent with the secure text field. It just reduces the chance of people getting hacked.\
  3690. --\
  3691. \
  3692. --  \
  3693. --  Adaptation of the Secure Hashing Algorithm (SHA-244/256)\
  3694. --  Found Here: http://lua-users.org/wiki/SecureHashAlgorithm\
  3695. --  \
  3696. --  Using an adapted version of the bit library\
  3697. --  Found Here: https://bitbucket.org/Boolsheet/bslf/src/1ee664885805/bit.lua\
  3698. --  \
  3699. \
  3700. local MOD = 2^32\
  3701. local MODM = MOD-1\
  3702. \
  3703. local function memoize(f)\
  3704.     local mt = {}\
  3705.     local t = setmetatable({}, mt)\
  3706.     function mt:__index(k)\
  3707.         local v = f(k)\
  3708.         t[k] = v\
  3709.         return v\
  3710.     end\
  3711.     return t\
  3712. end\
  3713. \
  3714. local function make_bitop_uncached(t, m)\
  3715.     local function bitop(a, b)\
  3716.         local res,p = 0,1\
  3717.         while a ~= 0 and b ~= 0 do\
  3718.             local am, bm = a % m, b % m\
  3719.             res = res + t[am][bm] * p\
  3720.             a = (a - am) / m\
  3721.             b = (b - bm) / m\
  3722.             p = p*m\
  3723.         end\
  3724.         res = res + (a + b) * p\
  3725.         return res\
  3726.     end\
  3727.     return bitop\
  3728. end\
  3729. \
  3730. local function make_bitop(t)\
  3731.     local op1 = make_bitop_uncached(t,2^1)\
  3732.     local op2 = memoize(function(a) return memoize(function(b) return op1(a, b) end) end)\
  3733.     return make_bitop_uncached(op2, 2 ^ (t.n or 1))\
  3734. end\
  3735. \
  3736. local bxor1 = make_bitop({[0] = {[0] = 0,[1] = 1}, [1] = {[0] = 1, [1] = 0}, n = 4})\
  3737. \
  3738. local function bxor(a, b, c, ...)\
  3739.     local z = nil\
  3740.     if b then\
  3741.         a = a % MOD\
  3742.         b = b % MOD\
  3743.         z = bxor1(a, b)\
  3744.         if c then z = bxor(z, c, ...) end\
  3745.         return z\
  3746.     elseif a then return a % MOD\
  3747.     else return 0 end\
  3748. end\
  3749. \
  3750. local function band(a, b, c, ...)\
  3751.     local z\
  3752.     if b then\
  3753.         a = a % MOD\
  3754.         b = b % MOD\
  3755.         z = ((a + b) - bxor1(a,b)) / 2\
  3756.         if c then z = bit32_band(z, c, ...) end\
  3757.         return z\
  3758.     elseif a then return a % MOD\
  3759.     else return MODM end\
  3760. end\
  3761. \
  3762. local function bnot(x) return (-1 - x) % MOD end\
  3763. \
  3764. local function rshift1(a, disp)\
  3765.     if disp < 0 then return lshift(a,-disp) end\
  3766.     return math.floor(a % 2 ^ 32 / 2 ^ disp)\
  3767. end\
  3768. \
  3769. local function rshift(x, disp)\
  3770.     if disp > 31 or disp < -31 then return 0 end\
  3771.     return rshift1(x % MOD, disp)\
  3772. end\
  3773. \
  3774. local function lshift(a, disp)\
  3775.     if disp < 0 then return rshift(a,-disp) end \
  3776.     return (a * 2 ^ disp) % 2 ^ 32\
  3777. end\
  3778. \
  3779. local function rrotate(x, disp)\
  3780.    x = x % MOD\
  3781.    disp = disp % 32\
  3782.    local low = band(x, 2 ^ disp - 1)\
  3783.    return rshift(x, disp) + lshift(low, 32 - disp)\
  3784. end\
  3785. \
  3786. local k = {\
  3787.     0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,\
  3788.     0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,\
  3789.     0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,\
  3790.     0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,\
  3791.     0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,\
  3792.     0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,\
  3793.     0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,\
  3794.     0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,\
  3795.     0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,\
  3796.     0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,\
  3797.     0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,\
  3798.     0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,\
  3799.     0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,\
  3800.     0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,\
  3801.     0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,\
  3802.     0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,\
  3803. }\
  3804. \
  3805. local function str2hexa(s)\
  3806.     return (string.gsub(s, \".\", function(c) return string.format(\"%02x\", string.byte(c)) end))\
  3807. end\
  3808. \
  3809. local function num2s(l, n)\
  3810.     local s = \"\"\
  3811.     for i = 1, n do\
  3812.         local rem = l % 256\
  3813.         s = string.char(rem) .. s\
  3814.         l = (l - rem) / 256\
  3815.     end\
  3816.     return s\
  3817. end\
  3818. \
  3819. local function s232num(s, i)\
  3820.     local n = 0\
  3821.     for i = i, i + 3 do n = n*256 + string.byte(s, i) end\
  3822.     return n\
  3823. end\
  3824. \
  3825. local function preproc(msg, len)\
  3826.     local extra = 64 - ((len + 9) % 64)\
  3827.     len = num2s(8 * len, 8)\
  3828.     msg = msg .. \"\\128\" .. string.rep(\"\\0\", extra) .. len\
  3829.     assert(#msg % 64 == 0)\
  3830.     return msg\
  3831. end\
  3832. \
  3833. local function initH256(H)\
  3834.     H[1] = 0x6a09e667\
  3835.     H[2] = 0xbb67ae85\
  3836.     H[3] = 0x3c6ef372\
  3837.     H[4] = 0xa54ff53a\
  3838.     H[5] = 0x510e527f\
  3839.     H[6] = 0x9b05688c\
  3840.     H[7] = 0x1f83d9ab\
  3841.     H[8] = 0x5be0cd19\
  3842.     return H\
  3843. end\
  3844. \
  3845. local function digestblock(msg, i, H)\
  3846.     local w = {}\
  3847.     for j = 1, 16 do w[j] = s232num(msg, i + (j - 1)*4) end\
  3848.     for j = 17, 64 do\
  3849.         local v = w[j - 15]\
  3850.         local s0 = bxor(rrotate(v, 7), rrotate(v, 18), rshift(v, 3))\
  3851.         v = w[j - 2]\
  3852.         w[j] = w[j - 16] + s0 + w[j - 7] + bxor(rrotate(v, 17), rrotate(v, 19), rshift(v, 10))\
  3853.     end\
  3854. \
  3855.     local a, b, c, d, e, f, g, h = H[1], H[2], H[3], H[4], H[5], H[6], H[7], H[8]\
  3856.     for i = 1, 64 do\
  3857.         local s0 = bxor(rrotate(a, 2), rrotate(a, 13), rrotate(a, 22))\
  3858.         local maj = bxor(band(a, b), band(a, c), band(b, c))\
  3859.         local t2 = s0 + maj\
  3860.         local s1 = bxor(rrotate(e, 6), rrotate(e, 11), rrotate(e, 25))\
  3861.         local ch = bxor (band(e, f), band(bnot(e), g))\
  3862.         local t1 = h + s1 + ch + k[i] + w[i]\
  3863.         h, g, f, e, d, c, b, a = g, f, e, d + t1, c, b, a, t1 + t2\
  3864.     end\
  3865. \
  3866.     H[1] = band(H[1] + a)\
  3867.     H[2] = band(H[2] + b)\
  3868.     H[3] = band(H[3] + c)\
  3869.     H[4] = band(H[4] + d)\
  3870.     H[5] = band(H[5] + e)\
  3871.     H[6] = band(H[6] + f)\
  3872.     H[7] = band(H[7] + g)\
  3873.     H[8] = band(H[8] + h)\
  3874. end\
  3875. \
  3876. function sha256(msg)\
  3877.     msg = preproc(msg, #msg)\
  3878.     local H = initH256({})\
  3879.     for i = 1, #msg, 64 do digestblock(msg, i, H) end\
  3880.     return str2hexa(num2s(H[1], 4) .. num2s(H[2], 4) .. num2s(H[3], 4) .. num2s(H[4], 4) ..\
  3881.         num2s(H[5], 4) .. num2s(H[6], 4) .. num2s(H[7], 4) .. num2s(H[8], 4))\
  3882. end",
  3883.     Peripheral = "GetPeripheral = function(_type)\
  3884.     for i, p in ipairs(GetPeripherals()) do\
  3885.         if p.Type == _type then\
  3886.             return p\
  3887.         end\
  3888.     end\
  3889. end\
  3890. \
  3891. Call = function(type, ...)\
  3892.     local tArgs = {...}\
  3893.     local p = GetPeripheral(type)\
  3894.     peripheral.call(p.Side, unpack(tArgs))\
  3895. end\
  3896. \
  3897. local getNames = peripheral.getNames or function()\
  3898.     local tResults = {}\
  3899.     for n,sSide in ipairs( rs.getSides() ) do\
  3900.         if peripheral.isPresent( sSide ) then\
  3901.             table.insert( tResults, sSide )\
  3902.             local isWireless = false\
  3903.             if pcall(function()isWireless = peripheral.call(sSide, 'isWireless') end) then\
  3904.                 isWireless = true\
  3905.             end     \
  3906.             if peripheral.getType( sSide ) == \"modem\" and not isWireless then\
  3907.                 local tRemote = peripheral.call( sSide, \"getNamesRemote\" )\
  3908.                 for n,sName in ipairs( tRemote ) do\
  3909.                     table.insert( tResults, sName )\
  3910.                 end\
  3911.             end\
  3912.         end\
  3913.     end\
  3914.     return tResults\
  3915. end\
  3916. \
  3917. GetPeripherals = function(filterType)\
  3918.     local peripherals = {}\
  3919.     for i, side in ipairs(getNames()) do\
  3920.         local name = peripheral.getType(side):gsub(\"^%l\", string.upper)\
  3921.         local code = string.upper(side:sub(1,1))\
  3922.         if side:find('_') then\
  3923.             code = side:sub(side:find('_')+1)\
  3924.         end\
  3925. \
  3926.         local dupe = false\
  3927.         for i, v in ipairs(peripherals) do\
  3928.             if v[1] == name .. ' ' .. code then\
  3929.                 dupe = true\
  3930.             end\
  3931.         end\
  3932. \
  3933.         if not dupe then\
  3934.             local _type = peripheral.getType(side)\
  3935.             local formattedType = _type:sub(1, 1):upper() .. _type:sub(2, -1)\
  3936.             local isWireless = false\
  3937.             if _type == 'modem' then\
  3938.                 if not pcall(function()isWireless = peripheral.call(side, 'isWireless') end) then\
  3939.                     isWireless = true\
  3940.                 end     \
  3941.                 if isWireless then\
  3942.                     _type = 'wireless_modem'\
  3943.                     formattedType = 'Wireless Modem'\
  3944.                     name = 'W '..name\
  3945.                 end\
  3946.             end\
  3947.             if not filterType or _type == filterType then\
  3948.                 table.insert(peripherals, {Name = name:sub(1,8) .. ' '..code, Fullname = name .. ' ('..side:sub(1, 1):upper() .. side:sub(2, -1)..')', Side = side, Type = _type, Wireless = isWireless, FormattedType = formattedType})\
  3949.             end\
  3950.         end\
  3951.     end\
  3952.     return peripherals\
  3953. end\
  3954. \
  3955. GetSide = function(side)\
  3956.     for i, p in ipairs(GetPeripherals()) do\
  3957.         if p.Side == side then\
  3958.             return p\
  3959.         end\
  3960.     end\
  3961. end\
  3962. \
  3963. PresentNamed = function(name)\
  3964.     return peripheral.isPresent(name)\
  3965. end\
  3966. \
  3967. CallType = function(type, ...)\
  3968.     local tArgs = {...}\
  3969.     local p = GetPeripheral(type)\
  3970.     return peripheral.call(p.Side, unpack(tArgs))\
  3971. end\
  3972. \
  3973. CallNamed = function(name, ...)\
  3974.     local tArgs = {...}\
  3975.     return peripheral.call(name, unpack(tArgs))\
  3976. end\
  3977. \
  3978. GetInfo = function(p)\
  3979.     local info = {}\
  3980.     local buttons = {}\
  3981.     if p.Type == 'computer' then\
  3982.         local id = peripheral.call(p.Side:lower(),'getID')\
  3983.         if id then\
  3984.             info = {\
  3985.                 ID = tostring(id)\
  3986.             }\
  3987.         else\
  3988.             info = {}\
  3989.         end\
  3990.     elseif p.Type == 'drive' then\
  3991.         local discType = 'No Disc'\
  3992.         local discID = nil\
  3993.         local mountPath = nil\
  3994.         local discLabel = nil\
  3995.         local songName = nil\
  3996.         if peripheral.call(p.Side:lower(), 'isDiskPresent') then\
  3997.             if peripheral.call(p.Side:lower(), 'hasData') then\
  3998.                 discType = 'Data'\
  3999.                 discID = peripheral.call(p.Side:lower(), 'getDiskID')\
  4000.                 if discID then\
  4001.                     discID = tostring(discID)\
  4002.                 else\
  4003.                     discID = 'None'\
  4004.                 end\
  4005.                 mountPath = '/'..peripheral.call(p.Side:lower(), 'getMountPath')..'/'\
  4006.                 discLabel = peripheral.call(p.Side:lower(), 'getDiskLabel')\
  4007.             else\
  4008.                 discType = 'Audio'\
  4009.                 songName = peripheral.call(p.Side:lower(), 'getAudioTitle')\
  4010.             end\
  4011.         end\
  4012.         if mountPath then\
  4013.             table.insert(buttons, {Text = 'View Files', OnClick = function(self, event, side, x, y)GoToPath(mountPath)end})\
  4014.         elseif discType == 'Audio' then\
  4015.             table.insert(buttons, {Text = 'Play', OnClick = function(self, event, side, x, y)\
  4016.                 if self.Text == 'Play' then\
  4017.                     disk.playAudio(p.Side:lower())\
  4018.                     self.Text = 'Stop'\
  4019.                 else\
  4020.                     disk.stopAudio(p.Side:lower())\
  4021.                     self.Text = 'Play'\
  4022.                 end\
  4023.             end})\
  4024.         else\
  4025.             diskOpenButton = nil\
  4026.         end\
  4027.         if discType ~= 'No Disc' then\
  4028.             table.insert(buttons, {Text = 'Eject', OnClick = function(self, event, side, x, y)disk.eject(p.Side:lower()) sleep(0) RefreshFiles() end})\
  4029.         end\
  4030. \
  4031.         info = {\
  4032.             ['Disc Type'] = discType,\
  4033.             ['Disc Label'] = discLabel,\
  4034.             ['Song Title'] = songName,\
  4035.             ['Disc ID'] = discID,\
  4036.             ['Mount Path'] = mountPath\
  4037.         }\
  4038.     elseif p.Type == 'printer' then\
  4039.         local pageSize = 'No Loaded Page'\
  4040.         local _, err = pcall(function() return tostring(peripheral.call(p.Side:lower(), 'getPgaeSize')) end)\
  4041.         if not err then\
  4042.             pageSize = tostring(peripheral.call(p.Side:lower(), 'getPageSize'))\
  4043.         end\
  4044.         info = {\
  4045.             ['Paper Level'] = tostring(peripheral.call(p.Side:lower(), 'getPaperLevel')),\
  4046.             ['Paper Size'] = pageSize,\
  4047.             ['Ink Level'] = tostring(peripheral.call(p.Side:lower(), 'getInkLevel'))\
  4048.         }\
  4049.     elseif p.Type == 'modem' then\
  4050.         info = {\
  4051.             ['Connected Peripherals'] = tostring(#peripheral.call(p.Side:lower(), 'getNamesRemote'))\
  4052.         }\
  4053.     elseif p.Type == 'monitor' then\
  4054.         local w, h = peripheral.call(p.Side:lower(), 'getSize')\
  4055.         local screenType = 'Black and White'\
  4056.         if peripheral.call(p.Side:lower(), 'isColour') then\
  4057.             screenType = 'Colour'\
  4058.         end\
  4059.         local buttonTitle = 'Use as Screen'\
  4060.         if OneOS.Settings:GetValues()['Monitor'] == p.Side:lower() then\
  4061.             buttonTitle = 'Use Computer Screen'\
  4062.         end\
  4063.         table.insert(buttons, {Text = buttonTitle, OnClick = function(self, event, side, x, y)\
  4064.                 self.Bedrock:DisplayAlertWindow('Reboot Required', \"To change screen you'll need to reboot your computer.\", {'Reboot', 'Cancel'}, function(value)\
  4065.                     if value == 'Reboot' then\
  4066.                         if buttonTitle == 'Use Computer Screen' then\
  4067.                             OneOS.Settings:SetValue('Monitor', nil)\
  4068.                         else\
  4069.                             OneOS.Settings:SetValue('Monitor', p.Side:lower())\
  4070.                         end\
  4071.                         OneOS.Reboot()\
  4072.                     end\
  4073.                 end)\
  4074.             end\
  4075.         })\
  4076.         info = {\
  4077.             ['Type'] = screenType,\
  4078.             ['Width'] = tostring(w),\
  4079.             ['Height'] = tostring(h),\
  4080.         }\
  4081.     end\
  4082.     info.Buttons = buttons\
  4083.     return info\
  4084. end",
  4085.     Wireless = "--This is just the OneOS Wireless API\
  4086. \
  4087. --OneOS uses channels between 4200 and 4300, avoid use where possible\
  4088. \
  4089. Channels = {\
  4090.     Ignored = 4299,\
  4091.     Ping = 4200,\
  4092.     PingReply = 4201,\
  4093.     oeedPayServerAvailable = 4260,\
  4094.     oeedPayServerAvailableReply = 4261,\
  4095.     oeedPayPocketPayPing = 4262,\
  4096.     oeedPayPocketPayPingReply = 4263,\
  4097.     oeedPayPocketPayPaymentInfo = 4264,\
  4098.     oeedPayPocketPayPaymentInfoReply = 4265,\
  4099.     oeedPayValidatePayment = 4266,\
  4100.     oeedPayValidatePaymentReply = 4267,\
  4101.     oeedPayPocketPayChallenge = 4268,\
  4102.     oeedPayPocketPayChallengeReply = 4269,\
  4103.     oeedPayPocketPayChallengeAnswer = 4270,\
  4104.     oeedPayPocketPayChallengeAnswerReply = 4271,\
  4105.     oeedPayPocketPayResult = 4272,\
  4106.     oeedPayBalanceCheck = 4273,\
  4107.     oeedPayBalanceCheckReply = 4274,\
  4108.     oeedPayAPIRequest = 4275,\
  4109.     oeedPayAPIRequestReply = 4276,\
  4110.     oeedPayNewAccount = 4277,\
  4111.     oeedPayNewAccountReply = 4278,\
  4112.     oeedPayGetAccountName = 4279,\
  4113.     oeedPayGetAccountNameReply = 4280,\
  4114. }\
  4115. \
  4116. local function isOpen(channel)\
  4117.     return Peripheral.CallType('wireless_modem', 'isOpen', channel)\
  4118. end\
  4119. \
  4120. local function open(channel)\
  4121.     if not isOpen(channel) then\
  4122.         Peripheral.CallType('wireless_modem', 'open', channel)\
  4123.     end\
  4124. end\
  4125. \
  4126. Open = open\
  4127. \
  4128. local function close(channel)\
  4129.     Peripheral.CallType('wireless_modem', 'close', channel)\
  4130. end\
  4131. \
  4132. local function closeAll()\
  4133.     Peripheral.CallType('wireless_modem', 'closeAll')\
  4134. end\
  4135. \
  4136. local function transmit(channel, replyChannel, message)\
  4137.     Peripheral.CallType('wireless_modem', 'transmit', channel, replyChannel, textutils.serialize(message))\
  4138. end\
  4139. \
  4140. function Present()\
  4141.     if Peripheral.GetPeripheral('wireless_modem') == nil then\
  4142.         return false\
  4143.     else\
  4144.         return true\
  4145.     end\
  4146. end\
  4147. \
  4148. local function FormatMessage(message, messageID, destinationID)\
  4149.     return {\
  4150.         content = textutils.serialize(message),\
  4151.         senderID = os.getComputerID(),\
  4152.         senderName = os.getComputerLabel(),\
  4153.         channel = channel,\
  4154.         replyChannel = reply,\
  4155.         messageID = messageID or math.random(10000),\
  4156.         destinationID = destinationID\
  4157.     }\
  4158. end\
  4159. \
  4160. local Timeout = function(func, time)\
  4161.     time = time or 1\
  4162.     parallel.waitForAny(func, function()\
  4163.         sleep(time)\
  4164.         --log('Timeout!'..time)\
  4165.     end)\
  4166. end\
  4167. \
  4168. RecieveMessage = function(_channel, messageID, timeout)\
  4169.     open(_channel)\
  4170.     local done = false\
  4171.     local event, side, channel, replyChannel, message = nil\
  4172.     Timeout(function()\
  4173.         while not done do\
  4174.             event, side, channel, replyChannel, message = os.pullEvent('modem_message')\
  4175.             if channel ~= _channel then\
  4176.                 event, side, channel, replyChannel, message = nil\
  4177.             else\
  4178.                 message = textutils.unserialize(message)\
  4179.                 message.content = textutils.unserialize(message.content)\
  4180.                 if messageID and messageID ~= message.messageID or (message.destinationID ~= nil and message.destinationID ~= os.getComputerID()) then\
  4181.                     event, side, channel, replyChannel, message = nil\
  4182.                 else\
  4183.                     done = true\
  4184.                 end\
  4185.             end\
  4186.         end\
  4187.     end,\
  4188.     timeout)\
  4189.     return event, side, channel, replyChannel, message\
  4190. end\
  4191. \
  4192. Initialise = function()\
  4193.     if Present() then\
  4194.         for i, c in pairs(Channels) do\
  4195.             open(c)\
  4196.         end\
  4197.     end\
  4198. end\
  4199. \
  4200. HandleMessage = function(event, side, channel, replyChannel, message, distance)\
  4201.     message = textutils.unserialize(message)\
  4202.     message.content = textutils.unserialize(message.content)\
  4203. \
  4204.     if channel == Channels.Ping then\
  4205.         if message.content == 'Ping!' then\
  4206.             SendMessage(replyChannel, 'Pong!', nil, message.messageID)\
  4207.         end\
  4208.     elseif message.destinationID ~= nil and message.destinationID ~= os.getComputerID() then\
  4209.     elseif Wireless.Responder then\
  4210.         Wireless.Responder(event, side, channel, replyChannel, message, distance)\
  4211.     end\
  4212. end\
  4213. \
  4214. SendMessage = function(channel, message, reply, messageID, destinationID)\
  4215.     reply = reply or channel + 1\
  4216.     open(channel)\
  4217.     open(reply)\
  4218.     local _message = FormatMessage(message, messageID, destinationID)\
  4219.     transmit(channel, reply, _message)\
  4220.     return _message\
  4221. end\
  4222. \
  4223. Ping = function()\
  4224.     local message = SendMessage(Channels.Ping, 'Ping!', Channels.PingReply)\
  4225.     RecieveMessage(Channels.PingReply, message.messageID)\
  4226. end",
  4227.   },
  4228. }
  4229.  
  4230. local function run(tArgs)
  4231.  
  4232.   local fnFile, err = loadstring(files['startup'], 'startup')
  4233.   if err then
  4234.     error(err)
  4235.   end
  4236.  
  4237.   local function split(str, pat)
  4238.      local t = {}
  4239.      local fpat = "(.-)" .. pat
  4240.      local last_end = 1
  4241.      local s, e, cap = str:find(fpat, 1)
  4242.      while s do
  4243.         if s ~= 1 or cap ~= "" then
  4244.      table.insert(t,cap)
  4245.         end
  4246.         last_end = e+1
  4247.         s, e, cap = str:find(fpat, last_end)
  4248.      end
  4249.      if last_end <= #str then
  4250.         cap = str:sub(last_end)
  4251.         table.insert(t, cap)
  4252.      end
  4253.      return t
  4254.   end
  4255.  
  4256.   local function resolveTreeForPath(path, single)
  4257.     local _files = files
  4258.     local parts = split(path, '/')
  4259.     if parts then
  4260.       for i, v in ipairs(parts) do
  4261.         if #v > 0 then
  4262.           if _files[v] then
  4263.             _files = _files[v]
  4264.           else
  4265.             _files = nil
  4266.             break
  4267.           end
  4268.         end
  4269.       end
  4270.     elseif #path > 0 and path ~= '/' then
  4271.       _files = _files[path]
  4272.     end
  4273.     if not single or type(_files) == 'string' then
  4274.       return _files
  4275.     end
  4276.   end
  4277.  
  4278.   local oldFs = fs
  4279.   local env
  4280.   env = {
  4281.     fs = {
  4282.       list = function(path)
  4283.               local list = {}
  4284.               if fs.exists(path) then
  4285.             list = fs.list(path)
  4286.               end
  4287.         for k, v in pairs(resolveTreeForPath(path)) do
  4288.           if not fs.exists(path .. '/' ..k) then
  4289.             table.insert(list, k)
  4290.           end
  4291.         end
  4292.         return list
  4293.       end,
  4294.  
  4295.       exists = function(path)
  4296.         if fs.exists(path) then
  4297.           return true
  4298.         elseif resolveTreeForPath(path) then
  4299.           return true
  4300.         else
  4301.           return false
  4302.         end
  4303.       end,
  4304.  
  4305.       isDir = function(path)
  4306.         if fs.isDir(path) then
  4307.           return true
  4308.         else
  4309.           local tree = resolveTreeForPath(path)
  4310.           if tree and type(tree) == 'table' then
  4311.             return true
  4312.           else
  4313.             return false
  4314.           end
  4315.         end
  4316.       end,
  4317.  
  4318.       isReadOnly = function(path)
  4319.         if not fs.isReadOnly(path) then
  4320.           return false
  4321.         else
  4322.           return true
  4323.         end
  4324.       end,
  4325.  
  4326.       getName = fs.getName,
  4327.  
  4328.       getSize = fs.getSize,
  4329.  
  4330.       getFreespace = fs.getFreespace,
  4331.  
  4332.       makeDir = fs.makeDir,
  4333.  
  4334.       move = fs.move,
  4335.  
  4336.       copy = fs.copy,
  4337.  
  4338.       delete = fs.delete,
  4339.  
  4340.       combine = fs.combine,
  4341.  
  4342.       open = function(path, mode)
  4343.         if fs.exists(path) then
  4344.           return fs.open(path, mode)
  4345.         elseif type(resolveTreeForPath(path)) == 'string' then
  4346.           local handle = {close = function()end}
  4347.           if mode == 'r' then
  4348.             local content = resolveTreeForPath(path)
  4349.             handle.readAll = function()
  4350.               return content
  4351.             end
  4352.  
  4353.             local line = 1
  4354.             local lines = split(content, '\n')
  4355.             handle.readLine = function()
  4356.               if line > #lines then
  4357.                 return nil
  4358.               else
  4359.                 return lines[line]
  4360.               end
  4361.               line = line + 1
  4362.             end
  4363.                       return handle
  4364.           else
  4365.             error('Cannot write to read-only file (compilr archived).')
  4366.           end
  4367.         else
  4368.           return fs.open(path, mode)
  4369.         end
  4370.       end
  4371.     },
  4372.  
  4373.     io = {
  4374.       input = io.input,
  4375.       output = io.output,
  4376.       type = io.type,
  4377.       close = io.close,
  4378.       write = io.write,
  4379.       flush = io.flush,
  4380.       lines = io.lines,
  4381.       read = io.read,
  4382.       open = function(path, mode)
  4383.         if fs.exists(path) then
  4384.           return io.open(path, mode)
  4385.         elseif type(resolveTreeForPath(path)) == 'string' then
  4386.           local content = resolveTreeForPath(path)
  4387.           local f = fs.open(path, 'w')
  4388.           f.write(content)
  4389.           f.close()
  4390.           if mode == 'r' then
  4391.             return io.open(path, mode)
  4392.           else
  4393.             error('Cannot write to read-only file (compilr archived).')
  4394.           end
  4395.         else
  4396.           return io.open(path, mode)
  4397.         end
  4398.       end
  4399.     },
  4400.  
  4401.     loadfile = function( _sFile )
  4402.         local file = env.fs.open( _sFile, "r" )
  4403.         if file then
  4404.             local func, err = loadstring( file.readAll(), fs.getName( _sFile ) )
  4405.             file.close()
  4406.             return func, err
  4407.         end
  4408.         return nil, "File not found: ".._sFile
  4409.     end,
  4410.  
  4411.     dofile = function( _sFile )
  4412.         local fnFile, e = env.loadfile( _sFile )
  4413.         if fnFile then
  4414.             setfenv( fnFile, getfenv(2) )
  4415.             return fnFile()
  4416.         else
  4417.             error( e, 2 )
  4418.         end
  4419.     end
  4420.   }
  4421.  
  4422.   setmetatable( env, { __index = _G } )
  4423.  
  4424.   local tAPIsLoading = {}
  4425.   env.os.loadAPI = function( _sPath )
  4426.       local sName = fs.getName( _sPath )
  4427.       if tAPIsLoading[sName] == true then
  4428.           printError( "API "..sName.." is already being loaded" )
  4429.           return false
  4430.       end
  4431.       tAPIsLoading[sName] = true
  4432.          
  4433.       local tEnv = {}
  4434.       setmetatable( tEnv, { __index = env } )
  4435.       local fnAPI, err = env.loadfile( _sPath )
  4436.       if fnAPI then
  4437.           setfenv( fnAPI, tEnv )
  4438.           fnAPI()
  4439.       else
  4440.           printError( err )
  4441.           tAPIsLoading[sName] = nil
  4442.           return false
  4443.       end
  4444.      
  4445.       local tAPI = {}
  4446.       for k,v in pairs( tEnv ) do
  4447.           tAPI[k] =  v
  4448.       end
  4449.      
  4450.       env[sName] = tAPI    
  4451.       tAPIsLoading[sName] = nil
  4452.       return true
  4453.   end
  4454.  
  4455.   env.shell = shell
  4456.  
  4457.   setfenv( fnFile, env )
  4458.   fnFile(unpack(tArgs))
  4459. end
  4460.  
  4461. local function extract()
  4462.     local function node(path, tree)
  4463.         if type(tree) == 'table' then
  4464.             fs.makeDir(path)
  4465.             for k, v in pairs(tree) do
  4466.                 node(path .. '/' .. k, v)
  4467.             end
  4468.         else
  4469.             local f = fs.open(path, 'w')
  4470.             if f then
  4471.                 f.write(tree)
  4472.                 f.close()
  4473.             end
  4474.         end
  4475.     end
  4476.     node('', files)
  4477. end
  4478.  
  4479. local tArgs = {...}
  4480. if #tArgs == 1 and tArgs[1] == '--extract' then
  4481.   extract()
  4482. else
  4483.   run(tArgs)
  4484. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement