Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local startupPage, mode = ...
- if not term.isColor() then
- print("WARNING: This program has been designed to run on Advanced Computer")
- print("It should run fine, but you won't be able to follow links or click buttons")
- print("To access button functions, use the following keys:")
- print("Quit - Q, Home - 0 or H, Reload - 5, F5 or R, Back - 4 or Backspace, Next - 6")
- print("Press any key to continue")
- os.pullEvent("key")
- end
- local title = "ERROR"
- local text = "ERROR: NO PAGE LOADED"
- local pos = 1
- local textLength = 1
- local lowercase = false
- local offlineMode = false
- if not http then offlineMode = true end
- if mode then
- if mode == "online" then
- if offlineMode then
- error("HTTP API not available", 0)
- end
- offlineMode = false
- elseif mode == "offline" then
- offlineMode = true
- else
- error("Unkown mode, use \"offline\" or \"online\"", 0)
- end
- end
- function error404()
- title = "404 ERROR"
- text = "\\v;Not Found!\\r;"
- end
- function loadOfflinePage(page)
- title = page
- if page == "index" then
- text = "Help topics available:\n"
- for i, topic in pairs(help.topics()) do
- text = text .. "\\l"..topic..";"..topic.."\\r;\n"
- end
- else
- local filename = help.lookup(page)
- if filename == nil then
- return error404()
- end
- local file = fs.open(filename, "r")
- text = file.readAll()
- file.close()
- end
- if page == "apis" then
- -- This is code fragment from "apis" program
- local tApis = {}
- for k,v in pairs( _G ) do
- if type(k) == "string" and type(v) == "table" and k ~= "_G" then
- table.insert( tApis, k )
- end
- end
- table.insert( tApis, "shell" )
- table.sort( tApis )
- text = text .. "\n\nList of available APIs:\n"
- for k,v in pairs(tApis) do
- local tag = "\\x"
- if help.lookup(v) then
- tag = "\\l"..v..";"
- end
- text = text .. tag .. v .. "\\r;\n"
- end
- end
- if page == "programs" then
- local programs = shell.programs()
- text = text .. "\n\nList of available programs:\n"
- for k,v in pairs(programs) do
- local tag = "\\x;"
- if help.lookup(v) then
- tag = "\\l"..v..";"
- end
- text = text .. tag .. v .. "\\r;\n"
- end
- end
- function detect_links(s)
- if s:sub(1,5) == "help " then
- local target = s:sub(6)
- if target == "<program>" then target = "programs" end
- if target == "index" or help.lookup(target) ~= nil then
- return "\"\\l"..target..";"..s.."\\r;\""
- end
- elseif s == "programs" or s == "apis" then
- return "\"\\l"..s..";"..s.."\\r;\""
- end
- return "\"\\x;"..s.."\\r;\""
- end
- text = string.gsub(text, "\"(.-)\"", detect_links)
- text = string.gsub(text, "https?://[a-zA-Z0-9./-]*", "\\u;%1\\r;")
- text = string.gsub(text, "Ctrl%+(.)", "\\v;Ctrl+%1\\r;")
- end
- function loadOnlinePage(page)
- if page == "Main Page" or page == "Main_Page" then
- -- There is just too much HTML in there
- title = "Main Page"
- text = " \\h; Welcome to the ComputerCraft Wiki! \\r;\n\n"
- test = text.."\\s;Wiki Contents\\r;\n"
- text = text.."\\lTutorials;Tutorial List\\r;\n"
- text = text.."\\lCategory:Programs;Program List\\r;\n"
- text = text.."\\lCategory:APIs;API List\\r;\n"
- --text = text.."\\lRecipes;Recipe List\\r;\n" --TODO: We'll handle images somehow
- text = text.."\\lCategory:Peripherals;Peripherals & Addons\\r;\n"
- text = text.."\\lCategory:Notable Programs;Notable Programs\\r;\n"
- text = text.."\\lCategory:OSes;Operating Systems\\r;\n"
- text = text.."\\lChangelog;ComuterCraft Changelog\\r;\n"
- --text = text.."\\lWiki Todo;Wiki Todo\\r;\n" -- I don't think we need that
- return
- end
- local h = http.get("http://computercraft.info/wiki/api.php?format=xml&action=query&prop=revisions&titles="..textutils.urlEncode(page).."&rvprop=content", {["User-Agent"] = "GUI Help Viewer by krzys_h"})
- if not h then
- title = "ERROR"
- text = "\\v;CONNECTION ERROR!\\r;\nMake sure you have internet connection and computercraft.info website is working"
- return
- end
- if h.getResponseCode() ~= 200 then
- title = h.getResponseCode().." ERROR"
- text = "\\v;SERVER RETURNED ERROR "..h.getResponseCode().."\\r;"
- h.close()
- return
- end
- local data = h.readAll()
- h.close()
- title = string.match(data, "title=\"(.-)\"")
- if title == nil then
- title = page
- end
- text = string.match(data, "<rev xml:space=\"preserve\">(.-)</rev>")
- if text == nil then
- return error404()
- end
- if text:sub(1, 10) == "#REDIRECT " then
- loadOnlinePage(text:sub(13, -3))
- return
- end
- text = string.gsub(text, "<", "<")
- text = string.gsub(text, ">", ">")
- text = string.gsub(text, """, "\"")
- text = string.gsub(text, "<!--(.-)-->", "")
- -- Computer terminal doesn't like special chars
- text = string.gsub(text, "’", "'")
- text = string.gsub(text, "—", "-")
- text = string.gsub(text, "<table.*>", "")
- text = string.gsub(text, "</table>", "")
- text = string.gsub(text, "<tr.->", "")
- text = string.gsub(text, "</tr>", "")
- text = string.gsub(text, "<td.->", "")
- text = string.gsub(text, "</td>", "")
- text = string.gsub(text, "__TOC__", "") --TODO: We could implement TOC...
- text = string.gsub(text, "__NOTOC__", "")
- function lowercase_match(s)
- lowercase = true
- return ""
- end
- text = string.gsub(text, "{{lowercase}}", lowercase_match)
- text = string.gsub(text, "{{Lowercase}}", lowercase_match)
- text = string.gsub(text, "'''(.-)'''", "%1")
- text = string.gsub(text, "''(.-)''", "%1")
- text = string.gsub(text, "'(.-)'", "%1")
- text = string.gsub(text, "{{type|(.-)}}", "\\t;%1\\r;")
- text = string.gsub(text, "{{Type|(.-)}}", "\\t;%1\\r;")
- text = string.gsub(text, "<var>(.-)</var>", "\\v;%1\\r;")
- text = string.gsub(text, "<code>(.-)</code>", "\\x;%1\\r;") --TODO: syntax highlighting?
- text = string.gsub(text, "<nowiki/>", "")
- text = string.gsub(text, "<em>(.-)</em>", "%1")
- text = string.gsub(text, "<br />", "\n")
- text = string.gsub(text, "<br/>", "\n")
- text = string.gsub(text, "<pre>(.-)</pre>", "\\x;%1\\r;")
- text = string.gsub(text, "<tt>(.-)</tt>", "\\x;%1\\r;")
- text = string.gsub(text, "<div(.+)>(.-)</div>", "%2")
- function link_replace(link)
- local linktarget = link
- local linkname = link
- local splitpos = link:find("|")
- if splitpos then
- linktarget = link:sub(1, splitpos-1)
- linkname = link:sub(splitpos+1)
- end
- if linktarget:sub(1, 1) == ":" then
- linktarget = linktarget:sub(2)
- end
- if linktarget:sub(1, 5) == "File:" then return "" end
- if linktarget:sub(1, 6) == "Image:" then return "" end
- return "\\l"..linktarget..";"..linkname.."\\r;"
- end
- text = string.gsub(text, "%[%[Category:(.-)%]%]", "") --TODO: category display?
- text = string.gsub(text, "%[%[(.-)%]%]", link_replace)
- text = string.gsub(text, "%[(.-) (.-)%]", "\\u;%2 (%1)\\r;")
- text = string.gsub(text, "%[(.-)%]", "\\u;%1\\r;")
- text = parseTemplates(text)
- text = string.gsub(text, "\n===(.-)===\n", "\n \\s;%1\\r;\n")
- text = string.gsub(text, "\n==(.-)==\n", "\n \\h;%1\\r;\n")
- text = string.gsub(text, "\n=(.-)=\n", "\n \\c;%1\\r;\n")
- if title:sub(1, 9) == "Category:" then
- local h = http.get("http://computercraft.info/wiki/api.php?format=xml&action=query&list=categorymembers&cmtitle="..textutils.urlEncode(page).."&cmlimit=500", {["User-Agent"] = "GUI Help Viewer by krzys_h"})
- if not h then
- title = "ERROR"
- text = "\\v;CONNECTION ERROR!\\r;\nMake sure you have internet connection and computercraft.info website is working"
- return
- end
- if h.getResponseCode() ~= 200 then
- title = h.getResponseCode().." ERROR"
- text = "\\v;SERVER RETURNED ERROR "..h.getResponseCode().."\\r;"
- h.close()
- return
- end
- local data = h.readAll()
- h.close()
- text = text .. "\n \\h; Pages in this category: \\r;\n"
- for page in string.gmatch(data, "title=\"(.-)\"") do
- text = text .. "\\l" .. page .. ";" .. page .. "\\r;\n"
- end
- end
- end
- function closest(...)
- local x = {...}
- local r
- for i, d in pairs(x) do
- if x[i] then
- if not r or x[i] < r then
- r = x[i]
- end
- end
- end
- return r
- end
- -- http://lua-users.org/wiki/StringTrim
- function trim(s)
- return (s:gsub("^%s*(.-)%s*$", "%1"))
- end
- function findParam(params, name)
- for id, data in pairs(params) do
- local x = string.find(data, "=")
- if x ~= nil then
- thisname = trim(string.sub(data, 1, x-1))
- if thisname == name then
- return string.sub(data, x+1)
- end
- end
- end
- if type(name) == "number" then
- return params[name]
- end
- return nil
- end
- function expandTemplate(name, params)
- if name == "API table" then
- return " \\a; "..findParam(params, 1).." (API) \\r;\n"..string.sub(findParam(params, 3), 3)
- end
- if name == "API table/row" then
- return findParam(params, 1).." \\z;--\\r; "..findParam(params, 2).." \\z;--\\r; "..findParam(params, 3).."\n"
- end
- if name == "Deprecated" then
- return "\\x;These functions have been removed from ComputerCraft\\r;"
- end
- if name == "Stub" then
- return "\\x;This article is a stub\\r;\n"
- end
- if name == "ToDelete" then
- return "\\x;This page is marked for deletion!\\r;\n"
- end
- if name == "Example" then
- local s = " \\s;Example\\r;\n"..findParam(params, "desc").."\n\n\\x;"..findParam(params, "code").."\\r;\n"
- if findParam(params, "output") then
- s = s .. "\nOutput:\n\\x;"..findParam(params, "output").."\\r;\n"
- end
- return s
- end
- if name == "Event" then
- local s = " \\a; Event "..findParam(params, "name").." \\r;\n"
- s = s .. findParam(params, "desc") .. "\n"
- for i=1,5 do
- if findParam(params, "return"..tostring(i)) then
- s = s .. "\\x;Return "..tostring(i)..":\\r; " .. findParam(params, "return"..tostring(i)) .. "\n"
- end
- end
- if findParam(params, "examples") then
- s = s .. "\n" .. findParam(params, "examples")
- end
- return s
- end
- if name == "Function" then
- s = " \\a; Function "..findParam(params, "name").." \\r;\n"
- s = s .. findParam(params, "desc") .. "\n"
- if findParam(params, "args") then
- s = s .. "\\x;Syntax:\\r; "..findParam(params, "name").."("..findParam(params, "args")..")\n"
- else
- s = s .. "\\x;Syntax:\\r; "..findParam(params, "name").."()\n"
- end
- if findParam(params, "returns") then
- s = s .. "\\x;Returns:\\r; "..findParam(params, "returns").."\n"
- else
- s = s .. "\\x;Returns:\\r; \\t;nil\\r;\n"
- end
- if findParam(params, "examples") then
- s = s .. "\n" .. findParam(params, "examples")
- end
- return s
- end
- return "\\v;UNKNOWN TEMPLATE: "..name.."\\r;"
- end
- function parseTemplate(s, p)
- local res = s
- local params = {}
- while true do
- nextparam = string.find(res, "|", p)
- subtemplate = string.find(res, "{{", p)
- templateend = string.find(res, "}}", p)
- local x = closest(nextparam, subtemplate, templateend)
- if x == nil then
- error("Unclosed template")
- elseif x == templateend or x == nextparam then
- param = trim(string.sub(res, p, x-1))
- table.insert(params, param)
- p = x+1
- if x == templateend then
- p = p + 1
- break
- end
- elseif x == subtemplate then
- pos_start = x-2
- _, tpl, pos_end = parseTemplate(res, x+2)
- res = string.sub(
- res, 1, pos_start) .. tpl .. string.sub(res, pos_end+1)
- end
- end
- local name = params[1]
- table.remove(params, 1)
- return res, expandTemplate(name, params), p
- end
- function parseTemplates(s)
- local p = 1
- local out = ""
- while p <= #s do
- templatepos = string.find(s, "{{", p)
- if templatepos == nil then
- out = out .. string.sub(s, p)
- break
- else
- out = out .. string.sub(s, p, templatepos-1)
- p = templatepos+2
- s, res, p = parseTemplate(s, p)
- out = out .. res
- end
- end
- return out
- end
- local hist = {}
- local hist_pos = 0
- function loadPage(page, new_page)
- if new_page == nil then new_page = true end
- lowercase = false
- if not offlineMode then
- title = "<LOADING>"
- text = " \\t;Loading, please wait...\\r;"
- pos = 1
- generateWindows()
- draw()
- loadOnlinePage(page)
- else
- loadOfflinePage(page)
- end
- if new_page then
- while hist_pos < #hist do
- table.remove(hist)
- end
- table.insert(hist, page)
- hist_pos = #hist
- end
- pos = 1
- if lowercase then
- title = title:sub(1,1):lower()..title:sub(2)
- end
- end
- function history(amount)
- local old_hist = hist_pos
- hist_pos = hist_pos + amount
- if hist_pos >= #hist then
- hist_pos = #hist
- end
- if hist_pos <= 1 then
- hist_pos = 1
- end
- if hist_pos ~= old_hist then
- loadPage(hist[hist_pos], false)
- generateWindows()
- end
- end
- function reload()
- loadPage(hist[hist_pos], false)
- generateWindows()
- end
- local w, h = term.getSize()
- local header = window.create(term.current(), 1, 1, w, 2)
- local content = window.create(term.current(), 1, 3, w, 1000)
- local links = {}
- function generateWindows()
- w, h = term.getSize()
- -- draw content
- local old = term.current()
- term.redirect(content)
- term.clear()
- term.setCursorPos(1, 1)
- local p = 1
- local linktarget, linkstartx, linkstarty, linkendx, linkendy
- local inCode = false
- links = {}
- while p <= #text do
- controlpos, controlend = string.find(text, "\\.-;", p)
- if controlpos == nil then
- write(string.sub(text, p))
- break
- else
- write(string.sub(text, p, controlpos-1))
- p = controlend + 1
- c = string.sub(text, controlpos+1, controlpos+1)
- if c == "l" and term.isColor() then -- l = link
- linktarget = string.sub(text, controlpos+2, controlend-1)
- linkstartx, linkstarty = term.getCursorPos()
- term.setTextColor(colors.lightBlue)
- end
- if c == "r" then -- r = reset formatting
- if linktarget then
- linkendx, linkendy = term.getCursorPos()
- table.insert(links, {startx = linkstartx, starty = linkstarty, endx = linkendx, endy = linkendy, target = linktarget})
- linktarget = nil
- end
- inCode = false
- term.setBackgroundColor(colors.black)
- term.setTextColor(colors.white)
- end
- if c == "t" and term.isColor() then -- t = type
- term.setTextColor(colors.lime)
- end
- if c == "c" and not inCode and term.isColor() then -- c = chapter header
- --TODO: I don't really like that color...
- term.setBackgroundColor(colors.yellow)
- term.setTextColor(colors.black)
- end
- if c == "h" and not inCode and term.isColor() then -- h = header
- term.setBackgroundColor(colors.blue)
- end
- if c == "s" and not inCode and term.isColor() then -- s = subheader
- term.setBackgroundColor(colors.purple)
- end
- if c == "a" and not inCode and term.isColor() then -- a = api header
- term.setBackgroundColor(colors.green)
- end
- if c == "z" and term.isColor() then -- z = table separator
- term.setTextColor(colors.gray)
- end
- if c == "v" and term.isColor() then -- v = var
- term.setTextColor(colors.red)
- end
- if c == "x" and term.isColor() then -- x = code / comment
- term.setTextColor(colors.lightGray)
- inCode = true
- end
- if c == "u" and term.isColor() then -- u = unclickable link (e.g. http)
- term.setTextColor(colors.blue)
- end
- end
- end
- _, textLength = term.getCursorPos()
- term.redirect(old)
- -- draw header
- local old = term.current()
- term.redirect(header)
- term.setBackgroundColor(colors.black)
- term.setTextColor(colors.white)
- term.clear()
- term.setCursorPos(1, 1)
- if not term.isColor() then
- term.setBackgroundColor(colors.white)
- term.setTextColor(colors.black)
- else
- term.setBackgroundColor(colors.lightGray)
- term.setTextColor(colors.red)
- end
- write("[X] ")
- if term.isColor() then
- term.setTextColor(colors.orange)
- end
- write("[H] ")
- if term.isColor() then
- term.setTextColor(colors.blue)
- end
- write("[<]")
- if term.isColor() then
- term.setTextColor(colors.lime)
- end
- write("[R]")
- if term.isColor() then
- term.setTextColor(colors.blue)
- end
- write("[>]")
- if term.isColor() then
- term.setTextColor(colors.black)
- end
- local t = " "..title.." --- GUI Help Viewer"
- if #t < w-17-11 then t = t .. " by krzys_h" end
- for i=0,w-#t-17 do t = t.." " end
- t = t:sub(1, w-17)
- print(t)
- term.redirect(old)
- end
- function draw()
- term.setBackgroundColor(colors.black)
- term.setTextColor(colors.white)
- term.clear()
- term.setCursorPos(1, 1)
- content.reposition(1, 3-pos+1, w, 1000)
- header.reposition(1, 1, w, 2)
- end
- function move(x)
- pos = pos + x
- if pos > textLength-(h-3) then pos = textLength-(h-3) end
- if pos < 1 then pos = 1 end
- end
- function getLink(x, y)
- for id, link in pairs(links) do
- if (y > link.starty or (y == link.starty and x >= link.startx)) and (y < link.endy or (y == link.endy and x < link.endx)) then
- return link
- end
- end
- return nil
- end
- function handleClick(x, y, button)
- if y == 1 then
- if x >= 1 and x <= 3 then
- os.queueEvent("terminate")
- end
- if x >= 5 and x <= 7 then
- loadPage(offlineMode and "intro" or "Main Page")
- generateWindows()
- end
- if x >= 9 and x <= 11 then
- history(-1)
- end
- if x >= 12 and x <= 14 then
- reload()
- end
- if x >= 15 and x <= 17 then
- history(1)
- end
- end
- local y = y - 2 + pos - 1
- local link = getLink(x, y)
- if link then
- loadPage(link.target)
- generateWindows()
- end
- end
- function handleScroll(scroll)
- move(scroll)
- end
- function handleKey(key)
- if key == keys.down then
- move(1)
- end
- if key == keys.up then
- move(-1)
- end
- if key == keys.pageDown then -- TODO: doesn't work?
- move(h-2)
- end
- if key == keys.pageUp then -- TODO: doesn't work?
- move(-(h-2))
- end
- if key == keys.home then
- pos = 1
- end
- if key == keys["end"] then -- Lua doesn't like keys.end
- if textLength > (h-3) then
- pos = textLength-(h-3)
- else
- pos = 1
- end
- end
- if key == keys.q then
- os.queueEvent("terminate")
- end
- if key == keys.backspace or key == keys.four or key == keys.numPad4 then
- history(-1)
- end
- if key == keys.six or key == keys.numPad6 then
- history(1)
- end
- if key == keys.f5 or key == keys.five or key == keys.numPad5 or key == keys.r then
- reload()
- end
- if key == keys.zero or key == keys.numPad0 or key == keys.h then
- loadPage(offlineMode and "intro" or "Main Page")
- generateWindows()
- end
- end
- if not startupPage or startupPage == "-" then
- if offlineMode then
- startupPage = "intro"
- else
- startupPage = "Main Page"
- end
- end
- loadPage(startupPage)
- generateWindows()
- while true do
- draw()
- local event = {os.pullEventRaw()}
- if event[1] == "mouse_click" then
- handleClick(event[3], event[4], event[2])
- end
- if event[1] == "mouse_scroll" then
- handleScroll(event[2])
- end
- if event[1] == "key" then
- handleKey(event[2])
- end
- if event[1] == "term_resize" then
- generateWindows()
- end
- if event[1] == "terminate" then
- term.clear()
- term.setCursorPos(1, 1)
- break
- end
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement