SquidDev

BobCatAPI

Jun 3rd, 2014
841
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 34.69 KB | None | 0 0
  1. do
  2.   Object = { __name = "Object" }
  3.  
  4.   function Object:init(...) end
  5.  
  6.   function Object:super(class)
  7.     local obj = self
  8.     return setmetatable({ }, { __index = function(t, k)
  9.         return function(...)
  10.             while class do
  11.                 local method = class[k]
  12.                 if method then
  13.                     return method(obj, ...)
  14.                 end
  15.                 class = class.__super
  16.             end
  17.         end
  18.     end })
  19.   end
  20.   function Object:createTable()
  21.     local obj = self
  22.     local class = obj:getClass()
  23.     return setmetatable({ }, { __index = function(t, k)
  24.         return function(...)
  25.             local method = class[k]
  26.             return method(obj, ...)
  27.         end
  28.     end })
  29.   end
  30.  
  31.   function Object:getSuper()
  32.     return rawget(self:getClass(), "__super")
  33.   end
  34.  
  35.   function Object:getClass()
  36.     return rawget(self, "__class")
  37.   end
  38.  
  39.   function Object:instanceof(targetType)
  40.     local objectType = self:getClass()
  41.     while objectType do
  42.         if targetType == objectType then
  43.             return true
  44.         end
  45.         objectType = objectType:getSuper()
  46.     end
  47.     return false
  48.   end
  49.  
  50.   function Object:toString()
  51.     return self:getClass():getName().." ("..tostring(self):sub(8, -1)..")"
  52.   end
  53.  
  54.   function Object:extend(obj)
  55.     self.__index = self -- This is a metatable
  56.  
  57.     local obj = obj or {}
  58.     obj.__super = self
  59.     local obj = setmetatable(obj, self)
  60.     return obj
  61.   end
  62.  
  63.   Class = Object:extend({__name = "Class"})
  64.   function Class:new(...)
  65.     local instance = { __class = self }
  66.     self:extend(instance)
  67.     instance:init(...)
  68.     return instance
  69.   end
  70.  
  71.   function Class:getName(name)
  72.     return self.__name
  73.   end
  74.   function Class:subClass(name)
  75.     local instance = { }
  76.     instance.__name = name
  77.  
  78.     return self:extend(instance)
  79.   end
  80. end
  81. do
  82.   local log = fs.open("BobCat.log", "w")
  83.  
  84.  
  85.   function Debug(Message, ...)
  86.     Log("DEBUG", Message, ...)
  87.   end
  88.   function Print(Message, ...)
  89.     print(Log("PRINT", Message, ...))
  90.   end
  91.   function PrintError(Message, ...)
  92.     printError(Log("WARNING", Message, ...))
  93.   end
  94.   function Error(Message, ...)
  95.     error(Log("ERROR", Message, ...))
  96.   end
  97.  
  98.   function Log(Type, Message, ...)
  99.     local str = string.format(tostring(Message), unpack({...}))
  100.     log.writeLine("["..Type.."] "..str)
  101.     log.flush()
  102.  
  103.     return str
  104.   end
  105. end
  106. do
  107.   --[=====================================================================[
  108.   v0.6 Copyright © 2013-2014 Gavin Kistner <[email protected]>; MIT Licensed
  109.   See http://github.com/Phrogz/SLAXML for details.
  110.   --]=====================================================================]
  111.   SLAXML = {
  112.     VERSION = "0.6",
  113.     _call = {
  114.         pi = function(target,content)
  115.             print(string.format("<?%s %s?>",target,content))
  116.         end,
  117.         comment = function(content)
  118.             print(string.format("<!-- %s -->",content))
  119.         end,
  120.         startElement = function(name,nsURI,nsPrefix)
  121.                              io.write("<")
  122.             if nsPrefix then io.write(nsPrefix,":") end
  123.                              io.write(name)
  124.             if nsURI    then io.write(" (ns='",nsURI,"')") end
  125.                              print(">")
  126.         end,
  127.         attribute = function(name,value,nsURI,nsPrefix)
  128.             io.write('  ')
  129.             if nsPrefix then io.write(nsPrefix,":") end
  130.                              io.write(name,'=',string.format('%q',value))
  131.             if nsURI    then io.write(" (ns='",nsURI,"')") end
  132.             io.write("\n")
  133.         end,
  134.         text = function(text)
  135.             print(string.format("  text: %q",text))
  136.         end,
  137.         closeElement = function(name,nsURI,nsPrefix)
  138.             print(string.format("</%s>",name))
  139.         end,
  140.     }
  141.   }
  142.  
  143.   function SLAXML:parser(callbacks)
  144.     return { _call=callbacks or self._call, parse=SLAXML.parse }
  145.   end
  146.  
  147.   function SLAXML:parse(xml,options)
  148.     if not options then options = { stripWhitespace=false } end
  149.  
  150.     -- Cache references for maximum speed
  151.     local find, sub, gsub, char, push, pop = string.find, string.sub, string.gsub, string.char, table.insert, table.remove
  152.     local first, last, match1, match2, match3, pos2, nsURI
  153.     local unpack = unpack or table.unpack
  154.     local pos = 1
  155.     local state = "text"
  156.     local textStart = 1
  157.     local currentElement={}
  158.     local currentAttributes={}
  159.     local currentAttributeCt -- manually track length since the table is re-used
  160.     local nsStack = {}
  161.  
  162.     local entityMap  = { ["lt"]="<", ["gt"]=">", ["amp"]="&", ["quot"]='"', ["apos"]="'" }
  163.     local entitySwap = function(orig,n,s) return entityMap[s] or n=="#" and char(s) or orig end
  164.     local function unescape(str) return gsub( str, '(&(#?)([%d%a]+);)', entitySwap ) end
  165.     local anyElement = false
  166.  
  167.     local function finishText()
  168.         if first>textStart and self._call.text then
  169.             local text = sub(xml,textStart,first-1)
  170.             if options.stripWhitespace then
  171.                 text = gsub(text,'^%s+','')
  172.                 text = gsub(text,'%s+$','')
  173.                 if #text==0 then text=nil end
  174.             end
  175.             if text then self._call.text(unescape(text)) end
  176.         end
  177.     end
  178.  
  179.     local function findPI()
  180.         first, last, match1, match2 = find( xml, '^<%?([:%a_][:%w_.-]*) ?(.-)%?>', pos )
  181.         if first then
  182.             finishText()
  183.             if self._call.pi then self._call.pi(match1,match2) end
  184.             pos = last+1
  185.             textStart = pos
  186.             return true
  187.         end
  188.     end
  189.  
  190.     local function findComment()
  191.         first, last, match1 = find( xml, '^<!%-%-(.-)%-%->', pos )
  192.         if first then
  193.             finishText()
  194.             if self._call.comment then self._call.comment(match1) end
  195.             pos = last+1
  196.             textStart = pos
  197.             return true
  198.         end
  199.     end
  200.  
  201.     local function nsForPrefix(prefix)
  202.         if prefix=='xml' then return 'http://www.w3.org/XML/1998/namespace' end -- http://www.w3.org/TR/xml-names/#ns-decl
  203.         for i=#nsStack,1,-1 do if nsStack[i][prefix] then return nsStack[i][prefix] end end
  204.         error(("Cannot find namespace for prefix %s"):format(prefix))
  205.     end
  206.  
  207.     local function startElement()
  208.         anyElement = true
  209.         first, last, match1 = find( xml, '^<([%a_][%w_.-]*)', pos )
  210.         if first then
  211.             currentElement[2] = nil -- reset the nsURI, since this table is re-used
  212.             currentElement[3] = nil -- reset the nsPrefix, since this table is re-used
  213.             finishText()
  214.             pos = last+1
  215.             first,last,match2 = find(xml, '^:([%a_][%w_.-]*)', pos )
  216.             if first then
  217.                 currentElement[1] = match2
  218.                 currentElement[3] = match1 -- Save the prefix for later resolution
  219.                 match1 = match2
  220.                 pos = last+1
  221.             else
  222.                 currentElement[1] = match1
  223.                 for i=#nsStack,1,-1 do if nsStack[i]['!'] then currentElement[2] = nsStack[i]['!']; break end end
  224.             end
  225.             currentAttributeCt = 0
  226.             push(nsStack,{})
  227.             return true
  228.         end
  229.     end
  230.  
  231.     local function findAttribute()
  232.         first, last, match1 = find( xml, '^%s+([:%a_][:%w_.-]*)%s*=%s*', pos )
  233.         if first then
  234.             pos2 = last+1
  235.             first, last, match2 = find( xml, '^"([^<"]*)"', pos2 ) -- FIXME: disallow non-entity ampersands
  236.             if first then
  237.                 pos = last+1
  238.                 match2 = unescape(match2)
  239.             else
  240.                 first, last, match2 = find( xml, "^'([^<']*)'", pos2 ) -- FIXME: disallow non-entity ampersands
  241.                 if first then
  242.                     pos = last+1
  243.                     match2 = unescape(match2)
  244.                 end
  245.             end
  246.         end
  247.         if match1 and match2 then
  248.             local currentAttribute = {match1,match2}
  249.             local prefix,name = string.match(match1,'^([^:]+):([^:]+)$')
  250.             if prefix then
  251.                 if prefix=='xmlns' then
  252.                     nsStack[#nsStack][name] = match2
  253.                 else
  254.                     currentAttribute[1] = name
  255.                     currentAttribute[4] = prefix
  256.                 end
  257.             else
  258.                 if match1=='xmlns' then
  259.                     nsStack[#nsStack]['!'] = match2
  260.                     currentElement[2]      = match2
  261.                 end
  262.             end
  263.             currentAttributeCt = currentAttributeCt + 1
  264.             currentAttributes[currentAttributeCt] = currentAttribute
  265.             return true
  266.         end
  267.     end
  268.  
  269.     local function findCDATA()
  270.         first, last, match1 = find( xml, '^<!%[CDATA%[(.-)%]%]>', pos )
  271.         if first then
  272.             finishText()
  273.             if self._call.text then self._call.text(match1) end
  274.             pos = last+1
  275.             textStart = pos
  276.             return true
  277.         end
  278.     end
  279.  
  280.     local function closeElement()
  281.         first, last, match1 = find( xml, '^%s*(/?)>', pos )
  282.         if first then
  283.             state = "text"
  284.             pos = last+1
  285.             textStart = pos
  286.  
  287.             -- Resolve namespace prefixes AFTER all new/redefined prefixes have been parsed
  288.             if currentElement[3] then currentElement[2] = nsForPrefix(currentElement[3])    end
  289.             if self._call.startElement then self._call.startElement(unpack(currentElement)) end
  290.             if self._call.attribute then
  291.                 for i=1,currentAttributeCt do
  292.                     if currentAttributes[i][4] then currentAttributes[i][3] = nsForPrefix(currentAttributes[i][4]) end
  293.                     self._call.attribute(unpack(currentAttributes[i]))
  294.                 end
  295.             end
  296.  
  297.             if match1=="/" then
  298.                 pop(nsStack)
  299.                 if self._call.closeElement then self._call.closeElement(unpack(currentElement)) end
  300.             end
  301.             return true
  302.         end
  303.     end
  304.  
  305.     local function findElementClose()
  306.         first, last, match1, match2 = find( xml, '^</([%a_][%w_.-]*)%s*>', pos )
  307.         if first then
  308.             nsURI = nil
  309.             for i=#nsStack,1,-1 do if nsStack[i]['!'] then nsURI = nsStack[i]['!']; break end end
  310.         else
  311.             first, last, match2, match1 = find( xml, '^</([%a_][%w_.-]*):([%a_][%w_.-]*)%s*>', pos )
  312.             if first then nsURI = nsForPrefix(match2) end
  313.         end
  314.         if first then
  315.             finishText()
  316.             if self._call.closeElement then self._call.closeElement(match1,nsURI) end
  317.             pos = last+1
  318.             textStart = pos
  319.             pop(nsStack)
  320.             return true
  321.         end
  322.     end
  323.  
  324.     while pos<#xml do
  325.         if state=="text" then
  326.             if not (findPI() or findComment() or findCDATA() or findElementClose()) then       
  327.                 if startElement() then
  328.                     state = "attributes"
  329.                 else
  330.                     first, last = find( xml, '^[^<]+', pos )
  331.                     pos = (first and last or pos) + 1
  332.                 end
  333.             end
  334.         elseif state=="attributes" then
  335.             if not findAttribute() then
  336.                 if not closeElement() then
  337.                     print(state)
  338.                     error("Was in an element and couldn't find attributes or the close.")
  339.                 end
  340.             end
  341.         end
  342.     end
  343.  
  344.     if not anyElement then error("Parsing did not discover any elements") end
  345.     if #nsStack > 0 then error("Parsing ended with unclosed elements") end
  346.   end
  347. end
  348. do
  349.   --@require Class.lua
  350.  
  351.   --Base class for pages
  352.   Page = Class:subClass("Page")
  353.   function Page:init(Url)
  354.     self.Url = Url
  355.     self.Title = Url
  356.  
  357.     self.Ids = { }
  358.     self.Clicks = { }
  359.   end
  360. end
  361. do
  362.   --@require ../../Class.lua
  363.  
  364.   --Base class for elements inside the DOM tree
  365.   Element = Class:subClass("Element")
  366.   function Element:init(Data, Page)
  367.     self.Type = Data.Type
  368.     self.Tag = Data.Tag
  369.  
  370.     self.Page = Page
  371.  
  372.     self.Parent = nil
  373.   end
  374.  
  375.   function Element:setParent(Parent)
  376.     self.Parent = Parent
  377.   end
  378.  
  379.   function Element:draw(Gui) end
  380.   function Element:stripWhitespace() end
  381.  
  382.   function Element:previousElement()
  383.     local previous = nil
  384.     for _, Child in pairs(self.Parent.Children) do
  385.         if Child == self then
  386.             break
  387.         end
  388.  
  389.         previous = Child
  390.     end
  391.     return previous
  392.   end
  393.  
  394.   function Element:nextElement()
  395.     local nextElement = nil
  396.     for _, Child in pairs(self.Parent.Children) do
  397.         if nextElement then
  398.             break
  399.         end
  400.  
  401.         if Child == self then
  402.             nextElement = true
  403.         end
  404.     end
  405.     return nextElement
  406.   end
  407.  
  408.  
  409.   --Text elements
  410.   TextElement = Element:subClass("TextElement")
  411.   function TextElement:init(Data, Page)
  412.     self:super(Element).init(Data, Page)
  413.  
  414.     self.Contents = Data.Value or ""
  415.   end
  416.   function TextElement:toString()
  417.     return self.Contents
  418.   end
  419.  
  420.   function TextElement:draw(Buffer)
  421.     --Buffer:print(self.Contents)
  422.     write(self.Contents)
  423.   end
  424.  
  425.   function TextElement:stripWhitespace()
  426.     self.Contents = self.Contents:gsub("%s+", " ")
  427.   end
  428. end
  429. do
  430.   --@require ../Class.lua
  431.   --@require ../Logger.lua
  432.  
  433.   local Tags = { }
  434.   local DefaultClass = nil
  435.  
  436.   TagRegister = Class:subClass("TagRegister")
  437.   function TagRegister.Register(tag, class)
  438.     Debug("Registering %s for tag %s", class:getName(), tag)
  439.     Tags[tag] = class
  440.   end
  441.  
  442.   function TagRegister.SetDefaultClass(class)
  443.     DefaultClass = class
  444.   end
  445.  
  446.   function TagRegister.CreateElement(Data, ThisPage)
  447.     local EClass = Tags[Data.Tag]
  448.     if EClass == nil then
  449.         EClass = DefaultClass
  450.     end
  451.     return EClass:new(Data, ThisPage)
  452.   end
  453. end
  454. do
  455.   --@require ../Class.lua
  456.   --From Lyqyd's FrameBuffer API (http://pastebin.com/Aaza6h5v)
  457.  
  458.   FrameBuffer = Class:subClass("FrameBuffer")
  459.   function FrameBuffer:init(sizeX, sizeY, colour)
  460.     self.text = {}
  461.     self.textColor = {}
  462.     self.backColor = {}
  463.     self.cursorX = 1
  464.     self.cursorY = 1
  465.     self.curTextColor = "0"
  466.     self.curBackColor = "f"
  467.     self.sizeX = sizeX or 51
  468.     self.sizeY = sizeY or -1
  469.     self.endY = sizeY or 0
  470.     if colour == nil then colour = true end
  471.     self.color = colour
  472.  
  473.     self:createLine()
  474.   end
  475.  
  476.   function FrameBuffer:write(text)
  477.     text = tostring(text)
  478.  
  479.     --Debug("Writing %s (%s, %s)", text, self.curTextColor, self.curBackColor)
  480.     local pos = self.cursorX
  481.     if (self.cursorY > self.sizeY and self.sizeY ~= -1) or self.cursorY < 1 then
  482.         self.cursorX = pos + #text
  483.         return
  484.    
  485.     end
  486.  
  487.     if pos + #text <= 1 then
  488.         --skip entirely.
  489.         self.cursorX = pos + #text
  490.         return
  491.     elseif pos < 1 then
  492.         --adjust text to fit on screen starting at one.
  493.         writeText = string.sub(text, math.abs(self.cursorX) + 2)
  494.         self.cursorX = 1
  495.     elseif pos > self.sizeX then
  496.         --if we're off the edge to the right, skip entirely.
  497.         self.cursorX = pos + #text
  498.         return
  499.     else
  500.         writeText = text
  501.     end
  502.  
  503.     local lineText = self.text[self.cursorY]
  504.     local lineColor = self.textColor[self.cursorY]
  505.     local lineBack = self.backColor[self.cursorY]
  506.     local preStop = self.cursorX - 1
  507.     local preStart = math.min(1, preStop)
  508.     local postStart = self.cursorX + string.len(writeText)
  509.     local postStop = self.sizeX
  510.     self.text[self.cursorY] = string.sub(lineText, preStart, preStop)..writeText..string.sub(lineText, postStart, postStop)
  511.     self.textColor[self.cursorY] = string.sub(lineColor, preStart, preStop)..string.rep(self.curTextColor, #writeText)..string.sub(lineColor, postStart, postStop)
  512.     self.backColor[self.cursorY] = string.sub(lineBack, preStart, preStop)..string.rep(self.curBackColor, #writeText)..string.sub(lineBack, postStart, postStop)
  513.     self.cursorX = pos + string.len(text)
  514.   end
  515.  
  516.   function FrameBuffer:createLine(line)
  517.     if line==nil then
  518.         self.endY = self.endY + 1
  519.         line = self.endY
  520.     elseif line > self.endY then
  521.         self.endY = line
  522.     end
  523.  
  524.     --Debug("Creating line at: %s", line)
  525.  
  526.     self.text[line] = string.rep(" ", self.sizeX)
  527.     self.textColor[line] = string.rep(self.curTextColor, self.sizeX)
  528.     self.backColor[line] = string.rep(self.curBackColor, self.sizeX)
  529.   end
  530.  
  531.   function FrameBuffer:clearLine()
  532.     self:createLine(self.cursorY)
  533.   end
  534.  
  535.   function FrameBuffer:clear()
  536.     self.text = {}
  537.     self.textColor = {}
  538.     self.backColor = {}
  539.     self.cursorX = 1
  540.     self.cursorY = 1
  541.     self.curTextColor = "0"
  542.     self.curBackColor = "f"
  543.     self.endY = self.sizeY or 0
  544.   end
  545.  
  546.   function FrameBuffer:getCursorPos()
  547.     return self.cursorX, self.cursorY
  548.   end
  549.   function FrameBuffer:setCursorPos(x, y)
  550.     self.cursorX = math.floor(tonumber(x)) or self.cursorX
  551.     self.cursorY = math.floor(tonumber(y)) or self.cursorY
  552.  
  553.  
  554.     --Debug("Cursor to %s, %s", x,y)
  555.  
  556.     if not self.text[y] then
  557.         local last = nil
  558.         for i = self.endY+1, y,1 do
  559.             if last ~= i then
  560.                 self:createLine(i)
  561.                 last = i
  562.             end
  563.         end
  564.     end
  565.   end
  566.   function FrameBuffer:setCursorBlink(b) end
  567.   function FrameBuffer:scroll(n)
  568.     self.cursorX = self.cursorX + n
  569.   end
  570.  
  571.   function FrameBuffer:getSize()
  572.     local y = self.sizeY
  573.     if y == -1 then
  574.         y = self.endY + 1000
  575.     end
  576.     return self.sizeX, y
  577.   end
  578.  
  579.   function FrameBuffer:setTextColor(clr)
  580.     if clr and clr <= 32768 and clr >= 1 then
  581.         if self.color then
  582.             self.curTextColor = string.format("%x", math.floor(math.log(clr) / math.log(2)))
  583.         elseif clr == 1 or clr == 32768 then
  584.             self.curTextColor = string.format("%x", math.floor(math.log(clr) / math.log(2)))
  585.         else
  586.             return nil, "Colour not supported"
  587.         end
  588.     end
  589.   end
  590.   FrameBuffer.setTextColour = FrameBuffer.setTextColor
  591.   function FrameBuffer:setBackgroundColor(clr)
  592.     if clr and clr <= 32768 and clr >= 1 then
  593.         if self.color then
  594.             self.curBackColor = string.format("%x", math.floor(math.log(clr) / math.log(2)))
  595.         elseif clr == 32768 or clr == 1 then
  596.             self.curBackColor = string.format("%x", math.floor(math.log(clr) / math.log(2)))
  597.         else
  598.             return nil, "Colour not supported"
  599.         end
  600.     end
  601.   end
  602.   FrameBuffer.setBackgroundColour = FrameBuffer.setBackgroundColor
  603.   function FrameBuffer:isColor()
  604.     return self.color == true
  605.   end
  606.   FrameBuffer.isColour = FrameBuffer.isColor
  607.  
  608.   function FrameBuffer:draw(redirect)
  609.     local x, y = redirect.getCursorPos()
  610.  
  611.     for p=1, self.endY do
  612.         self:drawLine(redirect, p)
  613.     end
  614.     redirect.setCursorPos(x + self.cursorX, y + self.cursorY)
  615.     redirect.setTextColor(2 ^ tonumber(self.curTextColor, 16))
  616.     redirect.setBackgroundColor(2 ^ tonumber(self.curBackColor, 16))
  617.   end
  618.  
  619.   function FrameBuffer:drawLine(redirect, line)
  620.     local x, y = redirect.getCursorPos()
  621.     local w, h = redirect.getSize()
  622.  
  623.     local thisY = line + y
  624.     --redirect.setCursorPos(x, thisY)
  625.  
  626.     --If off the edge
  627.     if not self.text[line] then
  628.         return
  629.     end
  630.  
  631.     local lineEnd = false
  632.     local offset = x
  633.     while not lineEnd do
  634.         local textColorString = string.match(string.sub(self.textColor[line], offset), string.sub(self.textColor[line], offset, offset).."*")
  635.         local backColorString = string.match(string.sub(self.backColor[line], offset), string.sub(self.backColor[line], offset, offset).."*")
  636.  
  637.         redirect.setTextColor(2 ^ tonumber(string.sub(textColorString, 1, 1), 16))
  638.         redirect.setBackgroundColor(2 ^ tonumber(string.sub(backColorString, 1, 1), 16))
  639.         redirect.write(string.sub(self.text[line], offset, offset + math.min(#textColorString, #backColorString) - 1))
  640.         offset = offset + math.min(#textColorString, #backColorString)
  641.  
  642.         if offset > self.sizeX then lineEnd = true end
  643.     end
  644.   end
  645.  
  646.   function FrameBuffer:getColours()
  647.     return {2 ^ tonumber(string.sub(self.curTextColor, 1, 1), 16), 2 ^ tonumber(string.sub(self.curBackColor, 1, 1), 16)}
  648.   end
  649.  
  650.   function FrameBuffer:restoreColours(colours)
  651.     self:setTextColour(colours[1])
  652.     self:setBackgroundColour(colours[2])
  653.   end
  654.  
  655.   function FrameBuffer:resetColours()
  656.     self:setBackgroundColour(colours.black)
  657.     self:setTextColour(colours.white)
  658.   end
  659. end
  660. do
  661.   --@require ../Class.lua
  662.   --@require ../Logger.lua
  663.   --@require FrameBuffer.lua
  664.  
  665.   GuiHost = Class:subClass("GuiHost")
  666.  
  667.   function GuiHost:init(Page, Document)
  668.     self.Page = Page
  669.     self.Document = Document
  670.  
  671.     self.Navigation = nil
  672.  
  673.     local w, h = term.getSize()
  674.     self.Buffer = FrameBuffer:new(w - 1, nil, true)
  675.  
  676.     self.y=0
  677.   end
  678.  
  679.   function GuiHost:prepareDraw()
  680.     local current = term.current()
  681.     Debug("Loading")
  682.     self.Buffer:clear()
  683.     term.redirect(self.Buffer:createTable())
  684.  
  685.     self.Document:draw(self.Buffer)
  686.     term.redirect(current)
  687.     Debug("Loaded")
  688.   end
  689.  
  690.   --Drawing
  691.   function GuiHost:setY(y)
  692.     local w, h = term.getSize()
  693.     if y >= 0 and y < (self.Buffer.endY - h + 2) then
  694.         self.y = y
  695.     end
  696.   end
  697.  
  698.   function GuiHost:draw()
  699.     term.setBackgroundColour(colours.black)
  700.     term.clear()
  701.     self:DrawHeader()
  702.     Debug("Drawing start")
  703.  
  704.     local w, h = term.getSize()
  705.  
  706.     for i=1, h, 1 do
  707.         term.setCursorPos(1, i+2)
  708.         self.Buffer:drawLine(term, i+self.y)
  709.     end
  710.     Debug("Drawing end")
  711.   end
  712.  
  713.   function GuiHost:DrawHeader()
  714.     term.setCursorPos(1,1)
  715.  
  716.     term.setBackgroundColour(colours.red)
  717.     term.setTextColour(colours.white)
  718.     term.write("<")
  719.  
  720.     term.setBackgroundColour(colours.orange)
  721.     term.write(">")
  722.  
  723.     term.setBackgroundColour(colours.lightGrey)
  724.    
  725.     term.write(" " .. self.Page.Url .. string.rep(" ", term.getSize() - #self.Page.Url))
  726.  
  727.     term.setTextColour(colours.blue)
  728.  
  729.     term.setCursorPos(1,2)
  730.     term.clearLine()
  731.     term.write(self.Page.Title)
  732.  
  733.     term.setCursorPos(1, 3)
  734.     term.setBackgroundColour(colours.black)
  735.     term.setTextColour(colours.white)
  736.   end
  737.  
  738.   function GuiHost:click(x, y)
  739.     if y <= 2 then
  740.         --Can't respond to navigation
  741.         if not self.Navigation then
  742.             return
  743.         end
  744.         --Action in headerbar
  745.         if y == 1 then
  746.             if x == 1 then
  747.                 self.Navigation:back()
  748.             elseif x == 2 then
  749.                 self.Navigation:forward()
  750.             else
  751.                 self:startEdit()
  752.             end
  753.         end
  754.     end
  755.   end
  756.  
  757.   function GuiHost:startEdit()
  758.    
  759.     term.setBackgroundColour(colours.lightGrey)
  760.     term.setTextColour(colours.white)
  761.     term.setCursorBlink(true)
  762.  
  763.     local line = self.Page.Url
  764.    
  765.     local w = term.getSize()
  766.     local sx = 4
  767.     local pos = #line
  768.  
  769.     term.setCursorPos(sx + pos, 1)
  770.    
  771.     local function redraw( customReplaceChar )
  772.         local scroll = 0
  773.         if sx + pos >= w then
  774.             scroll = (sx + pos) - w
  775.         end
  776.  
  777.         local cx,cy = term.getCursorPos()
  778.         term.setCursorPos( sx, cy )
  779.         local replace = customReplaceChar
  780.         if replace then
  781.             term.write( string.rep( replace, math.max( string.len(line) - scroll, 0 ) ) )
  782.         else
  783.             term.write( string.sub( line, scroll + 1 ) )
  784.         end
  785.         term.setCursorPos( sx + pos - scroll, cy )
  786.     end
  787.    
  788.     while true do
  789.         local event, param = os.pullEvent()
  790.         if event == "char" then
  791.             -- Typed key
  792.             line = string.sub( line, 1, pos ) .. param .. string.sub( line, pos + 1 )
  793.             pos = pos + 1
  794.             redraw()
  795.  
  796.         elseif event == "paste" then
  797.             -- Pasted text
  798.             line = string.sub( line, 1, pos ) .. param .. string.sub( line, pos + 1 )
  799.             pos = pos + string.len( param )
  800.             redraw()
  801.  
  802.         elseif event == "key" then
  803.             if param == keys.enter then
  804.                 -- Enter
  805.                 break
  806.             elseif param == 1 then
  807.                 --Escape
  808.                 term.setCursorBlink(false)
  809.                 return
  810.                
  811.             elseif param == keys.left then
  812.                 -- Left
  813.                 if pos > 0 then
  814.                     pos = pos - 1
  815.                     redraw()
  816.                 end
  817.                
  818.             elseif param == keys.right then
  819.                 -- Right                
  820.                 if pos < string.len(line) then
  821.                     redraw(" ")
  822.                     pos = pos + 1
  823.                     redraw()
  824.                 end
  825.             elseif param == keys.backspace then
  826.                 -- Backspace
  827.                 if pos > 0 then
  828.                     redraw(" ")
  829.                     line = string.sub( line, 1, pos - 1 ) .. string.sub( line, pos + 1 )
  830.                     pos = pos - 1                    
  831.                     redraw()
  832.                 end
  833.             elseif param == keys.home then
  834.                 -- Home
  835.                 redraw(" ")
  836.                 pos = 0
  837.                 redraw()        
  838.             elseif param == keys.delete then
  839.                 -- Delete
  840.                 if pos < string.len(line) then
  841.                     redraw(" ")
  842.                     line = string.sub( line, 1, pos ) .. string.sub( line, pos + 2 )                
  843.                     redraw()
  844.                 end
  845.             elseif param == keys["end"] then
  846.                 -- End
  847.                 redraw(" ")
  848.                 pos = string.len(line)
  849.                 redraw()
  850.             end
  851.  
  852.         elseif event == "term_resize" then
  853.             -- Terminal resized
  854.             w = term.getSize()
  855.             redraw()
  856.  
  857.         end
  858.     end
  859.  
  860.     term.setCursorBlink(false)
  861.    
  862.     self.Navigation:load(line)
  863.   end
  864. end
  865. do
  866.   --@require ../Class.lua
  867.   --@require ../Logger.lua
  868.   --@require Elements/Element.lua
  869.   --@require SlaXml.lua
  870.   --@require TagRegister.lua
  871.  
  872.   DomTreeParser = Class:subClass("DomTreeParser")
  873.  
  874.   local Push, Pop = table.insert, table.remove
  875.  
  876.   function DomTreeParser.Parse(Xml, ThisPage)
  877.     local Document = {
  878.         ['Type'] = 'document',
  879.         ['Tag'] = '#document',
  880.         ['Attributes'] = {}, --Should have none but prevents breaking it
  881.         ['Children'] = {},
  882.     }
  883.  
  884.     local Current = Document
  885.     local Children = Current.Children
  886.     local Stack = { Document }
  887.  
  888.     local Parser = SLAXML:parser({
  889.  
  890.         -- When "<foo" or <x:foo is seen
  891.         startElement = function(Tag, nsURI, nsPrefix)
  892.             Tag = Tag:lower()
  893.             local El = {
  894.                 ['Type'] = "element",
  895.                 ['Tag'] = Tag,
  896.                 ['Children'] = {},
  897.                 --['El'] = {}, -- (Only tags)
  898.                 ['Attributes'] = {},
  899.                 ['NsURI'] = nsURI,
  900.             }
  901.             if Current==Document then
  902.                 if Document.Root then
  903.                     Error(("Encountered element '%s' when the document already has a root '%s' element"):format(Tag, Document.Root.Name))
  904.                 end
  905.                 Document.Root = El
  906.             end
  907.  
  908.             Current = El
  909.             Push(Stack,El)
  910.         end,
  911.         -- attribute found on current element
  912.         attribute = function(Name, Value, nsURI, nsPrefix)
  913.             Name = Name:lower()
  914.             if not Current or Current.Type~="element" then
  915.                 Error(("Encountered an attribute %s=%s but it wasn't inside an element"):format(Name,Value))
  916.             end
  917.            
  918.             if nsURI ~= nil then
  919.                 Current.Attributes[nsURI .. ':' .. Name] = Value
  920.             else
  921.                 Current.Attributes[Name] = Value
  922.             end
  923.         end,
  924.         -- When "</foo>" or </x:foo> or "/>" is seen
  925.         closeElement = function(Tag, nsURI)
  926.             Tag = Tag:lower()
  927.  
  928.             if Current.Tag~=Tag or Current.Type~="element" then
  929.                 Error(("Received a close element notification for '%s' but was inside a '%s' %s"):format(Tag, Current.Tag, Current.Type))
  930.             end
  931.  
  932.             local El = Current
  933.  
  934.             Pop(Stack)
  935.             Current = Stack[#Stack]
  936.             Push(Current.Children, TagRegister.CreateElement(El, ThisPage))
  937.         end,
  938.         -- text and CDATA nodes
  939.         text = function(Text)
  940.             if Current.Type~='document' then
  941.                 if Current.Type~="element" then
  942.                     Error(("Received a text notification '%s' but was inside a %s"):format(Text, Current.Type))
  943.                 end
  944.                 Push(Current.Children, TextElement:new({
  945.                     ['Type']='text',
  946.                     ['Tag']='#text',
  947.                     ['Value']=Text,
  948.                 }, ThisPage))
  949.             end
  950.         end,
  951.     })
  952.  
  953.     -- Ignore whitespace-only text nodes and strip leading/trailing whitespace from text
  954.     -- (does not strip leading/trailing whitespace from CDATA)
  955.     Parser:parse(Xml, {stripWhitespace=true})
  956.  
  957.     return DocumentElement:new(Document)
  958.   end
  959. end
  960. do
  961.   --@require ../TagRegister.lua
  962.   --@require Element.lua
  963.  
  964.   --Base class for HTML elements inside the dom tree
  965.   HtmlElement = Element:subClass("HtmlElement")
  966.   TagRegister.SetDefaultClass(HtmlElement)
  967.  
  968.   function HtmlElement:init(Data, Page)
  969.     self:super(Element).init(Data, Page)
  970.  
  971.     self.Children = Data.Children
  972.     self.Attributes = Data.Attributes
  973.  
  974.     local Id = self.Attributes["id"]
  975.     if Id ~= nil and Id ~= "" then
  976.         Page.Ids[Id] = self
  977.     end
  978.  
  979.     for _, Child in ipairs(self.Children) do
  980.         Child:setParent(self)
  981.     end
  982.   end
  983.   function HtmlElement:innerHTML()
  984.     local Out = ''
  985.     for _, Child in ipairs(self.Children) do
  986.         Out = Out .. Child:toString()
  987.     end
  988.  
  989.     return Out
  990.   end
  991.   function HtmlElement:toString()
  992.     local Out = '<'..self.Tag
  993.     for Key,Value in pairs(self.Attributes) do
  994.         Out = Out .. ' ' .. Key ..'="'..Value..'"'
  995.     end
  996.  
  997.     Out = Out .. '>'
  998.     Out = Out .. self:innerHTML()
  999.  
  1000.     return Out.. '</'..self.Tag..'>'
  1001.   end
  1002.   function HtmlElement:subClass(name, tag)
  1003.     local instance = self:super(Element).subClass(name)
  1004.     if tag then
  1005.         TagRegister.Register(tag, instance)
  1006.     end
  1007.  
  1008.     return instance
  1009.   end
  1010.  
  1011.   function HtmlElement:height()
  1012.     local Height = 0
  1013.     for _, Child in pairs(self.Children) do
  1014.         Height = Height + Child.height()
  1015.     end
  1016.     return Height
  1017.   end
  1018.  
  1019.   function HtmlElement:draw(Gui)
  1020.     for _, Child in ipairs(self.Children) do
  1021.         Child:draw(Gui)
  1022.     end
  1023.   end
  1024.  
  1025.   function HtmlElement:stripWhitespace()
  1026.     for _,Child in ipairs(self.Children) do
  1027.         Child:stripWhitespace()
  1028.         if Child.Type == "text" and (Child.Contents == "" or Child.Contents == nil) then
  1029.             table.remove(self.Children, _)
  1030.         end
  1031.     end
  1032.   end
  1033.  
  1034.  
  1035.   --Root document
  1036.   DocumentElement = Element:subClass("DocumentElement")
  1037.   function DocumentElement:init(Data, Page)
  1038.     self:super(Element).init(Data, Page)
  1039.  
  1040.     self.Children = Data.Children
  1041.  
  1042.     self.Html = nil
  1043.     self.Head = nil
  1044.     self.Body = nil
  1045.     for _, Child in pairs(self.Children) do
  1046.         if Child.Tag == "html" then
  1047.             self.Html = Child
  1048.             self.Head = Child.Head
  1049.             self.Body = Child.Body
  1050.         end
  1051.     end
  1052.   end
  1053.  
  1054.   function DocumentElement:draw(Buffer)
  1055.     self.Body:draw(Buffer)
  1056.   end
  1057.  
  1058.   function DocumentElement:stripWhitespace()
  1059.     self.Body:stripWhitespace()
  1060.   end
  1061. end
  1062. do
  1063.   --@require HtmlElement.lua
  1064.  
  1065.   --The <html> tag
  1066.   HtmlTagElement = HtmlElement:subClass("HtmlTagElement", "html")
  1067.   function HtmlTagElement:init(Data, Page)
  1068.     self:super(HtmlElement).init(Data, Page)
  1069.  
  1070.     self.Head = nil
  1071.     self.Body = nil
  1072.     for _, Child in pairs(self.Children) do
  1073.         if Child.Tag == "head" then
  1074.             self.Head = Child
  1075.         elseif Child.Tag == "body" then
  1076.             self.Body = Child
  1077.         end
  1078.     end
  1079.   end
  1080.  
  1081.   --The <head> tag
  1082.   HeadElement = HtmlElement:subClass("HeadElement", "head")
  1083.   function HeadElement:width()
  1084.     return 0
  1085.   end
  1086.   function HeadElement:height()
  1087.     return 0
  1088.   end
  1089.  
  1090.   --The <title tag
  1091.   TitleElement = HtmlElement:subClass("TitleElement", "title")
  1092.   function TitleElement:init(Data, Page)
  1093.     self:super(HtmlElement).init(Data, Page)
  1094.     Page.Title = self:innerHTML()
  1095.   end
  1096. end
  1097. do
  1098.   --@require Class.lua
  1099.   --@require Html/DomTree.lua
  1100.   --@require Gui/GuiHelper.lua
  1101.   --@require Page.lua
  1102.  
  1103.   local ErrorPage = [[
  1104.   <html>
  1105.     <head>
  1106.         <title>Error</title>
  1107.     </head>
  1108.     <body>
  1109.         <h1>Sorry, could not access that page</h1>
  1110.     </body>
  1111.   </html>
  1112.   ]]
  1113.  
  1114.   local StartPage = [[
  1115.   <html>
  1116.     <head>
  1117.         <title>About BobCat</title>
  1118.     </head>
  1119.     <body>
  1120.         <h1>Hai!</h1>
  1121.     </body>
  1122.   </html>
  1123.   ]]
  1124.  
  1125.   Navigation = Class:subClass("Navigation")
  1126.   function Navigation:init()
  1127.     self.History = { "about:new"}
  1128.     self.HistoryIndex = 1
  1129.  
  1130.     self.Page = Page:new("about:new")
  1131.     self.Document = DomTreeParser.Parse(StartPage, self.Page)
  1132.  
  1133.     self.Gui = GuiHost:new(self.Page, self.Document)
  1134.     self.Gui.Navigation = self
  1135.     self.Gui:prepareDraw()
  1136.   end
  1137.  
  1138.   function Navigation:load(Url, ...)
  1139.     Debug("Going to page %s", Url)
  1140.  
  1141.     if Url:sub(0, 7) == "file://" then
  1142.         self:loadLocal(Url:sub(8), ...)
  1143.     elseif Url == "about:new" then
  1144.         self:_loadAbout()
  1145.     else
  1146.         self:loadRemote(Url, ...)
  1147.     end
  1148.   end
  1149.  
  1150.   function Navigation:loadRemote(Url, PostData, Headers, AddToNav)
  1151.     self.Page = Page:new(Url)
  1152.  
  1153.     local Result = http.post(Url, PostData, Headers)
  1154.     if not Result then
  1155.         self:_loadDocument(ErrorPage)
  1156.     else
  1157.         self:_loadDocument(Result:readAll())
  1158.         if AddToNav ~= false then
  1159.             self:_addNavigation(Url)
  1160.         end
  1161.     end
  1162.   end
  1163.  
  1164.   function Navigation:loadLocal(File, AddToNav)
  1165.     local Url = "file://"..tostring(File)
  1166.     self.Page = Page:new(Url)
  1167.  
  1168.     local Result = fs.open(File, "r")
  1169.     if not Result then
  1170.         self:_loadDocument(ErrorPage)
  1171.     else
  1172.         self:_loadDocument(Result.readAll())
  1173.  
  1174.         if AddToNav ~= false then
  1175.             self:_addNavigation(Url)
  1176.         end
  1177.     end
  1178.   end
  1179.  
  1180.   function Navigation:_loadAbout()
  1181.     self.Page = Page:new("about:new")
  1182.     self:_loadDocument(StartPage)
  1183.   end
  1184.  
  1185.   function Navigation:_addNavigation(Url)
  1186.     self.HistoryIndex = self.HistoryIndex + 1
  1187.  
  1188.     local length = #self.History
  1189.     if length > self.HistoryIndex then
  1190.         for i = self.HistoryIndex, length, 1 do
  1191.             table.remove(self.History, i)
  1192.         end
  1193.     end
  1194.  
  1195.     table.insert(self.History, Url)
  1196.   end
  1197.  
  1198.   function Navigation:_loadDocument(Contents)
  1199.     self.Document=DomTreeParser.Parse(Contents, self.Page)
  1200.  
  1201.     if not self.Document then
  1202.         Error("Could not load document")
  1203.     end
  1204.  
  1205.     self.Document:stripWhitespace()
  1206.  
  1207.     self.Gui.Page = self.Page
  1208.     self.Gui.Document = self.Document
  1209.  
  1210.     Debug("Gui Draw from Navigation")
  1211.     self.Gui:prepareDraw()
  1212.   end
  1213.  
  1214.   function Navigation:back()
  1215.     if self.HistoryIndex > 1 then
  1216.         self.HistoryIndex = self.HistoryIndex - 1
  1217.  
  1218.         self:refresh()
  1219.     end
  1220.   end
  1221.  
  1222.   function Navigation:forward()
  1223.     if self.HistoryIndex < #self.History then
  1224.         self.HistoryIndex = self.HistoryIndex + 1
  1225.  
  1226.         self:refresh()
  1227.     end
  1228.   end
  1229.  
  1230.   function Navigation:refresh()
  1231.     local Url = self.History[self.HistoryIndex]
  1232.  
  1233.     if Url:sub(0, 7) == "file://" then
  1234.         self:loadLocal(Url:sub(8), false)
  1235.     elseif Url == "about:new" then
  1236.         self:_loadAbout()
  1237.     else
  1238.         self:loadRemote(Url, nil, nil, false)
  1239.     end
  1240.   end
  1241. end
  1242. do
  1243.   --@require HtmlElement.lua
  1244.   --@require ../../Logger.lua
  1245.  
  1246.  
  1247.   --The <body> tag
  1248.   BodyElement = HtmlElement:subClass("BodyElement", "body")
  1249.  
  1250.   BlockElement = HtmlElement:subClass("BlockElement")
  1251.   function BlockElement:init(...)
  1252.     self:super(HtmlElement).init(...)
  1253.  
  1254.     self.PaddingTop = 1
  1255.     self.PaddingBottom = 1
  1256.   end
  1257.   function BlockElement:draw(Buffer)
  1258.     local x, y = Buffer:getCursorPos()
  1259.  
  1260.     if x ~= 1 then
  1261.         Buffer:setCursorPos(1, y + self.PaddingTop)
  1262.     else
  1263.         Buffer:setCursorPos(1, y + self.PaddingTop - 1)
  1264.     end
  1265.    
  1266.     self:blockDraw(Buffer)
  1267.  
  1268.     local x, y = Buffer:getCursorPos()
  1269.     Buffer:setCursorPos(1, y+self.PaddingBottom)
  1270.   end
  1271.   function BlockElement:blockDraw(buffer) self:super(HtmlElement).draw(Buffer) end
  1272.  
  1273.   HeaderOne = BlockElement:subClass("HeaderOne", "h1")
  1274.   function HeaderOne:init(...)
  1275.     self:super(BlockElement).init(...)
  1276.  
  1277.     self.PaddingTop = 2
  1278.   end
  1279.   function HeaderOne:blockDraw(Buffer)
  1280.     local col = Buffer:getColours()
  1281.  
  1282.     Buffer:setBackgroundColour(colours.blue)
  1283.     Buffer:setTextColour(colours.yellow)
  1284.  
  1285.     self:super(BlockElement).blockDraw(Buffer)
  1286.  
  1287.     Buffer:restoreColours(col)
  1288.   end
  1289.  
  1290.   HeaderTwo = BlockElement:subClass("HeaderTwo", "h2")
  1291.   function HeaderTwo:blockDraw(Buffer)
  1292.     local col = Buffer:getColours()
  1293.  
  1294.     Buffer:setBackgroundColour(colours.cyan)
  1295.     Buffer:setTextColour(colours.orange)
  1296.  
  1297.     self:super(BlockElement).blockDraw(Buffer)
  1298.  
  1299.     Buffer:restoreColours(col)
  1300.   end
  1301.  
  1302.  
  1303.   Link = HtmlElement:subClass("Link", "a")
  1304.   function Link:init(Data, Page)
  1305.     self:super(HtmlElement).init(Data, Page)
  1306.  
  1307.     self.Link = self.Attributes.href
  1308.   end
  1309.  
  1310.   function Link:draw(Buffer)
  1311.     local col = Buffer:getColours()
  1312.  
  1313.     Buffer:setTextColour(colours.lime)
  1314.  
  1315.     self:super(HtmlElement).draw(Buffer)
  1316.  
  1317.     Buffer:restoreColours(col)
  1318.   end
  1319.  
  1320.   Script = HtmlElement:subClass("Script", "script")
  1321.   function Script:draw(Buffer) end
  1322.  
  1323.   Paragraph = BlockElement:subClass("Paragraph", "p")
  1324. end
  1325. --@require Html/Elements/Element.lua
  1326. --@require Html/Elements/HtmlElement.lua
  1327. --@require Html/Elements/HeadElements.lua
  1328. --@require Html/Elements/BodyElements.lua
  1329.  
  1330. --@require Html/DomTree.lua
  1331. --@require Page.lua
  1332.  
  1333. --@require Navigation.lua
Advertisement
Add Comment
Please, Sign In to add comment