-- Hideously Smashed Together by Compilr, a Hideous Smash-Stuff-Togetherer, (c) 2014 oeed -- -- This file REALLLLLLLY isn't suitable to be used for anything other than being executed -- -- To extract all the files, run: " --extract" in the Shell -- local files = {["Wireless"]="--This is just the OneOS Wireless API\ \ --OneOS uses channels between 4200 and 4300, avoid use where possible\ \ Channels = {\ Ignored = 4299,\ Ping = 4200,\ PingReply = 4201,\ QuestServerRequest = 4250,\ QuestServerRequestReply = 4251,\ QuestServerNameAvailable = 4252,\ QuestServerNameAvailableReply = 4253,\ }\ \ local function isOpen(channel)\ return Peripheral.CallType('wireless_modem', 'isOpen', channel)\ end\ \ local function open(channel)\ if not isOpen(channel) then\ Peripheral.CallType('wireless_modem', 'open', channel)\ end\ end\ \ Open = open\ \ local function close(channel)\ Peripheral.CallType('wireless_modem', 'close', channel)\ end\ \ local function closeAll()\ Peripheral.CallType('wireless_modem', 'closeAll')\ end\ \ local function transmit(channel, replyChannel, message)\ Peripheral.CallType('wireless_modem', 'transmit', channel, replyChannel, textutils.serialize(message))\ end\ \ function Present()\ if Peripheral.GetPeripheral('wireless_modem') == nil then\ return false\ else\ return true\ end\ end\ \ local function FormatMessage(message, messageID, destinationID)\ return {\ content = textutils.serialize(message),\ senderID = os.getComputerID(),\ senderName = os.getComputerLabel(),\ channel = channel,\ replyChannel = reply,\ messageID = messageID or math.random(10000),\ destinationID = destinationID\ }\ end\ \ local Timeout = function(func, time)\ time = time or 1\ parallel.waitForAny(func, function()\ sleep(time)\ --log('Timeout!'..time)\ end)\ end\ \ RecieveMessage = function(_channel, messageID, timeout)\ open(_channel)\ local done = false\ local event, side, channel, replyChannel, message = nil\ Timeout(function()\ while not done do\ event, side, channel, replyChannel, message = os.pullEvent('modem_message')\ if channel ~= _channel then\ event, side, channel, replyChannel, message = nil\ else\ message = textutils.unserialize(message)\ message.content = textutils.unserialize(message.content)\ if messageID and messageID ~= message.messageID or (message.destinationID ~= nil and message.destinationID ~= os.getComputerID()) then\ event, side, channel, replyChannel, message = nil\ else\ done = true\ end\ end\ end\ end,\ timeout)\ return event, side, channel, replyChannel, message\ end\ \ Initialise = function()\ if Present() then\ for i, c in pairs(Channels) do\ open(c)\ end\ end\ end\ \ HandleMessage = function(event, side, channel, replyChannel, message, distance)\ message = textutils.unserialize(message)\ message.content = textutils.unserialize(message.content)\ \ if channel == Channels.Ping then\ if message.content == 'Ping!' then\ SendMessage(replyChannel, 'Pong!', nil, message.messageID)\ end\ elseif message.destinationID ~= nil and message.destinationID ~= os.getComputerID() then\ elseif Wireless.Responder then\ Wireless.Responder(event, side, channel, replyChannel, message, distance)\ end\ end\ \ SendMessage = function(channel, message, reply, messageID, destinationID)\ reply = reply or channel + 1\ open(channel)\ open(reply)\ local _message = FormatMessage(message, messageID, destinationID)\ transmit(channel, reply, _message)\ return _message\ end\ \ Ping = function()\ local message = SendMessage(Channels.Ping, 'Ping!', Channels.PingReply)\ RecieveMessage(Channels.PingReply, message.messageID)\ end",["parser"]="--[[\ This API was not created by myself, it was copied from https://github.com/voidfiles/webscript-lua-modules/tree/master/html\ \ It has been modified slightly to be more suitable for CCML (removed unused tags, cleaned up returned table a little, etc)\ \ Original License:\ \ Copyright (c) 2007 T. Kobayashi\ \ Permission is hereby granted, free of charge, to any person obtaining a \ copy of this software and associated documentation files (the \ \"Software\"), to deal in the Software without restriction, including \ without limitation the rights to use, copy, modify, merge, publish, \ distribute, sublicense, and/or sell copies of the Software, and to \ permit persons to whom the Software is furnished to do so, subject to \ the following conditions: \ \ The above copyright notice and this permission notice shall be included \ in all copies or substantial portions of the Software. \ \ THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS \ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF \ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. \ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY \ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, \ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE \ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ \ ]]\ \ entity = {\ nbsp = \" \",\ lt = \"<\",\ gt = \">\",\ quot = \"\\\"\",\ amp = \"&\",\ }\ \ -- keep unknown entity as is\ setmetatable(entity, {\ __index = function (t, key)\ return \"&\" .. key .. \";\"\ end\ })\ \ block = {\ \"address\",\ \"blockquote\",\ \"center\",\ \"dir\", \"div\", \"dl\",\ \"fieldset\", \"form\",\ \"h\", \"h1\", \"h2\", \"h3\", \"h4\", \"h5\", \"h6\", \"hr\", \ \"isindex\",\ \"menu\",\ \"noframes\",\ \"ol\",\ \"p\",\ \"pre\",\ \"table\",\ \"ul\",\ }\ \ inline = {\ \"a\", \"abbr\", \"acronym\", \"applet\",\ \"b\", \"basefont\", \"bdo\", \"big\", \"br\", \"button\",\ \"cite\", \"code\",\ \"dfn\",\ \"em\",\ \"font\",\ \"i\", \"iframe\", \"img\", \"input\",\ \"kbd\",\ \"label\",\ \"map\",\ \"object\",\ \"q\",\ \"s\", \"samp\", \"select\", \"small\", \"span\", \"strike\", \"strong\", \"sub\", \"sup\",\ \"textarea\", \"tt\",\ \"u\",\ \"var\",\ }\ \ tags = {\ a = { empty = false },\ abbr = {empty = false} ,\ acronym = {empty = false} ,\ address = {empty = false} ,\ applet = {empty = false} ,\ area = {empty = true} ,\ b = {empty = false} ,\ base = {empty = true} ,\ basefont = {empty = true} ,\ bdo = {empty = false} ,\ big = {empty = false} ,\ blockquote = {empty = false} ,\ body = { empty = false, },\ br = {empty = true} ,\ button = {empty = false} ,\ caption = {empty = false} ,\ center = {empty = false} ,\ cite = {empty = false} ,\ code = {empty = false} ,\ col = {empty = true} ,\ colgroup = {\ empty = false,\ optional_end = true,\ child = {\"col\",},\ },\ dd = {empty = false} ,\ del = {empty = false} ,\ dfn = {empty = false} ,\ dir = {empty = false} ,\ div = {empty = false} ,\ dl = {empty = false} ,\ dt = {\ empty = false,\ optional_end = true,\ child = {\ inline,\ \"del\",\ \"ins\",\ \"noscript\",\ \"script\",\ },\ },\ em = {empty = false} ,\ fieldset = {empty = false} ,\ font = {empty = false} ,\ form = {empty = false} ,\ frame = {empty = true} ,\ frameset = {empty = false} ,\ h1 = {empty = false} ,\ h2 = {empty = false} ,\ h3 = {empty = false} ,\ h4 = {empty = false} ,\ h5 = {empty = false} ,\ h6 = {empty = false} ,\ head = {empty = false} ,\ hr = {empty = true} ,\ html = {empty = false} ,\ i = {empty = false} ,\ iframe = {empty = false} ,\ img = {empty = true} ,\ input = {empty = true} ,\ ins = {empty = false} ,\ isindex = {empty = true} ,\ kbd = {empty = false} ,\ label = {empty = false} ,\ legend = {empty = false} ,\ li = {\ empty = false,\ optional_end = true,\ child = {\ inline,\ block,\ \"del\",\ \"ins\",\ \"noscript\",\ \"script\",\ },\ },\ link = {empty = true} ,\ map = {empty = false} ,\ menu = {empty = false} ,\ meta = {empty = true} ,\ noframes = {empty = false} ,\ noscript = {empty = false} ,\ object = {empty = false} ,\ ol = {empty = false} ,\ optgroup = {empty = false} ,\ option = {\ empty = false,\ optional_end = true,\ child = {},\ },\ p = {\ empty = false,\ optional_end = true,\ child = {\ inline,\ \"del\",\ \"ins\",\ \"noscript\",\ \"script\",\ },\ } ,\ param = {empty = true} ,\ pre = {empty = false} ,\ q = {empty = false} ,\ s = {empty = false} ,\ samp = {empty = false} ,\ script = {empty = false} ,\ select = {empty = false} ,\ small = {empty = false} ,\ span = {empty = false} ,\ strike = {empty = false} ,\ strong = {empty = false} ,\ style = {empty = false} ,\ sub = {empty = false} ,\ sup = {empty = false} ,\ table = {empty = false} ,\ tbody = {empty = false} ,\ td = {\ empty = false,\ optional_end = true,\ child = {\ inline,\ block,\ \"del\",\ \"ins\",\ \"noscript\",\ \"script\",\ },\ },\ textarea = {empty = false} ,\ tfoot = {\ empty = false,\ optional_end = true,\ child = {\"tr\",},\ },\ th = {\ empty = false,\ optional_end = true,\ child = {\ inline,\ block,\ \"del\",\ \"ins\",\ \"noscript\",\ \"script\",\ },\ },\ thead = {\ empty = false,\ optional_end = true,\ child = {\"tr\",},\ },\ title = {empty = false} ,\ tr = {\ empty = false,\ optional_end = true,\ child = {\ \"td\", \"th\",\ },\ },\ tt = {empty = false} ,\ u = {empty = false} ,\ ul = {empty = false} ,\ var = {empty = false} ,\ }\ \ setmetatable(tags, {\ __index = function (t, key)\ return {empty = false}\ end\ })\ \ -- string buffer implementation\ function newbuf ()\ local buf = {\ _buf = {},\ clear = function (self) self._buf = {}; return self end,\ content = function (self) return table.concat(self._buf) end,\ append = function (self, s)\ self._buf[#(self._buf) + 1] = s\ return self\ end,\ set = function (self, s) self._buf = {s}; return self end,\ }\ return buf\ end\ \ -- unescape character entities\ function unescape (s)\ function entity2string (e)\ return entity[e]\ end\ return s.gsub(s, \"&(#?%w+);\", entity2string)\ end\ \ -- iterator factory\ function makeiter (f)\ local co = coroutine.create(f)\ return function ()\ local code, res = coroutine.resume(co)\ return res\ end\ end\ \ -- constructors for token\ function Tag (s) \ return string.find(s, \"^\" then\ coroutine.yield(Tag(buf:append(c):content()))\ buf:clear()\ return text(f, buf)\ elseif c then\ buf:append(c)\ return tag(f, buf)\ else\ if buf:content() ~= \"\" then coroutine.yield(Tag(buf:content())) end\ end\ end\ \ function parse_starttag(tag)\ local tagname = string.match(tag, \"<%s*(%w+)\")\ local elem = {_attr = {}}\ elem._tag = tagname\ for key, _, val in string.gmatch(tag, \"(%w+)%s*=%s*([\\\"'])(.-)%2\", i) do\ local unescaped = unescape(val)\ elem._attr[key] = unescaped\ end\ return elem\ end\ \ function parse_endtag(tag)\ local tagname = string.match(tag, \"<%s*/%s*(%w+)\")\ return tagname\ end\ \ -- find last element that satisfies given predicate\ function rfind(t, pred)\ local length = #t\ for i=length,1,-1 do\ if pred(t[i]) then\ return i, t[i]\ end\ end\ end\ \ function flatten(t, acc)\ acc = acc or {}\ for i,v in ipairs(t) do\ if type(v) == \"table\" then\ flatten(v, acc)\ else\ acc[#acc + 1] = v\ end\ end\ return acc\ end\ \ function optional_end_p(elem)\ if tags[elem._tag].optional_end then\ return true\ else\ return false\ end\ end\ \ function valid_child_p(child, parent)\ local schema = tags[parent._tag].child\ if not schema then return true end\ \ for i,v in ipairs(flatten(schema)) do\ if v == child._tag then\ return true\ end\ end\ \ return false\ end\ \ -- tree builder\ function parse(f)\ local root = {_tag = \"#document\", _attr = {}}\ local stack = {root}\ for i in makeiter(function () return text(f, newbuf()) end) do\ if i.type == \"Start\" then\ local new = parse_starttag(i.value)\ local top = stack[#stack]\ \ while\ top._tag ~= \"#document\" and \ optional_end_p(top) and\ not valid_child_p(new, top)\ do\ stack[#stack] = nil \ top = stack[#stack]\ end\ \ top[#top+1] = new -- appendchild\ if not tags[new._tag].empty then \ stack[#stack+1] = new -- push\ end\ elseif i.type == \"End\" then\ local tag = parse_endtag(i.value)\ local openingpos = rfind(stack, function(v) \ if v._tag == tag then\ return true\ else\ return false\ end\ end)\ if openingpos then\ local length = #stack\ for j=length,openingpos,-1 do\ table.remove(stack, j)\ end\ end\ else -- Text\ if #string.gsub(string.gsub(i.value, \"%s+\", \"\"), '\\n', '') ~= 0 then\ local top = stack[#stack]\ top[#top+1] = i.value\ end\ end\ end\ return root\ end\ \ function parsestr(s)\ local handle = {\ _content = s,\ _pos = 1,\ read = function (self, length)\ if self._pos > string.len(self._content) then return end\ local ret = string.sub(self._content, self._pos, self._pos + length - 1)\ self._pos = self._pos + length\ return ret\ end\ }\ return parse(handle)\ end",["Elements"]={["SecureTextInput.lua"]="Inherit = 'TextInput'\ \ UpdateValue = function(self)\ self.Value = hash.sha256(self.Object.Text)\ end\ \ OnCreateObject = function(self, parentObject, y)\ return {\ Element = self,\ Y = y,\ X = 1,\ Width = self.Width,\ Type = \"SecureTextBox\",\ Text = self.Value,\ TextColour = self.TextColour,\ BackgroundColour = self.BackgroundColour,\ SelectedBackgroundColour = self.SelectedBackgroundColour,\ SelectedTextColour = self.SelectedTextColour,\ PlaceholderTextColour = self.PlaceholderTextColour,\ Placeholder = self.Placeholder,\ InputName = self.InputName,\ OnChange = function(_self, event, keychar)\ if keychar == keys.tab or keychar == keys.enter then\ local form = self\ local step = 0\ while form.Tag ~= 'form' and step < 50 do\ form = form.Parent\ end\ if keychar == keys.tab then\ if form and form.Object and form.Object.OnTab then\ form.Object:OnTab()\ end\ else\ if form and form.Submit then\ form:Submit(true)\ end\ end\ end\ end\ }\ end",["Image.lua"]="URL = nil\ Format = nil\ \ OnInitialise = function(self, node)\ local attr = self.Attributes\ if attr.src then\ self.URL = attr.src\ end\ \ if attr.type then\ self.Format = attr.type\ end\ \ if attr.height then\ self.Height = attr.height\ end\ \ if attr.width then\ self.Width = attr.width\ end\ end\ \ OnCreateObject = function(self, parentObject, y)\ return {\ Element = self,\ Y = y,\ X = 1,\ Width = self.Width,\ Height = self.Height,\ URL = self.URL,\ Format = self.Format,\ Type = \"WebImageView\"\ }\ end",["Script.lua"]="Text = nil\ URL = nil\ \ OnInitialise = function(self, node)\ local attr = self.Attributes\ self.Text = table.concat(node, '\\n')\ \ if attr.src then\ self.URL = attr.src\ self.Text = nil\ end\ end\ \ InsertScript = function(self, webView)\ if self.Text then\ webView:LoadScript(self.Text)\ elseif self.URL then\ fetchHTTPAsync(resolveFullUrl(self.URL), function(ok, event, response)\ if ok then\ self.Text = response.readAll()\ webView:LoadScript(self.Text)\ end\ end)\ \ end\ return nil\ end",["Select.lua"]="Width = 20\ InputName = ''\ \ OnInitialise = function(self, node)\ if attr.value then\ new.Text = attr.value\ end\ \ if attr.name then\ new.InputName = attr.name\ end\ end\ \ UpdateValue = function(self)\ self.Value = self.Object.MenuItems[self.Object.Selected].Value\ end\ \ OnCreateObject = function(self, parentObject, y)\ return {\ Element = self,\ Y = y,\ X = 1,\ Width = self.Width,\ Type = \"SelectView\",\ TextColour = self.TextColour,\ BackgroundColour = self.BackgroundColour,\ InputName = self.InputName,\ }\ end",["Paragraph.lua"]="Align = \"Left\"\ \ OnInitialise = function(self, node)\ local attr = self.Attributes\ self.Text = self.Text or ''\ if attr.align then\ if attr.align:lower() == 'left' or attr.align:lower() == 'center' or attr.align:lower() == 'right' then\ self.Align = attr.align:lower():gsub(\"^%l\", string.upper)\ end\ end\ end\ \ OnCreateObject = function(self, parentObject, y)\ return {\ Element = self,\ Y = y,\ X = 1,\ Width = self.Width,\ Height = self.Height,\ Align = self.Align,\ Type = \"Label\",\ Text = self.Text,\ TextColour = self.TextColour,\ BackgroundColour = self.BackgroundColour,\ OnUpdate = function(_self, value)\ if value == 'Text' then\ if not self.Attributes.height then\ _self.Height = #_self.Bedrock.Helpers.WrapText(_self.Text, _self.Width)\ _self.Bedrock:GetObject('WebView'):RepositionLayout()\ end\ end\ end\ }\ end",["HiddenInput.lua"]="InputName = ''\ \ OnInitialise = function(self, node)\ local attr = self.Attributes\ if attr.value then\ self.Value = attr.value\ end\ \ if attr.name then\ self.InputName = attr.name\ end\ end",["ButtonInput.lua"]="BackgroundColour = colours.lightGrey\ TextColour = colours.black\ Text = 'Submit'\ InputName = ''\ \ OnInitialise = function(self, node)\ local attr = self.Attributes\ if attr.value then\ self.Text = attr.value\ end\ \ if attr.name then\ self.InputName = attr.name\ end\ \ if not attr.width then\ self.Width = #self.Text + 2\ end\ end\ \ UpdateValue = function(self, force)\ if force then\ self.Value = self.Object.Text\ end\ end\ \ CreateObject = function(self, parentObject, y)\ return parentObject:AddObject({\ Element = self,\ Y = y,\ X = 1,\ Width = self.Width,\ Type = \"Button\",\ Text = self.Text,\ TextColour = self.TextColour,\ BackgroundColour = self.BackgroundColour,\ InputName = self.InputName,\ OnClick = function(_self, event, side, x, y)\ local form = self\ local step = 0\ while form.Tag ~= 'form' and step < 50 do\ form = form.Parent\ end\ self.Value = _self.Text\ if form and form.Submit then\ form:Submit()\ end\ end\ })\ end",["Float.lua"]="Align = \"Left\"\ \ OnInitialise = function(self, node)\ local attr = self.Attributes\ if attr.align then\ if attr.align:lower() == 'left' or attr.align:lower() == 'right' then\ self.Align = attr.align:lower():gsub(\"^%l\", string.upper)\ end\ end\ end\ \ OnCreateObject = function(self, parentObject, y)\ return {\ Element = self,\ Y = y,\ X = 1,\ Width = self.Width,\ Height = self.Height,\ Align = self.Align,\ BackgroundColour = self.BackgroundColour,\ Type = \"FloatView\"\ }\ end",["Element.lua"]="HasChildren = true\ Children = nil\ Tag = nil\ TextColour = colours.black\ BackgroundColour = colours.transparent\ Text = nil\ Attributes = nil\ Width = \"100%\"\ \ Initialise = function(self, node)\ local new = {} -- the new instance\ setmetatable( new, {__index = self} )\ local attr = node._attr\ new.Tag = node._tag\ new.Attributes = attr\ if new.HasChildren then\ new.Children = {}\ end\ \ if type(node[1]) == 'string' then\ new.Text = node[1]\ end\ \ if attr.colour then\ new.TextColour = self:ParseColour(attr.colour)\ elseif attr.color then\ new.TextColour = self:ParseColour(attr.color)\ end\ \ if attr.bgcolour then\ new.BackgroundColour = self:ParseColour(attr.bgcolour)\ elseif attr.bgcolor then\ new.BackgroundColour = self:ParseColour(attr.bgcolor)\ end\ \ if attr.height then\ new.Height = attr.height\ end\ \ if attr.width then\ new.Width = attr.width\ end\ \ if new.OnInitialise then\ new:OnInitialise(node)\ end\ \ return new\ end\ \ ParseColour = function(self, str)\ if str and type(str) == 'string' then\ if colours[str] and type(colours[str]) == 'number' then\ return colours[str]\ elseif colors[str] and type(colors[str]) == 'number' then\ return colors[str]\ end\ end\ end\ \ CreateObject = function(self, parentObject, y)\ local object\ if self.OnCreateObject then\ object = self:OnCreateObject()\ else\ object = {\ Element = self,\ Y = y,\ X = 1,\ Width = self.Width,\ Height = self.Height,\ BackgroundColour = self.BackgroundColour,\ Type = \"View\"\ }\ end\ \ if object then\ return parentObject:AddObject(object, parentObject, y)\ end\ end",["Divider.lua"]="Char = nil\ \ OnInitialise = function(self, node)\ local attr = self.Attributes\ self.Text = self.Text or ''\ if attr.char then\ if #attr.char == 1 then\ self.Char = attr.char\ end\ end\ end\ \ OnCreateObject = function(self, parentObject, y)\ return {\ Element = self,\ Y = y,\ X = 1,\ Width = self.Width,\ Height = self.Height,\ BackgroundColour = self.BackgroundColour,\ TextColour = self.TextColour,\ Type = \"DividerView\",\ Char = self.Char\ }\ end",["Link.lua"]="Align = 'Left'\ Width = \"100%\"\ TextColour = colours.blue\ UnderlineColour = nil\ UnderlineVisible = true\ URL = nil\ \ OnInitialise = function(self, node)\ local attr = self.Attributes\ if attr.href then\ self.URL = attr.href\ end\ \ if attr.ulcolour then\ if attr.ulcolour == 'none' then\ self.UnderlineVisible = false\ else\ self.UnderlineColour = self:ParseColour(attr.ulcolour)\ end\ elseif attr.ulcolor then\ if attr.ulcolor == 'none' then\ self.UnderlineVisible = false\ else\ self.UnderlineColour = self:ParseColour(attr.ulcolor)\ end\ end\ \ if attr.align then\ if attr.align:lower() == 'left' or attr.align:lower() == 'center' or attr.align:lower() == 'right' then\ self.Align = attr.align:lower():gsub(\"^%l\", string.upper)\ end\ end\ end\ \ OnCreateObject = function(self, parentObject, y)\ return {\ Element = self,\ Y = y,\ X = 1,\ Width = self.Width,\ Align = self.Align,\ Type = \"LinkView\",\ Text = self.Text,\ TextColour = self.TextColour,\ UnderlineColour = self.UnderlineColour,\ BackgroundColour = self.BackgroundColour,\ URL = resolveFullUrl(self.URL),\ UnderlineVisible = self.UnderlineVisible\ }\ end",["Center.lua"]="OnCreateObject = function(self, parentObject, y)\ return {\ Element = self,\ Y = y,\ X = 1,\ Width = \"100%\",\ Height = self.Height,\ BackgroundColour = self.BackgroundColour,\ Type = \"CenterView\"\ }\ end",["Form.lua"]="Submit = function(self, onEnter)\ local values = {}\ \ local node = false\ node = function(elem)\ if (elem.Tag == 'input' or elem.Tag == 'select') and elem.InputName then\ local findSubmit = (onEnter and elem.Attributes and elem.Attributes.type == 'submit')\ if elem.UpdateValue then\ elem:UpdateValue(findSubmit)\ end\ \ if findSubmit then\ onEnter = false\ end\ \ if elem.Value then\ values[elem.InputName] = elem.Value\ end\ end\ \ if elem.Children then\ for i, child in ipairs(elem.Children) do\ node(child)\ end\ end\ end\ \ node(self)\ \ local url = false\ if self.Attributes.action and #self.Attributes.action > 0 then\ url = resolveFullUrl(self.Attributes.action) --TODO: this needs to show the fake url to the user\ else\ url = getCurrentFakeUrl()\ end\ local data = ''\ for k, v in pairs(values) do\ data = data .. textutils.urlEncode(k) .. '=' .. textutils.urlEncode(v) .. '&'\ end\ data = data:sub(1, #data - 1)\ \ if self.Attributes.method and self.Attributes.method:lower() == 'post' then\ goToUrl(url, data)\ else\ goToUrl(url .. '?' .. data)\ end\ end\ \ OnCreateObject = function(self, parentObject, y)\ return {\ Element = self,\ Y = y,\ X = 1,\ Width = \"100%\",\ Height = self.Height,\ Type = \"FormView\"\ }\ end",["FileInput.lua"]="BackgroundColour = colours.lightGrey\ TextColour = colours.black\ Text = 'Choose File...'\ InputName = ''\ FilePath = 'test'\ \ OnInitialise = function(self, node)\ local attr = self.Attributes\ if attr.value then\ self.Text = attr.value\ end\ \ if attr.name then\ self.InputName = attr.name\ end\ \ if not attr.width then\ self.Width = #self.Text + 2\ end\ end\ \ UpdateValue = function(self, force)\ if self.FilePath then\ local f = fs.open(self.FilePath, 'r')\ if f then\ local content = f.readAll()\ self.Value = '{\"name\": \"' .. fs.getName(self.FilePath):gsub('\"', '\\\\\"') .. '\", \"content\": \"' .. content:gsub('\"', '\\\\\"') .. '\"}'\ f.close()\ end\ end\ end\ \ CreateObject = function(self, parentObject, y)\ return parentObject:AddObject({\ Element = self,\ Y = y,\ X = 1,\ Width = self.Width,\ Type = \"Button\",\ Text = self.Text,\ TextColour = self.TextColour,\ BackgroundColour = self.BackgroundColour,\ InputName = self.InputName,\ OnClick = function(_self, event, side, x, y)\ _self.Bedrock:DisplayOpenFileWindow(nil, function(success, path)\ if success then\ self.FilePath = path\ _self.Text = 'File: '..fs.getName(path)\ _self.Align = 'Left'\ end\ end)\ end\ })\ end",["Heading.lua"]="Align = \"Center\"\ \ OnInitialise = function(self, node)\ local attr = self.Attributes\ self.Text = self.Text or ''\ if attr.align then\ if attr.align:lower() == 'left' or attr.align:lower() == 'center' or attr.align:lower() == 'right' then\ self.Align = attr.align:lower():gsub(\"^%l\", string.upper)\ end\ end\ end\ \ OnCreateObject = function(self, parentObject, y)\ return {\ Element = self,\ Y = y,\ X = 1,\ Width = \"100%\",\ Align = self.Align,\ Type = \"HeadingView\",\ Text = self.Text,\ TextColour = self.TextColour,\ BackgroundColour = self.BackgroundColour\ }\ end",["TextInput.lua"]="SelectedBackgroundColour = colours.blue\ SelectedTextColour = colours.white\ PlaceholderTextColour = colours.grey\ Placeholder = ''\ Value = nil\ Attributes = nil\ Children = nil\ Tag = nil\ Width = 20\ InputName = ''\ \ OnInitialise = function(self, node)\ local attr = self.Attributes\ if attr.selbgcolour then\ self.SelectedBackgroundColour = self:ParseColour(attr.selbgcolour)\ elseif attr.selbgcolor then\ self.SelectedBackgroundColour = self:ParseColour(attr.selbgcolor)\ end\ \ if attr.selcolour then\ self.SelectedTextColour = self:ParseColour(attr.selcolour)\ elseif attr.selcolor then\ self.SelectedTextColour = self:ParseColour(attr.selcolor)\ end\ \ if attr.plcolour then\ self.PlaceholderTextColour = self:ParseColour(attr.plcolour)\ elseif attr.plcolour then\ self.PlaceholderTextColour = self:ParseColour(attr.plcolour)\ end\ \ if attr.value then\ self.Value = attr.value\ end\ \ if attr.placeholder then\ self.Placeholder = attr.placeholder\ end\ \ if attr.name then\ self.InputName = attr.name\ end\ end\ \ UpdateValue = function(self)\ self.Value = self.Object.Text\ end\ \ OnCreateObject = function(self, parentObject, y)\ return {\ Element = self,\ Y = y,\ X = 1,\ Width = self.Width,\ Type = \"TextBox\",\ Text = self.Value,\ TextColour = self.TextColour,\ BackgroundColour = self.BackgroundColour,\ SelectedBackgroundColour = self.SelectedBackgroundColour,\ SelectedTextColour = self.SelectedTextColour,\ PlaceholderTextColour = self.PlaceholderTextColour,\ Placeholder = self.Placeholder,\ InputName = self.InputName,\ OnChange = function(_self, event, keychar)\ if keychar == keys.tab or keychar == keys.enter then\ local form = self\ local step = 0\ while form.Tag ~= 'form' and step < 50 do\ form = form.Parent\ end\ if keychar == keys.tab then\ if form and form.Object and form.Object.OnTab then\ form.Object:OnTab()\ end\ else\ if form and form.Submit then\ form:Submit(true)\ end\ end\ end\ end\ }\ end",["ElementTree.lua"]="Tree = nil\ FailHandler = nil\ \ Initialise = function(self, html)\ local new = {} -- the new instance\ setmetatable( new, {__index = self} )\ local err = nil\ if html:sub(1,15) ~= '' then\ err = Errors.InvalidDoctype\ end\ \ html = html:gsub(\"\",\"\")\ \ local rawTree\ if not err then\ rawTree = parser.parsestr(html)[1]\ end\ \ if not err then\ _, notok = pcall(function() new:LoadTree(rawTree) end)\ if notok then\ error(notok)\ err = Errors.ParseFailed\ end\ end\ \ if err then\ return nil, err\ end\ return new\ end\ \ LoadTree = function(self, rawTree)\ local tree = {}\ local node = true\ node = function (tbl, tr, parent)\ for i, v in ipairs(tbl) do\ if type(v) == 'table' and v._tag then\ local class = self:GetElementClass(v._tag, v._attr)\ if not class or not class.Initialise then\ error('Unknown class: '..v._attr.type)\ end\ local element = class:Initialise(v)\ element.Parent = parent\ table.insert(tr, element)\ if element.Children then\ node(v, element.Children, element)\ end\ end\ end\ end\ \ node(rawTree, tree)\ self.Tree = tree\ end\ \ GetElement = function(self, tag)\ local node = true\ node = function(tbl)\ for i,v in ipairs(tbl) do\ if type(v) == 'table' and v.Tag then\ if v.Tag == tag then\ return v\ end\ if v.Children then\ local r = node(v.Children)\ if r then\ return r\ end\ end\ end\ end\ end\ return node(self.Tree)\ end\ \ GetElementClass = function(self, tag, attr)\ if tag == 'h' then\ return Heading\ elseif tag == 'div' then\ return Divider\ elseif tag == 'p' then\ return Paragraph\ elseif tag == 'center' then\ return Center\ elseif tag == 'img' then\ return Image\ elseif tag == 'a' then\ return Link\ elseif tag == 'float' then\ return Float\ elseif tag == 'br' then\ return Element\ elseif tag == 'input' then\ if attr.type == 'text' then\ return TextInput\ elseif attr.type == 'password' then\ return SecureTextInput\ elseif attr.type == 'submit' or attr.type == 'button' then\ return ButtonInput\ elseif attr.type == 'file' then\ return FileInput\ elseif attr.type == 'hidden' then\ return HiddenInput\ else\ return Element\ end\ elseif tag == 'select' then\ return Select\ elseif tag == 'option' then\ return SelectOption\ elseif tag == 'form' then\ return Form\ elseif tag == 'script' then\ return Script\ else\ return Element\ end\ end",["SelectOption.lua"]="Value = nil\ \ OnInitialise = function(self, node)\ if attr.value then\ new.Value = attr.value\ end\ end\ \ OnCreateObject = function(self, parentObject, y)\ if parentObject.AddMenuItem then\ parentObject:AddMenuItem({\ Value = self.Value,\ Text = self.Text,\ Type = \"Button\"\ })\ end\ end",},["startup"]="local QuestVersion = 'v0.9.0 Private Beta'\ if OneOS then\ QuestVersion = QuestVersion .. '-OneOS ' .. OneOS.Version\ end\ \ -- TODO: have a much simpler API loader... not a 70+ line one\ local Extension = function(path, addDot)\ if not path then\ return nil\ elseif not string.find(fs.getName(path), '%.') then\ if not addDot then\ return fs.getName(path)\ else\ return ''\ end\ else\ local _path = path\ if path:sub(#path) == '/' then\ _path = path:sub(1,#path-1)\ end\ local extension = _path:gmatch('%.[0-9a-z]+$')()\ if extension then\ extension = extension:sub(2)\ else\ --extension = nil\ return ''\ end\ if addDot then\ extension = '.'..extension\ end\ return extension:lower()\ end\ end\ \ local RemoveExtension = function(path)\ if path:sub(1,1) == '.' then\ return path\ end\ local extension = Extension(path)\ if extension == path then\ return fs.getName(path)\ end\ return string.gsub(path, extension, ''):sub(1, -2)\ end\ \ local tAPIsLoading = {}\ local function LoadAPI(_sPath)\ local sName = RemoveExtension(fs.getName( _sPath ))\ if tAPIsLoading[sName] == true then\ end\ tAPIsLoading[sName] = true\ \ local tEnv = {isStartup = true }\ setmetatable( tEnv, { __index = getfenv()} )\ local fnAPI, err = loadfile( _sPath )\ if fnAPI then\ setfenv( fnAPI, tEnv )\ fnAPI()\ else\ printError( err )\ log('Error: '..err)\ tAPIsLoading[sName] = nil\ return false\ end\ \ local tAPI = {}\ for k,v in pairs( tEnv ) do\ tAPI[k] = v\ end\ \ _G[sName] = tAPI\ end\ \ _G.Errors = {\ Unknown = 1,\ InvalidDoctype = 2,\ ParseFailed = 3,\ NotFound = 404,\ TimeoutStop = 408,\ }\ \ 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\ \ os.loadAPI('parser')\ os.loadAPI('hash')\ os.loadAPI('lQuery')\ os.loadAPI('Peripheral')\ os.loadAPI('Wireless')\ \ LoadAPI('Elements/Element.lua')\ \ LoadAPI('Elements/ElementTree.lua')\ \ local elements = {\ 'Script',\ 'Center',\ 'Link',\ 'Image',\ 'Divider',\ 'Heading',\ 'Paragraph',\ 'Float',\ 'TextInput',\ 'FileInput',\ 'SecureTextInput',\ 'Select',\ 'Form',\ 'SelectOption',\ 'ButtonInput',\ 'HiddenInput',\ }\ \ for i, v in ipairs(elements) do\ LoadAPI('Elements/' .. v .. '.lua')\ local env = getfenv()\ local super = Element\ if env[v].Inherit then\ super = env[env[v].Inherit]\ end\ env[v].__index = super\ setmetatable(env[v], env[v])\ end\ \ local program = Bedrock:Initialise()\ \ local function split(str, pat)\ local t = {}\ local fpat = \"(.-)\" .. pat\ local last_end = 1\ local s, e, cap = str:find(fpat, 1)\ while s do\ if s ~= 1 or cap ~= \"\" then\ table.insert(t,cap)\ end\ last_end = e+1\ s, e, cap = str:find(fpat, last_end)\ end\ if last_end <= #str then\ cap = str:sub(last_end)\ table.insert(t, cap)\ end\ return t\ end\ \ local httpQueue = {}\ \ program:RegisterEvent('http_success', function(self, event, url, response)\ for i, request in ipairs(httpQueue) do\ if request[3] == url then\ request[2](true, url, response)\ table.remove(httpQueue, i)\ break\ end\ end\ end)\ \ program:RegisterEvent('http_failure', function(self, event, url)\ for i, request in ipairs(httpQueue) do\ if request[3] == url then\ request[2](false, Errors.Unknown)\ table.remove(httpQueue, i)\ break\ end\ end\ end)\ \ program:RegisterEvent('modem_message', function(self, event, side, channel, replyChannel, message, distance)\ Wireless.HandleMessage(event, side, channel, replyChannel, message, distance)\ end)\ \ local wifiQueue = {}\ \ Wireless.Responder = function(event, side, channel, replyChannel, message, distance)\ if channel == Wireless.Channels.QuestServerRequestReply then\ for i, request in ipairs(wifiQueue) do\ if request[1] == message.content.url then\ if message.content.content then\ local line = 0\ local lines = split(message.content.content, '\\n')\ local handle = {\ readAll = function()return message.content.content end,\ readLine = function()\ line = line + 1\ return lines[line]\ end,\ close = function()end\ }\ request[2](true, message.content.url, handle)\ else\ request[2](false, 404)\ end\ table.remove(wifiQueue, i)\ break\ end\ end\ end\ end\ \ local function cancelHTTPAsync(url)\ for i, request in ipairs(httpQueue) do\ if request[1] == url then\ request[2](false, Errors.TimeoutStop)\ table.remove(httpQueue, i)\ break\ end\ end\ end\ \ local settings = false\ \ local function fetchHTTPAsync(url, callback, post)\ local components = urlComponents(url)\ if components.protocol == 'quest' then\ local file = fs.open(program.ProgramPath .. '/Pages/' .. components.filepathsansget, 'r')\ callback(true, url, file)\ elseif components.protocol == 'file' then\ local file = fs.open(components.sansprotocol, 'r')\ if file then\ callback(true, url, file)\ else\ callback(false)\ end\ elseif components.protocol == 'wifi' then\ if Wireless.Present() then\ table.insert(wifiQueue, {url, callback})\ Wireless.SendMessage(Wireless.Channels.QuestServerRequest, url)\ else\ callback(false, 7)\ end\ elseif components.protocol == 'http' then\ local _url = resolveQuestHostUrl(url)\ table.insert(httpQueue, {url, callback, _url})\ \ if not post then\ post = 'questClientIdentifier=' .. textutils.urlEncode(settings.ClientIdentifier)\ else\ post = post .. '&questClientIdentifier=' .. textutils.urlEncode(settings.ClientIdentifier)\ end\ http.request(_url, post, {\ ['User-Agent'] = 'Quest/'..QuestVersion\ })\ end\ end\ \ local questHost = 'http://quest.net76.net/sites/'\ \ local function findLast(haystack, needle)\ local i=haystack:match(\".*\"..needle..\"()\")\ if i==nil then return nil else return i-1 end\ end\ \ local hex_to_char = function(x)\ return string.char(tonumber(x, 16))\ end\ \ local function urlUnencode( str )\ -- essentially reverses textutils.urlDecode\ if str then\ str = string.gsub(str, \"+\", \" \")\ str = string.gsub(str, \"\\r\\n\", \"\\n\")\ term.setTextColor(colors.black)\ str = str:gsub(\"%%(%x%x)\", hex_to_char)\ end\ return str \ end\ \ local function urlComponents(url)\ if url then\ urlUnencode(textutils.urlEncode(url))\ local components = {}\ local parts = split(url, '[\\\\/]+')\ if url:find('://') and parts[1]:sub(#parts[1]) == ':' then\ components.protocol = parts[1]:sub(1, #parts[1]-1)\ components.sansprotocol = url:sub(#components.protocol + 4)\ components.host = parts[2]\ components.fullhost = components.protocol .. '://' .. parts[2] .. '/'\ components.filepath = url:sub(#components.fullhost)\ if components.filepath:sub(#components.filepath) ~= '/' and components.filepath:find('?') then\ components.filename = fs.getName(components.filepath:sub(1, components.filepath:find('?') - 1))\ else\ components.filename = fs.getName(components.filepath)\ end\ if components.filename == 'root' or components.filename == components.host then\ components.filename = ''\ end\ components.base = url:sub(1, findLast(url, '/'))\ components.get = {}\ components.filepathsansget = components.sansprotocol\ if url:find('?') then\ local start = url:find('?')\ components.filepathsansget = url:sub(#components.protocol + 4, start - 1)\ local getString = url:sub(start + 1)\ local values = split(getString, '&')\ for i, v in ipairs(values) do\ local keyvalue = split(v, '=')\ components.get[keyvalue[1]] = urlUnencode(keyvalue[2])\ end\ end\ return components\ end\ end\ end\ \ local function resolveQuestHostUrl(url)\ local components = urlComponents(url)\ local hostParts = split(components.host, '%.')\ local tld = hostParts[#hostParts]\ if tld == 'qst' and #hostParts == 2 then\ return questHost .. hostParts[1] .. components.filepath\ end\ return url\ end\ \ local function resolveFullUrl(url)\ if url and type(url) ~= 'string' then\ elseif url:find('://') then\ return url\ else\ local components = urlComponents(program:GetObject('WebView').URL)\ if components then\ if url:sub(1,1) == '/' then\ return components.fullhost .. url:sub(2)\ else\ return components.base .. url\ end\ end\ end\ end\ \ local function getCurrentUrl()\ return program:GetObject('WebView').URL\ end\ \ local function getCurrentFakeUrl()\ return program:GetObject('WebView').FakeURL\ end\ \ local function goToUrl(url, post)\ program:GetObject('WebView'):GoToURL(url, nil, nil, post)\ end\ \ -- Yes, it's evil and terrible, etc. But I'll hopefully change it later.\ _G.cancelHTTPAsync = cancelHTTPAsync\ _G.fetchHTTPAsync = fetchHTTPAsync\ _G.resolveFullUrl = resolveFullUrl\ _G.resolveQuestHostUrl = resolveQuestHostUrl\ _G.getCurrentUrl = getCurrentUrl\ _G.getCurrentFakeUrl = getCurrentFakeUrl\ _G.goToUrl = goToUrl\ _G.split = split\ _G.urlComponents = urlComponents\ _G.QuestVersion = QuestVersion\ \ local history = {}\ local historyItem = 0\ \ local function updateHistoryButtons()\ if history[historyItem-1] then\ program:GetObject('BackButton').Enabled = true\ else\ program:GetObject('BackButton').Enabled = false\ end\ \ if history[historyItem+1] then\ program:GetObject('ForwardButton').Enabled = true\ else\ program:GetObject('ForwardButton').Enabled = false\ end\ end\ \ local function addHistoryURL(url)\ for i, v in ipairs(history) do\ if i > historyItem then\ history[i] = nil\ end\ end\ table.insert(history, url)\ historyItem = #history\ updateHistoryButtons()\ end\ \ local defaultSettings = {\ Home = 'http://thehub.qst/',\ ClientIdentifier = nil\ }\ \ local function quit()\ term.setBackgroundColour(colors.black)\ shell.run('clear')\ print('Thanks for using Quest, created by oeed.')\ program:Quit()\ end\ \ local function goHome()\ goToUrl(settings.Home)\ end\ \ local function saveSettings()\ local f = fs.open('.Quest.settings', 'w')\ if f then\ f.write(textutils.serialize(settings))\ f.close()\ end\ end\ \ local function generateClientIdentifier()\ program:DisplayWindow({\ Children = {{\ X = 2,\ Y = 2,\ Type = \"Label\",\ Width = \"100%,-2\",\ Height = 2,\ Text = \"Registering computer with central server...\"\ }},\ Width = 28,\ Height = 4\ }, \"Please Wait\", false)\ program:Draw()\ local h = http.get('http://quest.net76.net/registerClient.php')\ program.Window:Close()\ \ if h then\ settings.ClientIdentifier = h.readAll()\ saveSettings()\ h.close()\ goHome()\ else\ program:DisplayAlertWindow(\"Register Failed\", \"Quest couldn't register your computer. There was something wrong with your internet connection. Please quit and try again.\", {'Quit'}, function(value)\ quit()\ end)\ end\ \ end\ \ local function loadSettings()\ if fs.exists('.Quest.settings') then\ local f = fs.open('.Quest.settings', 'r')\ if f then\ settings = textutils.unserialize(f.readAll())\ if not settings.ClientIdentifier then\ generateClientIdentifier()\ end\ return settings\ end\ end\ \ settings = defaultSettings\ generateClientIdentifier()\ end\ \ program:Run(function()\ local timeoutTimer = false\ \ program:LoadView('main')\ \ Wireless.Initialise()\ \ loadSettings()\ \ program:GetObject('BackButton').OnClick = function(self)\ if history[historyItem-1] then\ historyItem = historyItem - 1\ program:GetObject('WebView'):GoToURL(history[historyItem], nil, true)\ updateHistoryButtons()\ end\ end\ \ program:GetObject('ForwardButton').OnClick = function(self)\ if history[historyItem+1] then\ historyItem = historyItem + 1\ program:GetObject('WebView'):GoToURL(history[historyItem], nil, true)\ updateHistoryButtons()\ end\ end\ \ program:GetObject('URLTextBox').OnChange = function(self, event, keychar)\ if keychar == keys.enter then\ local url = self.Text\ if not url:find('://') then\ if url:find(' ') or not url:find('%.') then\ url = 'http://thehub.qst/search.php?q='..textutils.urlEncode(url)\ else\ url = 'http://' .. url \ end\ self.Text = url\ end\ program:GetObject('WebView'):GoToURL(url)\ end\ end\ \ program:GetObject('OptionsButton').OnClick = function(self, event, side, x, y)\ if self:ToggleMenu('optionsmenu', x, y) then\ program:GetObject('StopMenuItem').OnClick = function(self, event, side, x, y)\ program:GetObject('WebView'):Stop()\ end\ \ program:GetObject('ReloadMenuItem').OnClick = function(self, event, side, x, y)\ program:GetObject('WebView'):GoToURL(program:GetObject('WebView').URL)\ end\ \ program:GetObject('GoHomeMenuItem').OnClick = function(self, event, side, x, y)\ goHome()\ end\ \ program:GetObject('SetHomeMenuItem').OnClick = function(self, event, side, x, y)\ settings.Home = program:GetObject('WebView').FakeURL\ saveSettings()\ end\ \ program:GetObject('QuitMenuItem').OnClick = function(self, event, side, x, y)\ quit()\ end\ end\ end\ \ program:GetObject('WebView').OnPageLoadStart = function(self, url)\ program:SetActiveObject()\ -- program:GetObject('GoButton').Text = 'x'\ program:GetObject('URLTextBox').Visible = false\ program:GetObject('LoadingLabel').Visible = true\ program:GetObject('PageTitleLabel').Text = ''\ \ if url:find('http://') or url:find('https://') then\ timeoutTimer = program:StartTimer(function()\ program:GetObject('WebView'):Stop()\ end, 20)\ else\ timeoutTimer = program:StartTimer(function()\ program:GetObject('WebView'):Stop()\ end, 1)\ end\ end\ \ program:GetObject('WebView').OnPageLoadEnd = function(self, url, noHistory)\ program:GetObject('URLTextBox').Text = url\ program:GetObject('URLTextBox').Visible = true\ program:GetObject('LoadingLabel').Visible = false\ \ if self.Tree:GetElement('title') then\ program:GetObject('PageTitleLabel').Text = self.Tree:GetElement('title').Text\ end\ \ if not noHistory then\ addHistoryURL(url)\ end\ \ program.Timers[timeoutTimer] = nil\ end\ \ program:GetObject('WebView').OnPageLoadFailed = function(self, url, _error, noHistory)\ program:GetObject('URLTextBox').Text = url\ program:GetObject('URLTextBox').Visible = true\ program:GetObject('LoadingLabel').Visible = false\ program.Timers[timeoutTimer] = nil\ \ if not noHistory then\ addHistoryURL(url)\ end\ \ local get = ''\ _error = _error or 1\ if type(_error) == 'string' then\ get = '?reason='..textutils.urlEncode(_error)\ _error = 'text'\ end\ \ program:GetObject('WebView'):GoToURL('quest://'.._error..'.ccml'..get, true, true)\ end\ \ program:GetObject('Toolbar').OnClick = function(self, event, side, x, y)\ program:SetActiveObject()\ end\ \ program:GetObject('WebView').OnClick = function(self, event, side, x, y)\ program:SetActiveObject()\ end\ \ if settings.ClientIdentifier then\ goHome()\ end\ end)\ \ ",["Bedrock"]="--Bedrock Build: 262\ --This code is squished down in to one, rather hard to read file.\ --As such it is not much good for anything other than being loaded as an API.\ --If you want to look at the code to learn from it, copy parts or just take a look,\ --you should go to the GitHub repo. http://github.com/oeed/Bedrock/\ \ --\ -- Bedrock is the core program framework used by all OneOS and OneCode programs.\ -- Inspired by Apple's Cocoa framework.\ -- (c) oeed 2014\ --\ -- For documentation see the Bedrock wiki, github.com/oeed/Bedrock/wiki/\ --\ \ local apis = {\ [\"Drawing\"] = [[\ local round = function(num, idp)\ local mult = 10^(idp or 0)\ return math.floor(num * mult + 0.5) / mult\ end\ \ local _w, _h = term.getSize()\ local copyBuffer = nil\ \ Screen = {\ Width = _w,\ Height = _h\ }\ \ Constraints = {\ \ }\ \ CurrentConstraint = {1,1,_w,_h}\ IgnoreConstraint = false\ \ function AddConstraint(x, y, width, height)\ local x2 = x + width - 1\ local y2 = y + height - 1\ table.insert(Drawing.Constraints, {x, y, x2, y2})\ Drawing.GetConstraint()\ end\ \ function RemoveConstraint()\ --table.remove(Drawing.Constraints, #Drawing.Constraints)\ Drawing.Constraints[#Drawing.Constraints] = nil\ Drawing.GetConstraint()\ end\ \ function GetConstraint()\ local x = 1\ local y = 1\ local x2 = Drawing.Screen.Width\ local y2 = Drawing.Screen.Height\ for i, c in ipairs(Drawing.Constraints) do\ if x < c[1] then\ x = c[1]\ end\ if y < c[2] then\ y = c[2]\ end\ if x2 > c[3] then\ x2 = c[3]\ end\ if y2 > c[4] then\ y2 = c[4]\ end\ end\ Drawing.CurrentConstraint = {x, y, x2, y2}\ end\ \ function WithinContraint(x, y)\ return Drawing.IgnoreConstraint or\ (x >= Drawing.CurrentConstraint[1] and\ y >= Drawing.CurrentConstraint[2] and\ x <= Drawing.CurrentConstraint[3] and\ y <= Drawing.CurrentConstraint[4])\ end\ \ colours.transparent = 0\ colors.transparent = 0\ \ DrawCharacters = function (x, y, characters, textColour, bgColour)\ Drawing.WriteStringToBuffer(x, y, tostring(characters), textColour, bgColour)\ end\ \ DrawBlankArea = function (x, y, w, h, colour)\ if colour ~= colours.transparent then\ Drawing.DrawArea (x, y, w, h, \" \", 1, colour)\ end\ end\ \ DrawArea = function (x, y, w, h, character, textColour, bgColour)\ --width must be greater than 1, otherwise we get problems\ if w < 0 then\ w = w * -1\ elseif w == 0 then\ w = 1\ end\ \ for ix = 1, w do\ local currX = x + ix - 1\ for iy = 1, h do\ local currY = y + iy - 1\ Drawing.WriteToBuffer(currX, currY, character, textColour, bgColour)\ end\ end\ end\ \ DrawImage = function(_x,_y,tImage, w, h)\ if tImage then\ for y = 1, h do\ if not tImage[y] then\ break\ end\ for x = 1, w do\ if not tImage[y][x] then\ break\ end\ local bgColour = tImage[y][x]\ local textColour = tImage.textcol[y][x] or colours.white\ local char = tImage.text[y][x]\ Drawing.WriteToBuffer(x+_x-1, y+_y-1, char, textColour, bgColour)\ end\ end\ elseif w and h then\ Drawing.DrawBlankArea(_x, _y, w, h, colours.lightGrey)\ end\ end\ \ --using .nft\ LoadImage = function(path, global)\ local image = {\ text = {},\ textcol = {}\ }\ if fs.exists(path) then\ local _io = io\ if OneOS and global then\ _io = OneOS.IO\ end\ local file = _io.open(path, \"r\")\ if not file then\ error('Error Occured. _io:'..tostring(_io)..' OneOS: '..tostring(OneOS)..' OneOS.IO'..tostring(OneOS.IO)..' io: '..tostring(io))\ end\ local sLine = file:read()\ local num = 1\ while sLine do \ table.insert(image, num, {})\ table.insert(image.text, num, {})\ table.insert(image.textcol, num, {})\ \ --As we're no longer 1-1, we keep track of what index to write to\ local writeIndex = 1\ --Tells us if we've hit a 30 or 31 (BG and FG respectively)- next char specifies the curr colour\ local bgNext, fgNext = false, false\ --The current background and foreground colours\ local currBG, currFG = nil,nil\ for i=1,#sLine do\ local nextChar = string.sub(sLine, i, i)\ if nextChar:byte() == 30 then\ bgNext = true\ elseif nextChar:byte() == 31 then\ fgNext = true\ elseif bgNext then\ currBG = Drawing.GetColour(nextChar)\ if currBG == nil then\ currBG = colours.transparent\ end\ bgNext = false\ elseif fgNext then\ currFG = Drawing.GetColour(nextChar)\ if currFG == nil or currFG == colours.transparent then\ currFG = colours.white\ end\ fgNext = false\ else\ if nextChar ~= \" \" and currFG == nil then\ currFG = colours.white\ end\ image[num][writeIndex] = currBG\ image.textcol[num][writeIndex] = currFG\ image.text[num][writeIndex] = nextChar\ writeIndex = writeIndex + 1\ end\ end\ num = num+1\ sLine = file:read()\ end\ file:close()\ else\ return nil\ end\ return image\ end\ \ DrawCharactersCenter = function(x, y, w, h, characters, textColour,bgColour)\ w = w or Drawing.Screen.Width\ h = h or Drawing.Screen.Height\ x = x or 0\ y = y or 0\ x = math.floor((w - #characters) / 2) + x\ y = math.floor(h / 2) + y\ \ Drawing.DrawCharacters(x, y, characters, textColour, bgColour)\ end\ \ GetColour = function(hex)\ if hex == ' ' then\ return colours.transparent\ end\ local value = tonumber(hex, 16)\ if not value then return nil end\ value = math.pow(2,value)\ return value\ end\ \ Clear = function (_colour)\ _colour = _colour or colours.black\ Drawing.DrawBlankArea(1, 1, Drawing.Screen.Width, Drawing.Screen.Height, _colour)\ end\ \ Buffer = {}\ BackBuffer = {}\ \ TryRestore = false\ \ \ --TODO: make this quicker\ -- maybe sort the pixels in order of colour so it doesn't have to set the colour each time\ DrawBuffer = function()\ if TryRestore and Restore then\ Restore()\ end\ \ for y,row in pairs(Drawing.Buffer) do\ for x,pixel in pairs(row) do\ local shouldDraw = true\ local hasBackBuffer = true\ if Drawing.BackBuffer[y] == nil or Drawing.BackBuffer[y][x] == nil or #Drawing.BackBuffer[y][x] ~= 3 then\ hasBackBuffer = false\ end\ 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\ shouldDraw = false\ end\ if shouldDraw then\ term.setBackgroundColour(pixel[3])\ term.setTextColour(pixel[2])\ term.setCursorPos(x, y)\ term.write(pixel[1])\ end\ end\ end\ Drawing.BackBuffer = Drawing.Buffer\ Drawing.Buffer = {}\ end\ \ ClearBuffer = function()\ Drawing.Buffer = {}\ end\ \ WriteStringToBuffer = function (x, y, characters, textColour,bgColour)\ for i = 1, #characters do\ local character = characters:sub(i,i)\ Drawing.WriteToBuffer(x + i - 1, y, character, textColour, bgColour)\ end\ end\ \ WriteToBuffer = function(x, y, character, textColour,bgColour, cached)\ if not cached and not Drawing.WithinContraint(x, y) then\ return\ end\ x = round(x)\ y = round(y)\ \ if textColour == colours.transparent then\ character = ' '\ end\ \ if bgColour == colours.transparent then\ Drawing.Buffer[y] = Drawing.Buffer[y] or {}\ Drawing.Buffer[y][x] = Drawing.Buffer[y][x] or {\"\", colours.white, colours.black}\ Drawing.Buffer[y][x][1] = character\ Drawing.Buffer[y][x][2] = textColour\ else\ Drawing.Buffer[y] = Drawing.Buffer[y] or {}\ Drawing.Buffer[y][x] = {character, textColour, bgColour}\ end\ \ if copyBuffer then\ copyBuffer[y] = copyBuffer[y] or {}\ copyBuffer[y][x] = {character, textColour, bgColour} \ end\ end\ \ DrawCachedBuffer = function(buffer)\ for y, row in pairs(buffer) do\ for x, pixel in pairs(row) do\ WriteToBuffer(x, y, pixel[1], pixel[2], pixel[3], true)\ end\ end\ end\ \ StartCopyBuffer = function()\ copyBuffer = {}\ end\ \ EndCopyBuffer = function()\ local tmpCopy = copyBuffer\ copyBuffer = nil\ return tmpCopy\ end\ ]],\ [\"Helpers\"] = [[\ LongestString = function(input, key, isKey)\ local length = 0\ if isKey then\ for k, v in pairs(input) do\ local titleLength = string.len(k)\ if titleLength > length then\ length = titleLength\ end\ end\ else\ for i = 1, #input do\ local value = input[i]\ if key then\ if value[key] then\ value = value[key]\ else\ value = ''\ end\ end\ local titleLength = string.len(value)\ if titleLength > length then\ length = titleLength\ end\ end\ end\ return length\ end\ \ Split = function(str,sep)\ sep=sep or'/'\ return str:match(\"(.*\"..sep..\")\")\ end\ \ Extension = function(path, addDot)\ if not path then\ return nil\ elseif not string.find(fs.getName(path), '%.') then\ if not addDot then\ return fs.getName(path)\ else\ return ''\ end\ else\ local _path = path\ if path:sub(#path) == '/' then\ _path = path:sub(1,#path-1)\ end\ local extension = _path:gmatch('%.[0-9a-z]+$')()\ if extension then\ extension = extension:sub(2)\ else\ --extension = nil\ return ''\ end\ if addDot then\ extension = '.'..extension\ end\ return extension:lower()\ end\ end\ \ RemoveExtension = function(path)\ --local name = string.match(fs.getName(path), '(%a+)%.?.-')\ if path:sub(1,1) == '.' then\ return path\ end\ local extension = Helpers.Extension(path)\ if extension == path then\ return fs.getName(path)\ end\ return string.gsub(path, extension, ''):sub(1, -2)\ end\ \ RemoveFileName = function(path)\ if string.sub(path, -1) == '/' then\ path = string.sub(path, 1, -2)\ end\ local v = string.match(path, \"(.-)([^\\\\/]-%.?([^%.\\\\/]*))$\")\ if type(v) == 'string' then\ return v\ end\ return v[1]\ end\ \ TruncateString = function(sString, maxLength)\ if #sString > maxLength then\ sString = sString:sub(1,maxLength-3)\ if sString:sub(-1) == ' ' then\ sString = sString:sub(1,maxLength-4)\ end\ sString = sString .. '...'\ end\ return sString\ end\ \ TruncateStringStart = function(sString, maxLength)\ local len = #sString\ if #sString > maxLength then\ sString = sString:sub(len - maxLength, len - 3)\ if sString:sub(-1) == ' ' then\ sString = sString:sub(len - maxLength, len - 4)\ end\ sString = '...' .. sString\ end\ return sString\ end\ \ WrapText = function(text, maxWidth)\ local lines = {''}\ for word, space in text:gmatch('(%S+)(%s*)') do\ local temp = lines[#lines] .. word .. space:gsub('\\n','')\ if #temp > maxWidth then\ table.insert(lines, '')\ end\ if space:find('\\n') then\ lines[#lines] = lines[#lines] .. word\ \ space = space:gsub('\\n', function()\ table.insert(lines, '')\ return ''\ end)\ else\ lines[#lines] = lines[#lines] .. word .. space\ end\ end\ return lines\ end\ \ TidyPath = function(path)\ path = '/'..path\ if fs.exists(path) and fs.isDir(path) then\ path = path .. '/'\ end\ \ path, n = path:gsub(\"//\", \"/\")\ while n > 0 do\ path, n = path:gsub(\"//\", \"/\")\ end\ return path\ end\ \ Capitalise = function(str)\ return str:sub(1, 1):upper() .. str:sub(2, -1)\ end\ \ Round = function(num, idp)\ local mult = 10^(idp or 0)\ return math.floor(num * mult + 0.5) / mult\ end\ ]],\ [\"Object\"] = [[\ X = 1\ Y = 1\ Width = 1\ Height = 1\ Parent = nil\ OnClick = nil\ Visible = true\ IgnoreClick = false\ Name = nil \ ClipDrawing = true\ UpdateDrawBlacklist = {}\ Fixed = false\ \ DrawCache = {}\ \ NeedsDraw = function(self)\ if not self.Visible then\ return false\ end\ \ if not self.DrawCache.Buffer or self.DrawCache.AlwaysDraw or self.DrawCache.NeedsDraw then\ return true\ end\ \ if self.OnNeedsUpdate then\ if self.OnNeedsUpdate() then\ return true\ end\ end\ \ if self.Children then\ for i, v in ipairs(self.Children) do\ if v:NeedsDraw() then\ return true\ end\ end\ end\ end\ \ GetPosition = function(self)\ return self.Bedrock:GetAbsolutePosition(self)\ end\ \ GetOffsetPosition = function(self)\ if not self.Parent then\ return {X = 1, Y = 1}\ end\ \ local offset = {X = 0, Y = 0}\ if not self.Fixed and self.Parent.ChildOffset then\ offset = self.Parent.ChildOffset\ end\ \ return {X = self.X + offset.X, Y = self.Y + offset.Y}\ end\ \ Draw = function(self)\ if not self.Visible then\ return\ end\ \ self.DrawCache.NeedsDraw = false\ local pos = self:GetPosition()\ Drawing.StartCopyBuffer()\ \ if self.ClipDrawing then\ Drawing.AddConstraint(pos.X, pos.Y, self.Width, self.Height)\ end\ \ if self.OnDraw then\ self:OnDraw(pos.X, pos.Y)\ end\ \ self.DrawCache.Buffer = Drawing.EndCopyBuffer()\ \ if self.Children then\ for i, child in ipairs(self.Children) do\ local pos = child:GetOffsetPosition()\ if pos.Y + self.Height > 1 and pos.Y <= self.Height and pos.X + self.Width > 1 and pos.X <= self.Width then\ child:Draw()\ end\ end\ end\ \ if self.ClipDrawing then\ Drawing.RemoveConstraint()\ end \ end\ \ ForceDraw = function(self, ignoreChildren, ignoreParent, ignoreBedrock)\ if not ignoreBedrock and self.Bedrock then\ self.Bedrock:ForceDraw()\ end\ self.DrawCache.NeedsDraw = true\ if not ignoreParent and self.Parent then\ self.Parent:ForceDraw(true, nil, true)\ end\ if not ignoreChildren and self.Children then\ for i, child in ipairs(self.Children) do\ child:ForceDraw(nil, true, true)\ end\ end\ end\ \ OnRemove = function(self)\ if self == self.Bedrock:GetActiveObject() then\ self.Bedrock:SetActiveObject()\ end\ end\ \ local function ParseColour(value)\ if type(value) == 'string' then\ if colours[value] and type(colours[value]) == 'number' then\ return colours[value]\ elseif colors[value] and type(colors[value]) == 'number' then\ return colors[value]\ end\ elseif type(value) == 'number' and (value == colours.transparent or (value >= colours.white and value <= colours.black)) then\ return value\ end\ error('Invalid colour: \"'..tostring(value)..'\"')\ end\ \ Initialise = function(self, values)\ local _new = values -- the new instance\ _new.DrawCache = {\ NeedsDraw = true,\ AlwaysDraw = false,\ Buffer = nil\ }\ setmetatable(_new, {__index = self} )\ \ local new = {} -- the proxy\ setmetatable(new, {\ __index = function(t, k)\ if k:find('Color') then\ k = k:gsub('Color', 'Colour')\ end\ \ if k:find('Colour') then\ if _new[k] then\ return ParseColour(_new[k])\ end\ elseif _new[k] ~= nil then\ return _new[k]\ end\ end,\ \ __newindex = function (t,k,v)\ if k:find('Color') then\ k = k:gsub('Color', 'Colour')\ end\ \ if k == 'Width' or k == 'X' or k == 'Height' or k == 'Y' then\ v = new.Bedrock:ParseStringSize(new.Parent, k, v)\ end\ \ if v ~= _new[k] then\ _new[k] = v\ if t.OnUpdate then\ t:OnUpdate(k)\ end\ \ if t.UpdateDrawBlacklist[k] == nil then\ t:ForceDraw()\ end\ end\ end\ })\ if new.OnInitialise then\ new:OnInitialise()\ end\ \ return new\ end\ \ Click = function(self, event, side, x, y)\ if self.Visible and not self.IgnoreClick then\ if event == 'mouse_click' and self.OnClick and self:OnClick(event, side, x, y) ~= false then\ return true\ elseif event == 'mouse_drag' and self.OnDrag and self:OnDrag(event, side, x, y) ~= false then\ return true\ elseif event == 'mouse_scroll' and self.OnScroll and self:OnScroll(event, side, x, y) ~= false then\ return true\ else\ return false\ end\ else\ return false\ end\ \ end\ \ ToggleMenu = function(self, name, x, y)\ return self.Bedrock:ToggleMenu(name, self, x, y)\ end\ \ function OnUpdate(self, value)\ if value == 'Z' then\ self.Bedrock:ReorderObjects()\ end\ end\ ]],\ }\ local objects = {\ [\"Button\"] = [[\ BackgroundColour = colours.lightGrey\ ActiveBackgroundColour = colours.blue\ ActiveTextColour = colours.white\ TextColour = colours.black\ DisabledTextColour = colours.lightGrey\ Text = \"\"\ Toggle = nil\ Momentary = true\ AutoWidthAutoWidth = true\ Align = 'Center'\ Enabled = true\ \ OnUpdate = function(self, value)\ if value == 'Text' and self.AutoWidth then\ self.Width = #self.Text + 2\ end\ end\ \ OnDraw = function(self, x, y)\ local bg = self.BackgroundColour\ \ if self.Toggle then\ bg = self.ActiveBackgroundColour\ end\ \ local txt = self.TextColour\ if self.Toggle then\ txt = self.ActiveTextColour\ end\ if not self.Enabled then\ txt = self.DisabledTextColour\ end\ Drawing.DrawBlankArea(x, y, self.Width, self.Height, bg)\ \ local _x = 1\ if self.Align == 'Right' then\ _x = self.Width - #self.Text - 1\ elseif self.Align == 'Center' then\ _x = math.floor((self.Width - #self.Text) / 2)\ end\ Drawing.DrawCharacters(x + _x, y, self.Text, txt, bg)\ end\ \ OnLoad = function(self)\ if self.Toggle ~= nil then\ self.Momentary = false\ end\ end\ \ Click = function(self, event, side, x, y)\ if self.Visible and not self.IgnoreClick and self.Enabled and event ~= 'mouse_scroll' then\ if self.OnClick then\ if self.Momentary then\ self.Toggle = true\ self.Bedrock:StartTimer(function()self.Toggle = false end,0.25)\ elseif self.Toggle ~= nil then\ self.Toggle = not self.Toggle\ end\ \ self:OnClick(event, side, x, y, self.Toggle)\ else\ self.Toggle = not self.Toggle\ end\ return true\ else\ return false\ end\ end\ ]],\ [\"CollectionView\"] = [[\ Inherit = 'ScrollView'\ UpdateDrawBlacklist = {['NeedsItemUpdate']=true}\ \ TextColour = colours.black\ BackgroundColour = colours.white\ Items = false\ NeedsItemUpdate = false\ SpacingX = 2\ SpacingY = 1\ \ OnDraw = function(self, x, y)\ if self.NeedsItemUpdate then\ self:UpdateItems()\ self.NeedsItemUpdate = false\ end\ Drawing.DrawBlankArea(x, y, self.Width, self.Height, self.BackgroundColour)\ end\ \ local function MaxIcons(self, obj)\ local x, y = 2, 1\ if not obj.Height or not obj.Width then\ error('You must provide each object\\'s height when adding to a CollectionView.')\ end\ local slotHeight = obj.Height + self.SpacingY\ local slotWidth = obj.Width + self.SpacingX\ local maxX = math.floor((self.Width - 2) / slotWidth)\ return x, y, maxX, slotWidth, slotHeight\ end\ \ local function IconLocation(self, obj, i)\ local x, y, maxX, slotWidth, slotHeight = MaxIcons(self, obj)\ local rowPos = ((i - 1) % maxX)\ local colPos = math.ceil(i / maxX) - 1\ x = x + (slotWidth * rowPos)\ y = y + colPos * slotHeight\ return x, y\ end\ \ local function AddItem(self, v, i)\ local toggle = false\ if not self.CanSelect then\ toggle = nil\ end\ local x, y = IconLocation(self, v, i)\ local item = {\ [\"X\"]=x,\ [\"Y\"]=y,\ [\"Name\"]=\"CollectionViewItem\",\ [\"Type\"]=\"View\",\ [\"TextColour\"]=self.TextColour,\ [\"BackgroundColour\"]=0F,\ OnClick = function(itm)\ if self.CanSelect then\ for i2, _v in ipairs(self.Children) do\ _v.Toggle = false\ end\ self.Selected = itm\ end\ end\ }\ for k, _v in pairs(v) do\ item[k] = _v\ end\ self:AddObject(item)\ end\ \ \ UpdateItems = function(self)\ self:RemoveAllObjects()\ local groupMode = false\ for k, v in pairs(self.Items) do\ if type(k) == 'string' then\ groupMode = true\ break\ end\ end\ \ for i, v in ipairs(self.Items) do\ AddItem(self, v, i)\ end\ self:UpdateScroll()\ end\ \ OnUpdate = function(self, value)\ if value == 'Items' then\ self.NeedsItemUpdate = true\ end\ end\ ]],\ [\"ImageView\"] = [[\ Image = false\ \ OnDraw = function(self, x, y)\ Drawing.DrawImage(x, y, self.Image, self.Width, self.Height)\ end\ \ OnLoad = function(self)\ if self.Path and fs.exists(self.Path) then\ self.Image = Drawing.LoadImage(self.Path)\ end\ end\ \ OnUpdate = function(self, value)\ if value == 'Path' then\ if self.Path and fs.exists(self.Path) then\ self.Image = Drawing.LoadImage(self.Path)\ end\ end\ end\ ]],\ [\"Label\"] = [[\ TextColour = colours.black\ BackgroundColour = colours.transparent\ Text = \"\"\ AutoWidth = false\ Align = 'Left'\ \ local wrapText = function(text, maxWidth)\ local lines = {''}\ for word, space in text:gmatch('(%S+)(%s*)') do\ local temp = lines[#lines] .. word .. space:gsub('\\n','')\ if #temp > maxWidth then\ table.insert(lines, '')\ end\ if space:find('\\n') then\ lines[#lines] = lines[#lines] .. word\ \ space = space:gsub('\\n', function()\ table.insert(lines, '')\ return ''\ end)\ else\ lines[#lines] = lines[#lines] .. word .. space\ end\ end\ if #lines[1] == 0 then\ table.remove(lines,1)\ end\ return lines\ end\ \ OnUpdate = function(self, value)\ if value == 'Text' then\ if self.AutoWidth then\ self.Width = #self.Text\ else\ self.Height = #wrapText(self.Text, self.Width)\ end\ end\ end\ \ OnDraw = function(self, x, y)\ for i, v in ipairs(wrapText(self.Text, self.Width)) do\ local _x = 0\ if self.Align == 'Right' then\ _x = self.Width - #v\ elseif self.Align == 'Center' then\ _x = math.floor((self.Width - #v) / 2)\ end\ Drawing.DrawCharacters(x + _x, y + i - 1, v, self.TextColour, self.BackgroundColour)\ end\ end\ ]],\ [\"ListView\"] = [[\ Inherit = 'ScrollView'\ UpdateDrawBlacklist = {['NeedsItemUpdate']=true}\ \ TextColour = colours.black\ BackgroundColour = colours.white\ HeadingColour = colours.lightGrey\ SelectionBackgroundColour = colours.blue\ SelectionTextColour = colours.white\ Items = false\ CanSelect = false\ Selected = nil\ NeedsItemUpdate = false\ ItemMargin = 1\ HeadingMargin = 0\ TopMargin = 0\ \ OnDraw = function(self, x, y)\ if self.NeedsItemUpdate then\ self:UpdateItems()\ end\ Drawing.DrawBlankArea(x, y, self.Width, self.Height, self.BackgroundColour)\ end\ \ local function AddItem(self, v, x, y, group)\ local toggle = false\ if not self.CanSelect then\ toggle = nil\ elseif v.Selected then\ toggle = true\ end\ local item = {\ [\"Width\"]=self.Width,\ [\"X\"]=x,\ [\"Y\"]=y,\ [\"Name\"]=\"ListViewItem\",\ [\"Type\"]=\"Button\",\ [\"TextColour\"]=self.TextColour,\ [\"BackgroundColour\"]=0,\ [\"ActiveTextColour\"]=self.SelectionTextColour,\ [\"ActiveBackgroundColour\"]=self.SelectionBackgroundColour,\ [\"Align\"]='Left',\ [\"Toggle\"]=toggle,\ [\"Group\"]=group,\ OnClick = function(itm)\ if self.CanSelect then\ self:SelectItem(itm)\ elseif self.OnSelect then\ self:OnSelect(itm.Text)\ end\ end\ }\ if type(v) == 'table' then\ for k, _v in pairs(v) do\ item[k] = _v\ end\ else\ item.Text = v\ end\ \ local itm = self:AddObject(item)\ if v.Selected then\ self:SelectItem(itm)\ end\ end\ \ UpdateItems = function(self)\ if not self.Items or type(self.Items) ~= 'table' then\ self.Items = {}\ end\ self.Selected = nil\ self:RemoveAllObjects()\ local groupMode = false\ for k, v in pairs(self.Items) do\ if type(k) == 'string' then\ groupMode = true\ break\ end\ end\ \ if not groupMode then\ for i, v in ipairs(self.Items) do\ AddItem(self, v, self.ItemMargin, i)\ end\ else\ local y = self.TopMargin\ for k, v in pairs(self.Items) do\ y = y + 1\ AddItem(self, {Text = k, TextColour = self.HeadingColour, IgnoreClick = true}, self.HeadingMargin, y)\ for i, _v in ipairs(v) do\ y = y + 1\ AddItem(self, _v, 1, y, k)\ end\ y = y + 1\ end\ end\ self:UpdateScroll()\ self.NeedsItemUpdate = false\ end\ \ OnKeyChar = function(self, event, keychar)\ if keychar == keys.up or keychar == keys.down then\ local n = self:GetIndex(self.Selected)\ if keychar == keys.up then\ n = n - 1\ else\ n = n + 1\ end\ local new = self:GetNth(n)\ if new then\ self:SelectItem(new)\ end\ elseif keychar == keys.enter and self.Selected then\ self.Selected:Click('mouse_click', 1, 1, 1)\ end\ end\ \ --returns the index/'n' of the given item\ GetIndex = function(self, obj)\ local n = 1\ for i, v in ipairs(self.Children) do\ if not v.IgnoreClick then\ if obj == v then\ return n\ end\ n = n + 1\ end\ end\ end\ \ --gets the 'nth' list item (does not include headings)\ GetNth = function(self, n)\ local _n = 1\ for i, v in ipairs(self.Children) do\ if not v.IgnoreClick then\ if n == _n then\ return v\ end\ _n = _n + 1\ end\ end\ end\ \ SelectItem = function(self, item)\ for i, v in ipairs(self.Children) do\ v.Toggle = false\ end\ self.Selected = item\ item.Toggle = true\ if self.OnSelect then\ self:OnSelect(item.Text)\ end\ end\ \ OnUpdate = function(self, value)\ if value == 'Items' then\ self.NeedsItemUpdate = true\ end\ end\ ]],\ [\"Menu\"] = [[\ Inherit = 'View'\ \ TextColour = colours.black\ BackgroundColour = colours.white\ HideTop = false\ \ OnDraw = function(self, x, y)\ Drawing.IgnoreConstraint = true\ Drawing.DrawBlankArea(x + 1, y + (self.HideTop and 0 or 1), self.Width, self.Height + (self.HideTop and 1 or 0), colours.grey)\ Drawing.IgnoreConstraint = false\ Drawing.DrawBlankArea(x, y, self.Width, self.Height, self.BackgroundColour)\ end\ \ OnLoad = function(self)\ local owner = self.Owner\ if type(owner) == 'string' then\ owner = self.Bedrock:GetObject(self.Owner)\ end\ \ if owner then\ if self.X == 0 and self.Y == 0 then\ local pos = owner:GetPosition()\ self.X = pos.X\ self.Y = pos.Y + owner.Height\ end\ self.Owner = owner\ else\ self.Owner = nil\ end\ end\ \ OnUpdate = function(self, value)\ if value == 'Children' then\ self.Width = self.Bedrock.Helpers.LongestString(self.Children, 'Text') + 2\ self.Height = #self.Children + 1 + (self.HideTop and 0 or 1)\ if not self.BaseY then\ self.BaseY = self.Y\ end\ \ for i, v in ipairs(self.Children) do\ if v.TextColour then\ v.TextColour = self.TextColour\ end\ if v.BackgroundColour then\ v.BackgroundColour = colours.transparent\ end\ if v.Colour then\ v.Colour = colours.lightGrey\ end\ v.Align = 'Left'\ v.X = 1\ v.Y = i + (self.HideTop and 0 or 1)\ v.Width = self.Width\ v.Height = 1\ end\ \ self.Y = self.BaseY\ local pos = self:GetPosition()\ if pos.Y + self.Height + 1 > Drawing.Screen.Height then\ self.Y = self.BaseY - ((self.Height + pos.Y) - Drawing.Screen.Height)\ end\ \ if pos.X + self.Width > Drawing.Screen.Width then\ self.X = Drawing.Screen.Width - self.Width\ end\ end\ end\ \ Close = function(self, isBedrockCall)\ self.Bedrock.Menu = nil\ self.Parent:RemoveObject(self)\ if self.Owner and self.Owner.Toggle then\ self.Owner.Toggle = false\ end\ self.Parent:ForceDraw()\ self = nil\ end\ \ OnChildClick = function(self, child, event, side, x, y)\ self:Close()\ end\ ]],\ [\"ProgressBar\"] = [[\ BackgroundColour = colours.lightGrey\ BarColour = colours.blue\ TextColour = colours.white\ ShowText = false\ Value = 0\ Maximum = 1\ Indeterminate = false\ AnimationStep = 0\ \ OnDraw = function(self, x, y)\ Drawing.DrawBlankArea(x, y, self.Width, self.Height, self.BackgroundColour)\ \ -- if self.Indeterminate then\ -- for i = 1, self.Width do\ -- local s = x + i - 1 + self.AnimationStep\ -- if s % 4 == 1 or s % 4 == 2 then\ -- Drawing.DrawBlankArea(s, y, 1, self.Height, self.BarColour)\ -- end\ -- end\ -- self.AnimationStep = self.AnimationStep + 1\ -- if self.AnimationStep >= 4 then\ -- self.AnimationStep = 0\ -- end\ -- self.Bedrock:StartTimer(function()\ -- self:Draw()\ -- end, 0.25)\ -- else\ local values = self.Value\ local barColours = self.BarColour\ if type(values) == 'number' then\ values = {values}\ end\ if type(barColours) == 'number' then\ barColours = {barColours}\ end\ local total = 0\ local _x = x\ for i, v in ipairs(values) do\ local width = self.Bedrock.Helpers.Round((v / self.Maximum) * self.Width)\ total = total + v\ Drawing.DrawBlankArea(_x, y, width, self.Height, barColours[((i-1)%#barColours)+1])\ _x = _x + width\ end\ \ if self.ShowText then\ local text = self.Bedrock.Helpers.Round((total / self.Maximum) * 100) .. '%'\ Drawing.DrawCharactersCenter(x, y, self.Width, self.Height, text, self.TextColour, colours.transparent)\ end\ -- end\ end\ ]],\ [\"ScrollBar\"] = [[\ BackgroundColour = colours.lightGrey\ BarColour = colours.lightBlue\ Scroll = 0\ MaxScroll = 0\ ClickPoint = nil\ Fixed = true\ \ OnUpdate = function(self, value)\ if value == 'Text' and self.AutoWidth then\ self.Width = #self.Text + 2\ end\ end\ \ OnDraw = function(self, x, y)\ local barHeight = self.Height * (self.Height / (self.Height + self.MaxScroll))\ if barHeight < 3 then\ barHeight = 3\ end\ local percentage = (self.Scroll/self.MaxScroll)\ \ Drawing.DrawBlankArea(x, y, self.Width, self.Height, self.BackgroundColour)\ Drawing.DrawBlankArea(x, y + math.ceil(self.Height*percentage - barHeight*percentage), self.Width, barHeight, self.BarColour)\ end\ \ OnScroll = function(self, event, direction, x, y)\ if event == 'mouse_scroll' then\ direction = self.Bedrock.Helpers.Round(direction * 3)\ end\ if self.Scroll < 0 or self.Scroll > self.MaxScroll then\ return false\ end\ local old = self.Scroll\ self.Scroll = self.Bedrock.Helpers.Round(self.Scroll + direction)\ if self.Scroll < 0 then\ self.Scroll = 0\ elseif self.Scroll > self.MaxScroll then\ self.Scroll = self.MaxScroll\ end\ \ if self.Scroll ~= old and self.OnChange then\ self:OnChange()\ end\ end\ \ OnClick = function(self, event, side, x, y)\ if event == 'mouse_click' then\ self.ClickPoint = y\ else\ local gapHeight = self.Height - (self.Height * (self.Height / (self.Height + self.MaxScroll)))\ local barHeight = self.Height * (self.Height / (self.Height + self.MaxScroll))\ --local delta = (self.Height + self.MaxScroll) * ((y - self.ClickPoint) / barHeight)\ local delta = ((y - self.ClickPoint)/gapHeight)*self.MaxScroll\ --l(((y - self.ClickPoint)/gapHeight))\ --l(delta)\ self.Scroll = self.Bedrock.Helpers.Round(delta)\ --l(self.Scroll)\ --l('----')\ if self.Scroll < 0 then\ self.Scroll = 0\ elseif self.Scroll > self.MaxScroll then\ self.Scroll = self.MaxScroll\ end\ if self.OnChange then\ self:OnChange()\ end\ end\ \ local relScroll = self.MaxScroll * ((y-1)/self.Height)\ if y == self.Height then\ relScroll = self.MaxScroll\ end\ self.Scroll = self.Bedrock.Helpers.Round(relScroll)\ \ \ end\ \ OnDrag = OnClick\ ]],\ [\"ScrollView\"] = [[\ Inherit = 'View'\ ChildOffset = false\ ContentWidth = 0\ ContentHeight = 0\ \ CalculateContentSize = function(self)\ local function calculateObject(obj)\ local pos = obj:GetPosition()\ local x2 = pos.X + obj.Width - 1\ local y2 = pos.Y + obj.Height - 1\ if obj.Children then\ for i, child in ipairs(obj.Children) do\ local _x2, _y2 = calculateObject(child)\ if _x2 > x2 then\ x2 = _x2\ end\ if _y2 > y2 then\ y2 = _y2\ end\ end\ end\ return x2, y2\ end\ \ local pos = self:GetPosition()\ local x2, y2 = calculateObject(self)\ self.ContentWidth = x2 - pos.X + 1\ self.ContentHeight = y2 - pos.Y + 1\ end\ \ UpdateScroll = function(self)\ self.ChildOffset.Y = 0\ self:CalculateContentSize()\ if self.ContentHeight > self.Height then\ if not self:GetObject('ScrollViewScrollBar') then\ local _scrollBar = self:AddObject({\ [\"Name\"] = 'ScrollViewScrollBar',\ [\"Type\"] = 'ScrollBar',\ [\"X\"] = self.Width,\ [\"Y\"] = 1,\ [\"Width\"] = 1,\ [\"Height\"] = self.Height,\ [\"Z\"]=999\ })\ \ _scrollBar.OnChange = function(scrollBar)\ self.ChildOffset.Y = -scrollBar.Scroll\ for i, child in ipairs(self.Children) do\ child:ForceDraw()\ end\ end\ end\ self:GetObject('ScrollViewScrollBar').MaxScroll = self.ContentHeight - self.Height\ else\ self:RemoveObject('ScrollViewScrollBar')\ end\ end\ \ OnScroll = function(self, event, direction, x, y)\ if self:GetObject('ScrollViewScrollBar') then\ self:GetObject('ScrollViewScrollBar'):OnScroll(event, direction, x, y)\ end\ end\ \ OnLoad = function(self)\ if not self.ChildOffset or not self.ChildOffset.X or not self.ChildOffset.Y then\ self.ChildOffset = {X = 0, Y = 0}\ end\ end\ ]],\ [\"SecureTextBox\"] = [[\ Inherit = 'TextBox'\ MaskCharacter = '*'\ \ OnDraw = function(self, x, y)\ Drawing.DrawBlankArea(x, y, self.Width, self.Height, self.BackgroundColour)\ if self.CursorPos > #self.Text then\ self.CursorPos = #self.Text\ elseif self.CursorPos < 0 then\ self.CursorPos = 0\ end\ local text = ''\ \ for i = 1, #self.Text do\ text = text .. self.MaskCharacter\ end\ \ if self.Bedrock:GetActiveObject() == self then\ if #text > (self.Width - 2) then\ text = text:sub(#text-(self.Width - 3))\ self.Bedrock.CursorPos = {x + 1 + self.Width-2, y}\ else\ self.Bedrock.CursorPos = {x + 1 + self.CursorPos, y}\ end\ self.Bedrock.CursorColour = self.TextColour\ end\ \ if #tostring(text) == 0 then\ Drawing.DrawCharacters(x + 1, y, self.Placeholder, self.PlaceholderTextColour, self.BackgroundColour)\ else\ if not self.Selected then\ Drawing.DrawCharacters(x + 1, y, text, self.TextColour, self.BackgroundColour)\ else\ for i = 1, #text do\ local char = text:sub(i, i)\ local textColour = self.TextColour\ local backgroundColour = self.BackgroundColour\ if i > self.DragStart and i - 1 <= self.CursorPos then\ textColour = self.SelectedTextColour\ backgroundColour = self.SelectedBackgroundColour\ end\ Drawing.DrawCharacters(x + i, y, char, textColour, backgroundColour)\ end\ end\ end\ end\ ]],\ [\"Separator\"] = [[\ Colour = colours.grey\ \ OnDraw = function(self, x, y)\ local char = \"|\"\ if self.Width > self.Height then\ char = '-'\ end\ Drawing.DrawArea(x, y, self.Width, self.Height, char, self.Colour, colours.transparent)\ end\ ]],\ [\"TextBox\"] = [[\ BackgroundColour = colours.lightGrey\ SelectedBackgroundColour = colours.blue\ SelectedTextColour = colours.white\ TextColour = colours.black\ PlaceholderTextColour = colours.grey\ Placeholder = ''\ AutoWidth = false\ Text = \"\"\ CursorPos = nil\ Numerical = false\ DragStart = nil\ Selected = false\ SelectOnClick = false\ ActualDragStart = nil\ \ OnDraw = function(self, x, y)\ Drawing.DrawBlankArea(x, y, self.Width, self.Height, self.BackgroundColour)\ if self.CursorPos > #self.Text then\ self.CursorPos = #self.Text\ elseif self.CursorPos < 0 then\ self.CursorPos = 0\ end\ local text = self.Text\ local offset = self:TextOffset()\ if #text > (self.Width - 2) then\ text = text:sub(offset+1, offset + self.Width - 2)\ -- self.Bedrock.CursorPos = {x + 1 + self.Width-2, y}\ -- else\ end\ if self.Bedrock:GetActiveObject() == self then\ self.Bedrock.CursorPos = {x + 1 + self.CursorPos - offset, y}\ self.Bedrock.CursorColour = self.TextColour\ else\ self.Selected = false\ end\ \ if #tostring(text) == 0 then\ Drawing.DrawCharacters(x + 1, y, self.Placeholder, self.PlaceholderTextColour, self.BackgroundColour)\ else\ if not self.Selected then\ Drawing.DrawCharacters(x + 1, y, text, self.TextColour, self.BackgroundColour)\ else\ local startPos = self.DragStart - offset\ local endPos = self.CursorPos - offset\ if startPos > endPos then\ startPos = self.CursorPos - offset\ endPos = self.DragStart - offset\ end\ for i = 1, #text do\ local char = text:sub(i, i)\ local textColour = self.TextColour\ local backgroundColour = self.BackgroundColour\ \ if i > startPos and i - 1 <= endPos then\ textColour = self.SelectedTextColour\ backgroundColour = self.SelectedBackgroundColour\ end\ Drawing.DrawCharacters(x + i, y, char, textColour, backgroundColour)\ end\ end\ end\ end\ \ TextOffset = function(self)\ if #self.Text < (self.Width - 2) then\ return 0\ elseif self.Bedrock:GetActiveObject() ~= self then\ return 0\ else\ local textWidth = (self.Width - 2)\ local offset = self.CursorPos - textWidth\ if offset < 0 then\ offset = 0\ end\ return offset\ end\ end\ \ OnLoad = function(self)\ if not self.CursorPos then\ self.CursorPos = #self.Text\ end\ end\ \ OnClick = function(self, event, side, x, y)\ if self.Bedrock:GetActiveObject() ~= self and self.SelectOnClick then\ self.CursorPos = #self.Text - 1\ self.DragStart = 0\ self.ActualDragStart = x - 2 + self:TextOffset()\ self.Selected = true\ else\ self.CursorPos = x - 2 + self:TextOffset()\ self.DragStart = self.CursorPos\ self.Selected = false\ end\ self.Bedrock:SetActiveObject(self)\ end\ \ OnDrag = function(self, event, side, x, y)\ self.CursorPos = x - 2 + self:TextOffset()\ if self.ActualDragStart then\ self.DragStart = self.ActualDragStart\ self.ActualDragStart = nil\ end\ if self.DragStart then\ self.Selected = true\ end\ end\ \ OnKeyChar = function(self, event, keychar)\ local deleteSelected = function()\ if self.Selected then\ local startPos = self.DragStart\ local endPos = self.CursorPos\ if startPos > endPos then\ startPos = self.CursorPos\ endPos = self.DragStart\ end\ self.Text = self.Text:sub(1, startPos) .. self.Text:sub(endPos + 2)\ self.CursorPos = startPos\ self.DragStart = nil\ self.Selected = false\ return true\ end\ end\ \ if event == 'char' then\ deleteSelected()\ if self.Numerical then\ keychar = tostring(tonumber(keychar))\ end\ if keychar == 'nil' then\ return\ end\ self.Text = string.sub(self.Text, 1, self.CursorPos ) .. keychar .. string.sub( self.Text, self.CursorPos + 1 )\ if self.Numerical then\ self.Text = tostring(tonumber(self.Text))\ if self.Text == 'nil' then\ self.Text = '1'\ end\ end\ \ self.CursorPos = self.CursorPos + 1\ if self.OnChange then\ self:OnChange(event, keychar)\ end\ return false\ elseif event == 'key' then\ if keychar == keys.enter then\ if self.OnChange then\ self:OnChange(event, keychar)\ end\ elseif keychar == keys.left then\ -- Left\ if self.CursorPos > 0 then\ if self.Selected then\ self.CursorPos = self.DragStart\ self.DragStart = nil\ self.Selected = false\ else\ self.CursorPos = self.CursorPos - 1\ end\ if self.OnChange then\ self:OnChange(event, keychar)\ end\ end\ \ elseif keychar == keys.right then\ -- Right \ if self.CursorPos < string.len(self.Text) then\ if self.Selected then\ self.CursorPos = self.CursorPos\ self.DragStart = nil\ self.Selected = false\ else\ self.CursorPos = self.CursorPos + 1\ end\ if self.OnChange then\ self:OnChange(event, keychar)\ end\ end\ \ elseif keychar == keys.backspace then\ -- Backspace\ if not deleteSelected() and self.CursorPos > 0 then\ self.Text = string.sub( self.Text, 1, self.CursorPos - 1 ) .. string.sub( self.Text, self.CursorPos + 1 )\ self.CursorPos = self.CursorPos - 1 \ if self.Numerical then\ self.Text = tostring(tonumber(self.Text))\ if self.Text == 'nil' then\ self.Text = '1'\ end\ end\ if self.OnChange then\ self:OnChange(event, keychar)\ end\ end\ elseif keychar == keys.home then\ -- Home\ self.CursorPos = 0\ if self.OnChange then\ self:OnChange(event, keychar)\ end\ elseif keychar == keys.delete then\ if not deleteSelected() and self.CursorPos < string.len(self.Text) then\ self.Text = string.sub( self.Text, 1, self.CursorPos ) .. string.sub( self.Text, self.CursorPos + 2 ) \ if self.Numerical then\ self.Text = tostring(tonumber(self.Text))\ if self.Text == 'nil' then\ self.Text = '1'\ end\ end\ if self.OnChange then\ self:OnChange(keychar)\ end\ end\ elseif keychar == keys[\"end\"] then\ -- End\ self.CursorPos = string.len(self.Text)\ else\ if self.OnChange then\ self:OnChange(event, keychar)\ end\ return false\ end\ end\ end\ ]],\ [\"View\"] = [[\ BackgroundColour = colours.transparent\ Children = {}\ \ OnDraw = function(self, x, y)\ if self.BackgroundColour then\ Drawing.DrawBlankArea(x, y, self.Width, self.Height, self.BackgroundColour)\ end\ end\ \ OnInitialise = function(self)\ self.Children = {}\ end\ \ InitialiseFile = function(self, bedrock, file, name)\ local _new = {}\ _new.X = 1\ _new.Y = 1\ _new.Width = Drawing.Screen.Width\ _new.Height = Drawing.Screen.Height\ _new.BackgroundColour = file.BackgroundColour\ _new.Name = name\ _new.Children = {}\ _new.Bedrock = bedrock\ local new = self:Initialise(_new)\ for i, obj in ipairs(file.Children) do\ local view = bedrock:ObjectFromFile(obj, new)\ if not view.Z then\ view.Z = i\ end\ view.Parent = new\ table.insert(new.Children, view)\ end\ return new\ end\ \ function CheckClick(self, object, x, y)\ local offset = {X = 0, Y = 0}\ if not object.Fixed and self.ChildOffset then\ offset = self.ChildOffset\ end\ 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\ return true\ end\ end\ \ function DoClick(self, object, event, side, x, y)\ if object then\ if self:CheckClick(object, x, y) then\ local offset = {X = 0, Y = 0}\ if not object.Fixed and self.ChildOffset then\ offset = self.ChildOffset\ end\ return object:Click(event, side, x - object.X - offset.X + 1, y - object.Y + 1 - offset.Y)\ end\ end \ end\ \ Click = function(self, event, side, x, y, z)\ if self.Visible and not self.IgnoreClick then\ for i = #self.Children, 1, -1 do --children are ordered from smallest Z to highest, so this is done in reverse\ local child = self.Children[i]\ if self:DoClick(child, event, side, x, y) then\ if self.OnChildClick then\ self:OnChildClick(child, event, side, x, y)\ end\ return true\ end\ end\ if event == 'mouse_click' and self.OnClick and self:OnClick(event, side, x, y) ~= false then\ return true\ elseif event == 'mouse_drag' and self.OnDrag and self:OnDrag(event, side, x, y) ~= false then\ return true\ elseif event == 'mouse_scroll' and self.OnScroll and self:OnScroll(event, side, x, y) ~= false then\ return true\ else\ return false\ end\ else\ return false\ end\ end\ \ OnRemove = function(self)\ if self == self.Bedrock:GetActiveObject() then\ self.Bedrock:SetActiveObject()\ end\ for i, child in ipairs(self.Children) do\ child:OnRemove()\ end\ end\ \ local function findObjectNamed(view, name, minI)\ local minI = minI or 0\ if view and view.Children then\ for i, child in ipairs(view.Children) do\ if child.Name == name or child == name then\ return child, i, view\ elseif child.Children then\ local found, index, foundView = findObjectNamed(child, name)\ if found and minI <= index then\ return found, index, foundView\ end\ end\ end\ end\ end\ \ function AddObject(self, info, extra)\ if type(info) == 'string' then\ local h = fs.open(self.Bedrock.ViewPath..info..'.view', 'r')\ if h then\ info = textutils.unserialize(h.readAll())\ h.close()\ else\ error('Error in opening object: '..info)\ end\ end\ \ if extra then\ for k, v in pairs(extra) do\ if v then\ info[k] = v\ end\ end\ end\ \ local view = self.Bedrock:ObjectFromFile(info, self)\ if not view.Z then\ view.Z = #self.Children + 1\ end\ \ table.insert(self.Children, view)\ if self.Bedrock.View then\ self.Bedrock:ReorderObjects()\ end\ self:ForceDraw()\ return view\ end\ \ function GetObject(self, name)\ return findObjectNamed(self, name)\ end\ \ local function findObjects(view, name)\ local objects = {}\ if view and view.Children then\ for i, child in ipairs(view.Children) do\ if child.Name == name or child == name then\ table.insert(objects, child)\ elseif child.Children then\ local objs = findObjects(child, name)\ if objs then\ for i2, v in ipairs(objs) do\ table.insert(objects, v)\ end\ end\ end\ end\ end\ return objects\ end\ \ function GetObjects(self, name)\ return findObjects(self, name)\ end\ \ function RemoveObject(self, name)\ local obj, index, view = findObjectNamed(self, name, minI)\ if index then\ view.Children[index]:OnRemove()\ table.remove(view.Children, index)\ if view.OnUpdate then\ view:OnUpdate('Children')\ end\ return true\ end\ return false\ end\ \ function RemoveObjects(self, name)\ local i = 1\ while self:RemoveObject(name) and i < 100 do\ i = i + 1\ end\ \ end\ \ function RemoveAllObjects(self)\ for i, child in ipairs(self.Children) do\ child:OnRemove()\ self.Children[i] = nil\ end\ self:ForceDraw()\ end\ ]],\ [\"Window\"] = [[\ Inherit = 'View'\ \ ToolBarColour = colours.lightGrey\ ToolBarTextColour = colours.black\ ShadowColour = colours.grey\ Title = ''\ Flashing = false\ CanClose = true\ OnCloseButton = nil\ \ OnLoad = function(self)\ --self:GetObject('View') = self.Bedrock:ObjectFromFile({Type = 'View',Width = 10, Height = 5, BackgroundColour = colours.red}, self)\ end\ \ LoadView = function(self)\ local view = self:GetObject('View')\ if view.ToolBarColour then\ window.ToolBarColour = view.ToolBarColour\ end\ if view.ToolBarTextColour then\ window.ToolBarTextColour = view.ToolBarTextColour\ end\ view.X = 1\ view.Y = 2\ \ view:ForceDraw()\ self:OnUpdate('View')\ if self.OnViewLoad then\ self.OnViewLoad(view)\ end\ self.Bedrock:SetActiveObject(view)\ end\ \ SetView = function(self, view)\ self:RemoveObject('View')\ table.insert(self.Children, view)\ view.Parent = self\ self:LoadView()\ end\ \ Flash = function(self)\ self.Flashing = true\ self:ForceDraw()\ self.Bedrock:StartTimer(function()self.Flashing = false end, 0.4)\ end\ \ OnDraw = function(self, x, y)\ local toolBarColour = (self.Flashing and colours.white or self.ToolBarColour)\ local toolBarTextColour = (self.Flashing and colours.black or self.ToolBarTextColour)\ if toolBarColour then\ Drawing.DrawBlankArea(x, y, self.Width, 1, toolBarColour)\ end\ if toolBarTextColour then\ local title = self.Bedrock.Helpers.TruncateString(self.Title, self.Width - 2)\ Drawing.DrawCharactersCenter(self.X, self.Y, self.Width, 1, title, toolBarTextColour, toolBarColour)\ end\ Drawing.IgnoreConstraint = true\ Drawing.DrawBlankArea(x + 1, y + 1, self.Width, self.Height, self.ShadowColour)\ Drawing.IgnoreConstraint = false\ end\ \ Close = function(self)\ self.Bedrock.Window = nil\ self.Bedrock:RemoveObject(self)\ if self.OnClose then\ self:OnClose()\ end\ self = nil\ end\ \ OnUpdate = function(self, value)\ if value == 'View' and self:GetObject('View') then\ self.Width = self:GetObject('View').Width\ self.Height = self:GetObject('View').Height + 1\ self.X = math.ceil((Drawing.Screen.Width - self.Width) / 2)\ self.Y = math.ceil((Drawing.Screen.Height - self.Height) / 2)\ elseif value == 'CanClose' then\ self:RemoveObject('CloseButton')\ if self.CanClose then\ local button = self:AddObject({X = 1, Y = 1, Width = 1, Height = 1, Type = 'Button', BackgroundColour = colours.red, TextColour = colours.white, Text = 'x', Name = 'CloseButton'})\ button.OnClick = function(btn)\ if self.OnCloseButton then\ self:OnCloseButton()\ end\ self:Close()\ end\ end\ end\ end\ ]],\ }\ \ BasePath = ''\ ProgramPath = nil\ \ -- Program functions...\ \ local function main(...)\ -- Code here...\ end\ \ -- Run\ local args = {...}\ local _, err = pcall(function() main(unpack(args)) end)\ if err then\ -- Make a nice error handling screen here...\ term.setBackgroundColor(colors.black)\ term.setTextColor(colors.white)\ term.clear()\ term.setCursorPos(1, 3)\ print(\" An Error Has Occured! D:\\n\\n\")\ print(\" \" .. tostring(err) .. \"\\n\\n\")\ print(\" Press any key to exit...\")\ os.pullEvent(\"key\")\ end\ \ \ \ function LoadAPIs(self)\ local function loadAPI(name, content)\ local env = setmetatable({}, { __index = getfenv() })\ local func, err = loadstring(content, name..' (Bedrock API)')\ if not func then\ return false, printError(err)\ end\ setfenv(func, env)\ func()\ local api = {}\ for k,v in pairs(env) do\ api[k] = v\ end\ _G[name] = api\ return true\ end\ \ local env = getfenv()\ local function loadObject(name, content)\ loadAPI(name, content)\ if env[name].Inherit then\ if not getfenv()[env[name].Inherit] then \ if objects[env[name].Inherit] then\ loadObject(env[name].Inherit, objects[env[name].Inherit])\ elseif fs.exists(self.ProgramPath..'/Objects/'..env[name].Inherit..'.lua') then\ end\ end\ env[name].__index = getfenv()[env[name].Inherit]\ else\ env[name].__index = Object\ end\ setmetatable(env[name], env[name])\ end\ \ for k, v in pairs(apis) do\ loadAPI(k, v)\ if k == 'Helpers' then\ self.Helpers = Helpers\ end\ end\ \ for k, v in pairs(objects) do\ loadObject(k, v)\ end\ \ local privateObjPath = self.ProgramPath..'/Objects/'\ if fs.exists(privateObjPath) and fs.isDir(privateObjPath) then\ for i, v in ipairs(fs.list(privateObjPath)) do\ if v ~= '.DS_Store' then\ local name = string.match(v, '(%a+)%.?.-')\ local h = fs.open(privateObjPath..v, 'r')\ loadObject(name, h.readAll())\ h.close()\ end\ end\ end\ end\ \ AllowTerminate = true\ \ View = nil\ Menu = nil\ \ ActiveObject = nil\ \ DrawTimer = nil\ DrawTimerExpiry = 0\ \ IsDrawing = false\ \ Running = true\ \ DefaultView = 'main'\ \ EventHandlers = {\ \ }\ \ ObjectClickHandlers = {\ \ }\ \ ObjectUpdateHandlers = {\ \ }\ \ Timers = {\ \ }\ \ function Initialise(self, programPath)\ self.ProgramPath = programPath or self.ProgramPath\ if not programPath then\ if self.ProgramPath then\ local prgPath = self.ProgramPath\ local prgName = fs.getName(prgPath)\ if prgPath:find('/') then \ self.ProgramPath = prgPath:sub(1, #prgPath-#prgName-1)\ self.ProgramPath = prgPath:sub(1, #prgPath-#prgName-1) \ else \ self.ProgramPath = '' \ end\ else\ self.ProgramPath = ''\ end\ end\ self:LoadAPIs()\ self.ViewPath = self.ProgramPath .. '/Views/'\ --first, check that the barebones APIs are available\ local requiredApis = {\ 'Drawing',\ 'View'\ }\ local env = getfenv()\ for i,v in ipairs(requiredApis) do\ if not env[v] then\ error('The API: '..v..' is not loaded. Please make sure you load it to use Bedrock.')\ end\ end\ \ local copy = { }\ for k, v in pairs(self) do\ if k ~= 'Initialise' then\ copy[k] = v\ end\ end\ return setmetatable(copy, getmetatable(self))\ end\ \ function HandleClick(self, event, side, x, y)\ if self.Window then\ if not self.View:CheckClick(self.Window, x, y) then\ self.Window:Flash()\ else\ self.View:DoClick(self.Window, event, side, x, y)\ end\ elseif self.Menu then\ if not self.View:DoClick(self.Menu, event, side, x, y) then\ self.Menu:Close()\ end\ elseif self.View then\ if self.View:Click(event, side, x, y) ~= false then\ end \ end\ end\ \ function HandleKeyChar(self, event, keychar)\ if self:GetActiveObject() then\ local activeObject = self:GetActiveObject()\ if activeObject.OnKeyChar then\ if activeObject:OnKeyChar(event, keychar) ~= false then\ --self:Draw()\ end\ end\ end\ end\ \ function ToggleMenu(self, name, owner, x, y)\ if self.Menu then\ self.Menu:Close()\ return false\ else\ self:SetMenu(name, owner, x, y)\ return true\ end\ end\ \ function SetMenu(self, menu, owner, x, y)\ x = x or 1\ y = y or 1\ if self.Menu then\ self.Menu:Close()\ end \ if menu then\ local pos = owner:GetPosition()\ self.Menu = self:AddObject(menu, {Type = 'Menu', Owner = owner, X = pos.X + x - 1, Y = pos.Y + y})\ end\ end\ \ function ObjectClick(self, name, func)\ self.ObjectClickHandlers[name] = func\ end\ \ function ClickObject(self, object, event, side, x, y)\ if self.ObjectClickHandlers[object.Name] then\ return self.ObjectClickHandlers[object.Name](object, event, side, x, y)\ end\ return false\ end\ \ function ObjectUpdate(self, name, func)\ self.ObjectUpdateHandlers[name] = func\ end\ \ function UpdateObject(self, object, ...)\ if self.ObjectUpdateHandlers[object.Name] then\ self.ObjectUpdateHandlers[object.Name](object, ...)\ --self:Draw()\ end\ end\ \ function GetAbsolutePosition(self, obj)\ if not obj.Parent then\ return {X = obj.X, Y = obj.Y}\ else\ local pos = self:GetAbsolutePosition(obj.Parent)\ local x = pos.X + obj.X - 1\ local y = pos.Y + obj.Y - 1\ if not obj.Fixed and obj.Parent.ChildOffset then\ x = x + obj.Parent.ChildOffset.X\ y = y + obj.Parent.ChildOffset.Y\ end\ return {X = x, Y = y}\ end\ end\ \ function LoadView(self, name, draw)\ if self.View and self.OnViewClose then\ self.OnViewClose(self.View.Name)\ end\ if self.View then\ self.View:OnRemove()\ end\ local success = false\ \ if not fs.exists(self.ViewPath..name..'.view') then\ error('The view: '..name..'.view does not exist.')\ end\ \ local h = fs.open(self.ViewPath..name..'.view', 'r')\ if h then\ local view = textutils.unserialize(h.readAll())\ h.close()\ if view then\ self.View = View:InitialiseFile(self, view, name)\ self:ReorderObjects()\ \ if OneOS and view.ToolBarColour then\ OneOS.ToolBarColour = view.ToolBarColour\ end\ if OneOS and view.ToolBarTextColour then\ OneOS.ToolBarTextColour = view.ToolBarTextColour\ end\ if not self:GetActiveObject() then\ self:SetActiveObject()\ end\ success = true\ end\ end\ \ if success and self.OnViewLoad then\ self.OnViewLoad(name)\ end\ \ if draw ~= false then\ self:Draw()\ end\ \ if not success then\ error('Failed to load view: '..name..'. It probably isn\\'t formatted correctly. Did you forget a } or ,?')\ end\ \ return success\ end\ \ function InheritFile(self, file, name)\ local h = fs.open(self.ViewPath..name..'.view', 'r')\ if h then\ local super = textutils.unserialize(h.readAll())\ if super then\ if type(super) ~= 'table' then\ error('View: \"'..name..'.view\" is not formatted correctly.')\ end\ \ for k, v in pairs(super) do\ if not file[k] then\ file[k] = v\ end\ end\ return file\ end\ end\ return file\ end\ \ function ParseStringSize(self, parent, k, v)\ local parentSize = parent.Width\ if k == 'Height' or k == 'Y' then\ parentSize = parent.Height\ end\ local parts = {v}\ if type(v) == 'string' and string.find(v, ',') then\ parts = {}\ for word in string.gmatch(v, '([^,]+)') do\ table.insert(parts, word)\ end\ end\ \ v = 0\ for i2, part in ipairs(parts) do\ if type(part) == 'string' and part:sub(#part) == '%' then\ v = v + math.ceil(parentSize * (tonumber(part:sub(1, #part-1)) / 100))\ else\ v = v + tonumber(part)\ end\ end\ return v\ end\ \ function ObjectFromFile(self, file, view)\ local env = getfenv()\ if env[file.Type] then\ if not env[file.Type].Initialise then\ error('Malformed Object: '..file.Type)\ end\ local object = {}\ \ if file.InheritView then\ file = self:InheritFile(file, file.InheritView)\ end\ \ object.AutoWidth = true\ for k, v in pairs(file) do\ if k == 'Width' or k == 'X' or k == 'Height' or k == 'Y' then\ v = self:ParseStringSize(view, k, v)\ end\ \ if k == 'Width' then\ object.AutoWidth = false\ end\ if k ~= 'Children' then\ object[k] = v\ else\ object[k] = {}\ end\ end\ \ object.Parent = view\ object.Bedrock = self\ if not object.Name then\ object.Name = file.Type\ end\ \ object = env[file.Type]:Initialise(object)\ \ if file.Children then\ for i, obj in ipairs(file.Children) do\ local _view = self:ObjectFromFile(obj, object)\ if not _view.Z then\ _view.Z = i\ end\ _view.Parent = object\ table.insert(object.Children, _view)\ end\ end\ \ if not object.OnClick then\ object.OnClick = function(...) return self:ClickObject(...) end\ end\ --object.OnUpdate = function(...) self:UpdateObject(...) end\ \ if object.OnUpdate then\ for k, v in pairs(env[file.Type]) do\ object:OnUpdate(k)\ end\ \ for k, v in pairs(object.__index) do\ object:OnUpdate(k)\ end\ end\ \ if object.Active then\ object.Bedrock:SetActiveObject(object)\ end\ if object.OnLoad then\ object:OnLoad()\ end\ return object\ elseif not file.Type then\ error('No object type specified. (e.g. Type = \"Button\")')\ else\ error('No Object: '..file.Type..'. The API probably isn\\'t loaded')\ end\ end\ \ function ReorderObjects(self)\ if self.View and self.View.Children then\ table.sort(self.View.Children, function(a,b)\ return a.Z < b.Z \ end)\ end\ end\ \ function AddObject(self, info, extra)\ return self.View:AddObject(info, extra)\ end\ \ function GetObject(self, name)\ return self.View:GetObject(name)\ end\ \ function GetObjects(self, name)\ return self.View:GetObjects(name)\ end\ \ function RemoveObject(self, name)\ return self.View:RemoveObject(name)\ end\ \ function RemoveObjects(self, name)\ return self.View:RemoveObjects(name)\ end\ \ function ForceDraw(self)\ if not self.DrawTimer or self.DrawTimerExpiry <= os.clock() then\ self.DrawTimer = self:StartTimer(function()\ self.DrawTimer = nil\ self:Draw()\ end, 0.05)\ self.DrawTimerExpiry = os.clock() + 0.1\ end\ end\ \ function DisplayWindow(self, _view, title, canClose)\ if canClose == nil then\ canClose = true\ end\ if type(_view) == 'string' then\ local h = fs.open(self.ViewPath.._view..'.view', 'r')\ if h then\ _view = textutils.unserialize(h.readAll())\ h.close()\ end\ end\ \ self.Window = self:AddObject({Type = 'Window', Z = 999, Title = title, CanClose = canClose})\ _view.Type = 'View'\ _view.Name = 'View'\ _view.BackgroundColour = _view.BackgroundColour or colours.white\ self.Window:SetView(self:ObjectFromFile(_view, self.Window))\ end\ \ function DisplayAlertWindow(self, title, text, buttons, callback)\ local func = function(btn)\ self.Window:Close()\ if callback then\ callback(btn.Text)\ end\ end\ local children = {}\ local usedX = -1\ if buttons then\ for i, text in ipairs(buttons) do\ usedX = usedX + 3 + #text\ table.insert(children, {\ [\"Y\"]=\"100%,-1\",\ [\"X\"]=\"100%,-\"..usedX,\ [\"Name\"]=text..\"Button\",\ [\"Type\"]=\"Button\",\ [\"Text\"]=text,\ OnClick = func\ })\ end\ end\ \ local width = usedX + 2\ if width < 28 then\ width = 28\ end\ \ local canClose = true\ if buttons and #buttons~=0 then\ canClose = false\ end\ \ local height = 0\ if text then\ height = #Helpers.WrapText(text, width - 2)\ table.insert(children, {\ [\"Y\"]=2,\ [\"X\"]=2,\ [\"Width\"]=\"100%,-2\",\ [\"Height\"]=height,\ [\"Name\"]=\"Label\",\ [\"Type\"]=\"Label\",\ [\"Text\"]=text\ })\ end\ local view = {\ Children = children,\ Width=width,\ Height=3+height+(canClose and 0 or 1),\ OnKeyChar = function(_view, keychar)\ func({Text=buttons[1]})\ end\ }\ self:DisplayWindow(view, title, canClose)\ end\ \ function DisplayTextBoxWindow(self, title, text, callback, textboxText, cursorAtEnd)\ textboxText = textboxText or ''\ local func = function(btn)\ self.Window:Close()\ if callback then\ callback(btn.Text)\ end\ end\ local children = {\ {\ [\"Y\"]=\"100%,-1\",\ [\"X\"]=\"100%,-4\",\ [\"Name\"]=\"OkButton\",\ [\"Type\"]=\"Button\",\ [\"Text\"]=\"Ok\",\ OnClick = function()\ local text = self.Window:GetObject('TextBox').Text\ self.Window:Close()\ callback(true, text)\ end\ },\ {\ [\"Y\"]=\"100%,-1\",\ [\"X\"]=\"100%,-13\",\ [\"Name\"]=\"CancelButton\",\ [\"Type\"]=\"Button\",\ [\"Text\"]=\"Cancel\",\ OnClick = function()\ self.Window:Close()\ callback(false)\ end\ }\ }\ \ local height = -1\ if text and #text ~= 0 then\ height = #Helpers.WrapText(text, 26)\ table.insert(children, {\ [\"Y\"]=2,\ [\"X\"]=2,\ [\"Width\"]=\"100%,-2\",\ [\"Height\"]=height,\ [\"Name\"]=\"Label\",\ [\"Type\"]=\"Label\",\ [\"Text\"]=text\ })\ end\ table.insert(children,\ {\ [\"Y\"]=3+height,\ [\"X\"]=2,\ [\"Width\"]=\"100%,-2\",\ [\"Name\"]=\"TextBox\",\ [\"Type\"]=\"TextBox\",\ [\"Text\"]=textboxText,\ [\"CursorPos\"]=(cursorAtEnd and 0 or nil)\ })\ local view = {\ Children = children,\ Width=28,\ Height=5+height+(canClose and 0 or 1),\ }\ self:DisplayWindow(view, title)\ self.Window:GetObject('TextBox').OnUpdate = function(txtbox, keychar)\ if keychar == keys.enter then\ self.Window:Close()\ callback(true, txtbox.Text)\ end\ end\ self:SetActiveObject(self.Window:GetObject('TextBox'))\ self.Window.OnCloseButton = function()callback(false)end\ end\ \ function DisplayOpenFileWindow(self, title, callback)\ title = title or 'Open File'\ local func = function(btn)\ self.Window:Close()\ if callback then\ callback(btn.Text)\ end\ end\ \ local sidebarItems = {}\ \ --this is a really, really super bad way of doing it\ local separator = ' !'\ \ local function addFolder(path, level)\ for i, v in ipairs(fs.list(path)) do\ local fPath = path .. '/' .. v\ if fPath ~= '/rom' and fs.isDir(fPath) then\ table.insert(sidebarItems, level .. v..separator..fPath)\ addFolder(fPath, level .. ' ')\ end\ end\ end\ addFolder('','')\ \ local currentFolder = ''\ local selectedPath = nil\ \ local goToFolder = nil\ \ local children = {\ {\ [\"Y\"]=\"100%,-2\",\ [\"X\"]=1,\ [\"Height\"]=3,\ [\"Width\"]=\"100%\",\ [\"BackgroundColour\"]=colours.lightGrey,\ [\"Name\"]=\"SidebarListView\",\ [\"Type\"]=\"View\"\ },\ {\ [\"Y\"]=\"100%,-1\",\ [\"X\"]=\"100%,-4\",\ [\"Name\"]=\"OkButton\",\ [\"Type\"]=\"Button\",\ [\"Text\"]=\"Ok\",\ [\"BackgroundColour\"]=colours.white,\ [\"Enabled\"]=false,\ OnClick = function()\ if selectedPath then\ self.Window:Close()\ callback(true, Helpers.TidyPath(selectedPath))\ end\ end\ },\ {\ [\"Y\"]=\"100%,-1\",\ [\"X\"]=\"100%,-13\",\ [\"Name\"]=\"CancelButton\",\ [\"Type\"]=\"Button\",\ [\"Text\"]=\"Cancel\",\ [\"BackgroundColour\"]=colours.white,\ OnClick = function()\ self.Window:Close()\ callback(false)\ end\ },\ {\ [\"Y\"]=1,\ [\"X\"]=1,\ [\"Height\"]=\"100%,-3\",\ [\"Width\"]=\"40%,-1\",\ [\"Name\"]=\"SidebarListView\",\ [\"Type\"]=\"ListView\",\ [\"CanSelect\"]=true,\ [\"Items\"]={\ [\"Computer\"] = sidebarItems\ },\ OnSelect = function(listView, text)\ local _,s = text:find(separator)\ if s then\ local path = text:sub(s + 1)\ goToFolder(path)\ end\ end,\ OnClick = function(listView, event, side, x, y)\ if y == 1 then\ goToFolder('/')\ end\ end\ },\ {\ [\"Y\"]=1,\ [\"X\"]=\"40%\",\ [\"Height\"]=\"100%,-3\",\ [\"Width\"]=1,\ [\"Type\"]=\"Separator\"\ },\ {\ [\"Y\"]=1,\ [\"X\"]=\"40%,2\",\ [\"Width\"]=\"65%,-3\",\ [\"Height\"]=1,\ [\"Type\"]=\"Label\",\ [\"Name\"]=\"PathLabel\",\ [\"TextColour\"]=colours.lightGrey,\ [\"Text\"]='/hello/there'\ },\ {\ [\"Y\"]=2,\ [\"X\"]=\"40%,1\",\ [\"Height\"]=\"100%,-4\",\ [\"Width\"]=\"65%,-1\",\ [\"Name\"]=\"FilesListView\",\ [\"Type\"]=\"ListView\",\ [\"CanSelect\"]=true,\ [\"Items\"]={},\ OnSelect = function(listView, text)\ selectedPath = Helpers.TidyPath(currentFolder .. '/' .. text)\ self.Window:GetObject('OkButton').Enabled = true\ end,\ OnClick = function(listView, event, side, x, y)\ if y == 1 then\ goToFolder('/')\ end\ end\ },\ }\ local view = {\ Children = children,\ Width=40,\ Height= Drawing.Screen.Height - 4\ }\ self:DisplayWindow(view, title)\ \ goToFolder = function(path)\ path = Helpers.TidyPath(path)\ self.Window:GetObject('PathLabel').Text = path\ currentFolder = path\ \ local filesListItems = {}\ for i, v in ipairs(fs.list(path)) do\ if not fs.isDir(currentFolder .. v) then\ table.insert(filesListItems, v)\ end\ end\ self.Window:GetObject('OkButton').Enabled = false\ selectedPath = nil\ self.Window:GetObject('FilesListView').Items = filesListItems\ \ end\ \ goToFolder('')\ \ self.Window.OnCloseButton = function()callback(false)end\ end\ \ function RegisterEvent(self, event, func)\ if not self.EventHandlers[event] then\ self.EventHandlers[event] = {}\ end\ table.insert(self.EventHandlers[event], func)\ end\ \ function StartRepeatingTimer(self, func, interval)\ local int = interval\ if type(int) == 'function' then\ int = int()\ end\ if not int or int <= 0 then\ return\ end\ local timer = os.startTimer(int)\ \ self.Timers[timer] = {func, true, interval}\ return timer\ end\ \ function StartTimer(self, func, delay)\ local timer = os.startTimer(delay)\ self.Timers[timer] = {func, false}\ return timer\ end\ \ function StopTimer(self, timer)\ if self.Timers[timer] then\ self.Timers[timer] = nil\ end\ end\ \ function HandleTimer(self, event, timer)\ if self.Timers[timer] then\ local oldTimer = self.Timers[timer]\ self.Timers[timer] = nil\ local new = nil\ if oldTimer[2] then\ new = self:StartRepeatingTimer(oldTimer[1], oldTimer[3])\ end\ if oldTimer and oldTimer[1] then\ oldTimer[1](new)\ end\ elseif self.OnTimer then\ self.OnTimer(self, event, timer)\ end\ end\ \ function SetActiveObject(self, object)\ if object then\ if object ~= self.ActiveObject then\ self.ActiveObject = object\ object:ForceDraw()\ end\ elseif self.ActiveObject ~= nil then\ self.ActiveObject = nil\ self.CursorPos = nil\ self.View:ForceDraw()\ end\ end\ \ function GetActiveObject(self)\ return self.ActiveObject\ end\ \ OnTimer = nil\ OnClick = nil\ OnKeyChar = nil\ OnDrag = nil\ OnScroll = nil\ OnViewLoad = nil\ OnViewClose = nil\ OnDraw = nil\ OnQuit = nil\ \ local eventFuncs = {\ OnClick = {'mouse_click'},\ OnKeyChar = {'key', 'char'},\ OnDrag = {'mouse_drag'},\ OnScroll = {'mouse_scroll'},\ HandleClick = {'mouse_click', 'mouse_drag', 'mouse_scroll'},\ HandleKeyChar = {'key', 'char'},\ HandleTimer = {'timer'}\ }\ \ local drawCalls = 0\ local ignored = 0\ function Draw(self)\ self.IsDrawing = true\ if self.OnDraw then\ self:OnDraw()\ end\ \ if self.View and self.View:NeedsDraw() then\ self.View:Draw()\ Drawing.DrawBuffer()\ if isDebug then\ drawCalls = drawCalls + 1\ end\ elseif not self.View then\ print('No loaded view. You need to do program:LoadView first.')\ end \ \ if self:GetActiveObject() and self.CursorPos and type(self.CursorPos[1]) == 'number' and type(self.CursorPos[2]) == 'number' then\ term.setCursorPos(self.CursorPos[1], self.CursorPos[2])\ term.setTextColour(self.CursorColour)\ term.setCursorBlink(true)\ else\ term.setCursorBlink(false)\ end\ \ self.IsDrawing = false\ end\ \ function EventHandler(self)\ local event = { os.pullEventRaw() }\ \ if self.EventHandlers[event[1]] then\ for i, e in ipairs(self.EventHandlers[event[1]]) do\ e(self, unpack(event))\ end\ end\ end\ \ function Quit(self)\ self.Running = false\ if self.OnQuit then\ self:OnQuit()\ end\ if OneOS then\ OneOS.Close()\ end\ end\ \ function Run(self, ready)\ for name, events in pairs(eventFuncs) do\ if self[name] then\ for i, event in ipairs(events) do\ self:RegisterEvent(event, self[name])\ end\ end\ end\ \ if self.AllowTerminate then\ self:RegisterEvent('terminate', function()error('Terminated', 0) end)\ end\ \ if self.DefaultView and self.DefaultView ~= '' and fs.exists(self.ViewPath..self.DefaultView..'.view') then\ self:LoadView(self.DefaultView)\ end\ \ if ready then\ ready()\ end\ \ self:Draw()\ \ while self.Running do\ self:EventHandler()\ end\ end",[".gitignore"]=".DS_Store\ .Desktop.settings\ .OneOS.settings\ *.settings\ \ Desktop/.Desktop.settings\ \ .version",["hash"]="--\ -- Thanks to GravityScore for this!\ -- http://www.computercraft.info/forums2/index.php?/topic/8169-sha-256-in-pure-lua/\ --\ -- This is used to hash passwords sent with the secure text field. It just reduces the chance of people getting hacked.\ --\ \ -- \ -- Adaptation of the Secure Hashing Algorithm (SHA-244/256)\ -- Found Here: http://lua-users.org/wiki/SecureHashAlgorithm\ -- \ -- Using an adapted version of the bit library\ -- Found Here: https://bitbucket.org/Boolsheet/bslf/src/1ee664885805/bit.lua\ -- \ \ local MOD = 2^32\ local MODM = MOD-1\ \ local function memoize(f)\ local mt = {}\ local t = setmetatable({}, mt)\ function mt:__index(k)\ local v = f(k)\ t[k] = v\ return v\ end\ return t\ end\ \ local function make_bitop_uncached(t, m)\ local function bitop(a, b)\ local res,p = 0,1\ while a ~= 0 and b ~= 0 do\ local am, bm = a % m, b % m\ res = res + t[am][bm] * p\ a = (a - am) / m\ b = (b - bm) / m\ p = p*m\ end\ res = res + (a + b) * p\ return res\ end\ return bitop\ end\ \ local function make_bitop(t)\ local op1 = make_bitop_uncached(t,2^1)\ local op2 = memoize(function(a) return memoize(function(b) return op1(a, b) end) end)\ return make_bitop_uncached(op2, 2 ^ (t.n or 1))\ end\ \ local bxor1 = make_bitop({[0] = {[0] = 0,[1] = 1}, [1] = {[0] = 1, [1] = 0}, n = 4})\ \ local function bxor(a, b, c, ...)\ local z = nil\ if b then\ a = a % MOD\ b = b % MOD\ z = bxor1(a, b)\ if c then z = bxor(z, c, ...) end\ return z\ elseif a then return a % MOD\ else return 0 end\ end\ \ local function band(a, b, c, ...)\ local z\ if b then\ a = a % MOD\ b = b % MOD\ z = ((a + b) - bxor1(a,b)) / 2\ if c then z = bit32_band(z, c, ...) end\ return z\ elseif a then return a % MOD\ else return MODM end\ end\ \ local function bnot(x) return (-1 - x) % MOD end\ \ local function rshift1(a, disp)\ if disp < 0 then return lshift(a,-disp) end\ return math.floor(a % 2 ^ 32 / 2 ^ disp)\ end\ \ local function rshift(x, disp)\ if disp > 31 or disp < -31 then return 0 end\ return rshift1(x % MOD, disp)\ end\ \ local function lshift(a, disp)\ if disp < 0 then return rshift(a,-disp) end \ return (a * 2 ^ disp) % 2 ^ 32\ end\ \ local function rrotate(x, disp)\ x = x % MOD\ disp = disp % 32\ local low = band(x, 2 ^ disp - 1)\ return rshift(x, disp) + lshift(low, 32 - disp)\ end\ \ local k = {\ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,\ 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,\ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,\ 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,\ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,\ 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,\ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,\ 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,\ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,\ 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,\ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,\ 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,\ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,\ 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,\ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,\ 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,\ }\ \ local function str2hexa(s)\ return (string.gsub(s, \".\", function(c) return string.format(\"%02x\", string.byte(c)) end))\ end\ \ local function num2s(l, n)\ local s = \"\"\ for i = 1, n do\ local rem = l % 256\ s = string.char(rem) .. s\ l = (l - rem) / 256\ end\ return s\ end\ \ local function s232num(s, i)\ local n = 0\ for i = i, i + 3 do n = n*256 + string.byte(s, i) end\ return n\ end\ \ local function preproc(msg, len)\ local extra = 64 - ((len + 9) % 64)\ len = num2s(8 * len, 8)\ msg = msg .. \"\\128\" .. string.rep(\"\\0\", extra) .. len\ assert(#msg % 64 == 0)\ return msg\ end\ \ local function initH256(H)\ H[1] = 0x6a09e667\ H[2] = 0xbb67ae85\ H[3] = 0x3c6ef372\ H[4] = 0xa54ff53a\ H[5] = 0x510e527f\ H[6] = 0x9b05688c\ H[7] = 0x1f83d9ab\ H[8] = 0x5be0cd19\ return H\ end\ \ local function digestblock(msg, i, H)\ local w = {}\ for j = 1, 16 do w[j] = s232num(msg, i + (j - 1)*4) end\ for j = 17, 64 do\ local v = w[j - 15]\ local s0 = bxor(rrotate(v, 7), rrotate(v, 18), rshift(v, 3))\ v = w[j - 2]\ w[j] = w[j - 16] + s0 + w[j - 7] + bxor(rrotate(v, 17), rrotate(v, 19), rshift(v, 10))\ end\ \ local a, b, c, d, e, f, g, h = H[1], H[2], H[3], H[4], H[5], H[6], H[7], H[8]\ for i = 1, 64 do\ local s0 = bxor(rrotate(a, 2), rrotate(a, 13), rrotate(a, 22))\ local maj = bxor(band(a, b), band(a, c), band(b, c))\ local t2 = s0 + maj\ local s1 = bxor(rrotate(e, 6), rrotate(e, 11), rrotate(e, 25))\ local ch = bxor (band(e, f), band(bnot(e), g))\ local t1 = h + s1 + ch + k[i] + w[i]\ h, g, f, e, d, c, b, a = g, f, e, d + t1, c, b, a, t1 + t2\ end\ \ H[1] = band(H[1] + a)\ H[2] = band(H[2] + b)\ H[3] = band(H[3] + c)\ H[4] = band(H[4] + d)\ H[5] = band(H[5] + e)\ H[6] = band(H[6] + f)\ H[7] = band(H[7] + g)\ H[8] = band(H[8] + h)\ end\ \ function sha256(msg)\ msg = preproc(msg, #msg)\ local H = initH256({})\ for i = 1, #msg, 64 do digestblock(msg, i, H) end\ return str2hexa(num2s(H[1], 4) .. num2s(H[2], 4) .. num2s(H[3], 4) .. num2s(H[4], 4) ..\ num2s(H[5], 4) .. num2s(H[6], 4) .. num2s(H[7], 4) .. num2s(H[8], 4))\ end",["Pages"]={["404.ccml"]="\ \ \ Page Not Found\ \ \
\
\ 404 Page Not Found\
\
\

The page was not found on the server. Check the address and try again.

\
\ \ ",["6.ccml"]="\ \ \ Download Failed\ \ \
\
\ Download Failed\
\
\
\

The file you wanted failed to download. Try again or contact the file owner.

\
\ \ ",["7.ccml"]="\ \ \ No Modem Connected\ \ \
\
\ No Modem Connected\
\
\
\

Please attach a wirelss modem and restart Quest.

\
\ \ ",["download.ccml"]="\ \ \ Downloading File\ \ \ \
\
\ Downloading File\ \
\
\

\
\ \ ",["408.ccml"]="\ \ \ Page Not Found\ \ \
\
\ Page Load Cancelled\
\
\

The page either took too long to load or you cancelled loading it.

\
\ \ ",["text.ccml"]="\ \ \ An Error Occured\ \ \ \
\
\ An Error Occured\
\
\
\

Unknown Reason

\
\ \ ",["downloaded.ccml"]="\ \ \ Download Complete\ \ \ \
\
\ Download Complete\ \
\
\

\
\ \ ",["1.ccml"]="\ \ \ Failed to Download Page\ \ \
\
\ Failed to Download Page\
\
\
\

The page you requested failed to download. The page may not exist, the server may be experiencing problems or you have connection issues.

\
\ \ ",["2.ccml"]="\ \ \ Doctype Incorrect\ \ \
\
\ Doctype Incorrect\
\
\
\

This page has an incorrect doctype and can not be rendered. Quest can not view standard web pages.

\
\

If you made this page make sure the file starts with \"<!DOCTYPE ccml>\"

\
\ \ ",["3.ccml"]="\ \ \ Failed to Parse Page\ \ \
\
\ Failed to Parse Page\
\
\
\

The page you requested failed to parse. It is either malformed or your browser is out of date.

\
\ \ ",["4.ccml"]="\ \ \ HTTP Not Enabled\ \ \
\
\ HTTP Not Enabled\
\
\
\

You haven't enabled the HTTP API. To do so, take a look on the HTTP API page on the wiki for a link to a tutorial.

\
\ \ ",["5.ccml"]="\ \ \ Page Not Whitelisted\ \ \
\
\ Page Not Whitelisted\
\
\
\

The page you are trying to open isn't whitelisted. Please take a look on the forums or the wiki as to how to fix this.

\
\ \ ",},["lQuery"]="function fn(selector)\ if not selector then\ else\ local elements = lQuery.webView:ResolveElements(selector)\ local function relayout()\ lQuery.webView:RepositionLayout()\ end\ if elements and #elements > 0 then\ local each = function(func)\ for i, v in ipairs(elements) do\ func(v, i)\ end\ end\ \ local response = {\ text = function(text)\ each(function(elem)\ if elem.Text then\ elem.Text = tostring(text)\ end\ end)\ end,\ \ width = function(width)\ if type(width) == 'number' then\ each(function(elem)\ elem.Width = width\ end)\ relayout()\ end\ end,\ \ height = function(height)\ if type(height) == 'number' then\ each(function(elem)\ elem.Height = height\ end)\ relayout()\ end\ end,\ \ colour = function(colour)\ if type(colour) == 'number' then\ each(function(elem)\ if elem.TextColour then\ elem.TextColour = colour\ end\ end)\ end\ end,\ \ bgcolour = function(bgcolour)\ if type(bgcolour) == 'number' then\ each(function(elem)\ if elem.BackgroundColour then\ elem.BackgroundColour = bgcolour\ end\ end)\ end\ end,\ \ align = function(align)\ if type(align) == 'string' and align:lower() == 'left' or align:lower() == 'center' or align:lower() == 'right' then\ each(function(elem)\ if elem.Align then\ elem.Align = align:lower():gsub(\"^%l\", string.upper)\ end\ end)\ end\ end,\ \ attr = function(name)\ local values = {}\ each(function(elem)\ if elem.Element.Attributes and elem.Element.Attributes[name] then\ table.insert(values, elem.Element.Attributes[name])\ end\ end)\ return values\ end,\ \ remove = function()\ each(function(elem)\ lQuery.webView:RemoveElement(elem)\ relayout()\ end)\ end,\ \ focus = function()\ each(function(elem)\ elem.Bedrock:SetActiveObject(elem)\ end)\ end\ }\ response.color = response.colour\ response.bgcolor = response.bgcolour\ \ return response\ end\ end\ end",["Peripheral"]="GetPeripheral = function(_type)\ for i, p in ipairs(GetPeripherals()) do\ if p.Type == _type then\ return p\ end\ end\ end\ \ Call = function(type, ...)\ local tArgs = {...}\ local p = GetPeripheral(type)\ peripheral.call(p.Side, unpack(tArgs))\ end\ \ local getNames = peripheral.getNames or function()\ local tResults = {}\ for n,sSide in ipairs( rs.getSides() ) do\ if peripheral.isPresent( sSide ) then\ table.insert( tResults, sSide )\ local isWireless = false\ if pcall(function()isWireless = peripheral.call(sSide, 'isWireless') end) then\ isWireless = true\ end \ if peripheral.getType( sSide ) == \"modem\" and not isWireless then\ local tRemote = peripheral.call( sSide, \"getNamesRemote\" )\ for n,sName in ipairs( tRemote ) do\ table.insert( tResults, sName )\ end\ end\ end\ end\ return tResults\ end\ \ GetPeripherals = function(filterType)\ local peripherals = {}\ for i, side in ipairs(getNames()) do\ local name = peripheral.getType(side):gsub(\"^%l\", string.upper)\ local code = string.upper(side:sub(1,1))\ if side:find('_') then\ code = side:sub(side:find('_')+1)\ end\ \ local dupe = false\ for i, v in ipairs(peripherals) do\ if v[1] == name .. ' ' .. code then\ dupe = true\ end\ end\ \ if not dupe then\ local _type = peripheral.getType(side)\ local formattedType = _type:sub(1, 1):upper() .. _type:sub(2, -1)\ local isWireless = false\ if _type == 'modem' then\ if not pcall(function()isWireless = peripheral.call(side, 'isWireless') end) then\ isWireless = true\ end \ if isWireless then\ _type = 'wireless_modem'\ formattedType = 'Wireless Modem'\ name = 'W '..name\ end\ end\ if not filterType or _type == filterType then\ table.insert(peripherals, {Name = name:sub(1,8) .. ' '..code, Fullname = name .. ' ('..side:sub(1, 1):upper() .. side:sub(2, -1)..')', Side = side, Type = _type, Wireless = isWireless, FormattedType = formattedType})\ end\ end\ end\ return peripherals\ end\ \ GetSide = function(side)\ for i, p in ipairs(GetPeripherals()) do\ if p.Side == side then\ return p\ end\ end\ end\ \ PresentNamed = function(name)\ return peripheral.isPresent(name)\ end\ \ CallType = function(type, ...)\ local tArgs = {...}\ local p = GetPeripheral(type)\ return peripheral.call(p.Side, unpack(tArgs))\ end\ \ CallNamed = function(name, ...)\ local tArgs = {...}\ return peripheral.call(name, unpack(tArgs))\ end\ \ GetInfo = function(p)\ local info = {}\ local buttons = {}\ if p.Type == 'computer' then\ local id = peripheral.call(p.Side:lower(),'getID')\ if id then\ info = {\ ID = tostring(id)\ }\ else\ info = {}\ end\ elseif p.Type == 'drive' then\ local discType = 'No Disc'\ local discID = nil\ local mountPath = nil\ local discLabel = nil\ local songName = nil\ if peripheral.call(p.Side:lower(), 'isDiskPresent') then\ if peripheral.call(p.Side:lower(), 'hasData') then\ discType = 'Data'\ discID = peripheral.call(p.Side:lower(), 'getDiskID')\ if discID then\ discID = tostring(discID)\ else\ discID = 'None'\ end\ mountPath = '/'..peripheral.call(p.Side:lower(), 'getMountPath')..'/'\ discLabel = peripheral.call(p.Side:lower(), 'getDiskLabel')\ else\ discType = 'Audio'\ songName = peripheral.call(p.Side:lower(), 'getAudioTitle')\ end\ end\ if mountPath then\ table.insert(buttons, {Text = 'View Files', OnClick = function(self, event, side, x, y)GoToPath(mountPath)end})\ elseif discType == 'Audio' then\ table.insert(buttons, {Text = 'Play', OnClick = function(self, event, side, x, y)\ if self.Text == 'Play' then\ disk.playAudio(p.Side:lower())\ self.Text = 'Stop'\ else\ disk.stopAudio(p.Side:lower())\ self.Text = 'Play'\ end\ end})\ else\ diskOpenButton = nil\ end\ if discType ~= 'No Disc' then\ table.insert(buttons, {Text = 'Eject', OnClick = function(self, event, side, x, y)disk.eject(p.Side:lower()) sleep(0) RefreshFiles() end})\ end\ \ info = {\ ['Disc Type'] = discType,\ ['Disc Label'] = discLabel,\ ['Song Title'] = songName,\ ['Disc ID'] = discID,\ ['Mount Path'] = mountPath\ }\ elseif p.Type == 'printer' then\ local pageSize = 'No Loaded Page'\ local _, err = pcall(function() return tostring(peripheral.call(p.Side:lower(), 'getPgaeSize')) end)\ if not err then\ pageSize = tostring(peripheral.call(p.Side:lower(), 'getPageSize'))\ end\ info = {\ ['Paper Level'] = tostring(peripheral.call(p.Side:lower(), 'getPaperLevel')),\ ['Paper Size'] = pageSize,\ ['Ink Level'] = tostring(peripheral.call(p.Side:lower(), 'getInkLevel'))\ }\ elseif p.Type == 'modem' then\ info = {\ ['Connected Peripherals'] = tostring(#peripheral.call(p.Side:lower(), 'getNamesRemote'))\ }\ elseif p.Type == 'monitor' then\ local w, h = peripheral.call(p.Side:lower(), 'getSize')\ local screenType = 'Black and White'\ if peripheral.call(p.Side:lower(), 'isColour') then\ screenType = 'Colour'\ end\ local buttonTitle = 'Use as Screen'\ if OneOS.Settings:GetValues()['Monitor'] == p.Side:lower() then\ buttonTitle = 'Use Computer Screen'\ end\ table.insert(buttons, {Text = buttonTitle, OnClick = function(self, event, side, x, y)\ self.Bedrock:DisplayAlertWindow('Reboot Required', \"To change screen you'll need to reboot your computer.\", {'Reboot', 'Cancel'}, function(value)\ if value == 'Reboot' then\ if buttonTitle == 'Use Computer Screen' then\ OneOS.Settings:SetValue('Monitor', nil)\ else\ OneOS.Settings:SetValue('Monitor', p.Side:lower())\ end\ OneOS.Reboot()\ end\ end)\ end\ })\ info = {\ ['Type'] = screenType,\ ['Width'] = tostring(w),\ ['Height'] = tostring(h),\ }\ end\ info.Buttons = buttons\ return info\ end",["Objects"]={["HeadingView.lua"]="Inherit = 'View'\ Height = 3\ \ OnLoad = function(self)\ self:OnUpdate('Text')\ end\ \ OnUpdate = function(self, value)\ if value == 'Text' then\ self:RemoveAllObjects()\ self:AddObject({\ Y = 1,\ X = 1,\ Width = \"100%\",\ Align = \"Center\",\ Type = \"Label\",\ Text = self.Text,\ TextColour = self.TextColour,\ BackgroundColour = self.BackgroundColour\ })\ \ local underline = ''\ for i = 1, #self.Text + 2 do\ underline = underline .. '='\ end\ self:AddObject({\ Y = 2,\ X = 1,\ Width = \"100%\",\ Align = \"Center\",\ Type = \"Label\",\ Text = underline,\ TextColour = self.TextColour,\ BackgroundColour = self.BackgroundColour\ })\ end\ end",["WebImageView.lua"]="URL = false\ Image = false\ \ OnDraw = function(self, x, y)\ if not self.Image then\ Drawing.DrawBlankArea(x, y, self.Width, self.Height, colours.lightGrey)\ elseif self.Format == 'nft' then\ Drawing.DrawImage(x, y, self.Image, self.Width, self.Height)\ elseif self.Format == 'nfp' or self.Format == 'paint' then\ for _x, col in ipairs(self.Image) do\ for _y, colour in ipairs(col) do\ Drawing.WriteToBuffer(x+_x-1, y+_y-1, ' ', colours.white, colour)\ end\ end\ elseif self.Format == 'skch' or self.Format == 'sketch' then\ Drawing.DrawImage(x, y, self.Image, self.Width, self.Height)\ end\ end\ \ OnUpdate = function(self, value)\ if value == 'URL' and self.URL then\ fetchHTTPAsync(resolveFullUrl(self.URL), function(ok, event, response)\ if ok then\ local width, height = self.Width, self.Height\ \ local lines = {}\ for line in response.readLine do\ table.insert(lines, line)\ end\ response.close()\ local content = table.concat(lines, '\\n')\ \ if not self.Format then\ self.Format = self:DetermineFormat(content)\ end\ \ if self.Format == 'nft' then\ self.Image, self.Width, self.Height = self:ReadNFT(lines)\ elseif self.Format == 'nfp' or self.Format == 'paint' then\ self.Image, self.Width, self.Height = self:ReadNFP(lines)\ elseif self.Format == 'skch' or self.Format == 'sketch' then\ self.Image, self.Width, self.Height = self:ReadSKCH(content)\ end\ if (width ~= self.Width or height ~= self.Height) then\ self.Bedrock:GetObject('WebView'):RepositionLayout()\ end\ end\ end)\ end\ end\ \ DetermineFormat = function(self, content)\ if type(textutils.unserialize(content)) == 'table' then\ -- It's a serlized table, asume sketch\ return 'skch'\ elseif string.find(content, string.char(30)) or string.find(content, string.char(31)) then\ -- Contains the characters that set colours, asume nft\ return 'nft'\ else\ -- Otherwise asume nfp\ return 'nfp'\ end\ end\ \ ReadSKCH = function(self, content)\ local _layers = textutils.unserialize(content)\ local layers = {}\ \ local width, height = 1, 1\ \ for i, layer in ipairs(_layers) do\ if layer.Visible then\ local nft, w, h = self:ReadNFT(layer.Pixels)\ if w > width then\ width = w\ end\ if h > height then\ height = h\ end\ table.insert(layers, nft)\ end\ end\ \ --flatten the layers\ local image = {\ text = {},\ textcol = {}\ }\ \ for i, layer in ipairs(layers) do\ for y, row in ipairs(layer) do\ if not image[y] then\ image[y] = {}\ end\ for x, pixel in ipairs(row) do\ if not image[y][x] or pixel ~= colours.transparent then\ image[y][x] = pixel\ end\ end\ end\ for y, row in ipairs(layer.text) do\ if not image.text[y] then\ image.text[y] = {}\ end\ for x, pixel in ipairs(row) do\ if not image.text[y][x] or pixel ~= ' ' then\ image.text[y][x] = pixel\ end\ end\ end\ for y, row in ipairs(layer.textcol) do\ if not image.textcol[y] then\ image.textcol[y] = {}\ end\ for x, pixel in ipairs(row) do\ if not image.textcol[y][x] or layer.text[y][x] ~= ' ' then\ image.textcol[y][x] = pixel\ end\ end\ end\ end\ \ return image, width, height\ end\ \ local function getColourOf(hex)\ if hex == ' ' then\ return colours.transparent\ end\ local value = tonumber(hex, 16)\ if not value then return nil end\ value = math.pow(2,value)\ return value\ end\ \ ReadNFP = function(self, lines)\ local image = {}\ local y = 1\ for y, line in ipairs(lines) do\ for x = 1, #line do\ if not image[x] then\ image[x] = {}\ end\ image[x][y] = getColourOf(line:sub(x,x))\ end\ line = file.readLine()\ end\ file.close()\ return image, #image, #image[1]\ end\ \ ReadNFT = function(self, lines)\ local image = {\ text = {},\ textcol = {}\ }\ for num, sLine in ipairs(lines) do\ table.insert(image, num, {})\ table.insert(image.text, num, {})\ table.insert(image.textcol, num, {})\ local writeIndex = 1\ local bgNext, fgNext = false, false\ local currBG, currFG = nil,nil\ for i=1,#sLine do\ local nextChar = string.sub(sLine, i, i)\ if nextChar:byte() == 30 then\ bgNext = true\ elseif nextChar:byte() == 31 then\ fgNext = true\ elseif bgNext then\ currBG = Drawing.GetColour(nextChar)\ if currBG == nil then\ currBG = colours.transparent\ end\ bgNext = false\ elseif fgNext then\ currFG = Drawing.GetColour(nextChar)\ if currFG == nil or currFG == colours.transparent then\ currFG = colours.white\ end\ fgNext = false\ else\ if nextChar ~= \" \" and currFG == nil then\ currFG = colours.white\ end\ image[num][writeIndex] = currBG\ image.textcol[num][writeIndex] = currFG\ image.text[num][writeIndex] = nextChar\ writeIndex = writeIndex + 1\ end\ end\ end\ return image, #image[1], #image\ end",["CenterView.lua"]="Inherit = 'View'\ \ OnUpdate = function(self, value)\ if value == 'Children' or value == 'Width' then\ local y = 1\ for i, v in ipairs(self.Children) do\ v.Y = y\ y = y + v.Height\ v.X = math.floor((self.Width - v.Width) / 2) + 1\ end\ self.Height = y - 1\ end\ end",["DividerView.lua"]="Inherit = 'View'\ Char = nil\ \ OnDraw = function(self, x, y)\ if self.BackgroundColour then\ if self.Char then\ Drawing.DrawArea (x, y, self.Width, self.Height, self.Char, self.TextColour, self.BackgroundColour)\ else\ Drawing.DrawBlankArea(x, y, self.Width, self.Height, self.BackgroundColour)\ end\ end\ end",["LinkView.lua"]="Inherit = 'View'\ Height = 2\ UnderlineColour = colours.blue\ UnderlineVisible = true\ \ OnLoad = function(self)\ if self.Text and #self.Text > 0 then\ self:AddObject({\ Y = 1,\ X = 1,\ Width = self.Width,\ Align = self.Align,\ Type = \"Label\",\ Text = self.Text,\ TextColour = self.TextColour,\ BackgroundColour = self.BackgroundColour\ })\ end\ end\ \ OnRecalculateStart = function(self)\ self:RemoveObject('UnderlineLabel')\ end\ \ OnRecalculateEnd = function(self, currentY)\ if self.UnderlineVisible then\ local underline = ''\ local len = self.Width\ if self.Text then\ len = #self.Text\ end\ \ for i = 1, len do\ underline = underline .. '-'\ end\ local col = self.UnderlineColour\ if self.UnderlineColour == nil then\ col = self.TextColour\ end\ \ local ul = self:AddObject({\ Y = currentY,\ X = 1,\ Width = self.Width,\ Align = self.Align,\ Type = \"Label\",\ Name = \"UnderlineLabel\",\ Text = underline,\ TextColour = col,\ BackgroundColour = self.BackgroundColour\ }) \ return currentY + 1\ else\ return currentY\ end\ end\ \ OnClick = function(self)\ self.Bedrock:GetObject('WebView'):GoToURL(self.URL)\ end",["SelectView.lua"]="Inherit = 'Button'\ MenuItems = nil\ Children = {}\ Selected = nil\ \ OnUpdate = function(self, value)\ if value == 'Height' and self.Height ~= 1 then\ self.Height = 1\ end\ end\ \ Select = function(self, index)\ if self.MenuItems[index] then\ local text = self.MenuItems[index].Text\ for i = 1, self.Width - 3 - #text do\ text = text .. ' '\ end\ text = text .. 'V'\ self.Text = text\ self.Selected = index\ end\ end\ \ OnInitialise = function(self)\ self:ClearMenuItems()\ end\ \ ClearMenuItems = function(self)\ self.MenuItems = {}\ end\ \ AddMenuItem = function(self, item)\ table.insert(self.MenuItems, item)\ if not self.Selected then\ if #self.MenuItems ~= 0 then\ self:Select(1)\ end\ end\ end\ \ OnClick = function(self, event, side, x, y)\ if self:ToggleMenu({\ Type = \"Menu\",\ HideTop = true,\ Children = self.MenuItems\ }, x, 1) then\ for i, child in ipairs(self.Bedrock.Menu.Children) do\ child.OnClick = function(_self, event, side, x, y)\ self:Select(i)\ end\ end\ end\ end",["WebView.lua"]="Inherit = 'ScrollView'\ URL = nil\ FakeURL = nil\ LoadingURL = nil\ Tree = nil\ BackgroundColour = colours.white\ ScriptEnvironment = nil\ Timers = nil\ Download = nil\ \ -- TODO: strip this down to remove positioning stuff\ UpdateLayout = function(self)\ self:RemoveAllObjects()\ self.BackgroundColour = colours.white\ local body = self.Tree:GetElement('body')\ \ --TODO: check that body exists, if not redirect to an error page\ \ if body.BackgroundColour then\ self.BackgroundColour = body.BackgroundColour\ end\ \ local node = true\ node = function(children, parentObject)\ local currentY = 1\ for i, v in ipairs(children) do\ local object = v:CreateObject(parentObject, currentY)\ if object then\ v.Object = object\ if v.Children and #v.Children > 0 and object.Children then\ local usedY = node(v.Children, object)\ if not v.Attributes.height then\ object.Height = usedY\ end\ object:OnUpdate('Children')\ end\ -- if not object.Height then\ -- for k, v in pairs(object) do\ -- error(v)\ -- end \ -- error('Nope')\ -- end\ currentY = currentY + object.Height\ end\ end\ return currentY - 1\ end\ node(body.Children, self)\ \ self:RepositionLayout()\ \ local head = self.Tree:GetElement('head')\ if head then\ for i, child in ipairs(head.Children) do\ if child.Tag == 'script' then\ child:InsertScript(self)\ end\ end\ end\ end\ \ RepositionLayout = function(self)\ local node = true\ node = function(children, isFloat, parent)\ if parent.OnRecalculateStart then\ parent:OnRecalculateStart()\ end\ \ local currentY = 1\ local currentX = 1\ local tallestChild = 1\ for i, child in ipairs(children) do\ if child.Type ~= 'ScrollBar' then\ if isFloat then\ if currentX ~= 1 and parent.Width - currentX + 1 < child.Width then\ currentX = 1\ currentY = currentY + tallestChild\ tallestChild = 1\ end\ \ if parent.Align == \"Left\" then\ child.X = currentX\ elseif parent.Align == \"Right\" then\ child.X = parent.Width - currentX - child.Width + 2\ end\ end\ child.Y = currentY\ \ if child.Children and #child.Children > 0 then\ local usedY = node(child.Children, child.IsFloat, child)\ child:OnUpdate('Children')\ if not child.Element.Attributes.height then\ child.Height = usedY\ end\ end\ \ if child.Height > tallestChild then\ tallestChild = child.Height\ end\ \ if isFloat then\ currentX = currentX + child.Width\ else\ currentY = currentY + child.Height\ end\ end\ end\ if isFloat then\ currentY = currentY + tallestChild\ end\ if parent.OnRecalculateEnd then\ currentY = parent:OnRecalculateEnd(currentY)\ end\ return currentY - 1\ end\ node(self.Children, self.IsFloat, self)\ \ self:UpdateScroll()\ end\ \ GoToURL = function(self, url, nonVerbose, noHistory, post)\ self.BackgroundColour = colours.white\ self:RemoveAllObjects()\ if self.OnPageLoadStart and not nonVerbose then\ self:OnPageLoadStart(url, noHistory)\ end\ self.LoadingURL = url\ self.FakeURL = url\ self:InitialiseScriptEnvironment()\ \ if not http and url:find('http://') then\ if self.OnPageLoadFailed then\ self:OnPageLoadFailed(url, 4, noHistory)\ end\ return\ end\ \ -- error(fs.getName(url))\ local parts = urlComponents(url)\ -- if url:sub(#url) ~= '/' and url:find('?') then\ -- fileName = fs.getName(url:sub(1, url:find('?') - 1))\ -- else\ -- fileName = fs.getName(url)\ -- end\ \ local fileName = parts.filename\ local extension\ if fileName == '' or url:sub(#url) == '/' then\ extension = true\ else\ extension = fileName:match('%.[0-9a-z%?%%]+$')\ if extension then\ extension = extension:sub(2)\ end\ end\ \ if not url:find('quest://download.ccml') and not url:find('quest://downloaded.ccml') then\ self.Download = nil\ else\ extension = true\ end\ \ -- TODO: 404s are counted as downloads\ \ if not extension or (extension ~= true and extension ~= '' and extension ~= 'ccml' and extension ~= 'html' and extension ~= 'php' and extension ~= 'asp' and extension ~= 'aspx' and extension ~= 'jsp' and extension ~= 'qst' and extension ~= 'com' and extension ~= 'me' and extension ~= 'net' and extension ~= 'info' and extension ~= 'au' and extension ~= 'nz') then\ local downloadsFolder = '/Downloads/'\ if OneOS then\ downloadsFolder = '/Desktop/Documents/Downloads/'\ end\ if not fs.exists(downloadsFolder) then\ fs.makeDir(downloadsFolder)\ end\ \ local downloadPath = downloadsFolder..fileName\ local i = 1\ while fs.exists(downloadPath) do\ i = i + 1\ downloadPath = downloadsFolder..fileName .. ' (' .. i .. ')'\ end\ \ self.Download = url\ fetchHTTPAsync(url, function(ok, event, response)\ if self.Download == url then\ self.Download = nil\ if ok then\ if response.getResponseCode then\ local code = response.getResponseCode()\ if code ~= 200 then\ self:OnPageLoadFailed(url, 6, noHistory)\ response.close()\ return\ end\ end\ local f = fs.open(downloadPath, 'w')\ if f then\ f.write(response.readAll())\ f.close()\ self:GoToURL('quest://downloaded.ccml?path='..textutils.urlEncode(downloadPath), true, true)\ else\ self:OnPageLoadFailed(url, 6, noHistory)\ end\ response.close()\ else\ self:OnPageLoadFailed(url, 6, noHistory)\ end\ end\ end)\ \ self:GoToURL('quest://download.ccml?path='..textutils.urlEncode(downloadPath), true, true)\ self:OnPageLoadEnd(url, noHistory)\ else\ fetchHTTPAsync(url, function(ok, event, response)\ self.LoadingURL = nil\ if ok then\ if response.getResponseCode then\ local code = response.getResponseCode()\ if code ~= 200 then\ if self.OnPageLoadFailed then\ self:OnPageLoadFailed(url, code, noHistory)\ end\ response.close()\ return\ end\ end\ \ self.Tree, err = ElementTree:Initialise(response.readAll())\ response.close()\ if not err then\ self.URL = url\ self:UpdateLayout()\ if self.OnPageLoadEnd and not nonVerbose then\ self:OnPageLoadEnd(url, noHistory)\ end\ else\ if self.OnPageLoadFailed then\ self:OnPageLoadFailed(url, err, noHistory)\ end\ end\ elseif self.OnPageLoadFailed and not nonVerbose then\ self:OnPageLoadFailed(url, event, noHistory)\ end\ end, post)\ end\ end\ \ Stop = function(self)\ cancelHTTPAsync(self.LoadingURL)\ if self.OnPageLoadFailed then\ self:OnPageLoadFailed(url, Errors.TimeoutStop)\ end\ end\ \ ResolveElements = function(self, selector)\ local elements = {}\ local node = true\ local isClass = false\ if selector:sub(1,1) == '.' then\ isClass = true\ end\ \ node = function(tbl)\ for i,v in ipairs(tbl) do\ if type(v) == 'table' and v.Tag then\ if not isClass and v.Tag:lower() == selector:lower() then\ table.insert(elements, v.Object)\ elseif isClass and v.Attributes.class and v.Attributes.class:lower() == selector:lower():sub(2) then\ table.insert(elements, v.Object)\ end\ if v.Children then\ local r = node(v.Children)\ end\ end\ end\ end\ node(self.Tree.Tree)\ return elements\ end\ \ InitialiseScriptEnvironment = function(self)\ lQuery.webView = self\ if self.Timers then\ for i, timer in ipairs(self.Timers) do\ -- error('clear '..timer)\ self.Bedrock.Timers[timer] = nil\ end\ end\ self.Timers = {}\ \ local getValues = urlComponents(self.LoadingURL).get\ \ self.ScriptEnvironment = {\ keys = keys,\ printError = printError, -- maybe don't have this\ assert = assert,\ getfenv = getfenv,\ bit = bit,\ rawset = rawset,\ tonumber = tonumber,\ loadstring = loadstring,\ error = error, -- maybe don't have this\ tostring = tostring,\ type = type,\ coroutine = coroutine,\ next = next,\ unpack = unpack,\ colours = colours,\ pcall = pcall,\ math = math,\ pairs = pairs,\ rawget = rawget,\ _G = _G,\ __inext = __inext,\ read = read,\ ipairs = ipairs,\ xpcall = xpcall,\ rawequal = rawequal,\ setfenv = setfenv,\ http = http, --create an ajax thing to replace this\ string = string,\ setmetatable = setmetatable,\ getmetatable = getmetatable,\ table = table,\ parallel = parallel, -- this mightn't work properly\ textutils = textutils,\ colors = colors,\ vector = vector,\ select = select,\ os = {\ version = os.version,\ getComputerID = os.getComputerID,\ getComputerLabel = os.getComputerLabel,\ clock = os.clock,\ time = os.time,\ day = os.day,\ },\ lQuery = lQuery.fn,\ l = lQuery.fn,\ setTimeout = function(func, delay)\ if type(func) == 'function' and type(delay) == 'number' then\ local t = self.Bedrock:StartTimer(func, delay)\ table.insert(self.Timers, t)\ return t\ end\ end,\ setInterval = function(func, interval)\ if type(func) == 'function' and type(interval) == 'number' then\ local t = self.Bedrock:StartRepeatingTimer(function(timer)\ table.insert(self.Timers, timer)\ func()\ end, interval)\ table.insert(self.Timers, t)\ return t\ end\ end,\ clearTimeout = function(timer)\ self.Bedrock.Timers[timer] = nil\ end,\ clearInterval = function(timer)\ self.Bedrock.Timers[timer] = nil\ end,\ window = {\ location = self.URL,\ realLocation = self.LoadingURL,\ get = getValues,\ version = QuestVersion\ }\ }\ end\ \ LoadScript = function(self, script)\ local fn, err = loadstring(script, 'Script Tag Error: '..self.URL)\ if fn then\ setfenv(fn, self.ScriptEnvironment)\ fn()\ else\ local start = err:find(': ')\ self:OnPageLoadFailed(url, err:sub(start + 2), noHistory)\ end\ end\ \ RemoveElement = function(self, elem)\ local elements = {}\ local node = true\ node = function(tbl)\ for i,v in ipairs(tbl) do\ if type(v) == 'table' then\ if v == elem.Element then\ elem.Parent:RemoveObject(elem)\ v = nil\ return\ end\ if v.Children then\ local r = node(v.Children)\ end\ end\ end\ end\ node(self.Tree.Tree)\ end",["FloatView.lua"]="Inherit = 'View'\ IsFloat = true\ Align = \"Left\"",["FormView.lua"]="Inherit = 'View'\ \ OnTab = function(self)\ local active = self.Bedrock:GetActiveObject()\ local selected = nil\ local selectNext = false\ local function node(tree)\ for i, v in ipairs(tree) do\ if selectNext then\ if v.Type == 'TextBox' or v.Type == 'SecureTextBox' then\ selected = v\ return\ end\ elseif v == active then\ selectNext = true\ end\ if v.Children then\ node(v.Children)\ end\ end\ end\ node(self.Children)\ \ if selected then\ self.Bedrock:SetActiveObject(selected)\ end\ end",},["Views"]={["main.view"]="{\ [\"Children\"]={\ [1]={\ [\"Y\"]=1,\ [\"X\"]=1,\ [\"Name\"]=\"Toolbar\",\ [\"Type\"]=\"View\",\ [\"InheritView\"]=\"toolbar\"\ },\ [2]={\ [\"Y\"]=4,\ [\"X\"]=1,\ [\"Height\"]=\"100%,-3\",\ [\"Width\"]=\"100%\",\ [\"Name\"]=\"WebView\",\ [\"Type\"]=\"WebView\"\ },\ [3]={\ [\"Y\"]=1,\ [\"X\"]=1,\ [\"Height\"]=1,\ [\"Width\"]=\"100%\",\ [\"Name\"]=\"PageTitleLabel\",\ [\"Type\"]=\"Label\",\ [\"Align\"]=\"Center\",\ [\"TextColour\"]=128,\ },\ },\ [\"BackgroundColour\"]=1,\ [\"ToolBarColour\"]=256,\ [\"ToolBarTextColour\"]=1\ }",["optionsmenu.view"]="{\ [\"Type\"]=\"Menu\",\ [\"Owner\"]=\"OptionsButton\",\ [\"Children\"]={\ [1]={\ [\"Name\"]=\"StopMenuItem\",\ [\"Type\"]=\"Button\",\ [\"Text\"]=\"Stop\"\ },\ [2]={\ [\"Name\"]=\"ReloadMenuItem\",\ [\"Type\"]=\"Button\",\ [\"Text\"]=\"Reload\"\ },\ [3]={\ [\"Name\"]=\"GoHomeMenuItem\",\ [\"Type\"]=\"Button\",\ [\"Text\"]=\"Go Home\"\ },\ [4]={\ [\"Name\"]=\"SetHomeMenuItem\",\ [\"Type\"]=\"Button\",\ [\"Text\"]=\"Set Home\"\ },\ [5]={\ [\"Name\"]=\"Separator\",\ [\"Type\"]=\"Separator\"\ },\ [6]={\ [\"Name\"]=\"QuitMenuItem\",\ [\"Type\"]=\"Button\",\ [\"Text\"]=\"Quit\"\ }\ },\ }",["toolbar.view"]="{\ [\"Width\"]=\"100%\",\ [\"Height\"]=3,\ [\"Type\"]=\"View\",\ [\"BackgroundColour\"]=256,\ [\"Children\"]={\ [1]={\ [\"Y\"]=2,\ [\"X\"]=2,\ [\"Name\"]=\"BackButton\",\ [\"Type\"]=\"Button\",\ [\"Enabled\"]=false,\ [\"TextColour\"]=128,\ [\"Text\"]=\"<\",\ [\"BackgroundColour\"]=1\ },\ [2]={\ [\"Y\"]=2,\ [\"X\"]=6,\ [\"Name\"]=\"ForwardButton\",\ [\"Type\"]=\"Button\",\ [\"Enabled\"]=false,\ [\"TextColour\"]=128,\ [\"Text\"]=\">\",\ [\"BackgroundColour\"]=1\ },\ [3]={\ [\"Y\"]=2,\ [\"X\"]=10,\ [\"Width\"]=\"100%,-14\",\ [\"Type\"]=\"View\",\ [\"BackgroundColour\"]=1\ },\ [4]={\ [\"Y\"]=2,\ [\"X\"]=10,\ [\"Width\"]=\"100%,-14\",\ [\"Name\"]=\"URLTextBox\",\ [\"Type\"]=\"TextBox\",\ [\"TextColour\"]=128,\ [\"Placeholder\"]=\"Website URL...\",\ [\"PlaceholderTextColour\"]=256,\ [\"BackgroundColour\"]=0,\ [\"SelectOnClick\"]=true\ },\ [5]={\ [\"Y\"]=2,\ [\"X\"]=10,\ [\"Width\"]=\"100%,-14\",\ [\"Name\"]=\"LoadingLabel\",\ [\"Type\"]=\"Label\",\ [\"TextColour\"]=256,\ [\"Text\"]=\"Loading...\",\ [\"BackgroundColour\"]=1,\ [\"Align\"]=\"Center\",\ [\"Visible\"]=false\ },\ [6]={\ [\"Y\"]=2,\ [\"X\"]=\"100%,-3\",\ [\"Name\"]=\"OptionsButton\",\ [\"Type\"]=\"Button\",\ [\"Text\"]=\"V\",\ [\"TextColour\"]=128,\ [\"BackgroundColour\"]=1\ },\ },\ }",},} local function run(tArgs) local fnFile, err = loadstring(files['startup'], 'startup') if err then error(err) end local function split(str, pat) local t = {} local fpat = "(.-)" .. pat local last_end = 1 local s, e, cap = str:find(fpat, 1) while s do if s ~= 1 or cap ~= "" then table.insert(t,cap) end last_end = e+1 s, e, cap = str:find(fpat, last_end) end if last_end <= #str then cap = str:sub(last_end) table.insert(t, cap) end return t end local function resolveTreeForPath(path, single) local _files = files local parts = split(path, '/') if parts then for i, v in ipairs(parts) do if #v > 0 then if _files[v] then _files = _files[v] else _files = nil break end end end elseif #path > 0 and path ~= '/' then _files = _files[path] end if not single or type(_files) == 'string' then return _files end end local oldFs = fs local env env = { fs = { list = function(path) local list = {} if fs.exists(path) then list = fs.list(path) end for k, v in pairs(resolveTreeForPath(path)) do if not fs.exists(path .. '/' ..k) then table.insert(list, k) end end return list end, exists = function(path) if fs.exists(path) then return true elseif resolveTreeForPath(path) then return true else return false end end, isDir = function(path) if fs.isDir(path) then return true else local tree = resolveTreeForPath(path) if tree and type(tree) == 'table' then return true else return false end end end, isReadOnly = function(path) if not fs.isReadOnly(path) then return false else return true end end, getName = fs.getName, getSize = fs.getSize, getFreespace = fs.getFreespace, makeDir = fs.makeDir, move = fs.move, copy = fs.copy, delete = fs.delete, combine = fs.combine, open = function(path, mode) if fs.exists(path) then return fs.open(path, mode) elseif type(resolveTreeForPath(path)) == 'string' then local handle = {close = function()end} if mode == 'r' then local content = resolveTreeForPath(path) handle.readAll = function() return content end local line = 1 local lines = split(content, '\n') handle.readLine = function() if line > #lines then return nil else return lines[line] end line = line + 1 end return handle else error('Cannot write to read-only file (compilr archived).') end else return fs.open(path, mode) end end }, loadfile = function( _sFile ) local file = env.fs.open( _sFile, "r" ) if file then local func, err = loadstring( file.readAll(), fs.getName( _sFile ) ) file.close() return func, err end return nil, "File not found: ".._sFile end, dofile = function( _sFile ) local fnFile, e = env.loadfile( _sFile ) if fnFile then setfenv( fnFile, getfenv(2) ) return fnFile() else error( e, 2 ) end end } setmetatable( env, { __index = _G } ) local tAPIsLoading = {} env.os.loadAPI = function( _sPath ) local sName = fs.getName( _sPath ) if tAPIsLoading[sName] == true then printError( "API "..sName.." is already being loaded" ) return false end tAPIsLoading[sName] = true local tEnv = {} setmetatable( tEnv, { __index = env } ) local fnAPI, err = env.loadfile( _sPath ) if fnAPI then setfenv( fnAPI, tEnv ) fnAPI() else printError( err ) tAPIsLoading[sName] = nil return false end local tAPI = {} for k,v in pairs( tEnv ) do tAPI[k] = v end env[sName] = tAPI tAPIsLoading[sName] = nil return true end env.shell = shell setfenv( fnFile, env ) fnFile(unpack(tArgs)) end local function extract() local function node(path, tree) if type(tree) == 'table' then fs.makeDir(path) for k, v in pairs(tree) do node(path .. '/' .. k, v) end else local f = fs.open(path, 'w') if f then f.write(tree) f.close() end end end node('', files) end local tArgs = {...} if #tArgs == 1 and tArgs[1] == '--extract' then extract() else run(tArgs) end