Advertisement
minimite

as2ina

Feb 20th, 2015
268
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 126.80 KB | None | 0 0
  1. --Stand alone installer for CCAppStore 2 by oeed, installer by CodedPixelCube/minimite. For the automatic installer for CCAppStore 2 OneOS Edition (adds direct program to OneOS), see http://pastebin.com/v1EJNw9t, or the full installer, http://pastebin.com/kkebdzsx.
  2.  
  3. local pkg={
  4.   Bedrock = "--Bedrock Build: 483\
  5. --This code is squished down in to one, rather hard to read file.\
  6. --As such it is not much good for anything other than being loaded as an API.\
  7. --If you want to look at the code to learn from it, copy parts or just take a look,\
  8. --you should go to the GitHub repo. http://github.com/oeed/Bedrock/\
  9. \
  10. --\
  11. --      Bedrock is the core program framework used by all OneOS and OneCode programs.\
  12. --                          Inspired by Apple's Cocoa framework.\
  13. --                                     (c) oeed 2014\
  14. --\
  15. --        For documentation see the Bedrock wiki, github.com/oeed/Bedrock/wiki/\
  16. --\
  17. \
  18. local apis = {\
  19. [\"Drawing\"] = [[\
  20. local round = function(num, idp)\
  21.     local mult = 10^(idp or 0)\
  22.     return math.floor(num * mult + 0.5) / mult\
  23. end\
  24. \
  25. local _w, _h = term.getSize()\
  26. local copyBuffer = nil\
  27. \
  28. Screen = {\
  29.     Width = _w,\
  30.     Height = _h\
  31. }\
  32. \
  33. Constraints = {\
  34.     \
  35. }\
  36. \
  37. CurrentConstraint = {1,1,_w,_h}\
  38. IgnoreConstraint = false\
  39. \
  40. function AddConstraint(x, y, width, height)\
  41.     local x2 = x + width - 1\
  42.     local y2 = y + height - 1\
  43.     table.insert(Drawing.Constraints, {x, y, x2, y2})\
  44.     Drawing.GetConstraint()\
  45. end\
  46. \
  47. function RemoveConstraint()\
  48.     --table.remove(Drawing.Constraints, #Drawing.Constraints)\
  49.     Drawing.Constraints[#Drawing.Constraints] = nil\
  50.     Drawing.GetConstraint()\
  51. end\
  52. \
  53. function GetConstraint()\
  54.     local x = 1\
  55.     local y = 1\
  56.     local x2 = Drawing.Screen.Width\
  57.     local y2 = Drawing.Screen.Height\
  58.     for i, c in ipairs(Drawing.Constraints) do\
  59.         if x < c[1] then\
  60.             x = c[1]\
  61.         end\
  62.         if y < c[2] then\
  63.             y = c[2]\
  64.         end\
  65.         if x2 > c[3] then\
  66.             x2 = c[3]\
  67.         end\
  68.         if y2 > c[4] then\
  69.             y2 = c[4]\
  70.         end\
  71.     end\
  72.     Drawing.CurrentConstraint = {x, y, x2, y2}\
  73. end\
  74. \
  75. function WithinContraint(x, y)\
  76.     return Drawing.IgnoreConstraint or\
  77.           (x >= Drawing.CurrentConstraint[1] and\
  78.            y >= Drawing.CurrentConstraint[2] and\
  79.            x <= Drawing.CurrentConstraint[3] and\
  80.            y <= Drawing.CurrentConstraint[4])\
  81. end\
  82. \
  83. colours.transparent = 0\
  84. colors.transparent = 0\
  85. \
  86. Filters = {\
  87.     None = {\
  88.         [colours.white] = colours.white,\
  89.         [colours.orange] = colours.orange,\
  90.         [colours.magenta] = colours.magenta,\
  91.         [colours.lightBlue] = colours.lightBlue,\
  92.         [colours.yellow] = colours.yellow,\
  93.         [colours.lime] = colours.lime,\
  94.         [colours.pink] = colours.pink,\
  95.         [colours.grey] = colours.grey,\
  96.         [colours.lightGrey] = colours.lightGrey,\
  97.         [colours.cyan] = colours.cyan,\
  98.         [colours.purple] = colours.purple,\
  99.         [colours.blue] = colours.blue,\
  100.         [colours.brown] = colours.brown,\
  101.         [colours.green] = colours.green,\
  102.         [colours.red] = colours.red,\
  103.         [colours.black] = colours.black,\
  104.         [colours.transparent] = colours.transparent,\
  105.     },\
  106. \
  107.     Greyscale = {\
  108.         [colours.white] = colours.white,\
  109.         [colours.orange] = colours.lightGrey,\
  110.         [colours.magenta] = colours.lightGrey,\
  111.         [colours.lightBlue] = colours.lightGrey,\
  112.         [colours.yellow] = colours.lightGrey,\
  113.         [colours.lime] = colours.lightGrey,\
  114.         [colours.pink] = colours.lightGrey,\
  115.         [colours.grey] = colours.grey,\
  116.         [colours.lightGrey] = colours.lightGrey,\
  117.         [colours.cyan] = colours.grey,\
  118.         [colours.purple] = colours.grey,\
  119.         [colours.blue] = colours.grey,\
  120.         [colours.brown] = colours.grey,\
  121.         [colours.green] = colours.grey,\
  122.         [colours.red] = colours.grey,\
  123.         [colours.black] = colours.black,\
  124.         [colours.transparent] = colours.transparent,\
  125.     },\
  126. \
  127.     BlackWhite = {\
  128.         [colours.white] = colours.white,\
  129.         [colours.orange] = colours.white,\
  130.         [colours.magenta] = colours.white,\
  131.         [colours.lightBlue] = colours.white,\
  132.         [colours.yellow] = colours.white,\
  133.         [colours.lime] = colours.white,\
  134.         [colours.pink] = colours.white,\
  135.         [colours.grey] = colours.black,\
  136.         [colours.lightGrey] = colours.white,\
  137.         [colours.cyan] = colours.black,\
  138.         [colours.purple] = colours.black,\
  139.         [colours.blue] = colours.black,\
  140.         [colours.brown] = colours.black,\
  141.         [colours.green] = colours.black,\
  142.         [colours.red] = colours.black,\
  143.         [colours.black] = colours.black,\
  144.         [colours.transparent] = colours.transparent,\
  145.     },\
  146. \
  147.     Darker = {\
  148.         [colours.white] = colours.lightGrey,\
  149.         [colours.orange] = colours.red,\
  150.         [colours.magenta] = colours.purple,\
  151.         [colours.lightBlue] = colours.cyan,\
  152.         [colours.yellow] = colours.orange,\
  153.         [colours.lime] = colours.green,\
  154.         [colours.pink] = colours.magenta,\
  155.         [colours.grey] = colours.black,\
  156.         [colours.lightGrey] = colours.grey,\
  157.         [colours.cyan] = colours.blue,\
  158.         [colours.purple] = colours.grey,\
  159.         [colours.blue] = colours.grey,\
  160.         [colours.brown] = colours.grey,\
  161.         [colours.green] = colours.grey,\
  162.         [colours.red] = colours.brown,\
  163.         [colours.black] = colours.black,\
  164.         [colours.transparent] = colours.transparent,\
  165.     },\
  166. \
  167.     Lighter = {\
  168.         [colours.white] = colours.lightGrey,\
  169.         [colours.orange] = colours.yellow,\
  170.         [colours.magenta] = colours.pink,\
  171.         [colours.lightBlue] = colours.cyan,\
  172.         [colours.yellow] = colours.orange,\
  173.         [colours.lime] = colours.green,\
  174.         [colours.pink] = colours.magenta,\
  175.         [colours.grey] = colours.lightGrey,\
  176.         [colours.lightGrey] = colours.grey,\
  177.         [colours.cyan] = colours.lightBlue,\
  178.         [colours.purple] = colours.magenta,\
  179.         [colours.blue] = colours.lightBlue,\
  180.         [colours.brown] = colours.red,\
  181.         [colours.green] = colours.lime,\
  182.         [colours.red] = colours.orange,\
  183.         [colours.black] = colours.grey,\
  184.         [colours.transparent] = colours.transparent,\
  185.     },\
  186. \
  187.     Highlight = {\
  188.         [colours.white] = colours.lightGrey,\
  189.         [colours.orange] = colours.yellow,\
  190.         [colours.magenta] = colours.pink,\
  191.         [colours.lightBlue] = colours.cyan,\
  192.         [colours.yellow] = colours.orange,\
  193.         [colours.lime] = colours.green,\
  194.         [colours.pink] = colours.magenta,\
  195.         [colours.grey] = colours.lightGrey,\
  196.         [colours.lightGrey] = colours.grey,\
  197.         [colours.cyan] = colours.lightBlue,\
  198.         [colours.purple] = colours.magenta,\
  199.         [colours.blue] = colours.lightBlue,\
  200.         [colours.brown] = colours.red,\
  201.         [colours.green] = colours.lime,\
  202.         [colours.red] = colours.orange,\
  203.         [colours.black] = colours.grey,\
  204.         [colours.transparent] = colours.transparent,\
  205.     },\
  206. \
  207.     Invert = {\
  208.         [colours.white] = colours.black,\
  209.         [colours.orange] = colours.blue,\
  210.         [colours.magenta] = colours.green,\
  211.         [colours.lightBlue] = colours.brown,\
  212.         [colours.yellow] = colours.blue,\
  213.         [colours.lime] = colours.purple,\
  214.         [colours.pink] = colours.green,\
  215.         [colours.grey] = colours.lightGrey,\
  216.         [colours.lightGrey] = colours.grey,\
  217.         [colours.cyan] = colours.red,\
  218.         [colours.purple] = colours.green,\
  219.         [colours.blue] = colours.yellow,\
  220.         [colours.brown] = colours.lightBlue,\
  221.         [colours.green] = colours.purple,\
  222.         [colours.red] = colours.cyan,\
  223.         [colours.black] = colours.white,\
  224.         [colours.transparent] = colours.transparent,\
  225.     },\
  226. }\
  227. \
  228. function FilterColour(colour, filter)\
  229.     if filter[colour] then\
  230.         return filter[colour]\
  231.     else\
  232.         return colour\
  233.     end\
  234. end\
  235. \
  236. DrawCharacters = function (x, y, characters, textColour, bgColour)\
  237.     Drawing.WriteStringToBuffer(x, y, tostring(characters), textColour, bgColour)\
  238. end\
  239. \
  240. DrawBlankArea = function (x, y, w, h, colour)\
  241.     if colour ~= colours.transparent then\
  242.         Drawing.DrawArea (x, y, w, h, \" \", 1, colour)\
  243.     end\
  244. end\
  245. \
  246. DrawArea = function (x, y, w, h, character, textColour, bgColour)\
  247.     --width must be greater than 1, otherwise we get problems\
  248.     if w < 0 then\
  249.         w = w * -1\
  250.     elseif w == 0 then\
  251.         w = 1\
  252.     end\
  253. \
  254.     for ix = 1, w do\
  255.         local currX = x + ix - 1\
  256.         for iy = 1, h do\
  257.             local currY = y + iy - 1\
  258.             Drawing.WriteToBuffer(currX, currY, character, textColour, bgColour)\
  259.         end\
  260.     end\
  261. end\
  262. \
  263. DrawImage = function(_x,_y,tImage, w, h)\
  264.     if tImage then\
  265.         for y = 1, h do\
  266.             if not tImage[y] then\
  267.                 break\
  268.             end\
  269.             for x = 1, w do\
  270.                 if not tImage[y][x] then\
  271.                     break\
  272.                 end\
  273.                 local bgColour = tImage[y][x]\
  274.                 local textColour = tImage.textcol[y][x] or colours.white\
  275.                 local char = tImage.text[y][x]\
  276.                 Drawing.WriteToBuffer(x+_x-1, y+_y-1, char, textColour, bgColour)\
  277.             end\
  278.         end\
  279.     elseif w and h then\
  280.         Drawing.DrawBlankArea(_x, _y, w, h, colours.lightGrey)\
  281.     end\
  282. end\
  283. \
  284. --using .nft\
  285. LoadImage = function(path, global)\
  286.     local image = {\
  287.         text = {},\
  288.         textcol = {}\
  289.     }\
  290.     if fs.exists(path) then\
  291.         local _io = io\
  292.         if OneOS and global then\
  293.             _io = OneOS.IO\
  294.         end\
  295.        local file = _io.open(path, \"r\")\
  296.        if not file then\
  297.             error('Error Occured. _io:'..tostring(_io)..' OneOS: '..tostring(OneOS)..' OneOS.IO'..tostring(OneOS.IO)..' io: '..tostring(io))\
  298.        end\
  299.        local sLine = file:read()\
  300.        local num = 1\
  301.        while sLine do  \
  302.            table.insert(image, num, {})\
  303.            table.insert(image.text, num, {})\
  304.            table.insert(image.textcol, num, {})\
  305.                                        \
  306.            --As we're no longer 1-1, we keep track of what index to write to\
  307.            local writeIndex = 1\
  308.            --Tells us if we've hit a 30 or 31 (BG and FG respectively)- next char specifies the curr colour\
  309.            local bgNext, fgNext = false, false\
  310.            --The current background and foreground colours\
  311.            local currBG, currFG = nil,nil\
  312.            for i=1,#sLine do\
  313.                    local nextChar = string.sub(sLine, i, i)\
  314.                    if nextChar:byte() == 30 then\
  315.                            bgNext = true\
  316.                    elseif nextChar:byte() == 31 then\
  317.                            fgNext = true\
  318.                    elseif bgNext then\
  319.                            currBG = Drawing.GetColour(nextChar)\
  320.                             if currBG == nil then\
  321.                                 currBG = colours.transparent\
  322.                             end\
  323.                            bgNext = false\
  324.                    elseif fgNext then\
  325.                            currFG = Drawing.GetColour(nextChar)\
  326.                             if currFG == nil or currFG == colours.transparent then\
  327.                                 currFG = colours.white\
  328.                             end\
  329.                            fgNext = false\
  330.                    else\
  331.                            if nextChar ~= \" \" and currFG == nil then\
  332.                                    currFG = colours.white\
  333.                            end\
  334.                            image[num][writeIndex] = currBG\
  335.                            image.textcol[num][writeIndex] = currFG\
  336.                            image.text[num][writeIndex] = nextChar\
  337.                            writeIndex = writeIndex + 1\
  338.                    end\
  339.            end\
  340.            num = num+1\
  341.            sLine = file:read()\
  342.        end\
  343.        file:close()\
  344.    else\
  345.         return nil\
  346.     end\
  347.     return image\
  348. end\
  349. \
  350. DrawCharactersCenter = function(x, y, w, h, characters, textColour,bgColour)\
  351.     w = w or Drawing.Screen.Width\
  352.     h = h or Drawing.Screen.Height\
  353.     x = x or 0\
  354.     y = y or 0\
  355.     x = math.floor((w - #characters) / 2) + x\
  356.     y = math.floor(h / 2) + y\
  357. \
  358.     Drawing.DrawCharacters(x, y, characters, textColour, bgColour)\
  359. end\
  360. \
  361. GetColour = function(hex)\
  362.     if hex == ' ' then\
  363.         return colours.transparent\
  364.     end\
  365.    local value = tonumber(hex, 16)\
  366.    if not value then return nil end\
  367.    value = math.pow(2,value)\
  368.    return value\
  369. end\
  370. \
  371. Clear = function (_colour)\
  372.     _colour = _colour or colours.black\
  373.     Drawing.DrawBlankArea(1, 1, Drawing.Screen.Width, Drawing.Screen.Height, _colour)\
  374. end\
  375. \
  376. Buffer = {}\
  377. BackBuffer = {}\
  378. \
  379. TryRestore = false\
  380. \
  381. \
  382. --TODO: make this quicker\
  383. -- maybe sort the pixels in order of colour so it doesn't have to set the colour each time\
  384. DrawBuffer = function()\
  385.     if TryRestore and Restore then\
  386.         Restore()\
  387.     end\
  388. \
  389.     for y,row in pairs(Drawing.Buffer) do\
  390.         for x,pixel in pairs(row) do\
  391.             local shouldDraw = true\
  392.             local hasBackBuffer = true\
  393.             if Drawing.BackBuffer[y] == nil or Drawing.BackBuffer[y][x] == nil or #Drawing.BackBuffer[y][x] ~= 3 then\
  394.                 hasBackBuffer = false\
  395.             end\
  396.             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\
  397.                 shouldDraw = false\
  398.             end\
  399.             if shouldDraw then\
  400.                 term.setBackgroundColour(pixel[3])\
  401.                 term.setTextColour(pixel[2])\
  402.                 term.setCursorPos(x, y)\
  403.                 term.write(pixel[1])\
  404.             end\
  405.         end\
  406.     end\
  407.     Drawing.BackBuffer = Drawing.Buffer\
  408.     Drawing.Buffer = {}\
  409. end\
  410. \
  411. ClearBuffer = function()\
  412.     Drawing.Buffer = {}\
  413. end\
  414. \
  415. WriteStringToBuffer = function (x, y, characters, textColour,bgColour)\
  416.     for i = 1, #characters do\
  417.         local character = characters:sub(i,i)\
  418.         Drawing.WriteToBuffer(x + i - 1, y, character, textColour, bgColour)\
  419.     end\
  420. end\
  421. \
  422. WriteToBuffer = function(x, y, character, textColour,bgColour, cached)\
  423.     if not cached and not Drawing.WithinContraint(x, y) then\
  424.         return\
  425.     end\
  426.     x = round(x)\
  427.     y = round(y)\
  428. \
  429.     if textColour == colours.transparent then\
  430.         character = ' '\
  431.     end\
  432. \
  433.     if bgColour == colours.transparent then\
  434.         Drawing.Buffer[y] = Drawing.Buffer[y] or {}\
  435.         Drawing.Buffer[y][x] = Drawing.Buffer[y][x] or {\"\", colours.white, colours.black}\
  436.         Drawing.Buffer[y][x][1] = character\
  437.         Drawing.Buffer[y][x][2] = textColour\
  438.     else\
  439.         Drawing.Buffer[y] = Drawing.Buffer[y] or {}\
  440.         Drawing.Buffer[y][x] = {character, textColour, bgColour}\
  441.     end\
  442. \
  443.     if copyBuffer then\
  444.         copyBuffer[y] = copyBuffer[y] or {}\
  445.         copyBuffer[y][x] = {character, textColour, bgColour}        \
  446.     end\
  447. end\
  448. \
  449. DrawCachedBuffer = function(buffer)\
  450.     for y, row in pairs(buffer) do\
  451.         for x, pixel in pairs(row) do\
  452.             WriteToBuffer(x, y, pixel[1], pixel[2], pixel[3], true)\
  453.         end\
  454.     end\
  455. end\
  456. \
  457. StartCopyBuffer = function()\
  458.     copyBuffer = {}\
  459. end\
  460. \
  461. EndCopyBuffer = function()\
  462.     local tmpCopy = copyBuffer\
  463.     copyBuffer = nil\
  464.     return tmpCopy\
  465. end\
  466. ]],\
  467. [\"Helpers\"] = [[\
  468. LongestString = function(input, key, isKey)\
  469.     local length = 0\
  470.     if isKey then\
  471.         for k, v in pairs(input) do\
  472.             local titleLength = string.len(k)\
  473.             if titleLength > length then\
  474.                 length = titleLength\
  475.             end\
  476.         end\
  477.     else\
  478.         for i = 1, #input do\
  479.             local value = input[i]\
  480.             if key then\
  481.                 if value[key] then\
  482.                     value = value[key]\
  483.                 else\
  484.                     value = ''\
  485.                 end\
  486.             end\
  487.             local titleLength = string.len(value)\
  488.             if titleLength > length then\
  489.                 length = titleLength\
  490.             end\
  491.         end\
  492.     end\
  493.     return length\
  494. end\
  495. \
  496. Split = function(str,sep)\
  497.    sep=sep or'/'\
  498.    return str:match(\"(.*\"..sep..\")\")\
  499. end\
  500. \
  501. Extension = function(path, addDot)\
  502.     if not path then\
  503.         return nil\
  504.     elseif not string.find(fs.getName(path), '%.') then\
  505.         return ''\
  506.     else\
  507.         local _path = path\
  508.         if path:sub(#path) == '/' then\
  509.             _path = path:sub(1,#path-1)\
  510.         end\
  511.         local extension = _path:gmatch('%.[0-9a-z]+$')()\
  512.         if extension then\
  513.             extension = extension:sub(2)\
  514.         else\
  515.             --extension = nil\
  516.             return ''\
  517.         end\
  518.         if addDot then\
  519.             extension = '.'..extension\
  520.         end\
  521.         return extension:lower()\
  522.     end\
  523. end\
  524. \
  525. RemoveExtension = function(path)\
  526. --local name = string.match(fs.getName(path), '(%a+)%.?.-')\
  527.     if not path:find('%.') then\
  528.         return path\
  529.     end\
  530.     local extension = Helpers.Extension(path)\
  531.     if extension == path then\
  532.         return fs.getName(path)\
  533.     end\
  534.     return string.gsub(path, extension, ''):sub(1, -2)\
  535. end\
  536. \
  537. RemoveFileName = function(path)\
  538.     if string.sub(path, -1) == '/' then\
  539.         path = string.sub(path, 1, -2)\
  540.     end\
  541.     local v = string.match(path, \"(.-)([^\\\\/]-%.?([^%.\\\\/]*))$\")\
  542.     if type(v) == 'string' then\
  543.         return v\
  544.     end\
  545.     return v[1]\
  546. end\
  547. \
  548. TruncateString = function(sString, maxLength)\
  549.     if #sString > maxLength then\
  550.         sString = sString:sub(1,maxLength-3)\
  551.         if sString:sub(-1) == ' ' then\
  552.             sString = sString:sub(1,maxLength-4)\
  553.         end\
  554.         sString = sString  .. '...'\
  555.     end\
  556.     return sString\
  557. end\
  558. \
  559. TruncateStringStart = function(sString, maxLength)\
  560.     local len = #sString\
  561.     if #sString > maxLength then\
  562.         sString = sString:sub(len - maxLength, len - 3)\
  563.         if sString:sub(-1) == ' ' then\
  564.             sString = sString:sub(len - maxLength, len - 4)\
  565.         end\
  566.         sString = '...' .. sString\
  567.     end\
  568.     return sString\
  569. end\
  570. \
  571. WrapText = function(text, maxWidth)\
  572.     local lines = {''}\
  573.    for word, space in text:gmatch('(%S+)(%s*)') do\
  574.            local temp = lines[#lines] .. word .. space:gsub('\\n','')\
  575.            if #temp > maxWidth then\
  576.                    table.insert(lines, '')\
  577.            end\
  578.            if space:find('\\n') then\
  579.                    lines[#lines] = lines[#lines] .. word\
  580.                    \
  581.                    space = space:gsub('\\n', function()\
  582.                            table.insert(lines, '')\
  583.                            return ''\
  584.                    end)\
  585.            else\
  586.                    lines[#lines] = lines[#lines] .. word .. space\
  587.            end\
  588.    end\
  589.    if #lines[1] == 0 then\
  590.        table.remove(lines,1)\
  591.    end\
  592.     return lines\
  593. end\
  594. \
  595. TidyPath = function(path)\
  596.     path = '/'..path\
  597.     if fs.exists(path) and fs.isDir(path) then\
  598.         path = path .. '/'\
  599.     end\
  600. \
  601.     path, n = path:gsub(\"//\", \"/\")\
  602.     while n > 0 do\
  603.         path, n = path:gsub(\"//\", \"/\")\
  604.     end\
  605.     return path\
  606. end\
  607. \
  608. Capitalise = function(str)\
  609.     return str:sub(1, 1):upper() .. str:sub(2, -1)\
  610. end\
  611. \
  612. Round = function(num, idp)\
  613.     local mult = 10^(idp or 0)\
  614.     return math.floor(num * mult + 0.5) / mult\
  615. end\
  616. ]],\
  617. [\"Object\"] = [[\
  618. X = 1\
  619. Y = 1\
  620. Width = 1\
  621. Height = 1\
  622. Parent = nil\
  623. OnClick = nil\
  624. Visible = true\
  625. IgnoreClick = false\
  626. Name = nil \
  627. ClipDrawing = true\
  628. UpdateDrawBlacklist = {}\
  629. Fixed = false\
  630. Ready = false\
  631. \
  632. DrawCache = {}\
  633. \
  634. NeedsDraw = function(self)\
  635.     if not self.Visible then\
  636.         return false\
  637.     end\
  638.     \
  639.     if not self.DrawCache.Buffer or self.DrawCache.AlwaysDraw or self.DrawCache.NeedsDraw then\
  640.         return true\
  641.     end\
  642. \
  643.     if self.OnNeedsUpdate then\
  644.         if self.OnNeedsUpdate() then\
  645.             return true\
  646.         end\
  647.     end\
  648. \
  649.     if self.Children then\
  650.         for i, v in ipairs(self.Children) do\
  651.             if v:NeedsDraw() then\
  652.                 return true\
  653.             end\
  654.         end\
  655.     end\
  656. end\
  657. \
  658. GetPosition = function(self)\
  659.     return self.Bedrock:GetAbsolutePosition(self)\
  660. end\
  661. \
  662. GetOffsetPosition = function(self)\
  663.     if not self.Parent then\
  664.         return {X = 1, Y = 1}\
  665.     end\
  666. \
  667.     local offset = {X = 0, Y = 0}\
  668.     if not self.Fixed and self.Parent.ChildOffset then\
  669.         offset = self.Parent.ChildOffset\
  670.     end\
  671. \
  672.     return {X = self.X + offset.X, Y = self.Y + offset.Y}\
  673. end\
  674. \
  675. Draw = function(self)\
  676.     if not self.Visible then\
  677.         return\
  678.     end\
  679. \
  680.     self.DrawCache.NeedsDraw = false\
  681.     local pos = self:GetPosition()\
  682.     Drawing.StartCopyBuffer()\
  683. \
  684.     if self.ClipDrawing then\
  685.         Drawing.AddConstraint(pos.X, pos.Y, self.Width, self.Height)\
  686.     end\
  687. \
  688.     if self.OnDraw then\
  689.         self:OnDraw(pos.X, pos.Y)\
  690.     end\
  691. \
  692.     self.DrawCache.Buffer = Drawing.EndCopyBuffer()\
  693.     \
  694.     if self.Children then\
  695.         for i, child in ipairs(self.Children) do\
  696.             local pos = child:GetOffsetPosition()\
  697.             if pos.Y + self.Height > 1 and pos.Y <= self.Height and pos.X + self.Width > 1 and pos.X <= self.Width then\
  698.                 child:Draw()\
  699.             end\
  700.         end\
  701.     end\
  702. \
  703. \
  704.     if self.OnPostChildrenDraw then\
  705.         self:OnPostChildrenDraw(pos.X, pos.Y)\
  706.     end\
  707. \
  708.     if self.ClipDrawing then\
  709.         Drawing.RemoveConstraint()\
  710.     end \
  711. end\
  712. \
  713. ForceDraw = function(self, ignoreChildren, ignoreParent, ignoreBedrock)\
  714.     if not ignoreBedrock and self.Bedrock then\
  715.         self.Bedrock:ForceDraw()\
  716.     end\
  717.     self.DrawCache.NeedsDraw = true\
  718.     if not ignoreParent and self.Parent then\
  719.         self.Parent:ForceDraw(true, nil, true)\
  720.     end\
  721.     if not ignoreChildren and self.Children then\
  722.         for i, child in ipairs(self.Children) do\
  723.             child:ForceDraw(nil, true, true)\
  724.         end\
  725.     end\
  726. end\
  727. \
  728. OnRemove = function(self)\
  729.     if self == self.Bedrock:GetActiveObject() then\
  730.         self.Bedrock:SetActiveObject()\
  731.     end\
  732. end\
  733. \
  734. local function ParseColour(value)\
  735.     if type(value) == 'string' then\
  736.         if colours[value] and type(colours[value]) == 'number' then\
  737.             return colours[value]\
  738.         elseif colors[value] and type(colors[value]) == 'number' then\
  739.             return colors[value]\
  740.         end\
  741.     elseif type(value) == 'number' and (value == colours.transparent or (value >= colours.white and value <= colours.black)) then\
  742.         return value\
  743.     end\
  744.     error('Invalid colour: \"'..tostring(value)..'\"')\
  745. end\
  746. \
  747. Initialise = function(self, values)\
  748.     local _new = values    -- the new instance\
  749.     _new.DrawCache = {\
  750.         NeedsDraw = true,\
  751.         AlwaysDraw = false,\
  752.         Buffer = nil\
  753.     }\
  754.     setmetatable(_new, {__index = self} )\
  755. \
  756.     local new = {} -- the proxy\
  757.     setmetatable(new, {\
  758.         __index = function(t, k)\
  759.             if k:find('Color') then\
  760.                 k = k:gsub('Color', 'Colour')\
  761.             end\
  762. \
  763.             if k:find('Colour') and type(_new[k]) ~= 'table' and type(_new[k]) ~= 'function' then\
  764.                 if _new[k] then\
  765.                     return ParseColour(_new[k])\
  766.                 end\
  767.             elseif _new[k] ~= nil then\
  768.                 return _new[k]\
  769.             end\
  770.         end,\
  771. \
  772.         __newindex = function (t,k,v)\
  773.             if k:find('Color') then\
  774.                 k = k:gsub('Color', 'Colour')\
  775.             end\
  776. \
  777.             if k == 'Width' or k == 'X' or k == 'Height' or k == 'Y' then\
  778.                 v = new.Bedrock:ParseStringSize(new.Parent, k, v)\
  779.             end\
  780. \
  781.             if v ~= _new[k] then\
  782.                 _new[k] = v\
  783.                 if t.OnUpdate then\
  784.                     t:OnUpdate(k)\
  785.                 end\
  786. \
  787.                 if t.UpdateDrawBlacklist[k] == nil then\
  788.                     t:ForceDraw()\
  789.                 end\
  790.             end\
  791.         end\
  792.     })\
  793.     if new.OnInitialise then\
  794.         new:OnInitialise()\
  795.     end\
  796. \
  797.     return new\
  798. end\
  799. \
  800. AnimateValue = function(self, valueName, from, to, duration, done, tbl)\
  801.     tbl = tbl or self\
  802.     if type(tbl[valueName]) ~= 'number' then\
  803.         error('Animated value ('..valueName..') must be number.')\
  804.     elseif not self.Bedrock.AnimationEnabled then\
  805.         tbl[valueName] = to\
  806.         if done then\
  807.             done()\
  808.         end\
  809.         return\
  810.     end\
  811.     from = from or tbl[valueName]\
  812.     duration = duration or 0.2\
  813.     local delta = to - from\
  814. \
  815.     local startTime = os.clock()\
  816.     local previousFrame = startTime\
  817.     local frame\
  818.     frame = function()\
  819.         local time = os.clock()\
  820.         local totalTime = time - startTime\
  821.         local isLast = totalTime >= duration\
  822. \
  823.         if isLast then\
  824.             tbl[valueName] = to\
  825.             self:ForceDraw()\
  826.             if done then\
  827.                 done()\
  828.             end\
  829.         else\
  830.             tbl[valueName] = self.Bedrock.Helpers.Round(from + delta * (totalTime / duration))\
  831.             self:ForceDraw()\
  832.             self.Bedrock:StartTimer(function()\
  833.                 frame()\
  834.             end, 0.05)\
  835.         end\
  836.     end\
  837.     frame()\
  838. end\
  839. \
  840. Click = function(self, event, side, x, y)\
  841.     if self.Visible and not self.IgnoreClick then\
  842.         if event == 'mouse_click' and self.OnClick and self:OnClick(event, side, x, y) ~= false then\
  843.             return true\
  844.         elseif event == 'mouse_drag' and self.OnDrag and self:OnDrag(event, side, x, y) ~= false then\
  845.             return true\
  846.         elseif event == 'mouse_scroll' and self.OnScroll and self:OnScroll(event, side, x, y) ~= false then\
  847.             return true\
  848.         else\
  849.             return false\
  850.         end\
  851.     else\
  852.         return false\
  853.     end\
  854. \
  855. end\
  856. \
  857. ToggleMenu = function(self, name, x, y)\
  858.     return self.Bedrock:ToggleMenu(name, self, x, y)\
  859. end\
  860. \
  861. function OnUpdate(self, value)\
  862.     if value == 'Z' then\
  863.         self.Bedrock:ReorderObjects()\
  864.     end\
  865. end\
  866. ]],\
  867. }\
  868. local objects = {\
  869. [\"Button\"] = [[\
  870. BackgroundColour = colours.lightGrey\
  871. ActiveBackgroundColour = colours.blue\
  872. ActiveTextColour = colours.white\
  873. TextColour = colours.black\
  874. DisabledTextColour = colours.lightGrey\
  875. Text = \"\"\
  876. Toggle = nil\
  877. Momentary = true\
  878. AutoWidth = true\
  879. Align = 'Center'\
  880. Enabled = true\
  881. \
  882. OnUpdate = function(self, value)\
  883.     if value == 'Text' and self.AutoWidth then\
  884.         self.Width = #self.Text + 2\
  885.     end\
  886. end\
  887. \
  888. OnDraw = function(self, x, y)\
  889.     local bg = self.BackgroundColour\
  890. \
  891.     if self.Toggle then\
  892.         bg = self.ActiveBackgroundColour\
  893.     end\
  894. \
  895.     local txt = self.TextColour\
  896.     if self.Toggle then\
  897.         txt = self.ActiveTextColour\
  898.     end\
  899.     if not self.Enabled then\
  900.         txt = self.DisabledTextColour\
  901.     end\
  902.     Drawing.DrawBlankArea(x, y, self.Width, self.Height, bg)\
  903. \
  904.     local _x = 1\
  905.    if self.Align == 'Right' then\
  906.        _x = self.Width - #self.Text - 1\
  907.    elseif self.Align == 'Center' then\
  908.        _x = math.floor((self.Width - #self.Text) / 2)\
  909.    end\
  910. \
  911.     Drawing.DrawCharacters(x + _x, y-1+math.ceil(self.Height/2), self.Text, txt, bg)\
  912. end\
  913. \
  914. OnLoad = function(self)\
  915.     if self.Toggle ~= nil then\
  916.         self.Momentary = false\
  917.     end\
  918. end\
  919. \
  920. Click = function(self, event, side, x, y)\
  921.     if self.Visible and not self.IgnoreClick and self.Enabled and event ~= 'mouse_scroll' then\
  922.         if self.OnClick then\
  923.             if self.Momentary then\
  924.                 self.Toggle = true\
  925.                 self.Bedrock:StartTimer(function()self.Toggle = false end,0.25)\
  926.             elseif self.Toggle ~= nil then\
  927.                 self.Toggle = not self.Toggle\
  928.             end\
  929. \
  930.             self:OnClick(event, side, x, y, self.Toggle)\
  931.         else\
  932.             self.Toggle = not self.Toggle\
  933.         end\
  934.         return true\
  935.     else\
  936.         return false\
  937.     end\
  938. end\
  939. ]],\
  940. [\"CollectionView\"] = [[\
  941. Inherit = 'ScrollView'\
  942. UpdateDrawBlacklist = {['NeedsItemUpdate']=true}\
  943. \
  944. TextColour = colours.black\
  945. BackgroundColour = colours.white\
  946. Items = false\
  947. NeedsItemUpdate = false\
  948. SpacingX = 2\
  949. SpacingY = 1\
  950. \
  951. OnDraw = function(self, x, y)\
  952.     if self.NeedsItemUpdate then\
  953.         self:UpdateItems()\
  954.         self.NeedsItemUpdate = false\
  955.     end\
  956.     Drawing.DrawBlankArea(x, y, self.Width, self.Height, self.BackgroundColour)\
  957. end\
  958. \
  959. local function MaxIcons(self, obj)\
  960.     local x, y = 2, 1\
  961.     if not obj.Height or not obj.Width then\
  962.         error('You must provide each object\\'s height when adding to a CollectionView.')\
  963.     end\
  964.     local slotHeight = obj.Height + self.SpacingY\
  965.     local slotWidth = obj.Width + self.SpacingX\
  966.     local maxX = math.floor((self.Width - 2) / slotWidth)\
  967.     return x, y, maxX, slotWidth, slotHeight\
  968. end\
  969. \
  970. local function IconLocation(self, obj, i)\
  971.     local x, y, maxX, slotWidth, slotHeight = MaxIcons(self, obj)\
  972.     local rowPos = ((i - 1) % maxX)\
  973.     local colPos = math.ceil(i / maxX) - 1\
  974.     x = x + (slotWidth * rowPos)\
  975.     y = y + colPos * slotHeight\
  976.     return x, y\
  977. end\
  978. \
  979. local function AddItem(self, v, i)\
  980.     local toggle = false\
  981.     if not self.CanSelect then\
  982.         toggle = nil\
  983.     end\
  984.     local x, y = IconLocation(self, v, i)\
  985.     local item = {\
  986.         [\"X\"]=x,\
  987.         [\"Y\"]=y,\
  988.         [\"Name\"]=\"CollectionViewItem\",\
  989.         [\"Type\"]=\"View\",\
  990.         [\"TextColour\"]=self.TextColour,\
  991.         [\"BackgroundColour\"]=0,\
  992.         OnClick = function(itm)\
  993.             if self.CanSelect then\
  994.                 for i2, _v in ipairs(self.Children) do\
  995.                     _v.Toggle = false\
  996.                 end\
  997.                 self.Selected = itm\
  998.             end\
  999.         end\
  1000.    }\
  1001.     for k, _v in pairs(v) do\
  1002.         item[k] = _v\
  1003.     end\
  1004.     self:AddObject(item)\
  1005. end\
  1006. \
  1007. \
  1008. UpdateItems = function(self)\
  1009.     self:RemoveAllObjects()\
  1010.     local groupMode = false\
  1011.     for k, v in pairs(self.Items) do\
  1012.         if type(k) == 'string' then\
  1013.             groupMode = true\
  1014.             break\
  1015.         end\
  1016.     end\
  1017. \
  1018.     for i, v in ipairs(self.Items) do\
  1019.         AddItem(self, v, i)\
  1020.     end\
  1021.     self:UpdateScroll()\
  1022. end\
  1023. \
  1024. OnUpdate = function(self, value)\
  1025.     if value == 'Items' then\
  1026.         self.NeedsItemUpdate = true\
  1027.     end\
  1028. end\
  1029. ]],\
  1030. [\"ImageView\"] = [[\
  1031. Image = false\
  1032. \
  1033. OnDraw = function(self, x, y)\
  1034.     Drawing.DrawImage(x, y, self.Image, self.Width, self.Height)\
  1035. end\
  1036. \
  1037. OnLoad = function(self)\
  1038.     if self.Path and fs.exists(self.Path) then\
  1039.         self.Image = Drawing.LoadImage(self.Path)\
  1040.     end\
  1041. end\
  1042. \
  1043. OnUpdate = function(self, value)\
  1044.     if value == 'Path' then\
  1045.         if self.Path and fs.exists(self.Path) then\
  1046.             self.Image = Drawing.LoadImage(self.Path)\
  1047.         end\
  1048.     end\
  1049. end\
  1050. ]],\
  1051. [\"Label\"] = [[\
  1052. TextColour = colours.black\
  1053. BackgroundColour = colours.transparent\
  1054. Text = \"\"\
  1055. AutoWidth = false\
  1056. Align = 'Left'\
  1057. \
  1058. local wrapText = function(text, maxWidth)\
  1059.    local lines = {''}\
  1060.    for word, space in text:gmatch('(%S+)(%s*)') do\
  1061.        local temp = lines[#lines] .. word .. space:gsub('\\n','')\
  1062.        if #temp > maxWidth then\
  1063.            table.insert(lines, '')\
  1064.        end\
  1065.        if space:find('\\n') then\
  1066.            lines[#lines] = lines[#lines] .. word\
  1067.            \
  1068.            space = space:gsub('\\n', function()\
  1069.                    table.insert(lines, '')\
  1070.                    return ''\
  1071.            end)\
  1072.        else\
  1073.            lines[#lines] = lines[#lines] .. word .. space\
  1074.        end\
  1075.    end\
  1076.    if #lines[1] == 0 then\
  1077.        table.remove(lines,1)\
  1078.    end\
  1079.    return lines\
  1080. end\
  1081. \
  1082. OnUpdate = function(self, value)\
  1083.    if value == 'Text' then\
  1084.        if self.AutoWidth then\
  1085.            self.Width = #self.Text\
  1086.        else\
  1087.            self.Height = #wrapText(self.Text, self.Width)\
  1088.        end\
  1089.    end\
  1090. end\
  1091. \
  1092. OnDraw = function(self, x, y)\
  1093.     for i, v in ipairs(wrapText(self.Text, self.Width)) do\
  1094.        local _x = 0\
  1095.        if self.Align == 'Right' then\
  1096.            _x = self.Width - #v\
  1097.        elseif self.Align == 'Center' then\
  1098.            _x = math.floor((self.Width - #v) / 2)\
  1099.        end\
  1100.         Drawing.DrawCharacters(x + _x, y + i - 1, v, self.TextColour, self.BackgroundColour)\
  1101.     end\
  1102. end\
  1103. ]],\
  1104. [\"ListView\"] = [[\
  1105. Inherit = 'ScrollView'\
  1106. UpdateDrawBlacklist = {['NeedsItemUpdate']=true}\
  1107. \
  1108. TextColour = colours.black\
  1109. BackgroundColour = colours.white\
  1110. HeadingColour = colours.lightGrey\
  1111. SelectionBackgroundColour = colours.blue\
  1112. SelectionTextColour = colours.white\
  1113. Items = false\
  1114. CanSelect = false\
  1115. Selected = nil\
  1116. NeedsItemUpdate = false\
  1117. ItemMargin = 1\
  1118. HeadingMargin = 0\
  1119. TopMargin = 0\
  1120. \
  1121. OnDraw = function(self, x, y)\
  1122.     if self.NeedsItemUpdate then\
  1123.         self:UpdateItems()\
  1124.     end\
  1125.     Drawing.DrawBlankArea(x, y, self.Width, self.Height, self.BackgroundColour)\
  1126. end\
  1127. \
  1128. local function AddItem(self, v, x, y, group)\
  1129.     local toggle = false\
  1130.     if not self.CanSelect then\
  1131.         toggle = nil\
  1132.     elseif v.Selected then\
  1133.         toggle = true\
  1134.     end\
  1135.     local item = {\
  1136.         [\"Width\"]=self.Width,\
  1137.         [\"X\"]=x,\
  1138.         [\"Y\"]=y,\
  1139.         [\"Name\"]=\"ListViewItem\",\
  1140.         [\"Type\"]=\"Button\",\
  1141.         [\"TextColour\"]=self.TextColour,\
  1142.         [\"BackgroundColour\"]=0,\
  1143.         [\"ActiveTextColour\"]=self.SelectionTextColour,\
  1144.         [\"ActiveBackgroundColour\"]=self.SelectionBackgroundColour,\
  1145.         [\"Align\"]='Left',\
  1146.         [\"Toggle\"]=toggle,\
  1147.         [\"Group\"]=group,\
  1148.         OnClick = function(itm)\
  1149.             if self.CanSelect then\
  1150.                 self:SelectItem(itm)\
  1151.             elseif self.OnSelect then\
  1152.                 self:OnSelect(itm.Text)\
  1153.             end\
  1154.         end\
  1155.    }\
  1156.    if type(v) == 'table' then\
  1157.         for k, _v in pairs(v) do\
  1158.             item[k] = _v\
  1159.         end\
  1160.    else\
  1161.         item.Text = v\
  1162.    end\
  1163.     \
  1164.     local itm = self:AddObject(item)\
  1165.     if v.Selected then\
  1166.         self:SelectItem(itm)\
  1167.     end\
  1168. end\
  1169. \
  1170. UpdateItems = function(self)\
  1171.     if not self.Items or type(self.Items) ~= 'table' then\
  1172.         self.Items = {}\
  1173.     end\
  1174.     self.Selected = nil\
  1175.     self:RemoveAllObjects()\
  1176.     local groupMode = false\
  1177.     for k, v in pairs(self.Items) do\
  1178.         if type(k) == 'string' then\
  1179.             groupMode = true\
  1180.             break\
  1181.         end\
  1182.     end\
  1183. \
  1184.     if not groupMode then\
  1185.         for i, v in ipairs(self.Items) do\
  1186.             AddItem(self, v, self.ItemMargin, i)\
  1187.         end\
  1188.     else\
  1189.         local y = self.TopMargin\
  1190.         for k, v in pairs(self.Items) do\
  1191.             y = y + 1\
  1192.             AddItem(self, {Text = k, TextColour = self.HeadingColour, IgnoreClick = true}, self.HeadingMargin, y)\
  1193.             for i, _v in ipairs(v) do\
  1194.                 y = y + 1\
  1195.                 AddItem(self, _v, 1, y, k)\
  1196.             end\
  1197.             y = y + 1\
  1198.         end\
  1199.     end\
  1200.     self:UpdateScroll()\
  1201.     self.NeedsItemUpdate = false\
  1202. end\
  1203. \
  1204. OnKeyChar = function(self, event, keychar)\
  1205.     if keychar == keys.up or keychar == keys.down then\
  1206.         local n = self:GetIndex(self.Selected)\
  1207.         if keychar == keys.up then\
  1208.             n = n - 1\
  1209.         else\
  1210.             n = n + 1\
  1211.         end\
  1212.         local new = self:GetNth(n)\
  1213.         if new then\
  1214.             self:SelectItem(new)\
  1215.         end\
  1216.     elseif keychar == keys.enter and self.Selected then\
  1217.         self.Selected:Click('mouse_click', 1, 1, 1)\
  1218.     end\
  1219. end\
  1220. \
  1221. --returns the index/'n' of the given item\
  1222. GetIndex = function(self, obj)\
  1223.     local n = 1\
  1224.     for i, v in ipairs(self.Children) do\
  1225.         if not v.IgnoreClick then\
  1226.             if obj == v then\
  1227.                 return n\
  1228.             end\
  1229.             n = n + 1\
  1230.         end\
  1231.     end\
  1232. end\
  1233. \
  1234. --gets the 'nth' list item (does not include headings)\
  1235. GetNth = function(self, n)\
  1236.     local _n = 1\
  1237.     for i, v in ipairs(self.Children) do\
  1238.         if not v.IgnoreClick then\
  1239.             if n == _n then\
  1240.                 return v\
  1241.             end\
  1242.             _n = _n + 1\
  1243.         end\
  1244.     end\
  1245. end\
  1246. \
  1247. SelectItem = function(self, item)\
  1248.     for i, v in ipairs(self.Children) do\
  1249.         v.Toggle = false\
  1250.     end\
  1251.     self.Selected = item\
  1252.     item.Toggle = true\
  1253.     if self.OnSelect then\
  1254.         self:OnSelect(item.Text)\
  1255.     end\
  1256. end\
  1257. \
  1258. OnUpdate = function(self, value)\
  1259.     if value == 'Items' then\
  1260.         self.NeedsItemUpdate = true\
  1261.     end\
  1262. end\
  1263. ]],\
  1264. [\"Menu\"] = [[\
  1265. Inherit = 'View'\
  1266. \
  1267. TextColour = colours.black\
  1268. BackgroundColour = colours.white\
  1269. HideTop = false\
  1270. Prepared = false\
  1271. \
  1272. OnDraw = function(self, x, y)\
  1273.     Drawing.IgnoreConstraint = true\
  1274.     Drawing.DrawBlankArea(x + 1, y + (self.HideTop and 0 or 1), self.Width, self.Height + (self.HideTop and 1 or 0), colours.grey)\
  1275.     Drawing.IgnoreConstraint = false\
  1276.     Drawing.DrawBlankArea(x, y, self.Width, self.Height, self.BackgroundColour)\
  1277. end\
  1278. \
  1279. OnLoad = function(self)\
  1280.     local owner = self.Owner\
  1281.     if type(owner) == 'string' then\
  1282.         owner = self.Bedrock:GetObject(self.Owner)\
  1283.     end\
  1284. \
  1285.     if owner then\
  1286.         if self.X == 0 and self.Y == 0 then\
  1287.             local pos = owner:GetPosition()\
  1288.             self.X = pos.X\
  1289.             self.Y = pos.Y + owner.Height\
  1290.         end\
  1291.         self.Owner = owner\
  1292.     else\
  1293.         self.Owner = nil\
  1294.     end\
  1295. end\
  1296. \
  1297. OnUpdate = function(self, value)\
  1298.     if value == 'Children' then\
  1299.         self.Height = #self.Children + 1 + (self.HideTop and 0 or 1)\
  1300.         if not self.BaseY then\
  1301.             self.BaseY = self.Y\
  1302.         end\
  1303.         if #self.Children > 0 and self.Children[1].Type == 'Button' then\
  1304.             self.Width = self.Bedrock.Helpers.LongestString(self.Children, 'Text') + 2\
  1305.             for i, v in ipairs(self.Children) do\
  1306.                 if v.TextColour then\
  1307.                     v.TextColour = self.TextColour\
  1308.                 end\
  1309.                 if v.BackgroundColour then\
  1310.                     v.BackgroundColour = colours.transparent\
  1311.                 end\
  1312.                 if v.Colour then\
  1313.                     v.Colour = colours.lightGrey\
  1314.                 end\
  1315.                 v.Align = 'Left'\
  1316.                 v.X = 1\
  1317.                 v.Y = i + (self.HideTop and 0 or 1)\
  1318.                 v.Width = self.Width\
  1319.                 v.Height = 1\
  1320.             end\
  1321.         elseif #self.Children > 0 and self.Children[1].Type == 'MenuItem' then\
  1322.             local width = 1\
  1323.             for i, v in ipairs(self.Children) do\
  1324.                 if v.Width > width then\
  1325.                     width = v.Width\
  1326.                 end\
  1327.             end\
  1328.             self.Width = width\
  1329.             for i, v in ipairs(self.Children) do\
  1330.                 if v.TextColour then\
  1331.                     v.TextColour = self.TextColour\
  1332.                 end\
  1333.                 if v.Colour then\
  1334.                     v.Colour = colours.lightGrey\
  1335.                 end\
  1336.                 v.X = 1\
  1337.                 v.Y = i + (self.HideTop and 0 or 1)\
  1338.                 v.Width = width\
  1339.                 v.Height = 1\
  1340.             end\
  1341.         end\
  1342. \
  1343.         self.Y = self.BaseY\
  1344.         local pos = self:GetPosition()\
  1345.         if pos.Y + self.Height + 1 > Drawing.Screen.Height then\
  1346.             self.Y = self.BaseY - ((self.Height +  pos.Y) - Drawing.Screen.Height)\
  1347.         end\
  1348.         \
  1349.         if pos.X + self.Width > Drawing.Screen.Width then\
  1350.             self.X = Drawing.Screen.Width - self.Width\
  1351.         end\
  1352.     end\
  1353. end\
  1354. \
  1355. Close = function(self, isBedrockCall)\
  1356.     self.Bedrock.Menu = nil\
  1357.     if not self.Prepared then\
  1358.         self.Parent:RemoveObject(self)\
  1359.     else\
  1360.         self.Visible = false\
  1361.     end\
  1362. \
  1363.     if self.Owner and self.Owner.Toggle then\
  1364.         self.Owner.Toggle = false\
  1365.     end\
  1366.     self.Parent:ForceDraw()\
  1367.     self = nil\
  1368. end\
  1369. \
  1370. OnChildClick = function(self, child, event, side, x, y)\
  1371.     self:Close()\
  1372. end\
  1373. ]],\
  1374. [\"MenuItem\"] = [[\
  1375. BackgroundColour = colours.white\
  1376. TextColour = colours.black\
  1377. DisabledTextColour = colours.lightGrey\
  1378. ShortcutTextColour = colours.grey\
  1379. Text = \"\"\
  1380. Enabled = true\
  1381. Shortcut = nil\
  1382. ShortcutPadding = 2\
  1383. ShortcutName = nil\
  1384. \
  1385. OnUpdate = function(self, value)\
  1386.     if value == 'Text' then\
  1387.         if self.Shortcut then\
  1388.             self.Width = #self.Text + 2 + self.ShortcutPadding + #self.Shortcut\
  1389.         else\
  1390.             self.Width = #self.Text + 2\
  1391.         end\
  1392.     elseif value == 'OnClick' then\
  1393.         self:RegisterShortcut()\
  1394.     end\
  1395. end\
  1396. \
  1397. OnDraw = function(self, x, y)\
  1398.     Drawing.DrawBlankArea(x, y, self.Width, self.Height, self.BackgroundColour)\
  1399. \
  1400.     local txt = self.TextColour\
  1401.     if not self.Enabled then\
  1402.         txt = self.DisabledTextColour\
  1403.     end\
  1404.     Drawing.DrawCharacters(x + 1, y, self.Text, txt, colours.transparent)\
  1405. \
  1406.     if self.Shortcut then\
  1407.         local shrt = self.ShortcutTextColour\
  1408.         if not self.Enabled then\
  1409.             shrt = self.DisabledTextColour\
  1410.         end\
  1411.         Drawing.DrawCharacters(x + self.Width - #self.Shortcut - 1, y, self.Shortcut, shrt, colours.transparent)\
  1412.     end\
  1413. end\
  1414. \
  1415. ParseShortcut = function(self)\
  1416.     local special = {\
  1417.         ['^'] = keys.leftShift,\
  1418.         ['<'] = keys.delete,\
  1419.         ['>'] = keys.delete,\
  1420.         ['#'] = keys.leftCtrl,\
  1421.         ['~'] = keys.leftAlt,\
  1422.     }\
  1423. \
  1424.     local keys = {}\
  1425.     for i = 1, #self.Shortcut do\
  1426.         local c = self.Shortcut:sub(i,i)\
  1427.         table.insert(keys, special[c] or c:lower())\
  1428.     end\
  1429.     return keys\
  1430. end\
  1431. \
  1432. RegisterShortcut = function(self)\
  1433.     if self.Shortcut then\
  1434.         self.Shortcut = self.Shortcut:upper()\
  1435.         self.ShortcutName = self.Bedrock:RegisterKeyboardShortcut(self:ParseShortcut(), function()\
  1436.             if self.OnClick and self.Enabled then\
  1437.                 if self.Parent.Owner then\
  1438.                     self.Parent:Close()\
  1439.                     self.Parent.Owner.Toggle = true\
  1440.                     self.Bedrock:StartTimer(function()\
  1441.                         self.Parent.Owner.Toggle = false\
  1442.                     end, 0.3)\
  1443.                 end\
  1444.                 return self:OnClick('keyboard_shortcut', 1, 1, 1)\
  1445.             else\
  1446.                 return false\
  1447.             end\
  1448.         end)\
  1449.     end\
  1450. end\
  1451. \
  1452. OnRemove = function(self)\
  1453.     if self.ShortcutName then\
  1454.         self.Bedrock:UnregisterKeyboardShortcut(self.ShortcutName)\
  1455.     end\
  1456. end\
  1457. \
  1458. OnLoad = function(self)\
  1459.     if self.OnClick ~= nil then\
  1460.         self:RegisterShortcut()\
  1461.     end\
  1462.     -- self:OnUpdate('Text')\
  1463. end\
  1464. ]],\
  1465. [\"NumberBox\"] = [[\
  1466. Inherit = 'View'\
  1467. \
  1468. Value = 1\
  1469. Minimum = 1\
  1470. Maximum = 99\
  1471. BackgroundColour = colours.lightGrey\
  1472. TextBoxTimer = nil\
  1473. Width = 7\
  1474. \
  1475. OnLoad = function(self)\
  1476.     self:AddObject({\
  1477.         X = self.Width - 1,\
  1478.         Y = 1,\
  1479.         Width = 1,\
  1480.         AutoWidth = false,\
  1481.         Text = '-',\
  1482.         Type = 'Button',\
  1483.         Name = 'AddButton',\
  1484.         BackgroundColour = colours.transparent,\
  1485.         OnClick = function()\
  1486.             self:ShiftValue(-1)\
  1487.         end\
  1488.     })\
  1489. \
  1490.     self:AddObject({\
  1491.         X = self.Width,\
  1492.         Y = 1,\
  1493.         Width = 1,\
  1494.         AutoWidth = false,\
  1495.         Text = '+',\
  1496.         Type = 'Button',\
  1497.         Name = 'SubButton',\
  1498.         BackgroundColour = colours.transparent,\
  1499.         OnClick = function()\
  1500.             self:ShiftValue(1)\
  1501.         end\
  1502.     })\
  1503. \
  1504.     self:AddObject({\
  1505.         X = 1,\
  1506.         Y = 1,\
  1507.         Width = self.Width - 2,\
  1508.         Text = tostring(self.Value),\
  1509.         Align = 'Center',\
  1510.         Type = 'TextBox',\
  1511.         BackgroundColour = colours.transparent,\
  1512.         OnChange = function(_self, event, keychar)\
  1513.             if keychar == keys.enter then\
  1514.                 self:SetValue(tonumber(_self.Text))\
  1515.                 self.TextBoxTimer = nil\
  1516.             end\
  1517.             if self.TextBoxTimer then\
  1518.                 self.Bedrock:StopTimer(self.TextBoxTimer)\
  1519.             end\
  1520. \
  1521.             self.TextBoxTimer = self.Bedrock:StartTimer(function(_, timer)\
  1522.                 if timer and timer == self.TextBoxTimer then\
  1523.                     self:SetValue(tonumber(_self.Text))\
  1524.                     self.TextBoxTimer = nil\
  1525.                 end\
  1526.             end, 2)\
  1527.         end\
  1528.     })\
  1529. end\
  1530. \
  1531. OnScroll = function(self, event, dir, x, y)\
  1532.     self:ShiftValue(-dir)\
  1533. end\
  1534. \
  1535. ShiftValue = function(self, delta)\
  1536.     local val = tonumber(self:GetObject('TextBox').Text) or self.Minimum\
  1537.     self:SetValue(val + delta)\
  1538. end\
  1539. \
  1540. SetValue = function(self, newValue)\
  1541.     newValue = newValue or 0\
  1542.     if self.Maximum and newValue > self.Maximum then\
  1543.         newValue = self.Maximum\
  1544.     elseif self.Minimum and newValue < self.Minimum then\
  1545.         newValue = self.Minimum\
  1546.     end\
  1547.     self.Value = newValue\
  1548.     if self.OnChange then\
  1549.         self:OnChange()\
  1550.     end\
  1551. end\
  1552. \
  1553. OnUpdate = function(self, value)\
  1554.     if value == 'Value' then\
  1555.         local textbox = self:GetObject('TextBox')\
  1556.         if textbox then\
  1557.             textbox.Text = tostring(self.Value)\
  1558.         end\
  1559.     end\
  1560. end\
  1561. ]],\
  1562. [\"ProgressBar\"] = [[\
  1563. BackgroundColour = colours.lightGrey\
  1564. BarColour = colours.blue\
  1565. TextColour = colours.white\
  1566. ShowText = false\
  1567. Value = 0\
  1568. Maximum = 1\
  1569. Indeterminate = false\
  1570. AnimationStep = 0\
  1571. \
  1572. OnDraw = function(self, x, y)\
  1573.     Drawing.DrawBlankArea(x, y, self.Width, self.Height, self.BackgroundColour)\
  1574. \
  1575.     -- if self.Indeterminate then\
  1576.     --  for i = 1, self.Width do\
  1577.     --      local s = x + i - 1 + self.AnimationStep\
  1578.     --      if s % 4 == 1 or s % 4 == 2 then\
  1579.     --          Drawing.DrawBlankArea(s, y, 1, self.Height, self.BarColour)\
  1580.     --      end\
  1581.     --  end\
  1582.     --  self.AnimationStep = self.AnimationStep + 1\
  1583.     --  if self.AnimationStep >= 4 then\
  1584.     --      self.AnimationStep = 0\
  1585.     --  end\
  1586.     --  self.Bedrock:StartTimer(function()\
  1587.     --      self:Draw()\
  1588.     --  end, 0.25)\
  1589.     -- else\
  1590.         local values = self.Value\
  1591.         local barColours = self.BarColour\
  1592.         if type(values) == 'number' then\
  1593.             values = {values}\
  1594.         end\
  1595.         if type(barColours) == 'number' then\
  1596.             barColours = {barColours}\
  1597.         end\
  1598.         local total = 0\
  1599.         local _x = x\
  1600.         for i, v in ipairs(values) do\
  1601.             local width = self.Bedrock.Helpers.Round((v / self.Maximum) * self.Width)\
  1602.             total = total + v\
  1603.             Drawing.DrawBlankArea(_x, y, width, self.Height, barColours[((i-1)%#barColours)+1])\
  1604.             _x = _x + width\
  1605.         end\
  1606. \
  1607.         if self.ShowText then\
  1608.             local text = self.Bedrock.Helpers.Round((total / self.Maximum) * 100) .. '%'\
  1609.             Drawing.DrawCharactersCenter(x, y, self.Width, self.Height, text, self.TextColour, colours.transparent)\
  1610.         end\
  1611.     -- end\
  1612. end\
  1613. ]],\
  1614. [\"ScrollBar\"] = [[\
  1615. BackgroundColour = colours.lightGrey\
  1616. BarColour = colours.lightBlue\
  1617. Scroll = 0\
  1618. MaxScroll = 0\
  1619. ClickPoint = nil\
  1620. Fixed = true\
  1621. \
  1622. OnUpdate = function(self, value)\
  1623.     if value == 'Text' and self.AutoWidth then\
  1624.         self.Width = #self.Text + 2\
  1625.     end\
  1626. end\
  1627. \
  1628. OnDraw = function(self, x, y)\
  1629.     local barHeight = self.Height * (self.Height / (self.Height + self.MaxScroll))\
  1630.    if barHeight < 3 then\
  1631.      barHeight = 3\
  1632.    end\
  1633.    local percentage = (self.Scroll/self.MaxScroll)\
  1634. \
  1635.    Drawing.DrawBlankArea(x, y, self.Width, self.Height, self.BackgroundColour)\
  1636.    Drawing.DrawBlankArea(x, y + math.ceil(self.Height*percentage - barHeight*percentage), self.Width, barHeight, self.BarColour)\
  1637. end\
  1638. \
  1639. OnScroll = function(self, event, direction, x, y)\
  1640.     if event == 'mouse_scroll' then\
  1641.         direction = self.Bedrock.Helpers.Round(direction * 3)\
  1642.     end\
  1643.     if self.Scroll < 0 or self.Scroll > self.MaxScroll then\
  1644.         return false\
  1645.     end\
  1646.     local old = self.Scroll\
  1647.     self.Scroll = self.Bedrock.Helpers.Round(self.Scroll + direction)\
  1648.     if self.Scroll < 0 then\
  1649.         self.Scroll = 0\
  1650.     elseif self.Scroll > self.MaxScroll then\
  1651.         self.Scroll = self.MaxScroll\
  1652.     end\
  1653. \
  1654.     if self.Scroll ~= old and self.OnChange then\
  1655.         self:OnChange()\
  1656.     end\
  1657. end\
  1658. \
  1659. OnClick = function(self, event, side, x, y)\
  1660.     if event == 'mouse_click' then\
  1661.         self.ClickPoint = y\
  1662.     else\
  1663.         if self.ClickPoint then\
  1664.             local gapHeight = self.Height - (self.Height * (self.Height / (self.Height + self.MaxScroll)))\
  1665.             local barHeight = self.Height * (self.Height / (self.Height + self.MaxScroll))\
  1666.             --local delta = (self.Height + self.MaxScroll) * ((y - self.ClickPoint) / barHeight)\
  1667.             local delta = ((y - self.ClickPoint)/gapHeight)*self.MaxScroll\
  1668.             --l(((y - self.ClickPoint)/gapHeight))\
  1669.             --l(delta)\
  1670.             self.Scroll = self.Bedrock.Helpers.Round(delta)\
  1671.             --l(self.Scroll)\
  1672.             --l('----')\
  1673.             if self.Scroll < 0 then\
  1674.                 self.Scroll = 0\
  1675.             elseif self.Scroll > self.MaxScroll then\
  1676.                 self.Scroll = self.MaxScroll\
  1677.             end\
  1678.             if self.OnChange then\
  1679.                 self:OnChange()\
  1680.             end\
  1681.         end\
  1682.     end\
  1683. \
  1684.     local relScroll = self.MaxScroll * ((y-1)/self.Height)\
  1685.     if y == self.Height then\
  1686.         relScroll = self.MaxScroll\
  1687.     end\
  1688.     self.Scroll = self.Bedrock.Helpers.Round(relScroll)\
  1689. \
  1690. \
  1691. end\
  1692. \
  1693. OnDrag = OnClick\
  1694. ]],\
  1695. [\"ScrollView\"] = [[\
  1696. Inherit = 'View'\
  1697. ChildOffset = false\
  1698. ContentWidth = 0\
  1699. ContentHeight = 0\
  1700. ScrollBarBackgroundColour = colours.lightGrey\
  1701. ScrollBarColour = colours.lightBlue\
  1702. \
  1703. CalculateContentSize = function(self)\
  1704.     local function calculateObject(obj)\
  1705.         local pos = obj:GetPosition()\
  1706.         local x2 = pos.X + obj.Width - 1\
  1707.         local y2 = pos.Y + obj.Height - 1\
  1708.         if obj.Children then\
  1709.             for i, child in ipairs(obj.Children) do\
  1710.                 local _x2, _y2 = calculateObject(child)\
  1711.                 if _x2 > x2 then\
  1712.                     x2 = _x2\
  1713.                 end\
  1714.                 if _y2 > y2 then\
  1715.                     y2 = _y2\
  1716.                 end\
  1717.             end\
  1718.         end\
  1719.         return x2, y2\
  1720.     end\
  1721. \
  1722.     local pos = self:GetPosition()\
  1723.     local x2, y2 = calculateObject(self)\
  1724.     self.ContentWidth = x2 - pos.X + 1\
  1725.     self.ContentHeight = y2 - pos.Y + 1\
  1726. end\
  1727. \
  1728. UpdateScroll = function(self)\
  1729.     self.ChildOffset.Y = 0\
  1730.     self:CalculateContentSize()\
  1731.     if self.ContentHeight > self.Height then\
  1732.         if not self:GetObject('ScrollViewScrollBar') then\
  1733.             local _scrollBar = self:AddObject({\
  1734.                 [\"Name\"] = 'ScrollViewScrollBar',\
  1735.                 [\"Type\"] = 'ScrollBar',\
  1736.                 [\"X\"] = self.Width,\
  1737.                 [\"Y\"] = 1,\
  1738.                 [\"Width\"] = 1,\
  1739.                 [\"Height\"] = self.Height,\
  1740.                 [\"BackgroundColour\"] = self.ScrollBarBackgroundColour,\
  1741.                 [\"BarColour\"] = self.ScrollBarColour,\
  1742.                 [\"Z\"]=999\
  1743.             })\
  1744. \
  1745.             _scrollBar.OnChange = function(scrollBar)\
  1746.                 self.ChildOffset.Y = -scrollBar.Scroll\
  1747.                 for i, child in ipairs(self.Children) do\
  1748.                     child:ForceDraw()\
  1749.                 end\
  1750.             end\
  1751.         end\
  1752. \
  1753.         if self:GetObject('ScrollViewScrollBar') then\
  1754.             self:GetObject('ScrollViewScrollBar').MaxScroll = self.ContentHeight - self.Height\
  1755.         end\
  1756.     else\
  1757.         self:RemoveObject('ScrollViewScrollBar')\
  1758.     end\
  1759. end\
  1760. \
  1761. OnScroll = function(self, event, direction, x, y)\
  1762.     if self:GetObject('ScrollViewScrollBar') then\
  1763.         self:GetObject('ScrollViewScrollBar'):OnScroll(event, direction, x, y)\
  1764.     end\
  1765. end\
  1766. \
  1767. OnLoad = function(self)\
  1768.     if not self.ChildOffset or not self.ChildOffset.X or not self.ChildOffset.Y then\
  1769.         self.ChildOffset = {X = 0, Y = 0}\
  1770.     end\
  1771.     self:UpdateScroll()\
  1772. end\
  1773. ]],\
  1774. [\"SecureTextBox\"] = [[\
  1775. Inherit = 'TextBox'\
  1776. MaskCharacter = '*'\
  1777. \
  1778. OnDraw = function(self, x, y)\
  1779.     Drawing.DrawBlankArea(x, y, self.Width, self.Height, self.BackgroundColour)\
  1780.     if self.CursorPos > #self.Text then\
  1781.         self.CursorPos = #self.Text\
  1782.     elseif self.CursorPos < 0 then\
  1783.         self.CursorPos = 0\
  1784.     end\
  1785.     local text = ''\
  1786. \
  1787.     for i = 1, #self.Text do\
  1788.         text = text .. self.MaskCharacter\
  1789.     end\
  1790. \
  1791.     if self.Bedrock:GetActiveObject() == self then\
  1792.         if #text > (self.Width - 2) then\
  1793.             text = text:sub(#text-(self.Width - 3))\
  1794.             self.Bedrock.CursorPos = {x + 1 + self.Width-2, y}\
  1795.         else\
  1796.             self.Bedrock.CursorPos = {x + 1 + self.CursorPos, y}\
  1797.         end\
  1798.         self.Bedrock.CursorColour = self.TextColour\
  1799.     end\
  1800. \
  1801.     if #tostring(text) == 0 then\
  1802.         Drawing.DrawCharacters(x + 1, y, self.Placeholder, self.PlaceholderTextColour, self.BackgroundColour)\
  1803.     else\
  1804.         if not self.Selected then\
  1805.             Drawing.DrawCharacters(x + 1, y, text, self.TextColour, self.BackgroundColour)\
  1806.         else\
  1807.             for i = 1, #text do\
  1808.                 local char = text:sub(i, i)\
  1809.                 local textColour = self.TextColour\
  1810.                 local backgroundColour = self.BackgroundColour\
  1811.                 if i > self.DragStart and i - 1 <= self.CursorPos then\
  1812.                     textColour = self.SelectedTextColour\
  1813.                     backgroundColour = self.SelectedBackgroundColour\
  1814.                 end\
  1815.                 Drawing.DrawCharacters(x + i, y, char, textColour, backgroundColour)\
  1816.             end\
  1817.         end\
  1818.     end\
  1819. end\
  1820. ]],\
  1821. [\"Separator\"] = [[\
  1822. Colour = colours.grey\
  1823. \
  1824. OnDraw = function(self, x, y)\
  1825.     local char = \"|\"\
  1826.     if self.Width > self.Height then\
  1827.         char = '-'\
  1828.     end\
  1829.     Drawing.DrawArea(x, y, self.Width, self.Height, char, self.Colour, colours.transparent)\
  1830. end\
  1831. ]],\
  1832. [\"TextBox\"] = [[\
  1833. BackgroundColour = colours.lightGrey\
  1834. SelectedBackgroundColour = colours.blue\
  1835. SelectedTextColour = colours.white\
  1836. TextColour = colours.black\
  1837. PlaceholderTextColour = colours.grey\
  1838. Placeholder = ''\
  1839. AutoWidth = false\
  1840. Text = \"\"\
  1841. CursorPos = nil\
  1842. Numerical = false\
  1843. DragStart = nil\
  1844. Selected = false\
  1845. SelectOnClick = false\
  1846. ActualDragStart = nil\
  1847. \
  1848. OnDraw = function(self, x, y)\
  1849.     Drawing.DrawBlankArea(x, y, self.Width, self.Height, self.BackgroundColour)\
  1850.     if self.CursorPos > #self.Text then\
  1851.         self.CursorPos = #self.Text\
  1852.     elseif self.CursorPos < 0 then\
  1853.         self.CursorPos = 0\
  1854.     end\
  1855.     local text = self.Text\
  1856.     local offset = self:TextOffset()\
  1857.     if #text > (self.Width - 2) then\
  1858.         text = text:sub(offset+1, offset + self.Width - 2)\
  1859.         -- self.Bedrock.CursorPos = {x + 1 + self.Width-2, y}\
  1860.     -- else\
  1861.     end\
  1862.     if self.Bedrock:GetActiveObject() == self then\
  1863.         self.Bedrock.CursorPos = {x + 1 + self.CursorPos - offset, y}\
  1864.         self.Bedrock.CursorColour = self.TextColour\
  1865.     else\
  1866.         self.Selected = false\
  1867.     end\
  1868. \
  1869.     if #tostring(text) == 0 then\
  1870.         Drawing.DrawCharacters(x + 1, y, self.Placeholder, self.PlaceholderTextColour, self.BackgroundColour)\
  1871.     else\
  1872.         if not self.Selected then\
  1873.             Drawing.DrawCharacters(x + 1, y, text, self.TextColour, self.BackgroundColour)\
  1874.         else\
  1875.             local startPos = self.DragStart - offset\
  1876.             local endPos = self.CursorPos - offset\
  1877.             if startPos > endPos then\
  1878.                 startPos = self.CursorPos - offset\
  1879.                 endPos = self.DragStart - offset\
  1880.             end\
  1881.             for i = 1, #text do\
  1882.                 local char = text:sub(i, i)\
  1883.                 local textColour = self.TextColour\
  1884.                 local backgroundColour = self.BackgroundColour\
  1885. \
  1886.                 if i > startPos and i - 1 <= endPos then\
  1887.                     textColour = self.SelectedTextColour\
  1888.                     backgroundColour = self.SelectedBackgroundColour\
  1889.                 end\
  1890.                 Drawing.DrawCharacters(x + i, y, char, textColour, backgroundColour)\
  1891.             end\
  1892.         end\
  1893.     end\
  1894. end\
  1895. \
  1896. TextOffset = function(self)\
  1897.     if #self.Text < (self.Width - 2) then\
  1898.         return 0\
  1899.     elseif self.Bedrock:GetActiveObject() ~= self then\
  1900.         return 0\
  1901.     else\
  1902.         local textWidth = (self.Width - 2)\
  1903.         local offset = self.CursorPos - textWidth\
  1904.         if offset < 0 then\
  1905.             offset = 0\
  1906.         end\
  1907.         return offset\
  1908.     end\
  1909. end\
  1910. \
  1911. OnLoad = function(self)\
  1912.     if not self.CursorPos then\
  1913.         self.CursorPos = #self.Text\
  1914.     end\
  1915. end\
  1916. \
  1917. OnClick = function(self, event, side, x, y)\
  1918.     if self.Bedrock:GetActiveObject() ~= self and self.SelectOnClick then\
  1919.         self.CursorPos = #self.Text - 1\
  1920.         self.DragStart = 0\
  1921.         self.ActualDragStart = x - 2 + self:TextOffset()\
  1922.         self.Selected = true\
  1923.     else\
  1924.         self.CursorPos = x - 2 + self:TextOffset()\
  1925.         self.DragStart = self.CursorPos\
  1926.         self.Selected = false\
  1927.     end\
  1928.     self.Bedrock:SetActiveObject(self)\
  1929. end\
  1930. \
  1931. OnDrag = function(self, event, side, x, y)\
  1932.     self.CursorPos = x - 2 + self:TextOffset()\
  1933.     if self.ActualDragStart then\
  1934.         self.DragStart = self.ActualDragStart\
  1935.         self.ActualDragStart = nil\
  1936.     end\
  1937.     if self.DragStart then\
  1938.         self.Selected = true\
  1939.     end\
  1940. end\
  1941. \
  1942. OnKeyChar = function(self, event, keychar)\
  1943.     local deleteSelected = function()\
  1944.         if self.Selected then\
  1945.             local startPos = self.DragStart\
  1946.             local endPos = self.CursorPos\
  1947.             if startPos > endPos then\
  1948.                 startPos = self.CursorPos\
  1949.                 endPos = self.DragStart\
  1950.             end\
  1951.             self.Text = self.Text:sub(1, startPos) .. self.Text:sub(endPos + 2)\
  1952.             self.CursorPos = startPos\
  1953.             self.DragStart = nil\
  1954.             self.Selected = false\
  1955.             return true\
  1956.         end\
  1957.     end\
  1958. \
  1959.     if event == 'char' then\
  1960.         deleteSelected()\
  1961.         if self.Numerical then\
  1962.             keychar = tostring(tonumber(keychar))\
  1963.         end\
  1964.         if keychar == 'nil' then\
  1965.             return\
  1966.         end\
  1967.         self.Text = string.sub(self.Text, 1, self.CursorPos ) .. keychar .. string.sub( self.Text, self.CursorPos + 1 )\
  1968.         if self.Numerical then\
  1969.             self.Text = tostring(tonumber(self.Text))\
  1970.             if self.Text == 'nil' then\
  1971.                 self.Text = '1'\
  1972.             end\
  1973.         end\
  1974.         \
  1975.         self.CursorPos = self.CursorPos + 1\
  1976.         if self.OnChange then\
  1977.             self:OnChange(event, keychar)\
  1978.         end\
  1979.         return false\
  1980.     elseif event == 'key' then\
  1981.         if keychar == keys.enter then\
  1982.             if self.OnChange then\
  1983.                 self:OnChange(event, keychar)\
  1984.             end\
  1985.         elseif keychar == keys.left then\
  1986.             -- Left\
  1987.             if self.CursorPos > 0 then\
  1988.                 if self.Selected then\
  1989.                     self.CursorPos = self.DragStart\
  1990.                     self.DragStart = nil\
  1991.                     self.Selected = false\
  1992.                 else\
  1993.                     self.CursorPos = self.CursorPos - 1\
  1994.                 end\
  1995.                 if self.OnChange then\
  1996.                     self:OnChange(event, keychar)\
  1997.                 end\
  1998.             end\
  1999.             \
  2000.         elseif keychar == keys.right then\
  2001.             -- Right                \
  2002.             if self.CursorPos < string.len(self.Text) then\
  2003.                 if self.Selected then\
  2004.                     self.CursorPos = self.CursorPos\
  2005.                     self.DragStart = nil\
  2006.                     self.Selected = false\
  2007.                 else\
  2008.                     self.CursorPos = self.CursorPos + 1\
  2009.                 end\
  2010.                 if self.OnChange then\
  2011.                     self:OnChange(event, keychar)\
  2012.                 end\
  2013.             end\
  2014.         \
  2015.         elseif keychar == keys.backspace then\
  2016.             -- Backspace\
  2017.             if not deleteSelected() and self.CursorPos > 0 then\
  2018.                 self.Text = string.sub( self.Text, 1, self.CursorPos - 1 ) .. string.sub( self.Text, self.CursorPos + 1 )\
  2019.                 self.CursorPos = self.CursorPos - 1                 \
  2020.                 if self.Numerical then\
  2021.                     self.Text = tostring(tonumber(self.Text))\
  2022.                     if self.Text == 'nil' then\
  2023.                         self.Text = '1'\
  2024.                     end\
  2025.                 end\
  2026.                 if self.OnChange then\
  2027.                     self:OnChange(event, keychar)\
  2028.                 end\
  2029.             end\
  2030.         elseif keychar == keys.home then\
  2031.             -- Home\
  2032.             self.CursorPos = 0\
  2033.             if self.OnChange then\
  2034.                 self:OnChange(event, keychar)\
  2035.             end\
  2036.         elseif keychar == keys.delete then\
  2037.             if not deleteSelected() and self.CursorPos < string.len(self.Text) then\
  2038.                 self.Text = string.sub( self.Text, 1, self.CursorPos ) .. string.sub( self.Text, self.CursorPos + 2 )       \
  2039.                 if self.Numerical then\
  2040.                     self.Text = tostring(tonumber(self.Text))\
  2041.                     if self.Text == 'nil' then\
  2042.                         self.Text = '1'\
  2043.                     end\
  2044.                 end\
  2045.                 if self.OnChange then\
  2046.                     self:OnChange(keychar)\
  2047.                 end\
  2048.             end\
  2049.         elseif keychar == keys[\"end\"] then\
  2050.             -- End\
  2051.             self.CursorPos = string.len(self.Text)\
  2052.         else\
  2053.             if self.OnChange then\
  2054.                 self:OnChange(event, keychar)\
  2055.             end\
  2056.             return false\
  2057.         end\
  2058.     end\
  2059. end\
  2060. ]],\
  2061. [\"View\"] = [[\
  2062. BackgroundColour = colours.transparent\
  2063. Children = {}\
  2064. \
  2065. OnDraw = function(self, x, y)\
  2066.     if self.BackgroundColour then\
  2067.         Drawing.DrawBlankArea(x, y, self.Width, self.Height, self.BackgroundColour)\
  2068.     end\
  2069. end\
  2070. \
  2071. OnInitialise = function(self)\
  2072.     self.Children = {}\
  2073. end\
  2074. \
  2075. InitialiseFile = function(self, bedrock, file, name)\
  2076.     local _new = {}\
  2077.     _new.X = 1\
  2078.     _new.Y = 1\
  2079.     _new.Width = Drawing.Screen.Width\
  2080.     _new.Height = Drawing.Screen.Height\
  2081.     _new.BackgroundColour = file.BackgroundColour\
  2082.     _new.Name = name\
  2083.     _new.Children = {}\
  2084.     _new.Bedrock = bedrock\
  2085.     local new = self:Initialise(_new)\
  2086.     for i, obj in ipairs(file.Children) do\
  2087.         local view = bedrock:ObjectFromFile(obj, new)\
  2088.         if not view.Z then\
  2089.             view.Z = i\
  2090.         end\
  2091.         view.Parent = new\
  2092.         table.insert(new.Children, view)\
  2093.     end\
  2094.     return new\
  2095. end\
  2096. \
  2097. function CheckClick(self, object, x, y)\
  2098.     local offset = {X = 0, Y = 0}\
  2099.     if not object.Fixed and self.ChildOffset then\
  2100.         offset = self.ChildOffset\
  2101.     end\
  2102.     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\
  2103.         return true\
  2104.     end\
  2105. end\
  2106. \
  2107. function DoClick(self, object, event, side, x, y)\
  2108.     if object then\
  2109.         if self:CheckClick(object, x, y) then\
  2110.             local offset = {X = 0, Y = 0}\
  2111.             if not object.Fixed and self.ChildOffset then\
  2112.                 offset = self.ChildOffset\
  2113.             end\
  2114.             return object:Click(event, side, x - object.X - offset.X + 1, y - object.Y + 1 - offset.Y)\
  2115.         end\
  2116.     end \
  2117. end\
  2118. \
  2119. Click = function(self, event, side, x, y, z)\
  2120.     if self.Visible and not self.IgnoreClick then\
  2121.         for i = #self.Children, 1, -1 do --children are ordered from smallest Z to highest, so this is done in reverse\
  2122.             local child = self.Children[i]\
  2123.             if self:DoClick(child, event, side, x, y) then\
  2124.                 if self.OnChildClick then\
  2125.                     self:OnChildClick(child, event, side, x, y)\
  2126.                 end\
  2127.                 return true\
  2128.             end\
  2129.         end\
  2130.         if event == 'mouse_click' and self.OnClick and self:OnClick(event, side, x, y) ~= false then\
  2131.             return true\
  2132.         elseif event == 'mouse_drag' and self.OnDrag and self:OnDrag(event, side, x, y) ~= false then\
  2133.             return true\
  2134.         elseif event == 'mouse_scroll' and self.OnScroll and self:OnScroll(event, side, x, y) ~= false then\
  2135.             return true\
  2136.         else\
  2137.             return false\
  2138.         end\
  2139.     else\
  2140.         return false\
  2141.     end\
  2142. end\
  2143. \
  2144. OnRemove = function(self)\
  2145.     if self == self.Bedrock:GetActiveObject() then\
  2146.         self.Bedrock:SetActiveObject()\
  2147.     end\
  2148.     for i, child in ipairs(self.Children) do\
  2149.         child:OnRemove()\
  2150.     end\
  2151. end\
  2152. \
  2153. local function findObjectNamed(view, name, minI)\
  2154.     local minI = minI or 0\
  2155.     if view and view.Children then\
  2156.         for i, child in ipairs(view.Children) do\
  2157.             if child.Name == name or child == name then\
  2158.                 return child, i, view\
  2159.             elseif child.Children then\
  2160.                 local found, index, foundView = findObjectNamed(child, name)\
  2161.                 if found and minI <= index then\
  2162.                     return found, index, foundView\
  2163.                 end\
  2164.             end\
  2165.         end\
  2166.     end\
  2167. end\
  2168. \
  2169. function ReorderObjects(self)\
  2170.     if self.Children then\
  2171.         table.sort(self.Children, function(a,b)\
  2172.             return a.Z < b.Z \
  2173.         end)\
  2174.         for i, v in ipairs(self.Children) do\
  2175.             if v.ReorderObjects then\
  2176.                 v:ReorderObjects()\
  2177.             end\
  2178.         end\
  2179.     end\
  2180. end\
  2181. \
  2182. function AddObject(self, info, extra, first)\
  2183.     if type(info) == 'string' then\
  2184.         local h = fs.open(self.Bedrock.ViewPath..info..'.view', 'r')\
  2185.         if h then\
  2186.             info = textutils.unserialize(h.readAll())\
  2187.             h.close()\
  2188.         else\
  2189.             error('Error in opening object: '..info)\
  2190.         end\
  2191.     end\
  2192. \
  2193.     if extra then\
  2194.         for k, v in pairs(extra) do\
  2195.             if v then\
  2196.                 info[k] = v\
  2197.             end\
  2198.         end\
  2199.     end\
  2200. \
  2201.     local view = self.Bedrock:ObjectFromFile(info, self)\
  2202.     if not view.Z then\
  2203.         if first then\
  2204.             view.Z = 1\
  2205.         else\
  2206.             view.Z = #self.Children + 1\
  2207.         end\
  2208.     end\
  2209.     \
  2210.     if first then\
  2211.         table.insert(self.Children, 1, view)\
  2212.     else\
  2213.         table.insert(self.Children, view)\
  2214.     end\
  2215.     if self.Bedrock.View then\
  2216.         self.Bedrock:ReorderObjects()\
  2217.     end\
  2218.     self:ForceDraw()\
  2219.     return view\
  2220. end\
  2221. \
  2222. function GetObject(self, name)\
  2223.     return findObjectNamed(self, name)\
  2224. end\
  2225. \
  2226. local function findObjects(view, name)\
  2227.     local objects = {}\
  2228.     if view and view.Children then\
  2229.         for i, child in ipairs(view.Children) do\
  2230.             if child.Name == name or child == name then\
  2231.                 table.insert(objects, child)\
  2232.             elseif child.Children then\
  2233.                 local objs = findObjects(child, name)\
  2234.                 if objs then\
  2235.                     for i2, v in ipairs(objs) do\
  2236.                         table.insert(objects, v)\
  2237.                     end\
  2238.                 end\
  2239.             end\
  2240.         end\
  2241.     end\
  2242.     return objects\
  2243. end\
  2244. \
  2245. function GetObjects(self, name)\
  2246.     return findObjects(self, name)\
  2247. end\
  2248. \
  2249. function RemoveObject(self, name)\
  2250.     local obj, index, view = findObjectNamed(self, name, minI)\
  2251.     if index then\
  2252.         view.Children[index]:OnRemove()\
  2253.         table.remove(view.Children, index)\
  2254.         if view.OnUpdate then\
  2255.             view:OnUpdate('Children')\
  2256.         end\
  2257.         return true\
  2258.     end\
  2259.     return false\
  2260. end\
  2261. \
  2262. function RemoveObjects(self, name)\
  2263.     local i = 1\
  2264.     while self:RemoveObject(name) and i < 100 do\
  2265.         i = i + 1\
  2266.     end\
  2267.     \
  2268. end\
  2269. \
  2270. function RemoveAllObjects(self)\
  2271.     for i, child in ipairs(self.Children) do\
  2272.         child:OnRemove()\
  2273.         self.Children[i] = nil\
  2274.     end\
  2275.     self:ForceDraw()\
  2276. end\
  2277. ]],\
  2278. [\"Window\"] = [[\
  2279. Inherit = 'View'\
  2280. \
  2281. ToolBarColour = colours.lightGrey\
  2282. ToolBarTextColour = colours.black\
  2283. ShadowColour = colours.grey\
  2284. Title = ''\
  2285. Flashing = false\
  2286. CanClose = true\
  2287. OnCloseButton = nil\
  2288. OldActiveObject = nil\
  2289. \
  2290. LoadView = function(self)\
  2291.     local view = self:GetObject('View')\
  2292.     if view.ToolBarColour then\
  2293.         window.ToolBarColour = view.ToolBarColour\
  2294.     end\
  2295.     if view.ToolBarTextColour then\
  2296.         window.ToolBarTextColour = view.ToolBarTextColour\
  2297.     end\
  2298.     view.X = 1\
  2299.     view.Y = 2\
  2300. \
  2301.     view:ForceDraw()\
  2302.     self:OnUpdate('View')\
  2303.     if self.OnViewLoad then\
  2304.         self.OnViewLoad(view)\
  2305.     end\
  2306.     self.OldActiveObject = self.Bedrock:GetActiveObject()\
  2307.     self.Bedrock:SetActiveObject(view)\
  2308. end\
  2309. \
  2310. SetView = function(self, view)\
  2311.     self:RemoveObject('View')\
  2312.     table.insert(self.Children, view)\
  2313.     view.Parent = self\
  2314.     self:LoadView()\
  2315. end\
  2316. \
  2317. Flash = function(self)\
  2318.     self.Flashing = true\
  2319.     self:ForceDraw()\
  2320.     self.Bedrock:StartTimer(function()self.Flashing = false end, 0.4)\
  2321. end\
  2322. \
  2323. OnDraw = function(self, x, y)\
  2324.     local toolBarColour = (self.Flashing and colours.grey or self.ToolBarColour)\
  2325.     local toolBarTextColour = (self.Flashing and colours.black or self.ToolBarTextColour)\
  2326.     if toolBarColour then\
  2327.         Drawing.DrawBlankArea(x, y, self.Width, 1, toolBarColour)\
  2328.     end\
  2329.     if toolBarTextColour then\
  2330.         local title = self.Bedrock.Helpers.TruncateString(self.Title, self.Width - 2)\
  2331.         Drawing.DrawCharactersCenter(self.X, self.Y, self.Width, 1, title, toolBarTextColour, toolBarColour)\
  2332.     end\
  2333.     Drawing.IgnoreConstraint = true\
  2334.     Drawing.DrawBlankArea(x + 1, y + 1, self.Width, self.Height, self.ShadowColour)\
  2335.     Drawing.IgnoreConstraint = false\
  2336. end\
  2337. \
  2338. Close = function(self)\
  2339.     self.Bedrock:SetActiveObject(self.OldActiveObject)\
  2340.     self.Bedrock.Window = nil\
  2341.     self.Bedrock:RemoveObject(self)\
  2342.     if self.OnClose then\
  2343.         self:OnClose()\
  2344.     end\
  2345.     self = nil\
  2346. end\
  2347. \
  2348. OnUpdate = function(self, value)\
  2349.     if value == 'View' and self:GetObject('View') then\
  2350.         self.Width = self:GetObject('View').Width\
  2351.         self.Height = self:GetObject('View').Height + 1\
  2352.         self.X = math.ceil((Drawing.Screen.Width - self.Width) / 2)\
  2353.         self.Y = math.ceil((Drawing.Screen.Height - self.Height) / 2)\
  2354.     elseif value == 'CanClose' then\
  2355.         self:RemoveObject('CloseButton')\
  2356.         if self.CanClose then\
  2357.             local button = self:AddObject({X = 1, Y = 1, Width = 1, Height = 1, Type = 'Button', BackgroundColour = colours.red, TextColour = colours.white, Text = 'x', Name = 'CloseButton'})\
  2358.             button.OnClick = function(btn)\
  2359.                 if self.OnCloseButton then\
  2360.                     self:OnCloseButton()\
  2361.                 end\
  2362.                 self:Close()\
  2363.             end\
  2364.         end\
  2365.     end\
  2366. end\
  2367. ]],\
  2368. }\
  2369. BasePath = ''\
  2370. ProgramPath = nil\
  2371. \
  2372. -- Program functions...\
  2373. \
  2374. local function main(...)\
  2375.     -- Code here...\
  2376. end\
  2377. \
  2378. -- Run\
  2379. local args = {...}\
  2380. local _, err = pcall(function() main(unpack(args)) end)\
  2381. if err then\
  2382.     -- Make a nice error handling screen here...\
  2383.     term.setBackgroundColor(colors.black)\
  2384.     term.setTextColor(colors.white)\
  2385.     term.clear()\
  2386.     term.setCursorPos(1, 3)\
  2387.     print(\" An Error Has Occured! D:\\n\\n\")\
  2388.     print(\" \" .. tostring(err) .. \"\\n\\n\")\
  2389.     print(\" Press any key to exit...\")\
  2390.     os.pullEvent(\"key\")\
  2391. end\
  2392. \
  2393. function LoadAPIs(self)\
  2394.     local function loadAPI(name, content)\
  2395.         local env = setmetatable({}, { __index = getfenv() })\
  2396.         local func, err = loadstring(content, name..' (Bedrock API)')\
  2397.         if not func then\
  2398.             return false, printError(err)\
  2399.         end\
  2400.         setfenv(func, env)\
  2401.         func()\
  2402.         local api = {}\
  2403.         for k,v in pairs(env) do\
  2404.             api[k] = v\
  2405.         end\
  2406.         _G[name] = api\
  2407.         return true\
  2408.     end\
  2409. \
  2410.     local env = getfenv()\
  2411.     local function loadObject(name, content)\
  2412.         loadAPI(name, content)\
  2413.         if env[name].Inherit then\
  2414.             if not getfenv()[env[name].Inherit] then    \
  2415.                 if objects[env[name].Inherit] then\
  2416.                     loadObject(env[name].Inherit, objects[env[name].Inherit])\
  2417.                 elseif fs.exists(self.ProgramPath..'/Objects/'..env[name].Inherit..'.lua') then\
  2418.                     local h = fs.open(self.ProgramPath..'/Objects/'..env[name].Inherit..'.lua', 'r')\
  2419.                     loadObject(env[name].Inherit, h.readAll())\
  2420.                     h.close()\
  2421.                     loadObject(name, content)\
  2422.                     return\
  2423.                 end\
  2424.             end\
  2425.             env[name].__index = getfenv()[env[name].Inherit]\
  2426.         else\
  2427.             env[name].__index = Object\
  2428.         end\
  2429.         setmetatable(env[name], env[name])\
  2430.     end\
  2431. \
  2432.     for k, v in pairs(apis) do\
  2433.         loadAPI(k, v)\
  2434.         if k == 'Helpers' then\
  2435.             self.Helpers = Helpers\
  2436.         end\
  2437.     end\
  2438. \
  2439.     for k, v in pairs(objects) do\
  2440.         loadObject(k, v)\
  2441.     end\
  2442.     \
  2443.     local privateObjPath = self.ProgramPath..'/Objects/'\
  2444.     if fs.exists(privateObjPath) and fs.isDir(privateObjPath) then\
  2445.         for i, v in ipairs(fs.list(privateObjPath)) do\
  2446.             if v ~= '.DS_Store' then\
  2447.                 local name = string.match(v, '(%a+)%.?.-')\
  2448.                 local h = fs.open(privateObjPath..v, 'r')\
  2449.                 loadObject(name, h.readAll())\
  2450.                 h.close()\
  2451.             end\
  2452.         end\
  2453.     end\
  2454.     \
  2455.     local privateAPIPath = self.ProgramPath..'/APIs/'\
  2456.     if fs.exists(privateAPIPath) and fs.isDir(privateAPIPath) then\
  2457.         for i, v in ipairs(fs.list(privateAPIPath)) do\
  2458.             if v ~= '.DS_Store' then\
  2459.                 local name = string.match(v, '(%a+)%.?.-')\
  2460.                 local h = fs.open(privateAPIPath..v, 'r')\
  2461.                 loadAPI(name, h.readAll())\
  2462.                 h.close()\
  2463.             end\
  2464.         end\
  2465.     end\
  2466. end\
  2467. \
  2468. AllowTerminate = true\
  2469. \
  2470. View = nil\
  2471. Menu = nil\
  2472. \
  2473. ActiveObject = nil\
  2474. \
  2475. DrawTimer = nil\
  2476. DrawTimerExpiry = 0\
  2477. \
  2478. IsDrawing = false\
  2479. \
  2480. Running = true\
  2481. \
  2482. DefaultView = 'main'\
  2483. \
  2484. AnimationEnabled = true\
  2485. \
  2486. EventHandlers = {\
  2487.     \
  2488. }\
  2489. \
  2490. ObjectClickHandlers = {\
  2491.     \
  2492. }\
  2493. \
  2494. ObjectUpdateHandlers = {\
  2495.     \
  2496. }\
  2497. \
  2498. Timers = {\
  2499.     \
  2500. }\
  2501. \
  2502. ModifierKeys = {}\
  2503. KeyboardShortcuts = {}\
  2504. \
  2505. keys.leftCommand = 219\
  2506. keys.rightCommand = 220\
  2507. \
  2508. function Initialise(self, programPath)\
  2509.     self.ProgramPath = programPath or self.ProgramPath\
  2510.     if not programPath then\
  2511.         if self.ProgramPath then\
  2512.             local prgPath = self.ProgramPath\
  2513.             local prgName = fs.getName(prgPath)\
  2514.             if prgPath:find('/') then \
  2515.                 self.ProgramPath = prgPath:sub(1, #prgPath-#prgName-1)\
  2516.                 self.ProgramPath = prgPath:sub(1, #prgPath-#prgName-1) \
  2517.             else \
  2518.                 self.ProgramPath = '' \
  2519.             end\
  2520.         else\
  2521.             self.ProgramPath = ''\
  2522.         end\
  2523.     end\
  2524.     self:LoadAPIs()\
  2525.     self.ViewPath = self.ProgramPath .. '/Views/'\
  2526.     --first, check that the barebones APIs are available\
  2527.     local requiredApis = {\
  2528.         'Drawing',\
  2529.         'View'\
  2530.     }\
  2531.     local env = getfenv()\
  2532.     for i,v in ipairs(requiredApis) do\
  2533.         if not env[v] then\
  2534.             error('The API: '..v..' is not loaded. Please make sure you load it to use Bedrock.')\
  2535.         end\
  2536.     end\
  2537. \
  2538.     local copy = { }\
  2539.     for k, v in pairs(self) do\
  2540.         if k ~= 'Initialise' then\
  2541.             copy[k] = v\
  2542.         end\
  2543.     end\
  2544.     return setmetatable(copy, getmetatable(self))\
  2545. end\
  2546. \
  2547. function HandleClick(self, event, side, x, y)\
  2548.     if self.Menu then\
  2549.         if not self.View:DoClick(self.Menu, event, side, x, y) then\
  2550.             self.Menu:Close()\
  2551.         end\
  2552.     elseif self.Window then\
  2553.         if not self.View:CheckClick(self.Window, x, y) then\
  2554.             self.Window:Flash()\
  2555.         else\
  2556.             self.View:DoClick(self.Window, event, side, x, y)\
  2557.         end\
  2558.     elseif self.View then\
  2559.         if self.View:Click(event, side, x, y) ~= false then\
  2560.         end     \
  2561.     end\
  2562. end\
  2563. \
  2564. function UnregisterKeyboardShortcut(self, name)\
  2565.     if name then\
  2566.         self.KeyboardShortcuts[name] = nil\
  2567.     end\
  2568. end\
  2569. \
  2570. function RegisterKeyboardShortcut(self, keys, func, name)\
  2571.     name = name or tostring(math.random(1, 10000))\
  2572.     if type(keys[1]) == 'table' then\
  2573.         for i, v in ipairs(keys) do\
  2574.             self.KeyboardShortcuts[name] = {Keys = v, Function = func}\
  2575.         end\
  2576.     else\
  2577.         self.KeyboardShortcuts[name] = {Keys = keys, Function = func}\
  2578.     end\
  2579.     return name\
  2580. end\
  2581. \
  2582. function TryKeyboardShortcuts(self, keychar)\
  2583.     if keychar == keys.backspace then\
  2584.         keychar = keys.delete\
  2585.     end\
  2586. \
  2587.     local len = 1 -- + keychar\
  2588.     for k, v in pairs(self.ModifierKeys) do\
  2589.         len = len + 1\
  2590.     end\
  2591. \
  2592.     for _, shortcut in pairs(self.KeyboardShortcuts) do\
  2593.         local match = true\
  2594.         for i2, key in ipairs(shortcut.Keys) do\
  2595.             if self.ModifierKeys[key] == nil and key ~= keychar then\
  2596.                 match = false\
  2597.             end\
  2598.         end\
  2599. \
  2600.         if match and #shortcut.Keys == len then\
  2601.             return shortcut.Function() ~= false\
  2602.         end\
  2603.     end\
  2604. end\
  2605. \
  2606. function HandleKeyChar(self, event, keychar)\
  2607.     if keychar == keys.leftCtrl or keychar == keys.leftShift or keychar == keys.leftAlt or keychar == keys.leftCommand or keychar == keys.rightCommand or keychar == keys.rightCtrl or keychar == keys.rightShift or keychar == keys.rightAlt then\
  2608.         if keychar == keys.leftCommand or keychar == keys.rightCommand or keychar == keys.rightCtrl then\
  2609.             keychar = keys.leftCtrl\
  2610.         elseif keychar == keys.rightAlt then\
  2611.             keychar = keys.leftAlt\
  2612.         elseif keychar == keys.rightShift then\
  2613.             keychar = keys.leftShift\
  2614.         end\
  2615.         self.ModifierKeys[keychar] = self:StartTimer(function(_, timer)\
  2616.             if timer == self.ModifierKeys[keychar] then\
  2617.                 self.ModifierKeys[keychar] = nil\
  2618.             end\
  2619.         end, 1)\
  2620.     elseif self:TryKeyboardShortcuts(keychar) then\
  2621.         return\
  2622.     end\
  2623. \
  2624.     if self:GetActiveObject() then\
  2625.         local activeObject = self:GetActiveObject()\
  2626.         if activeObject.OnKeyChar then\
  2627.             if activeObject:OnKeyChar(event, keychar) ~= false then\
  2628.                 --self:Draw()\
  2629.             end\
  2630.         end\
  2631.     end\
  2632. end\
  2633. \
  2634. PreparedMenus = {}\
  2635. \
  2636. function PrepareMenu(self, name)\
  2637.     local menu = self:AddObject(name, {Type = 'Menu', X = 1, Y = 1, Prepared = true})\
  2638.     menu.Visible = false\
  2639.     self.PreparedMenus[name] = menu\
  2640.     return menu\
  2641. end\
  2642. \
  2643. function ToggleMenu(self, name, owner, x, y)\
  2644.     if self.Menu then\
  2645.         self.Menu:Close()\
  2646.         return false\
  2647.     else\
  2648.         self:SetMenu(name, owner, x, y)\
  2649.         return true\
  2650.     end\
  2651. end\
  2652. \
  2653. function SetMenu(self, menu, owner, x, y)\
  2654.     x = x or 1\
  2655.     y = y or 1\
  2656.     if self.Menu then\
  2657.         self.Menu:Close()\
  2658.     end \
  2659.     if menu then\
  2660.         local pos = owner:GetPosition()\
  2661.         if self.PreparedMenus[menu] then\
  2662.             self.Menu = self.PreparedMenus[menu]\
  2663.             self.Menu.Visible = true\
  2664.             self.Menu.Owner = owner\
  2665.             self.Menu.X = pos.X + x - 1\
  2666.             self.Menu.Y = pos.Y + y\
  2667.             self.Menu.Z = self.View.Children[#self.View.Children].Z + 1\
  2668.             self:ReorderObjects()\
  2669.         else\
  2670.             self.Menu = self:AddObject(menu, {Type = 'Menu', Owner = owner, X = pos.X + x - 1, Y = pos.Y + y, Z = self.View.Children[#self.View.Children].Z + 1})\
  2671.         end\
  2672.     end\
  2673. end\
  2674. \
  2675. function ObjectClick(self, name, func)\
  2676.     self.ObjectClickHandlers[name] = func\
  2677. end\
  2678. \
  2679. function ClickObject(self, object, event, side, x, y)\
  2680.     if self.ObjectClickHandlers[object.Name] then\
  2681.         return self.ObjectClickHandlers[object.Name](object, event, side, x, y)\
  2682.     end\
  2683.     return false\
  2684. end\
  2685. \
  2686. function ObjectUpdate(self, name, func)\
  2687.     self.ObjectUpdateHandlers[name] = func\
  2688. end\
  2689. \
  2690. function UpdateObject(self, object, ...)\
  2691.     if self.ObjectUpdateHandlers[object.Name] then\
  2692.         self.ObjectUpdateHandlers[object.Name](object, ...)\
  2693.         --self:Draw()\
  2694.     end\
  2695. end\
  2696. \
  2697. function GetAbsolutePosition(self, obj)\
  2698.     if not obj.Parent then\
  2699.         return {X = obj.X, Y = obj.Y}\
  2700.     else\
  2701.         local pos = self:GetAbsolutePosition(obj.Parent)\
  2702.         local x = pos.X + obj.X - 1\
  2703.         local y = pos.Y + obj.Y - 1\
  2704.         if not obj.Fixed and obj.Parent.ChildOffset then\
  2705.             x = x + obj.Parent.ChildOffset.X\
  2706.             y = y + obj.Parent.ChildOffset.Y\
  2707.         end\
  2708.         return {X = x, Y = y}\
  2709.     end\
  2710. end\
  2711. \
  2712. function LoadView(self, name, draw)\
  2713.     if self.View and self.OnViewClose then\
  2714.         self.OnViewClose(self.View.Name)\
  2715.     end\
  2716.     if self.View then\
  2717.         self.View:OnRemove()\
  2718.     end\
  2719.     local success = false\
  2720. \
  2721.     if Drawing.Screen.Width <= 26 and fs.exists(self.ViewPath..name..'-pocket.view') then\
  2722.         name = name..'-pocket'\
  2723.     elseif Drawing.Screen.Width <= 39 and fs.exists(self.ViewPath..name..'-turtle.view') then\
  2724.         name = name..'-turtle'\
  2725.     end\
  2726. \
  2727.     if not fs.exists(self.ViewPath..name..'.view') then\
  2728.         error('The view: '..name..'.view does not exist.')\
  2729.     end\
  2730. \
  2731.     local h = fs.open(self.ViewPath..name..'.view', 'r')\
  2732.     if h then\
  2733.         local view = textutils.unserialize(h.readAll())\
  2734.         h.close()\
  2735.         if view then\
  2736.             self.View = View:InitialiseFile(self, view, name)\
  2737.             self:ReorderObjects()\
  2738. \
  2739.             if OneOS and view.ToolBarColour then\
  2740.                 OneOS.ToolBarColour = view.ToolBarColour\
  2741.             end\
  2742.             if OneOS and view.ToolBarTextColour then\
  2743.                 OneOS.ToolBarTextColour = view.ToolBarTextColour\
  2744.             end\
  2745.             if not self:GetActiveObject() then\
  2746.                 self:SetActiveObject()\
  2747.             end\
  2748.             success = true\
  2749.         end\
  2750.     end\
  2751. \
  2752.     if success and self.OnViewLoad then\
  2753.         self.OnViewLoad(name)\
  2754.     end\
  2755. \
  2756.     if draw ~= false then\
  2757.         self:Draw()\
  2758.     end\
  2759. \
  2760.     if not success then\
  2761.         error('Failed to load view: '..name..'. It probably isn\\'t formatted correctly. Did you forget a } or ,?')\
  2762.     end\
  2763. \
  2764.     return success\
  2765. end\
  2766. \
  2767. function InheritFile(self, file, name)\
  2768.     local h = fs.open(self.ViewPath..name..'.view', 'r')\
  2769.     if h then\
  2770.         local super = textutils.unserialize(h.readAll())\
  2771.         if super then\
  2772.             if type(super) ~= 'table' then\
  2773.                 error('View: \"'..name..'.view\" is not formatted correctly.')\
  2774.             end\
  2775. \
  2776.             for k, v in pairs(super) do\
  2777.                 if not file[k] then\
  2778.                     file[k] = v\
  2779.                 end\
  2780.             end\
  2781.             return file\
  2782.         end\
  2783.     end\
  2784.     return file\
  2785. end\
  2786. \
  2787. function ParseStringSize(self, parent, k, v)\
  2788.     local parentSize = parent.Width\
  2789.     if k == 'Height' or k == 'Y' then\
  2790.         parentSize = parent.Height\
  2791.     end\
  2792.     local parts = {v}\
  2793.     if type(v) == 'string' and string.find(v, ',') then\
  2794.         parts = {}\
  2795.         for word in string.gmatch(v, '([^,]+)') do\
  2796.             table.insert(parts, word)\
  2797.         end\
  2798.     end\
  2799. \
  2800.     v = 0\
  2801.     for i2, part in ipairs(parts) do\
  2802.         if type(part) == 'string' and part:sub(#part) == '%' then\
  2803.             v = v + math.ceil(parentSize * (tonumber(part:sub(1, #part-1)) / 100))\
  2804.         else\
  2805.             v = v + tonumber(part)\
  2806.         end\
  2807.     end\
  2808.     return v\
  2809. end\
  2810. \
  2811. function ObjectFromFile(self, file, view)\
  2812.     local env = getfenv()\
  2813.     if env[file.Type] then\
  2814.         if not env[file.Type].Initialise then\
  2815.             error('Malformed Object: '..file.Type)\
  2816.         end\
  2817.         local object = {}\
  2818. \
  2819.         if file.InheritView then\
  2820.             file = self:InheritFile(file, file.InheritView)\
  2821.         end\
  2822.         \
  2823.         object.AutoWidth = true\
  2824.         for k, v in pairs(file) do\
  2825.             if k == 'Width' or k == 'X' or k == 'Height' or k == 'Y' then\
  2826.                 v = self:ParseStringSize(view, k, v)\
  2827.             end\
  2828. \
  2829.             if k == 'Width' then\
  2830.                 object.AutoWidth = false\
  2831.             end\
  2832.             if k ~= 'Children' then\
  2833.                 object[k] = v\
  2834.             else\
  2835.                 object[k] = {}\
  2836.             end\
  2837.         end\
  2838. \
  2839.         object.Parent = view\
  2840.         object.Bedrock = self\
  2841.         if not object.Name then\
  2842.             object.Name = file.Type\
  2843.         end\
  2844. \
  2845.         object = env[file.Type]:Initialise(object)\
  2846. \
  2847.         if file.Children then\
  2848.             for i, obj in ipairs(file.Children) do\
  2849.                 local _view = self:ObjectFromFile(obj, object)\
  2850.                 if not _view.Z then\
  2851.                     _view.Z = i\
  2852.                 end\
  2853.                 _view.Parent = object\
  2854.                 table.insert(object.Children, _view)\
  2855.             end\
  2856.         end\
  2857. \
  2858.         if not object.OnClick then\
  2859.             object.OnClick = function(...) return self:ClickObject(...) end\
  2860.         end\
  2861.         --object.OnUpdate = function(...) self:UpdateObject(...) end\
  2862. \
  2863.         if object.OnUpdate then\
  2864.             for k, v in pairs(env[file.Type]) do\
  2865.                 object:OnUpdate(k)\
  2866.             end\
  2867. \
  2868.             for k, v in pairs(object.__index) do\
  2869.                 object:OnUpdate(k)\
  2870.             end\
  2871.         end\
  2872. \
  2873.         if object.Active then\
  2874.             object.Bedrock:SetActiveObject(object)\
  2875.         end\
  2876.         if object.OnLoad then\
  2877.             object:OnLoad()\
  2878.         end\
  2879.         object.Ready = true\
  2880.         return object\
  2881.     elseif not file.Type then\
  2882.         error('No object type specified. (e.g. Type = \"Button\")')\
  2883.     else\
  2884.         error('No Object: '..file.Type..'. The API probably isn\\'t loaded')\
  2885.     end\
  2886. end\
  2887. \
  2888. function ReorderObjects(self)\
  2889.     if self.View and self.View.Children then\
  2890.         self.View:ReorderObjects()\
  2891.     end\
  2892. end\
  2893. \
  2894. function AddObject(self, info, extra, first)\
  2895.     return self.View:AddObject(info, extra, first)\
  2896. end\
  2897. \
  2898. function GetObject(self, name)\
  2899.     return self.View:GetObject(name)\
  2900. end\
  2901. \
  2902. function GetObjects(self, name)\
  2903.     return self.View:GetObjects(name)\
  2904. end\
  2905. \
  2906. function RemoveObject(self, name)\
  2907.     return self.View:RemoveObject(name)\
  2908. end\
  2909. \
  2910. function RemoveObjects(self, name)\
  2911.     return self.View:RemoveObjects(name)\
  2912. end\
  2913. \
  2914. DrawEvent = nil\
  2915. \
  2916. function HandleDraw(self, event, id)\
  2917.     if id == self.DrawEvent then\
  2918.         self.DrawEvent = nil\
  2919.         self:Draw()\
  2920.     end\
  2921. end\
  2922. \
  2923. function ForceDraw(self)\
  2924.     if not self.DrawEvent then--or self.DrawTimerExpiry <= os.clock() then\
  2925.         self.DrawEvent = math.random()\
  2926.         os.queueEvent('bedrock_draw', self.DrawEvent)\
  2927.         -- self:StartTimer(function()\
  2928.         --  self.DrawTimer = nil\
  2929.         --  self:Draw()\
  2930.         -- end, 0.05)\
  2931.         -- self.DrawTimerExpiry = os.clock() + 0.1\
  2932.     end\
  2933. end\
  2934. \
  2935. function DisplayWindow(self, _view, title, canClose)\
  2936.     if canClose == nil then\
  2937.         canClose = true\
  2938.     end\
  2939.     if type(_view) == 'string' then\
  2940.         local h = fs.open(self.ViewPath.._view..'.view', 'r')\
  2941.         if h then\
  2942.             _view = textutils.unserialize(h.readAll())\
  2943.             h.close()\
  2944.         end\
  2945.     end\
  2946. \
  2947.     self.Window = self:AddObject({Type = 'Window', Z = 999, Title = title, CanClose = canClose})\
  2948.     _view.Type = 'View'\
  2949.     _view.Name = 'View'\
  2950.     _view.BackgroundColour = _view.BackgroundColour or colours.white\
  2951.     self.Window:SetView(self:ObjectFromFile(_view, self.Window))\
  2952. end\
  2953. \
  2954. function DisplayAlertWindow(self, title, text, buttons, callback)\
  2955.     local func = function(btn)\
  2956.         self.Window:Close()\
  2957.         if callback then\
  2958.             callback(btn.Text)\
  2959.         end\
  2960.     end\
  2961.     local children = {}\
  2962.     local usedX = -1\
  2963.     if buttons then\
  2964.         for i, text in ipairs(buttons) do\
  2965.             usedX = usedX + 3 + #text\
  2966.             table.insert(children, {\
  2967.                 [\"Y\"]=\"100%,-1\",\
  2968.                 [\"X\"]=\"100%,-\"..usedX,\
  2969.                 [\"Name\"]=text..\"Button\",\
  2970.                 [\"Type\"]=\"Button\",\
  2971.                 [\"Text\"]=text,\
  2972.                 OnClick = func\
  2973.             })\
  2974.         end\
  2975.     end\
  2976. \
  2977.     local width = usedX + 2\
  2978.     if width < 28 then\
  2979.         width = 28\
  2980.     end\
  2981. \
  2982.     local canClose = true\
  2983.     if buttons and #buttons~=0 then\
  2984.         canClose = false\
  2985.     end\
  2986. \
  2987.     local height = 0\
  2988.     if text then\
  2989.         height = #Helpers.WrapText(text, width - 2)\
  2990.         table.insert(children, {\
  2991.             [\"Y\"]=2,\
  2992.             [\"X\"]=2,\
  2993.             [\"Width\"]=\"100%,-2\",\
  2994.             [\"Height\"]=height,\
  2995.             [\"Name\"]=\"Label\",\
  2996.             [\"Type\"]=\"Label\",\
  2997.             [\"Text\"]=text\
  2998.         })\
  2999.     end\
  3000.     local view = {\
  3001.         Children = children,\
  3002.         Width=width,\
  3003.         Height=3+height+(canClose and 0 or 1),\
  3004.         OnKeyChar = function(_view, keychar)\
  3005.             func({Text=buttons[1]})\
  3006.         end\
  3007.     }\
  3008.     self:DisplayWindow(view, title, canClose)\
  3009. end\
  3010. \
  3011. function DisplayTextBoxWindow(self, title, text, callback, textboxText, cursorAtEnd)\
  3012.     textboxText = textboxText or ''\
  3013.     local children = {\
  3014.         {\
  3015.             [\"Y\"]=\"100%,-1\",\
  3016.             [\"X\"]=\"100%,-4\",\
  3017.             [\"Name\"]=\"OkButton\",\
  3018.             [\"Type\"]=\"Button\",\
  3019.             [\"Text\"]=\"Ok\",\
  3020.             OnClick = function()\
  3021.                 local text = self.Window:GetObject('TextBox').Text\
  3022.                 self.Window:Close()\
  3023.                 callback(true, text)\
  3024.             end\
  3025.         },\
  3026.         {\
  3027.             [\"Y\"]=\"100%,-1\",\
  3028.             [\"X\"]=\"100%,-13\",\
  3029.             [\"Name\"]=\"CancelButton\",\
  3030.             [\"Type\"]=\"Button\",\
  3031.             [\"Text\"]=\"Cancel\",\
  3032.             OnClick = function()\
  3033.                 self.Window:Close()\
  3034.                 callback(false)\
  3035.             end\
  3036.         }\
  3037.     }\
  3038. \
  3039.     local height = -1\
  3040.     if text and #text ~= 0 then\
  3041.         height = #Helpers.WrapText(text, 26)\
  3042.         table.insert(children, {\
  3043.             [\"Y\"]=2,\
  3044.             [\"X\"]=2,\
  3045.             [\"Width\"]=\"100%,-2\",\
  3046.             [\"Height\"]=height,\
  3047.             [\"Name\"]=\"Label\",\
  3048.             [\"Type\"]=\"Label\",\
  3049.             [\"Text\"]=text\
  3050.         })\
  3051.     end\
  3052.     table.insert(children,\
  3053.         {\
  3054.             [\"Y\"]=3+height,\
  3055.             [\"X\"]=2,\
  3056.             [\"Width\"]=\"100%,-2\",\
  3057.             [\"Name\"]=\"TextBox\",\
  3058.             [\"Type\"]=\"TextBox\",\
  3059.             [\"Text\"]=textboxText,\
  3060.             [\"CursorPos\"]=(cursorAtEnd or 0)\
  3061.         })\
  3062.     local view = {\
  3063.         Children = children,\
  3064.         Width=28,\
  3065.         Height=5+height+(canClose and 0 or 1),\
  3066.     }\
  3067.     self:DisplayWindow(view, title)\
  3068.     self.Window:GetObject('TextBox').OnChange = function(txtbox, event, keychar)\
  3069.         if keychar == keys.enter then\
  3070.             self.Window:Close()\
  3071.             callback(true, txtbox.Text)\
  3072.         end\
  3073.     end\
  3074.     self:SetActiveObject(self.Window:GetObject('TextBox'))\
  3075.     self.Window.OnCloseButton = function()callback(false)end\
  3076. end\
  3077. \
  3078. function DisplayOpenFileWindow(self, title, callback)\
  3079.     title = title or 'Open File'\
  3080.     local func = function(btn)\
  3081.         self.Window:Close()\
  3082.         if callback then\
  3083.             callback(btn.Text)\
  3084.         end\
  3085.     end\
  3086. \
  3087.     local sidebarItems = {}\
  3088. \
  3089.     --this is a really, really super bad way of doing it\
  3090.     local separator = '                               !'\
  3091. \
  3092.     local function addFolder(path, level)\
  3093.         for i, v in ipairs(_fs.list(path)) do\
  3094.             local fPath = path .. '/' .. v\
  3095.             if fPath ~= '/rom' and _fs.isDir(fPath) then\
  3096.                 table.insert(sidebarItems, level .. v..separator..fPath)\
  3097.                 addFolder(fPath, level .. '  ')\
  3098.             end\
  3099.         end\
  3100.     end\
  3101.     \
  3102.     if OneOS then\
  3103.         _fs = OneOS.FS\
  3104.     end\
  3105. \
  3106.     addFolder('','')\
  3107. \
  3108.     local currentFolder = ''\
  3109.     local selectedPath = nil\
  3110. \
  3111.     local goToFolder = nil\
  3112. \
  3113.     local children = {\
  3114.         {\
  3115.             [\"Y\"]=\"100%,-2\",\
  3116.             [\"X\"]=1,\
  3117.             [\"Height\"]=3,\
  3118.             [\"Width\"]=\"100%\",\
  3119.             [\"BackgroundColour\"]=colours.lightGrey,\
  3120.             [\"Name\"]=\"SidebarListView\",\
  3121.             [\"Type\"]=\"View\"\
  3122.         },\
  3123.         {\
  3124.             [\"Y\"]=\"100%,-1\",\
  3125.             [\"X\"]=\"100%,-4\",\
  3126.             [\"Name\"]=\"OkButton\",\
  3127.             [\"Type\"]=\"Button\",\
  3128.             [\"Text\"]=\"Ok\",\
  3129.             [\"BackgroundColour\"]=colours.white,\
  3130.             [\"Enabled\"]=false,\
  3131.             OnClick = function()\
  3132.                 if selectedPath then\
  3133.                     self.Window:Close()\
  3134.                     callback(true, Helpers.TidyPath(selectedPath))\
  3135.                 end\
  3136.             end\
  3137.         },\
  3138.         {\
  3139.             [\"Y\"]=\"100%,-1\",\
  3140.             [\"X\"]=\"100%,-13\",\
  3141.             [\"Name\"]=\"CancelButton\",\
  3142.             [\"Type\"]=\"Button\",\
  3143.             [\"Text\"]=\"Cancel\",\
  3144.             [\"BackgroundColour\"]=colours.white,\
  3145.             OnClick = function()\
  3146.                 self.Window:Close()\
  3147.                 callback(false)\
  3148.             end\
  3149.         },\
  3150.         {\
  3151.             [\"Y\"]=1,\
  3152.             [\"X\"]=1,\
  3153.             [\"Height\"]=\"100%,-3\",\
  3154.             [\"Width\"]=\"40%,-1\",\
  3155.             [\"Name\"]=\"SidebarListView\",\
  3156.             [\"Type\"]=\"ListView\",\
  3157.             [\"CanSelect\"]=true,\
  3158.             [\"Items\"]={\
  3159.                 [\"Computer\"] = sidebarItems\
  3160.             },\
  3161.             OnSelect = function(listView, text)\
  3162.                 local _,s = text:find(separator)\
  3163.                 if s then\
  3164.                     local path = text:sub(s + 1)\
  3165.                     goToFolder(path)\
  3166.                 end\
  3167.             end,\
  3168.             OnClick = function(listView, event, side, x, y)\
  3169.                 if y == 1 then\
  3170.                     goToFolder('/')\
  3171.                 end\
  3172.             end\
  3173.         },\
  3174.         {\
  3175.             [\"Y\"]=1,\
  3176.             [\"X\"]=\"40%\",\
  3177.             [\"Height\"]=\"100%,-3\",\
  3178.             [\"Width\"]=1,\
  3179.             [\"Type\"]=\"Separator\"\
  3180.         },\
  3181.         {\
  3182.             [\"Y\"]=1,\
  3183.             [\"X\"]=\"40%,2\",\
  3184.             [\"Width\"]=\"65%,-3\",\
  3185.             [\"Height\"]=1,\
  3186.             [\"Type\"]=\"Label\",\
  3187.             [\"Name\"]=\"PathLabel\",\
  3188.             [\"TextColour\"]=colours.lightGrey,\
  3189.             [\"Text\"]='/'\
  3190.         },\
  3191.         {\
  3192.             [\"Y\"]=2,\
  3193.             [\"X\"]=\"40%,1\",\
  3194.             [\"Height\"]=\"100%,-4\",\
  3195.             [\"Width\"]=\"65%,-1\",\
  3196.             [\"Name\"]=\"FilesListView\",\
  3197.             [\"Type\"]=\"ListView\",\
  3198.             [\"CanSelect\"]=true,\
  3199.             [\"Items\"]={},\
  3200.             OnSelect = function(listView, text)\
  3201.                 selectedPath = Helpers.TidyPath(currentFolder .. '/' .. text)\
  3202.                 self.Window:GetObject('OkButton').Enabled = true\
  3203.             end,\
  3204.             OnClick = function(listView, event, side, x, y)\
  3205.                 if y == 1 then\
  3206.                     goToFolder('/')\
  3207.                 end\
  3208.             end\
  3209.         },\
  3210.     }\
  3211.     local view = {\
  3212.         Children = children,\
  3213.         Width=40,\
  3214.         Height= Drawing.Screen.Height - 4,\
  3215.         OnCloseButton=function()\
  3216.             callback(false)\
  3217.         end\
  3218.     }\
  3219.     self:DisplayWindow(view, title)\
  3220. \
  3221.     goToFolder = function(path)\
  3222.         path = Helpers.TidyPath(path)\
  3223.         self.Window:GetObject('PathLabel').Text = path\
  3224.         currentFolder = path\
  3225. \
  3226.         local filesListItems = {}\
  3227.         for i, v in ipairs(_fs.list(path)) do\
  3228.             if not _fs.isDir(currentFolder .. v) then\
  3229.                 table.insert(filesListItems, v)\
  3230.             end\
  3231.         end\
  3232.         self.Window:GetObject('OkButton').Enabled = false\
  3233.         selectedPath = nil\
  3234.         self.Window:GetObject('FilesListView').Items = filesListItems\
  3235. \
  3236.     end\
  3237. \
  3238.     if startPath then\
  3239.         goToFolder(startPath)\
  3240.     elseif OneOS then\
  3241.         goToFolder('/Desktop/Documents/')\
  3242.     else\
  3243.         goToFolder('')\
  3244.     end\
  3245. \
  3246.     self.Window.OnCloseButton = function()callback(false)end\
  3247. end\
  3248. \
  3249. function DisplaySaveFileWindow(self, title, callback, extension, startPath)\
  3250.     local _fs = fs\
  3251.     if extension and extension:sub(1,1) ~= '.' then\
  3252.         extension = '.' .. extension\
  3253.     end\
  3254.     extension = extension or ''\
  3255. \
  3256.     title = title or 'Save File'\
  3257.     local func = function(btn)\
  3258.         self.Window:Close()\
  3259.         if callback then\
  3260.             callback(btn.Text)\
  3261.         end\
  3262.     end\
  3263. \
  3264.     local sidebarItems = {}\
  3265. \
  3266.     --this is a really, really super bad way of doing it\
  3267.     local separator = '                                                       !'\
  3268. \
  3269.     local function addFolder(path, level)\
  3270.         for i, v in ipairs(_fs.list(path)) do\
  3271.             local fPath = path .. '/' .. v\
  3272.             if fPath ~= '/rom' and _fs.isDir(fPath) then\
  3273.                 table.insert(sidebarItems, level .. v..separator..fPath)\
  3274.                 addFolder(fPath, level .. '  ')\
  3275.             end\
  3276.         end\
  3277.     end\
  3278.     \
  3279.     if OneOS then\
  3280.         _fs = OneOS.FS\
  3281.     end\
  3282.     addFolder('','')\
  3283. \
  3284.     local currentFolder = ''\
  3285.     local selectedPath = nil\
  3286. \
  3287.     local goToFolder = nil\
  3288. \
  3289.     local function updatePath()\
  3290.         local text = self:GetObject('FileNameTextBox').Text\
  3291.         if #text == 0 then\
  3292.             self.Window:GetObject('OkButton').Enabled = false\
  3293.             selectedPath = Helpers.TidyPath(currentFolder)\
  3294.         else\
  3295.             self.Window:GetObject('OkButton').Enabled = true\
  3296.             selectedPath = Helpers.TidyPath(currentFolder .. '/' .. text .. extension)\
  3297.         end\
  3298.         self:GetObject('PathLabel').Text = selectedPath\
  3299.     end\
  3300. \
  3301.     local children = {\
  3302.         {\
  3303.             [\"Y\"]=\"100%,-2\",\
  3304.             [\"X\"]=1,\
  3305.             [\"Height\"]=3,\
  3306.             [\"Width\"]=\"100%\",\
  3307.             [\"BackgroundColour\"]=colours.lightGrey,\
  3308.             [\"Type\"]=\"View\"\
  3309.         },\
  3310.         {\
  3311.             [\"Y\"]=\"100%,-1\",\
  3312.             [\"X\"]=\"100%,-4\",\
  3313.             [\"Name\"]=\"OkButton\",\
  3314.             [\"Type\"]=\"Button\",\
  3315.             [\"Text\"]=\"Ok\",\
  3316.             [\"BackgroundColour\"]=colours.white,\
  3317.             [\"Enabled\"]=false,\
  3318.             OnClick = function()\
  3319.                 if selectedPath then\
  3320.                     local text = self:GetObject('FileNameTextBox').Text\
  3321.                     self.Window:Close()\
  3322.                     callback(true, selectedPath, text)\
  3323.                 end\
  3324.             end\
  3325.         },\
  3326.         {\
  3327.             [\"Y\"]=\"100%,-1\",\
  3328.             [\"X\"]=\"100%,-13\",\
  3329.             [\"Name\"]=\"CancelButton\",\
  3330.             [\"Type\"]=\"Button\",\
  3331.             [\"Text\"]=\"Cancel\",\
  3332.             [\"BackgroundColour\"]=colours.white,\
  3333.             OnClick = function()\
  3334.                 self.Window:Close()\
  3335.                 callback(false)\
  3336.             end\
  3337.         },\
  3338.         {\
  3339.             [\"Y\"]=\"100%,-2\",\
  3340.             [\"X\"]=3,\
  3341.             [\"Width\"]=\"100%,-4\",\
  3342.             [\"Name\"]=\"PathLabel\",\
  3343.             [\"Type\"]=\"Label\",\
  3344.             [\"Text\"]=\"/\",\
  3345.             [\"TextColour\"]=colours.grey\
  3346.         },\
  3347.         {\
  3348.             [\"Y\"]=\"100%,-1\",\
  3349.             [\"X\"]=3,\
  3350.             [\"Width\"]=\"100%,-17\",\
  3351.             [\"Name\"]=\"FileNameTextBox\",\
  3352.             [\"Type\"]=\"TextBox\",\
  3353.             [\"Placeholder\"]=\"File Name\",\
  3354.             [\"Active\"]=true,\
  3355.             [\"BackgroundColour\"]=colours.white,\
  3356.             OnChange = function(_self, event, keychar)\
  3357.                 if keychar == keys.enter then\
  3358.                     self:GetObject('OkButton'):OnClick()\
  3359.                 else\
  3360.                     updatePath()\
  3361.                 end\
  3362.             end\
  3363.         },\
  3364.         {\
  3365.             [\"Y\"]=1,\
  3366.             [\"X\"]=2,\
  3367.             [\"Height\"]=\"100%,-3\",\
  3368.             [\"Width\"]=\"100%,-1\",\
  3369.             [\"Name\"]=\"SidebarListView\",\
  3370.             [\"Type\"]=\"ListView\",\
  3371.             [\"CanSelect\"]=true,\
  3372.             [\"Items\"]={\
  3373.                 [\"Computer\"] = sidebarItems\
  3374.             },\
  3375.             OnSelect = function(listView, text)\
  3376.                 local _,s = text:find(separator)\
  3377.                 if s then\
  3378.                     local path = text:sub(s + 1)\
  3379.                     goToFolder(path)\
  3380.                 end\
  3381.             end,\
  3382.             OnClick = function(listView, event, side, x, y)\
  3383.                 if y == 1 then\
  3384.                     goToFolder('/')\
  3385.                 end\
  3386.             end\
  3387.         },\
  3388.     }\
  3389.     local view = {\
  3390.         Children = children,\
  3391.         Width=35,\
  3392.         Height= Drawing.Screen.Height - 4,\
  3393.         OnCloseButton=function()\
  3394.             callback(false)\
  3395.         end\
  3396.     }\
  3397.     self:DisplayWindow(view, title)\
  3398. \
  3399.     self:SetActiveObject(self.Window:GetObject('FileNameTextBox'))\
  3400. \
  3401.     goToFolder = function(path)\
  3402.         path = Helpers.TidyPath(path)\
  3403.         currentFolder = path\
  3404.         selectedPath = nil\
  3405.         updatePath()\
  3406.     end\
  3407. \
  3408.     if startPath then\
  3409.         goToFolder(startPath)\
  3410.     elseif OneOS then\
  3411.         goToFolder('/Desktop/Documents/')\
  3412.     else\
  3413.         goToFolder('')\
  3414.     end\
  3415. \
  3416.     self.Window.OnCloseButton = function()callback(false)end\
  3417. end\
  3418. \
  3419. function RegisterEvent(self, event, func)\
  3420.     if not self.EventHandlers[event] then\
  3421.         self.EventHandlers[event] = {}\
  3422.     end\
  3423.     table.insert(self.EventHandlers[event], func)\
  3424. end\
  3425. \
  3426. function StartRepeatingTimer(self, func, interval)\
  3427.     local int = interval\
  3428.     if type(int) == 'function' then\
  3429.         int = int()\
  3430.     end\
  3431.     if not int or int <= 0 then\
  3432.         return\
  3433.     end\
  3434.     local timer = os.startTimer(int)\
  3435. \
  3436.     self.Timers[timer] = {func, true, interval}\
  3437.     return timer\
  3438. end\
  3439. \
  3440. function StartTimer(self, func, delay)\
  3441.     local timer = os.startTimer(delay)\
  3442.     self.Timers[timer] = {func, false}\
  3443.     return timer\
  3444. end\
  3445. \
  3446. function StopTimer(self, timer)\
  3447.     if self.Timers[timer] then\
  3448.         self.Timers[timer] = nil\
  3449.     end\
  3450. end\
  3451. \
  3452. function HandleTimer(self, event, timer)\
  3453.     if self.Timers[timer] then\
  3454.         local oldTimer = self.Timers[timer]\
  3455.         self.Timers[timer] = nil\
  3456.         local new = nil\
  3457.         if oldTimer[2] then\
  3458.             new = self:StartRepeatingTimer(oldTimer[1], oldTimer[3])\
  3459.         end\
  3460.         if oldTimer and oldTimer[1] then\
  3461.             oldTimer[1](new, timer)\
  3462.         end\
  3463.     elseif self.OnTimer then\
  3464.         self.OnTimer(self, event, timer)\
  3465.     end\
  3466. end\
  3467. \
  3468. function SetActiveObject(self, object)\
  3469.     if object then\
  3470.         if object ~= self.ActiveObject then\
  3471.             self.ActiveObject = object\
  3472.             object:ForceDraw()\
  3473.         end\
  3474.     elseif self.ActiveObject ~= nil then\
  3475.         self.ActiveObject = nil\
  3476.         self.CursorPos = nil\
  3477.         self.View:ForceDraw()\
  3478.     end\
  3479. end\
  3480. \
  3481. function GetActiveObject(self)\
  3482.     return self.ActiveObject\
  3483. end\
  3484. \
  3485. OnTimer = nil\
  3486. OnClick = nil\
  3487. OnKeyChar = nil\
  3488. OnDrag = nil\
  3489. OnScroll = nil\
  3490. OnViewLoad = nil\
  3491. OnViewClose = nil\
  3492. OnDraw = nil\
  3493. OnQuit = nil\
  3494. \
  3495. local eventFuncs = {\
  3496.     OnClick = {'mouse_click', 'monitor_touch'},\
  3497.     OnKeyChar = {'key', 'char'},\
  3498.     OnDrag = {'mouse_drag'},\
  3499.     OnScroll = {'mouse_scroll'},\
  3500.     HandleClick = {'mouse_click', 'mouse_drag', 'mouse_scroll', 'monitor_touch'},\
  3501.     HandleKeyChar = {'key', 'char'},\
  3502.     HandleTimer = {'timer'},\
  3503.     HandleDraw = {'bedrock_draw'}\
  3504. }\
  3505. \
  3506. local drawCalls = 0\
  3507. local ignored = 0\
  3508. function Draw(self)\
  3509.     self.IsDrawing = true\
  3510.     if self.OnDraw then\
  3511.         self:OnDraw()\
  3512.     end\
  3513. \
  3514.     if self.View and self.View:NeedsDraw() then\
  3515.         self.View:Draw()\
  3516.         Drawing.DrawBuffer()\
  3517.         if isDebug then\
  3518.             drawCalls = drawCalls + 1\
  3519.         end\
  3520.     elseif not self.View then\
  3521.         print('No loaded view. You need to do program:LoadView first.')\
  3522.     end \
  3523. \
  3524.     if self:GetActiveObject() and self.CursorPos and type(self.CursorPos[1]) == 'number' and type(self.CursorPos[2]) == 'number' then\
  3525.         term.setCursorPos(self.CursorPos[1], self.CursorPos[2])\
  3526.         term.setTextColour(self.CursorColour)\
  3527.         term.setCursorBlink(true)\
  3528.     else\
  3529.         term.setCursorBlink(false)\
  3530.     end\
  3531. \
  3532.     self.IsDrawing = false\
  3533. end\
  3534. \
  3535. function EventHandler(self)\
  3536.     local event = { os.pullEventRaw() }\
  3537.     \
  3538.     if self.EventHandlers[event[1]] then\
  3539.         for i, e in ipairs(self.EventHandlers[event[1]]) do\
  3540.             e(self, unpack(event))\
  3541.         end\
  3542.     end\
  3543. end\
  3544. \
  3545. function Quit(self)\
  3546.     self.Running = false\
  3547.     if self.OnQuit then\
  3548.         self:OnQuit()\
  3549.     end\
  3550.     if OneOS then\
  3551.         OneOS.Close()\
  3552.     end\
  3553. end\
  3554. \
  3555. function Run(self, ready)\
  3556.     if not  term.isColour or not term.isColour() then\
  3557.         print('This program requires an advanced (golden) comptuer to run, sorry.')\
  3558.         error('', 0)\
  3559.     end\
  3560. \
  3561.     for name, events in pairs(eventFuncs) do\
  3562.         if self[name] then\
  3563.             for i, event in ipairs(events) do\
  3564.                 self:RegisterEvent(event, self[name])\
  3565.             end\
  3566.         end\
  3567.     end\
  3568. \
  3569.     if self.AllowTerminate then\
  3570.         self:RegisterEvent('terminate', function()error('Terminated', 0) end)\
  3571.     end\
  3572. \
  3573.     if self.DefaultView and self.DefaultView ~= '' and fs.exists(self.ViewPath..self.DefaultView..'.view') then\
  3574.         self:LoadView(self.DefaultView)\
  3575.     end\
  3576. \
  3577.     if ready then\
  3578.         ready()\
  3579.     end\
  3580.     \
  3581.     self:Draw()\
  3582. \
  3583.     while self.Running do\
  3584.         self:EventHandler()\
  3585.     end\
  3586. end",
  3587.   APIs = {
  3588.     [ "JSON.lua" ] = "local base = _G\
  3589. \
  3590. -----------------------------------------------------------------------------\
  3591. -- Module declaration\
  3592. -----------------------------------------------------------------------------\
  3593. \
  3594. -- Public functions\
  3595. \
  3596. -- Private functions\
  3597. local decode_scanArray\
  3598. local decode_scanComment\
  3599. local decode_scanConstant\
  3600. local decode_scanNumber\
  3601. local decode_scanObject\
  3602. local decode_scanString\
  3603. local decode_scanWhitespace\
  3604. local encodeString\
  3605. local isArray\
  3606. local isEncodable\
  3607. \
  3608. -----------------------------------------------------------------------------\
  3609. -- PUBLIC FUNCTIONS\
  3610. -----------------------------------------------------------------------------\
  3611. --- Encodes an arbitrary Lua object / variable.\
  3612. -- @param v The Lua object / variable to be JSON encoded.\
  3613. -- @return String containing the JSON encoding in internal Lua string format (i.e. not unicode)\
  3614. function encode (v)\
  3615.  -- Handle nil values\
  3616.  if v==nil then\
  3617.    return \"null\"\
  3618.  end\
  3619. \
  3620.  local vtype = base.type(v)  \
  3621. \
  3622.  -- Handle strings\
  3623.  if vtype=='string' then    \
  3624.    return '\"' .. encodeString(v) .. '\"'      -- Need to handle encoding in string\
  3625.  end\
  3626. \
  3627.  -- Handle booleans\
  3628.  if vtype=='number' or vtype=='boolean' then\
  3629.    return base.tostring(v)\
  3630.  end\
  3631. \
  3632.  -- Handle tables\
  3633.  if vtype=='table' then\
  3634.    local rval = {}\
  3635.    -- Consider arrays separately\
  3636.    local bArray, maxCount = isArray(v)\
  3637.    if bArray then\
  3638.      for i = 1,maxCount do\
  3639.        table.insert(rval, encode(v[i]))\
  3640.      end\
  3641.    else -- An object, not an array\
  3642.      for i,j in base.pairs(v) do\
  3643.        if isEncodable(i) and isEncodable(j) then\
  3644.          table.insert(rval, '\"' .. encodeString(i) .. '\":' .. encode(j))\
  3645.        end\
  3646.      end\
  3647.    end\
  3648.    if bArray then\
  3649.      return '[' .. table.concat(rval,',') ..']'\
  3650.    else\
  3651.      return '{' .. table.concat(rval,',') .. '}'\
  3652.    end\
  3653.  end\
  3654. \
  3655.  -- Handle null values\
  3656.  if vtype=='function' and v==null then\
  3657.    return 'null'\
  3658.  end\
  3659. \
  3660.  base.assert(false,'encode attempt to encode unsupported type ' .. vtype .. ':' .. base.tostring(v))\
  3661. end\
  3662. \
  3663. \
  3664. --- Decodes a JSON string and returns the decoded value as a Lua data structure / value.\
  3665. -- @param s The string to scan.\
  3666. -- @param [startPos] Optional starting position where the JSON string is located. Defaults to 1.\
  3667. -- @param Lua object, number The object that was scanned, as a Lua table / string / number / boolean or nil,\
  3668. -- and the position of the first character after\
  3669. -- the scanned JSON object.\
  3670. function decode(s, startPos)\
  3671.  startPos = startPos and startPos or 1\
  3672.  startPos = decode_scanWhitespace(s,startPos)\
  3673.  base.assert(startPos<=string.len(s), 'Unterminated JSON encoded object found at position in [' .. s .. ']')\
  3674.  local curChar = string.sub(s,startPos,startPos)\
  3675.  -- Object\
  3676.  if curChar=='{' then\
  3677.    return decode_scanObject(s,startPos)\
  3678.  end\
  3679.  -- Array\
  3680.  if curChar=='[' then\
  3681.    return decode_scanArray(s,startPos)\
  3682.  end\
  3683.  -- Number\
  3684.  if string.find(\"+-0123456789.e\", curChar, 1, true) then\
  3685.    return decode_scanNumber(s,startPos)\
  3686.  end\
  3687.  -- String\
  3688.  if curChar=='\"' or curChar==\"'\" then\
  3689.    return decode_scanString(s,startPos)\
  3690.  end\
  3691.  if string.sub(s,startPos,startPos+1)=='/*' then\
  3692.    return decode(s, decode_scanComment(s,startPos))\
  3693.  end\
  3694.  -- Otherwise, it must be a constant\
  3695.  return decode_scanConstant(s,startPos)\
  3696. end\
  3697. \
  3698. --- The null function allows one to specify a null value in an associative array (which is otherwise\
  3699. -- discarded if you set the value with 'nil' in Lua. Simply set t = { first=json.null }\
  3700. function null()\
  3701.  return null -- so json.null() will also return null ;-)\
  3702. end\
  3703. -----------------------------------------------------------------------------\
  3704. -- Internal, PRIVATE functions.\
  3705. -- Following a Python-like convention, I have prefixed all these 'PRIVATE'\
  3706. -- functions with an underscore.\
  3707. -----------------------------------------------------------------------------\
  3708. \
  3709. --- Scans an array from JSON into a Lua object\
  3710. -- startPos begins at the start of the array.\
  3711. -- Returns the array and the next starting position\
  3712. -- @param s The string being scanned.\
  3713. -- @param startPos The starting position for the scan.\
  3714. -- @return table, int The scanned array as a table, and the position of the next character to scan.\
  3715. function decode_scanArray(s,startPos)\
  3716.  local array = {}   -- The return value\
  3717.  local stringLen = string.len(s)\
  3718.  base.assert(string.sub(s,startPos,startPos)=='[','decode_scanArray called but array does not start at position ' .. startPos .. ' in string:\\n'..s )\
  3719.  startPos = startPos + 1\
  3720.  -- Infinite loop for array elements\
  3721.  repeat\
  3722.    startPos = decode_scanWhitespace(s,startPos)\
  3723.    base.assert(startPos<=stringLen,'JSON String ended unexpectedly scanning array.')\
  3724.    local curChar = string.sub(s,startPos,startPos)\
  3725.    if (curChar==']') then\
  3726.      return array, startPos+1\
  3727.    end\
  3728.    if (curChar==',') then\
  3729.      startPos = decode_scanWhitespace(s,startPos+1)\
  3730.    end\
  3731.    base.assert(startPos<=stringLen, 'JSON String ended unexpectedly scanning array.')\
  3732.    object, startPos = decode(s,startPos)\
  3733.    table.insert(array,object)\
  3734.  until false\
  3735. end\
  3736. \
  3737. --- Scans a comment and discards the comment.\
  3738. -- Returns the position of the next character following the comment.\
  3739. -- @param string s The JSON string to scan.\
  3740. -- @param int startPos The starting position of the comment\
  3741. function decode_scanComment(s, startPos)\
  3742.  base.assert( string.sub(s,startPos,startPos+1)=='/*', \"decode_scanComment called but comment does not start at position \" .. startPos)\
  3743.  local endPos = string.find(s,'*/',startPos+2)\
  3744.  base.assert(endPos~=nil, \"Unterminated comment in string at \" .. startPos)\
  3745.  return endPos+2  \
  3746. end\
  3747. \
  3748. --- Scans for given constants: true, false or null\
  3749. -- Returns the appropriate Lua type, and the position of the next character to read.\
  3750. -- @param s The string being scanned.\
  3751. -- @param startPos The position in the string at which to start scanning.\
  3752. -- @return object, int The object (true, false or nil) and the position at which the next character should be\
  3753. -- scanned.\
  3754. function decode_scanConstant(s, startPos)\
  3755.  local consts = { [\"true\"] = true, [\"false\"] = false, [\"null\"] = nil }\
  3756.  local constNames = {\"true\",\"false\",\"null\"}\
  3757. \
  3758.  for i,k in base.pairs(constNames) do\
  3759.    --print (\"[\" .. string.sub(s,startPos, startPos + string.len(k) -1) ..\"]\", k)\
  3760.    if string.sub(s,startPos, startPos + string.len(k) -1 )==k then\
  3761.      return consts[k], startPos + string.len(k)\
  3762.    end\
  3763.  end\
  3764.  base.assert(nil, 'Failed to scan constant from string ' .. s .. ' at starting position ' .. startPos)\
  3765. end\
  3766. \
  3767. --- Scans a number from the JSON encoded string.\
  3768. -- (in fact, also is able to scan numeric +- eqns, which is not\
  3769. -- in the JSON spec.)\
  3770. -- Returns the number, and the position of the next character\
  3771. -- after the number.\
  3772. -- @param s The string being scanned.\
  3773. -- @param startPos The position at which to start scanning.\
  3774. -- @return number, int The extracted number and the position of the next character to scan.\
  3775. function decode_scanNumber(s,startPos)\
  3776.  local endPos = startPos+1\
  3777.  local stringLen = string.len(s)\
  3778.  local acceptableChars = \"+-0123456789.e\"\
  3779.  while (string.find(acceptableChars, string.sub(s,endPos,endPos), 1, true)\
  3780.   and endPos<=stringLen\
  3781.   ) do\
  3782.    endPos = endPos + 1\
  3783.  end\
  3784.  local stringValue = 'return ' .. string.sub(s,startPos, endPos-1)\
  3785.  local stringEval = base.loadstring(stringValue)\
  3786.  base.assert(stringEval, 'Failed to scan number [ ' .. stringValue .. '] in JSON string at position ' .. startPos .. ' : ' .. endPos)\
  3787.  return stringEval(), endPos\
  3788. end\
  3789. \
  3790. --- Scans a JSON object into a Lua object.\
  3791. -- startPos begins at the start of the object.\
  3792. -- Returns the object and the next starting position.\
  3793. -- @param s The string being scanned.\
  3794. -- @param startPos The starting position of the scan.\
  3795. -- @return table, int The scanned object as a table and the position of the next character to scan.\
  3796. function decode_scanObject(s,startPos)\
  3797.  local object = {}\
  3798.  local stringLen = string.len(s)\
  3799.  local key, value\
  3800.  base.assert(string.sub(s,startPos,startPos)=='{','decode_scanObject called but object does not start at position ' .. startPos .. ' in string:\\n' .. s)\
  3801.  startPos = startPos + 1\
  3802.  repeat\
  3803.    startPos = decode_scanWhitespace(s,startPos)\
  3804.    base.assert(startPos<=stringLen, 'JSON string ended unexpectedly while scanning object.')\
  3805.    local curChar = string.sub(s,startPos,startPos)\
  3806.    if (curChar=='}') then\
  3807.      return object,startPos+1\
  3808.    end\
  3809.    if (curChar==',') then\
  3810.      startPos = decode_scanWhitespace(s,startPos+1)\
  3811.    end\
  3812.    base.assert(startPos<=stringLen, 'JSON string ended unexpectedly scanning object.')\
  3813.    -- Scan the key\
  3814.    key, startPos = decode(s,startPos)\
  3815.    base.assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key)\
  3816.    startPos = decode_scanWhitespace(s,startPos)\
  3817.    base.assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key)\
  3818.    base.assert(string.sub(s,startPos,startPos)==':','JSON object key-value assignment mal-formed at ' .. startPos)\
  3819.    startPos = decode_scanWhitespace(s,startPos+1)\
  3820.    base.assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key)\
  3821.    value, startPos = decode(s,startPos)\
  3822.    object[key]=value\
  3823.  until false  -- infinite loop while key-value pairs are found\
  3824. end\
  3825. \
  3826. --- Scans a JSON string from the opening inverted comma or single quote to the\
  3827. -- end of the string.\
  3828. -- Returns the string extracted as a Lua string,\
  3829. -- and the position of the next non-string character\
  3830. -- (after the closing inverted comma or single quote).\
  3831. -- @param s The string being scanned.\
  3832. -- @param startPos The starting position of the scan.\
  3833. -- @return string, int The extracted string as a Lua string, and the next character to parse.\
  3834. function decode_scanString(s,startPos)\
  3835.  base.assert(startPos, 'decode_scanString(..) called without start position')\
  3836.  local startChar = string.sub(s,startPos,startPos)\
  3837.  base.assert(startChar==\"'\" or startChar=='\"','decode_scanString called for a non-string')\
  3838.  local escaped = false\
  3839.  local endPos = startPos + 1\
  3840.  local bEnded = false\
  3841.  local stringLen = string.len(s)\
  3842.  repeat\
  3843.    local curChar = string.sub(s,endPos,endPos)\
  3844.    -- Character escaping is only used to escape the string delimiters\
  3845.    if not escaped then\
  3846.      if curChar=='\\\\' then\
  3847.        escaped = true\
  3848.      else\
  3849.        bEnded = curChar==startChar\
  3850.      end\
  3851.    else\
  3852.      -- If we're escaped, we accept the current character come what may\
  3853.      escaped = false\
  3854.    end\
  3855.    endPos = endPos + 1\
  3856.    base.assert(endPos <= stringLen+1, \"String decoding failed: unterminated string at position \" .. endPos)\
  3857.  until bEnded\
  3858.  local stringValue = 'return ' .. string.sub(s, startPos, endPos-1)\
  3859.  local stringEval = base.loadstring(stringValue)\
  3860.  base.assert(stringEval, 'Failed to load string [ ' .. stringValue .. '] in JSON4Lua.decode_scanString at position ' .. startPos .. ' : ' .. endPos)\
  3861.  return stringEval(), endPos  \
  3862. end\
  3863. \
  3864. --- Scans a JSON string skipping all whitespace from the current start position.\
  3865. -- Returns the position of the first non-whitespace character, or nil if the whole end of string is reached.\
  3866. -- @param s The string being scanned\
  3867. -- @param startPos The starting position where we should begin removing whitespace.\
  3868. -- @return int The first position where non-whitespace was encountered, or string.len(s)+1 if the end of string\
  3869. -- was reached.\
  3870. function decode_scanWhitespace(s,startPos)\
  3871.  local whitespace=\" \\n\\r\\t\"\
  3872.  local stringLen = string.len(s)\
  3873.  while ( string.find(whitespace, string.sub(s,startPos,startPos), 1, true)  and startPos <= stringLen) do\
  3874.    startPos = startPos + 1\
  3875.  end\
  3876.  return startPos\
  3877. end\
  3878. \
  3879. --- Encodes a string to be JSON-compatible.\
  3880. -- This just involves back-quoting inverted commas, back-quotes and newlines, I think ;-)\
  3881. -- @param s The string to return as a JSON encoded (i.e. backquoted string)\
  3882. -- @return The string appropriately escaped.\
  3883. function encodeString(s)\
  3884.  s = string.gsub(s,'\\\\','\\\\\\\\')\
  3885.  s = string.gsub(s,'\"','\\\\\"')\
  3886.  s = string.gsub(s,\"'\",\"\\\\'\")\
  3887.  s = string.gsub(s,'\\n','\\\\n')\
  3888.  s = string.gsub(s,'\\t','\\\\t')\
  3889.  return s\
  3890. end\
  3891. \
  3892. -- Determines whether the given Lua type is an array or a table / dictionary.\
  3893. -- We consider any table an array if it has indexes 1..n for its n items, and no\
  3894. -- other data in the table.\
  3895. -- I think this method is currently a little 'flaky', but can't think of a good way around it yet...\
  3896. -- @param t The table to evaluate as an array\
  3897. -- @return boolean, number True if the table can be represented as an array, false otherwise. If true,\
  3898. -- the second returned value is the maximum\
  3899. -- number of indexed elements in the array.\
  3900. function isArray(t)\
  3901.  -- Next we count all the elements, ensuring that any non-indexed elements are not-encodable\
  3902.  -- (with the possible exception of 'n')\
  3903.  local maxIndex = 0\
  3904.  for k,v in base.pairs(t) do\
  3905.    if (base.type(k)=='number' and math.floor(k)==k and 1<=k) then   -- k,v is an indexed pair\
  3906.      if (not isEncodable(v)) then return false end   -- All array elements must be encodable\
  3907.      maxIndex = math.max(maxIndex,k)\
  3908.    else\
  3909.      if (k=='n') then\
  3910.        if v ~= table.getn(t) then return false end  -- False if n does not hold the number of elements\
  3911.      else -- Else of (k=='n')\
  3912.        if isEncodable(v) then return false end\
  3913.      end  -- End of (k~='n')\
  3914.    end -- End of k,v not an indexed pair\
  3915.  end  -- End of loop across all pairs\
  3916.  return true, maxIndex\
  3917. end\
  3918. \
  3919. --- Determines whether the given Lua object / table / variable can be JSON encoded. The only\
  3920. -- types that are JSON encodable are: string, boolean, number, nil, table and json.null.\
  3921. -- In this implementation, all other types are ignored.\
  3922. -- @param o The object to examine.\
  3923. -- @return boolean True if the object should be JSON encoded, false if it should be ignored.\
  3924. function isEncodable(o)\
  3925.  local t = base.type(o)\
  3926.  return (t=='string' or t=='boolean' or t=='number' or t=='nil' or t=='table') or (t=='function' and o==null)\
  3927. end",
  3928.     [ "App.lua" ] = "ID = nil\
  3929. Author = nil\
  3930. Category = nil\
  3931. Name = nil\
  3932. Description = nil\
  3933. Icon = nil\
  3934. \
  3935. Bedrock = nil\
  3936. \
  3937. local function parseNFT(lines)\
  3938.     local image = {\
  3939.         text = {},\
  3940.         textcol = {}\
  3941.     }\
  3942.     for y, line in ipairs(lines) do\
  3943.        table.insert(image, y, {})\
  3944.        table.insert(image.text, y, {})\
  3945.        table.insert(image.textcol, y, {})\
  3946. \
  3947.         local bgNext, fgNext = false, false\
  3948.         local currBG, currFG = nil,nil\
  3949.         local writePosition = 1\
  3950.         for x = 1, #line do\
  3951. \
  3952.             local nextChar = string.sub(line, x, x)\
  3953.            if nextChar:byte() == 30 then\
  3954.                bgNext = true\
  3955.            elseif nextChar:byte() == 31 then\
  3956.                fgNext = true\
  3957.            elseif bgNext then\
  3958.                currBG = Drawing.GetColour(nextChar)\
  3959.                if currBG == nil then\
  3960.                     currBG = colours.transparent\
  3961.                end\
  3962.                bgNext = false\
  3963.            elseif fgNext then\
  3964.                currFG = Drawing.GetColour(nextChar)\
  3965.                fgNext = false\
  3966.            else\
  3967.                if nextChar ~= \" \" and currFG == nil then\
  3968.                        currFG = colours.white\
  3969.                end\
  3970.                 image[y][writePosition] = currBG\
  3971.                image.textcol[y][writePosition] = currFG\
  3972.                image.text[y][writePosition] = nextChar\
  3973.                writePosition = writePosition + 1\
  3974.            end\
  3975.         end\
  3976.     end\
  3977.     return image\
  3978. end\
  3979. \
  3980. local function split(a,e)\
  3981.     local t,e=e or\":\",{}\
  3982.     local t=string.format(\"([^%s]+)\",t)\
  3983.     a:gsub(t,function(t)e[#e+1]=t end)\
  3984.     return e\
  3985. end\
  3986. \
  3987. Initialise = function(self, data, bedrock)\
  3988.     local new = {}    -- the new instance\
  3989.     setmetatable(new, {__index = self} )\
  3990. \
  3991.     new.ID = data.id\
  3992.     new.Author = tostring(data.user.username)\
  3993.     new.Category = tostring(data.category)\
  3994.     new.Name = tostring(data.name)\
  3995.     new.Description = tostring(data.description)\
  3996.     new.Icon = parseNFT(split(data.icon, '\\n'))\
  3997. \
  3998.     new.Bedrock = bedrock\
  3999. \
  4000.     return new\
  4001. end\
  4002. \
  4003. Install = function(self)\
  4004.     self.Bedrock:OpenPage('InstallPageView', {\
  4005.         App = self\
  4006.     })\
  4007. end\
  4008. \
  4009. InstallData = function(self, data)\
  4010.     if type(data) ~= 'string' or #data == 0 then\
  4011.         return false, 'EMPTY'\
  4012.     end\
  4013. \
  4014.     local pack = JSON.decode(data)\
  4015. \
  4016.     if not pack then\
  4017.         return false, 'PACKAGE'\
  4018.     end\
  4019. \
  4020.     local _fs = fs\
  4021.     if OneOS then\
  4022.         _fs = OneOS.FS\
  4023.     end\
  4024.     local function makeFile(_path,_content)\
  4025.         local file=_fs.open(_path,\"w\")\
  4026.         if file then\
  4027.             file.write(_content)\
  4028.             file.close()\
  4029.         else\
  4030.             error(_path)\
  4031.         end\
  4032.     end\
  4033.     local function makeFolder(_path,_content)\
  4034.         _fs.makeDir(_path)\
  4035.         for k,v in pairs(_content) do\
  4036.             if type(v)==\"table\" then\
  4037.                 makeFolder(_path..\"/\"..k,v)\
  4038.             else\
  4039.                 makeFile(_path..\"/\"..k,v)\
  4040.             end\
  4041.         end\
  4042.     end\
  4043. \
  4044.     local installPath = '/'\
  4045.     local removeSpaces = true\
  4046.     local alwaysFolder = false\
  4047.     local fullPath = false\
  4048. \
  4049.     if OneOS then\
  4050.         installPath = '/Programs/' .. self.Name .. '.program/'\
  4051.         removeSpaces = false\
  4052.         alwaysFolder = true\
  4053.         fullPath = true\
  4054. \
  4055.         -- TODO: app icon\
  4056.     end\
  4057. \
  4058.     local appName = self.Name\
  4059.     local keyCount = 0\
  4060.     for k, v in pairs(pack) do\
  4061.         keyCount = keyCount + 1\
  4062.     end\
  4063.     if removeSpaces then\
  4064.         appName = appName:gsub(\" \", \"\")\
  4065.     end\
  4066.     local location = installPath .. '/'\
  4067. \
  4068.     if not fullPath then\
  4069.         location = location .. appName\
  4070.     end\
  4071.     if keyCount == 1 and not alwaysFolder then\
  4072.         makeFile(location, pack['startup'])\
  4073.     else\
  4074.         makeFolder(location, pack)\
  4075.         location = location .. '/startup'\
  4076.     end\
  4077. \
  4078.     return self.Bedrock.Helpers.TidyPath(location)\
  4079. end",
  4080.   },
  4081.   Views = {
  4082.     [ "main.view" ] = "{\
  4083.     Children = {\
  4084.         {\
  4085.             X = '50%,-13',\
  4086.             Y = 1,\
  4087.             Text = 'Home',\
  4088.             TextColour = 'grey',\
  4089.             Name = 'HomeLabel',\
  4090.             Type = 'Label'\
  4091.         },\
  4092.         {\
  4093.             X = '50%,-5',\
  4094.             Y = 1,\
  4095.             Text = 'Categories',\
  4096.             TextColour = 'grey',\
  4097.             Name = 'CategoriesLabel',\
  4098.             Type = 'Label'\
  4099.         },\
  4100.         {\
  4101.             X = '50%,9',\
  4102.             Y = 1,\
  4103.             Text = 'Search',\
  4104.             TextColour = 'grey',\
  4105.             Name = 'SearchLabel',\
  4106.             Type = 'Label'\
  4107.         },\
  4108.         {\
  4109.             X = 1,\
  4110.             Y = 2,\
  4111.             Type = 'Separator',\
  4112.             Width = '100%',\
  4113.             Colour = 'lightGrey'\
  4114.         },\
  4115. \
  4116.         {\
  4117.             X = 1,\
  4118.             Y = 3,\
  4119.             Width = '100%',\
  4120.             Height = '100%,-2',\
  4121.             Type = 'ScrollView',\
  4122.             Name = 'ContentScrollView',\
  4123.             Children = {\
  4124.                 {\
  4125.                     X = 1,\
  4126.                     Y = 1,\
  4127.                     Type = 'HomePageView',\
  4128.                     Name = 'CurrentPage',\
  4129.                     Width = '100%',\
  4130.                     Height = '100%'\
  4131.                 }\
  4132.             }\
  4133.         },\
  4134. \
  4135.         {\
  4136.             X = '100%',\
  4137.             Y = 1,\
  4138.             Text = 'x',\
  4139.             TextColour = 'blue',\
  4140.             Name = 'QuitLabel',\
  4141.             Type = 'Label'\
  4142.         },\
  4143. \
  4144.         \
  4145.     },\
  4146.     BackgroundColour = 'white'\
  4147. }",
  4148.   },
  4149.   appstore = "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\
  4150. \
  4151. \
  4152. local program = Bedrock:Initialise()\
  4153. \
  4154. program.AppStoreURL = 'http://appstore.olivercooper.me/'\
  4155. \
  4156. program:RegisterKeyboardShortcut({'\\\\'}, function()os.reboot()end)\
  4157. \
  4158. program.OnKeyChar = function(self, event, keychar)\
  4159.     if (keychar == keys.up or keychar == keys.down) then --and not program:GetActiveObject() then\
  4160.         local currentPage = self:GetObject('CurrentPage')\
  4161.         if currentPage then\
  4162.             currentPage:OnScroll('mouse_scroll', (keychar == keys.up) and -1 or 1, 1, 1)\
  4163.         end\
  4164.     end\
  4165. end\
  4166. \
  4167. program.OpenPage = function(self, pageType, values)\
  4168.     program:SetActiveObject()\
  4169. \
  4170.     local currentPage = self:GetObject('CurrentPage')\
  4171.     local previousPage = self:GetObject('PreviousPage')\
  4172. \
  4173.     if currentPage then\
  4174.         currentPage.Name = 'PreviousPage'\
  4175.         currentPage.Visible = false\
  4176.     end\
  4177. \
  4178.     if previousPage then\
  4179.         self:RemoveObject(previousPage)\
  4180.     end\
  4181. \
  4182.     if not values then\
  4183.         values = {}\
  4184.     end\
  4185.     values.Type = pageType\
  4186.     values.Name = 'CurrentPage'\
  4187.     values.X = 1\
  4188.     values.Y = 1\
  4189.     values.Width = '100%'\
  4190.     values.Height = '100%'\
  4191.     self:GetObject('ContentScrollView'):AddObject(values)\
  4192. end\
  4193. \
  4194. program.OpenPreviousPage = function(self)\
  4195.     program:SetActiveObject()\
  4196.     \
  4197.     local currentPage = self:GetObject('CurrentPage')\
  4198.     local previousPage = self:GetObject('PreviousPage')\
  4199.     if previousPage then\
  4200.         previousPage.Name = 'CurrentPage'\
  4201.         previousPage.Visible = true\
  4202.         \
  4203.         if currentPage then\
  4204.             self:RemoveObject(currentPage)\
  4205.         end\
  4206.     else\
  4207.         self:OpenPage('HomePageView')\
  4208.     end\
  4209. end\
  4210. \
  4211. program:Run(function()\
  4212.     program:RegisterEvent('http_success', function(self, event, url, h)\
  4213.         local currentPage = self:GetObject('CurrentPage')\
  4214.         if currentPage and not currentPage.Failed and not currentPage.Success and currentPage.RequestURL == url then\
  4215.             currentPage.Success = true\
  4216.             currentPage:OnDataLoad(url, h.readAll())\
  4217.             h.close()\
  4218.         end\
  4219.     end)\
  4220. \
  4221.     program:RegisterEvent('http_failure', function(self, event, url)\
  4222.         local currentPage = self:GetObject('CurrentPage')\
  4223.         if currentPage and not currentPage.Failed and not currentPage.Success and currentPage.RequestURL == url then\
  4224.             currentPage.Failed = true\
  4225.             currentPage:OnDataFailed(url, 'GENERIC')\
  4226.         end\
  4227.     end)\
  4228. \
  4229.     program:GetObject('HomeLabel').OnClick = function(self)\
  4230.         program:OpenPage('HomePageView')\
  4231.     end\
  4232. \
  4233.     program:GetObject('CategoriesLabel').OnClick = function(self)\
  4234.         program:OpenPage('CategoriesPageView')\
  4235.     end\
  4236. \
  4237.     program:GetObject('SearchLabel').OnClick = function(self)\
  4238.         program:OpenPage('SearchPageView')\
  4239.     end\
  4240. \
  4241.     if OneOS then\
  4242.         program:GetObject('QuitLabel').Visible = false\
  4243.     else\
  4244.         program:GetObject('QuitLabel').OnClick = function()\
  4245.             term.setCursorPos(1, 1)\
  4246.             term.setBackgroundColour(colors.black)\
  4247.             term.setTextColor(colours.white)\
  4248.             term.clear()\
  4249.             print('Thanks for using the App Store!')\
  4250.             print('(c) 2013 - 2015 oeed')\
  4251.             program:Quit()\
  4252.         end\
  4253.     end\
  4254. \
  4255. end)",
  4256.   Objects = {
  4257.     [ "InstallPageView.lua" ] = "Inherit = 'PageView'\
  4258. \
  4259. TimeoutLength = 40\
  4260. \
  4261. OnLoad = function(self)\
  4262.     self:RemoveAllObjects()\
  4263.     ScrollView.OnLoad(self)\
  4264.     self:AddObject({\
  4265.         X = '50%,-4',\
  4266.         Y = '50%,-3',\
  4267.         Type = 'LoadingIndicator',\
  4268.     })\
  4269.     self:AddObject({\
  4270.         X = 1,\
  4271.         Y = '50%,1',\
  4272.         Width = '100%',\
  4273.         Align = 'Center',\
  4274.         Type = 'Label',\
  4275.         TextColour = 'blue',\
  4276.         Text = 'Downloading ' .. self.App.Name\
  4277.     })\
  4278.     self:FetchData()\
  4279. end\
  4280. \
  4281. OnDataLoad = function(self, url, data)\
  4282.     local location, err = self.App:InstallData(data)\
  4283.     if err then\
  4284.         self.Success = false\
  4285.         self.Failed = true\
  4286.         self:OnDataFailed(nil, err)\
  4287.         return\
  4288.     end\
  4289. \
  4290.     self:RemoveAllObjects()\
  4291. \
  4292.     self:AddObject({\
  4293.         X = 1,\
  4294.         Y = '50%,-1',\
  4295.         Width = '100%',\
  4296.         Align = 'Center',\
  4297.         Type = 'Label',\
  4298.         TextColour = 'blue',\
  4299.         Text = 'Installation Successful'\
  4300.     })\
  4301. \
  4302.     self:AddObject({\
  4303.         X = 1,\
  4304.         Y = '50%,0',\
  4305.         Width = '100%',\
  4306.         Align = 'Center',\
  4307.         Type = 'Label',\
  4308.         TextColour = 'lightBlue',\
  4309.         Text = location\
  4310.     })\
  4311. \
  4312.     self:AddObject({\
  4313.         X = 1,\
  4314.         Y = '50%,2',\
  4315.         X = '50%,-4',\
  4316.         Type = 'Button',\
  4317.         TextColour = 'white',\
  4318.         BackgroundColour = 'blue',\
  4319.         Text = 'Okay',\
  4320.         OnClick = function()\
  4321.             self.Bedrock:OpenPreviousPage()\
  4322.         end\
  4323.     })\
  4324. end\
  4325. \
  4326. DataURL = function(self, info)\
  4327.     return self.Bedrock.AppStoreURL .. 'api/?command=application&subcommand=download&id=' .. textutils.urlEncode(self.App.ID)\
  4328. end",
  4329.     [ "LoadingIndicator.lua" ] = "Width = 7\
  4330. Height = 5\
  4331. \
  4332. Step = 1\
  4333. \
  4334. UpdateDrawBlacklist = {\
  4335.     ['Step'] = true,\
  4336. }\
  4337. \
  4338. OnDraw = function(self, x, y)\
  4339.     local reverseStep = 9\
  4340.     local resetStep = 18\
  4341.     if self.Step > resetStep then\
  4342.         self.Step = 0\
  4343.     end\
  4344. \
  4345.     self.Step = self.Step + 1\
  4346. \
  4347.     for i = 0, 3 do\
  4348.         local _y = math.min(math.max(0, self.Step - 2 * i), 3)\
  4349.         if self.Step > reverseStep then\
  4350.             _y = 3 - math.min(math.max(0, self.Step - reverseStep - 2 * i), 3)\
  4351.         end\
  4352. \
  4353.         local colour = (_y == 3 and colours.blue or (_y == 0 and colours.white or colours.lightBlue))\
  4354.         Drawing.WriteToBuffer(x + i * 2, y + _y - 1, ' ', colours.black, colour)\
  4355.     end\
  4356. \
  4357.     Drawing.DrawCharacters(x, y + 4, 'Loading', colours.blue, colours.transparent)\
  4358. \
  4359.     self.Bedrock:StartTimer(function()\
  4360.         self:ForceDraw()\
  4361.     end, 0.05)\
  4362. end",
  4363.     [ "AppView.lua" ] = "Inherit = 'View'\
  4364. \
  4365. Width = 24\
  4366. Height = 4\
  4367. App = nil\
  4368. \
  4369. OnLoad = function(self)\
  4370.     self:AddObject({\
  4371.         X = 1,\
  4372.         Y = 1,\
  4373.         Width = 4,\
  4374.         Height = 3,\
  4375.         Type = 'ImageView',\
  4376.         -- Path = '/Resources/Ink'\
  4377.     })\
  4378. \
  4379.     self:AddObject({\
  4380.         X = 6,\
  4381.         Y = 1,\
  4382.         Type = 'Label',\
  4383.         Name = 'NameLabel',\
  4384.         Text = ''\
  4385.     })\
  4386. \
  4387.     self:AddObject({\
  4388.         X = 6,\
  4389.         Y = 2,\
  4390.         Type = 'Label',\
  4391.         Name = 'AuthorLabel',\
  4392.         Text = '',\
  4393.         TextColour = 'lightGrey'\
  4394.     })\
  4395. \
  4396.     self:AddObject({\
  4397.         X = 6,\
  4398.         Y = 3,\
  4399.         Type = 'Label',\
  4400.         -- Name = 'AuthorLabel',\
  4401.         Text = 'Install >',\
  4402.         TextColour = 'blue',\
  4403.         OnClick = function()\
  4404.             if self.App then\
  4405.                 self.App:Install()\
  4406.             end\
  4407.         end\
  4408.     })\
  4409. \
  4410.     self:OnUpdate('App')\
  4411. end\
  4412. \
  4413. \
  4414. \
  4415. OnUpdate = function(self, value)\
  4416.     if value == 'App' then\
  4417.         self:GetObject('NameLabel').Text = self.Bedrock.Helpers.TruncateString(self.App.Name, self.Width - 6)\
  4418.         self:GetObject('AuthorLabel').Text = self.Bedrock.Helpers.TruncateString(self.App.Author, self.Width - 6)\
  4419.         self:GetObject('ImageView').Image = self.App.Icon\
  4420.     end\
  4421. end",
  4422.     [ "SearchPageView.lua" ] = "Inherit = 'PageView'\
  4423. \
  4424. OnLoad = function(self)\
  4425.     ScrollView.OnLoad(self)\
  4426. \
  4427.     self:AddObject({\
  4428.         X = 1,\
  4429.         Y = '50%,-3',\
  4430.         Width = '100%',\
  4431.         Align = 'Center',\
  4432.         TextColour = 'grey',\
  4433.         Type = 'Label',\
  4434.         Text = 'Search'\
  4435.     })\
  4436. \
  4437.     self:AddObject({\
  4438.         X = 1,\
  4439.         Y = '50%,-2',\
  4440.         Width = '100%',\
  4441.         Align = 'Center',\
  4442.         TextColour = 'lightGrey',\
  4443.         Type = 'Label',\
  4444.         Text = 'Find that app you\\'re looking for'\
  4445.     })\
  4446. \
  4447.     self:AddObject({\
  4448.         X = '50%,-12',\
  4449.         Y = '50%',\
  4450.         Width = 21,\
  4451.         Type = 'TextBox',\
  4452.         Active = true,\
  4453.         Placeholder = 'Search...',\
  4454.         Name = 'SearchBox',\
  4455.         OnChange = function(_, event, keychar)\
  4456.             if keychar == keys.enter then\
  4457.                 self:GetObject('SearchButton'):OnClick()\
  4458.             end\
  4459.         end\
  4460.     })\
  4461. \
  4462.     self:AddObject({\
  4463.         X = '50%,10',\
  4464.         Y = '50%',\
  4465.         Type = 'Button',\
  4466.         BackgroundColour = 'blue',\
  4467.         TextColour = 'white',\
  4468.         Text = 'Go',\
  4469.         Name = 'SearchButton',\
  4470.         OnClick = function()\
  4471.             local text = self:GetObject('SearchBox').Text\
  4472. \
  4473.             if text ~= '' then\
  4474.                 self.Bedrock:OpenPage('SearchResultsPageView', {SearchTerm = text})\
  4475.             end\
  4476.         end\
  4477.     })\
  4478. end",
  4479.     [ "PageView.lua" ] = "Inherit = 'ScrollView'\
  4480. \
  4481. PageInfo = nil\
  4482. Failed = false\
  4483. Success = false\
  4484. RequestURL = nil\
  4485. TimeoutTimer = nil\
  4486. TimeoutLength = 5\
  4487. LoadingText = nil\
  4488. \
  4489. OnLoad = function(self)\
  4490.     self.Failed = false\
  4491.     self.Success = false\
  4492.     self:RemoveAllObjects()\
  4493.     ScrollView.OnLoad(self)\
  4494.     self:AddObject({\
  4495.         X = '50%,-4',\
  4496.         Y = '50%,-3',\
  4497.         Type = 'LoadingIndicator',\
  4498.     })\
  4499.     self:FetchData()\
  4500. end\
  4501. \
  4502. FetchData = function(self)\
  4503.     local url = self:DataURL(self.PageInfo)\
  4504.     self.RequestURL = url\
  4505.     local reason\
  4506. \
  4507.     if not http then\
  4508.         reason = 'NOHTTP'\
  4509.     elseif http.checkURL and not http.checkURL(url) then\
  4510.         reason = 'BLOCKED'\
  4511.     end\
  4512. \
  4513.     if not reason then\
  4514.         local ok = http.request(url)\
  4515.         if ok then\
  4516.             self.TimeoutTimer = self.Bedrock:StartTimer(function()\
  4517.                 if not self.Failed and not self.Success then\
  4518.                     self.Failed = true\
  4519.                     self:OnDataFailed(url, 'TIMEOUT')\
  4520.                 end\
  4521.             end, self.TimeoutLength)\
  4522.         else\
  4523.             reason = 'REQUEST'\
  4524.         end\
  4525.     end\
  4526. \
  4527.     if reason then\
  4528.         self.Failed = true\
  4529.         self:OnDataFailed(url, reason)\
  4530.     end\
  4531. end\
  4532. \
  4533. OnDataFailed = function(self, url, reason)\
  4534.     self:RemoveAllObjects()\
  4535.     self:AddObject({\
  4536.         X = 1,\
  4537.         Width = '100%',\
  4538.         Align = 'Center',\
  4539.         Y = '50%,-1',\
  4540.         Type = 'Label',\
  4541.         Text = 'Loading Failed '..reason,\
  4542.         TextColour = 'red'\
  4543.     })\
  4544. \
  4545.     self:AddObject({\
  4546.         X = 1,\
  4547.         Y = '50%,1',\
  4548.         X = '50%,-7',\
  4549.         Type = 'Button',\
  4550.         TextColour = 'white',\
  4551.         BackgroundColour = 'red',\
  4552.         Text = 'Back',\
  4553.         OnClick = function()\
  4554.             self.Bedrock:OpenPreviousPage()\
  4555.         end\
  4556.     })\
  4557. \
  4558.     self:AddObject({\
  4559.         X = 1,\
  4560.         Y = '50%,1',\
  4561.         X = '50%,1',\
  4562.         Type = 'Button',\
  4563.         TextColour = 'white',\
  4564.         BackgroundColour = 'red',\
  4565.         Text = 'Retry',\
  4566.         OnClick = function()\
  4567.             self:OnLoad()\
  4568.         end\
  4569.     })\
  4570. end\
  4571. \
  4572. OnDataLoaded = function()\
  4573. \
  4574. end",
  4575.     [ "HomePageView.lua" ] = "Inherit = 'PageView'\
  4576. \
  4577. OnDataLoad = function(self, url, data)\
  4578.     self:RemoveAllObjects()\
  4579. \
  4580.     local categories = textutils.unserialize(data)\
  4581. \
  4582.     local function load(from)\
  4583.         local to = {}\
  4584.         for i, v in ipairs(from) do\
  4585.             local app = App:Initialise(v, self.Bedrock)\
  4586.             table.insert(to, app)\
  4587.         end\
  4588.         return to\
  4589.     end\
  4590. \
  4591.     local topcharts = load(categories.topcharts)\
  4592. \
  4593.     local newest = load(categories.newest)\
  4594. \
  4595.     local featured = load(categories.featured)\
  4596. \
  4597.     self:AddObject({\
  4598.         X = 1,\
  4599.         Y = 1,\
  4600.         Type = 'AppCollectionView',\
  4601.         Width = '100%',\
  4602.         Title = 'Most Popular Apps',\
  4603.         Height = 2 + math.ceil(#topcharts / 2) * 4,\
  4604.         Name = 'PopularAppCollectionView',\
  4605.         Items = topcharts\
  4606.     })\
  4607. \
  4608.     self:AddObject({\
  4609.         X = 1,\
  4610.         Y = 4 + #topcharts * 2,\
  4611.         Type = 'AppCollectionView',\
  4612.         Width = '100%',\
  4613.         Title = 'Featured Apps',\
  4614.         Height = 2 + math.ceil(#featured / 2) * 4,\
  4615.         Name = 'FeaturedAppCollectionView',\
  4616.         Items = featured\
  4617.     })\
  4618. \
  4619.     self:AddObject({\
  4620.         X = 1,\
  4621.         Y = 7 + #topcharts * 2 + #featured * 2,\
  4622.         Type = 'AppCollectionView',\
  4623.         Width = '100%',\
  4624.         Title = 'Most Recently Added Apps',\
  4625.         Height = 2 + math.ceil(#newest / 2) * 4,\
  4626.         Name = 'RecentAppCollectionView',\
  4627.         Items = newest\
  4628.     })\
  4629. \
  4630.     self:UpdateScroll()\
  4631. end\
  4632. \
  4633. DataURL = function(self, info)\
  4634.     return self.Bedrock.AppStoreURL .. 'api/?command=application&subcommand=home'\
  4635. end",
  4636.     [ "CategoriesPageView.lua" ] = "Inherit = 'PageView'\
  4637. \
  4638. OnDataLoad = function(self, url, data)\
  4639.     self:RemoveAllObjects()\
  4640. \
  4641.     local categories = textutils.unserialize(data)\
  4642. \
  4643.     local function load(from)\
  4644.         local to = {}\
  4645.         for i, v in ipairs(from) do\
  4646.             local app = App:Initialise(v, self.Bedrock)\
  4647.             table.insert(to, app)\
  4648.         end\
  4649.         return to\
  4650.     end\
  4651. \
  4652.     local y = 1\
  4653.     for category, programs in pairs(categories) do\
  4654.         local apps = load(programs)\
  4655. \
  4656.         self:AddObject({\
  4657.             X = 1,\
  4658.             Y = y,\
  4659.             Type = 'AppCollectionView',\
  4660.             Width = '100%',\
  4661.             Title = 'Top ' .. category .. ' Programs',\
  4662.             Height = 2 + math.ceil(#apps / 2) * 4,\
  4663.             Name = category .. 'AppCollectionView',\
  4664.             Items = apps,\
  4665.             OnSeeMore = function()\
  4666.                 self.Bedrock:OpenPage('CategoryPageView', {\
  4667.                     CategoryName = category\
  4668.                 })\
  4669.             end\
  4670.         })\
  4671. \
  4672.         y = y + 4 + #apps * 2\
  4673.     end\
  4674. \
  4675.     self:UpdateScroll()\
  4676. end\
  4677. \
  4678. DataURL = function(self, info)\
  4679.     return self.Bedrock.AppStoreURL .. 'api/?command=application&subcommand=categories'\
  4680. end",
  4681.     [ "AppCollectionView.lua" ] = "Inherit = 'View'\
  4682. \
  4683. Items = nil\
  4684. Title = nil\
  4685. \
  4686. OnSeeMore = nil\
  4687. \
  4688. OnLoad = function(self)\
  4689.     self:OnUpdate('Items')\
  4690. end\
  4691. \
  4692. OnUpdate = function(self, value)\
  4693.     if value == 'Items' then\
  4694.         self:RemoveAllObjects()\
  4695. \
  4696.         self:AddObject({\
  4697.             X = 2,  \
  4698.             Y = 2,\
  4699.             Type = 'Label',\
  4700.             Text = self.Title,\
  4701.             TextColour = colours.lightGrey\
  4702.         })\
  4703. \
  4704.         if self.OnSeeMore then\
  4705.             self:AddObject({\
  4706.                 X = 3 + #self.Title,    \
  4707.                 Y = 2,\
  4708.                 Type = 'Label',\
  4709.                 Text = 'See More >',\
  4710.                 TextColour = colours.blue,\
  4711.                 OnClick = self.OnSeeMore\
  4712.             })\
  4713.         end\
  4714. \
  4715.         local xStart = 2 + math.floor((self.Width % AppView.Width) / 2)\
  4716.         local x = xStart\
  4717.         local y = 4\
  4718.         for i, app in ipairs(self.Items) do\
  4719.             self:AddObject({\
  4720.                 X = x,  \
  4721.                 Y = y,\
  4722.                 Type = 'AppView',\
  4723.                 App = app\
  4724.             })\
  4725. \
  4726.             x = x + AppView.Width\
  4727.             if x + AppView.Width > self.Width then\
  4728.                 x = xStart\
  4729.                 y = y + AppView.Height\
  4730.             end\
  4731.         end\
  4732.     end\
  4733. end",
  4734.     [ "AppDetailsView.lua" ] = "Inherit = 'View'\
  4735. \
  4736. App = nil\
  4737. \
  4738. OnLoad = function(self)\
  4739.     self:AddObject({\
  4740.         X = 3,\
  4741.         Y = 2,\
  4742.         Width = 4,\
  4743.         Height = 3,\
  4744.         Type = 'ImageView',\
  4745.     })\
  4746. \
  4747.     self:AddObject({\
  4748.         X = 8,\
  4749.         Y = 2,\
  4750.         Type = 'Label',\
  4751.         Name = 'NameLabel',\
  4752.         Text = ''\
  4753.     })\
  4754. \
  4755.     self:AddObject({\
  4756.         X = 8,\
  4757.         Y = 3,\
  4758.         Type = 'Label',\
  4759.         Name = 'AuthorLabel',\
  4760.         Text = '',\
  4761.         TextColour = 'lightGrey'\
  4762.     })\
  4763. \
  4764.     self:AddObject({\
  4765.         X = 8,\
  4766.         Y = 4,\
  4767.         Type = 'Label',\
  4768.         Name = 'CategoryLabel',\
  4769.         Text = '',\
  4770.         TextColour = 'grey'\
  4771.     })\
  4772. \
  4773.     self:AddObject({\
  4774.         X = '100%,-10',\
  4775.         Y = 2,\
  4776.         Type = 'Button',\
  4777.         Name = 'InstallButton',\
  4778.         Text = 'Install',\
  4779.         TextColour = 'white',\
  4780.         BackgroundColour = 'blue',\
  4781.         -- Visible = false\
  4782.     })\
  4783. \
  4784.     self:AddObject({\
  4785.         X = 3,\
  4786.         Y = 6,\
  4787.         Type = 'Label',\
  4788.         Text = 'Description',\
  4789.         TextColour = 'lightGrey'\
  4790.     })\
  4791. \
  4792.     self:AddObject({\
  4793.         X = 3,\
  4794.         Y = 7,\
  4795.         Width = '100%,-4',\
  4796.         Type = 'Label',\
  4797.         Name = 'DescriptionLabel',\
  4798.         Text = 'No description',\
  4799.         TextColour = 'grey'\
  4800.     })\
  4801. \
  4802.     self:AddObject({\
  4803.         X = 3,\
  4804.         Y = 6,\
  4805.         Type = 'Label',\
  4806.         Text = 'Description',\
  4807.         TextColour = 'lightGrey'\
  4808.     })\
  4809. \
  4810. \
  4811.     self:OnUpdate('App')\
  4812. end\
  4813. \
  4814. OnUpdate = function(self, value)\
  4815.     if value == 'App' and self.App then\
  4816.         self:GetObject('NameLabel').Text = self.App.Name\
  4817.         self:GetObject('AuthorLabel').Text = self.App.Author\
  4818.         self:GetObject('CategoryLabel').Text = self.App.Category\
  4819.         self:GetObject('ImageView').Path = 'Resources/'..self.App.Name\
  4820.         -- self:GetObject('InstallButton').X = 10 + math.max(#self.App.Name, #self.App.Author, #self.App.Category)\
  4821. \
  4822.         self:GetObject('DescriptionLabel').Text = self.App.Description\
  4823. \
  4824.     end\
  4825. end",
  4826.     [ "CategoryPageView.lua" ] = "Inherit = 'PageView'\
  4827. \
  4828. OnDataLoad = function(self, url, data)\
  4829.     self:RemoveAllObjects()\
  4830. \
  4831.     local programs = textutils.unserialize(data)\
  4832. \
  4833.     local function load(from)\
  4834.         local to = {}\
  4835.         for i, v in ipairs(from) do\
  4836.             local app = App:Initialise(v, self.Bedrock)\
  4837.             table.insert(to, app)\
  4838.         end\
  4839.         return to\
  4840.     end\
  4841. \
  4842.     local apps = load(programs)\
  4843. \
  4844.     self:AddObject({\
  4845.         X = 1,\
  4846.         Y = 1,\
  4847.         Type = 'AppCollectionView',\
  4848.         Width = '100%',\
  4849.         Title = 'All ' .. self.CategoryName .. ' Programs',\
  4850.         Height = 2 + math.ceil(#apps / 2) * 4,\
  4851.         Name = self.CategoryName .. 'AppCollectionView',\
  4852.         Items = apps\
  4853.     })\
  4854. \
  4855.     self:UpdateScroll()\
  4856. end\
  4857. \
  4858. DataURL = function(self, info)\
  4859.     return self.Bedrock.AppStoreURL .. 'api/?command=application&subcommand=category&name=' .. textutils.urlEncode(self.CategoryName)\
  4860. end",
  4861.     [ "SearchResultsPageView.lua" ] = "Inherit = 'PageView'\
  4862. \
  4863. OnDataLoad = function(self, url, data)\
  4864.     self:RemoveAllObjects()\
  4865. \
  4866.     local _data = textutils.unserialize(data)\
  4867. \
  4868.     local function load(from)\
  4869.         local to = {}\
  4870.         for i, v in ipairs(from) do\
  4871.             local app = App:Initialise(v, self.Bedrock)\
  4872.             table.insert(to, app)\
  4873.         end\
  4874.         return to\
  4875.     end\
  4876. \
  4877.     local results = load(_data)\
  4878. \
  4879.     self:AddObject({\
  4880.         X = 1,\
  4881.         Y = 1,\
  4882.         Type = 'AppCollectionView',\
  4883.         Width = '100%',\
  4884.         Title = 'Search Results For: ' .. self.SearchTerm,\
  4885.         Height = 2 + math.ceil(#results / 2) * 4,\
  4886.         Name = 'SearchResultsCollectionView',\
  4887.         Items = results\
  4888.     })\
  4889. \
  4890.     if #results == 0 then\
  4891.         self:AddObject({\
  4892.             X = 1,\
  4893.             Y = '50%',\
  4894.             Width = '100%',\
  4895.             Align = 'Center',\
  4896.             Type = 'Label',\
  4897.             TextColour = 'grey',\
  4898.             Text = 'No apps found'\
  4899.         })\
  4900.     end\
  4901. \
  4902.     self:UpdateScroll()\
  4903. end\
  4904. \
  4905. DataURL = function(self, info)\
  4906.     return self.Bedrock.AppStoreURL .. 'api/?command=application&subcommand=search&name=' .. textutils.urlEncode(self.SearchTerm)\
  4907. end",
  4908.   },
  4909. }
  4910. local function makeFile(_path,_content)
  4911.  local file=fs.open(_path,"w")
  4912.  file.write(_content)
  4913.  file.close()
  4914. end
  4915. local function makeFolder(_path,_content)
  4916.  fs.makeDir(_path)
  4917.  for k,v in pairs(_content) do
  4918.   if type(v)=="table" then
  4919.    makeFolder(_path.."/"..k,v)
  4920.   else
  4921.    makeFile(_path.."/"..k,v)
  4922.   end
  4923.  end
  4924. end
  4925. local sDest= installLocation or '/'
  4926. if sDest=="root" then
  4927.  sDest="/"
  4928. end
  4929. local tPackage=pkg
  4930. makeFolder(sDest,tPackage)
  4931. print("Installed Stand Alone CCAppStore by oeed, installer by CodedPixelCube/minimite. Run 'appstore' to boot up CCAppStore")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement