SHARE
TWEET

oeedPay Server

Oeed Nov 8th, 2014 (edited) 241 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  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
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
Not a member of Pastebin yet?
Sign Up, it unlocks many cool features!
 
Top