Advertisement
Oeed

oeedPay Station

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