· 5 years ago · Jan 20, 2021, 11:44 AM
1--- An API for logging in computer craft, based of lua logging.
2-- @classmod logging
3-- @author Wobbo
4-- @alias mt
5
6local mt = {}
7mt.__index = mt
8
9--- The DEBUG level designates fine-grained informational events that are most useful to debug an application.
10DEBUG = "DEBUG"
11
12--- The INFO level designates informational messages that highlight the progress of the application at coarse-grained level.
13INFO = "INFO"
14
15--- The WARN level designates potentially harmful situations.
16WARN = "WARN"
17
18--- The ERROR level designates error events that still allow the application to continue running.
19ERROR = "ERROR"
20
21--- The FATAL level designates very severe error events that would lead the application to abort.
22FATAL = "FATAL"
23
24--- A table to keep track of the order between levels. There is a field for each valid level. In order to be logged, the order of a level has to be equal to or greater than the current logging level.
25-- @usage logging.order[logging.DEBUG] -- returns the order of DEBUG.
26-- @field DEBUG The order of `logging.DEBUG`
27-- @field INFO The order of `logging.INFO`
28-- @field WARN The order of `logging.WARN`
29-- @field ERROR The order of `logging.ERROR`
30-- @field FATAL The order of `logging.FATAL`
31order = {DEBUG = 1, INFO = 2, WARN = 3, ERROR = 4, FATAL = 5}
32
33function mt.__gc(self) if self.file then self.file:close() end end
34
35local function newArgs(func, file, format, level)
36 local obj = setmetatable({}, mt)
37 obj.func = func
38 if file then
39 local handle
40 if not fs.exists(file) then
41 handle = io.open(file, "w")
42 else
43 handle = io.open(file, "a")
44 end
45 obj.file = handle
46 end
47 obj.format = format or "%date %time %level %message"
48 obj:setLevel(level or INFO)
49 return obj
50end
51
52--- Create a new logging object.
53-- Can also be called as `logging.new{func = func, file = file, format = format}`. Either a file or a function is advised, but not obligatory.
54-- @function new
55-- @constructor
56-- @tparam function func The function that is called when a message is logged. This function recieves a self argument, the level that is calles with and the message that is logged.
57-- @string file The file that is logged to.
58-- @string format The format that is used for logging to a file. The day can be inserted by `%date`, the time by using `%time`, the level by using `%level` and the message itself by using `%message`.
59-- @string level The initial level for the `logging` object.
60-- @treturn logging The new logging object.
61-- @usage logger = logging.new{func = function(level, message)
62-- print(level..": "..message)
63-- end,
64-- file = "usage.log",
65-- format = "%date %time Usage: %level %message"}
66-- @usage logger = logging.new(function(level, message)
67-- print(message)
68-- os.queueEvent("log Usage", level, message)
69-- end)
70-- @usage logger = logging.new{function(self, level, message)
71-- print(level..": "..message) end,
72-- level = logging.DEBUG}
73function new(...)
74 local t
75 if type(...) == "table" then
76 t = ...
77 else
78 t = {...}
79 end
80 return newArgs(t.func or t[1], t.file or t[2], t.timeFmt or t[3], t.level or t[4])
81end
82
83--- Sets the minimum level needed for a message to be logged. Default is `INFO`.
84-- @string level The minimum level.
85-- @usage logger:setLevel(logging.WARN)
86function mt:setLevel(level)
87 if order[level] then
88 self.level = level
89 else
90 error("undefinded level "..level)
91 end
92end
93
94--- Logs a message if the specified level is higher then the loggers level.
95-- This function will call the
96-- @string level The level of the message.
97-- @string message The message that need to be logged.
98-- @usage logger:log(logging.INFO, "Prepared the environment")
99function mt:log(level, message)
100 if order[level] < order[self.level] then return end
101 if self.func then
102 self:func(level, message)
103 end
104 if self.file then
105 self.file:write(prepMsg(self.format, message, os.day(), textutils.formatTime(os.time(), true), level)..'\n')
106 self.file:flush()
107 end
108end
109
110--- Log a message with DEBUG level.
111-- @string message The message to be logged.
112-- @usage logger:debug("Found the logging API")
113-- @see log
114-- @see DEBUG
115function mt:debug(message)
116 self:log(DEBUG, message)
117end
118
119
120--- Log a message with INFO level.
121-- @string message The message to be logged.
122-- @usage logger:info("Prepared the invironment")
123-- @see log
124-- @see INFO
125function mt:info(message)
126 self:log(INFO, message)
127end
128
129--- Log a message with WARN level.
130-- @string message The message to be logged.
131-- @usage logger:warn("Expected integer, got string")
132-- @see log
133-- @see WARN
134function mt:warn(message)
135 self:log(WARN, message)
136end
137
138--- Log a message with ERROR level.
139-- @string message The message to be logged.
140-- @usage logger:error("No modem found!")
141-- @see log
142-- @see ERROR
143function mt:error(message)
144 self:log(ERROR, message)
145end
146
147--- Log a message with FATAL level.
148-- @string message The message to be logged.
149-- @usage logger:fatal("No fuel found to refuel. Sending distress signal and aborting.")
150-- @see log
151-- @see FATAL
152function mt:fatal(message)
153 self:log(FATAL, message)
154end
155
156
157--- Prepare a message using a specified pattern.
158-- @constructor
159-- @tparam string pattern The pattern used for the message. Defaults to `"%date %time %level %message"`. The day can be inserted by `%date`, the time by using `%time`, the level by using `%level` and the message itself by using `%message`.
160-- @string message The message that needs to be formatted. The value for `%message`
161-- @number date The value for `%date`.
162-- @string time The value for `%time`. This needs to be formatted beforehand.
163-- @string level The value for `%level`.
164-- @treturn string The prepared message.
165function prepMsg(pattern, message, date, time, level)
166 local logMsg = pattern or "%date %time %level %message"
167 message = string.gsub(message, "%%", "%%%%")
168 logMsg = string.gsub(logMsg,"%%date", date)
169 logMsg = string.gsub(logMsg, "%%time", time)
170 logMsg = string.gsub(logMsg, "%%level", level)
171 return string.gsub(logMsg, "%%message", message)
172end
173
174-------------------------------------------------------------------------------------
175-- Wojbies API 4.1 - Bigfont - functions to write bigger font using drawing sybols --
176-------------------------------------------------------------------------------------
177-- Copyright (c) 2015-2020 Wojbie (wojbie@wojbie.net)
178-- Redistribution and use in source and binary forms, with or without modification, are permitted (subject to the limitations in the disclaimer below) provided that the following conditions are met:
179-- 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
180-- 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
181-- 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
182-- 4. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
183-- 5. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
184-- NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
185
186--### Initializing
187bigFont = shell and {} or (_ENV or getfenv())
188bigFont.versionName = "Bigfont By Wojbie"
189bigFont.versionNum = 4.1 --2020-01-28
190
191--### Font database
192local rawFont = {{"\32\32\32\137\156\148\158\159\148\135\135\144\159\139\32\136\157\32\159\139\32\32\143\32\32\143\32\32\32\32\32\32\32\32\147\148\150\131\148\32\32\32\151\140\148\151\140\147", "\32\32\32\149\132\149\136\156\149\144\32\133\139\159\129\143\159\133\143\159\133\138\32\133\138\32\133\32\32\32\32\32\32\150\150\129\137\156\129\32\32\32\133\131\129\133\131\132", "\32\32\32\130\131\32\130\131\32\32\129\32\32\32\32\130\131\32\130\131\32\32\32\32\143\143\143\32\32\32\32\32\32\130\129\32\130\135\32\32\32\32\131\32\32\131\32\131", "\139\144\32\32\143\148\135\130\144\149\32\149\150\151\149\158\140\129\32\32\32\135\130\144\135\130\144\32\149\32\32\139\32\159\148\32\32\32\32\159\32\144\32\148\32\147\131\132", "\159\135\129\131\143\149\143\138\144\138\32\133\130\149\149\137\155\149\159\143\144\147\130\132\32\149\32\147\130\132\131\159\129\139\151\129\148\32\32\139\131\135\133\32\144\130\151\32", "\32\32\32\32\32\32\130\135\32\130\32\129\32\129\129\131\131\32\130\131\129\140\141\132\32\129\32\32\129\32\32\32\32\32\32\32\131\131\129\32\32\32\32\32\32\32\32\32", "\32\32\32\32\149\32\159\154\133\133\133\144\152\141\132\133\151\129\136\153\32\32\154\32\159\134\129\130\137\144\159\32\144\32\148\32\32\32\32\32\32\32\32\32\32\32\151\129", "\32\32\32\32\133\32\32\32\32\145\145\132\141\140\132\151\129\144\150\146\129\32\32\32\138\144\32\32\159\133\136\131\132\131\151\129\32\144\32\131\131\129\32\144\32\151\129\32", "\32\32\32\32\129\32\32\32\32\130\130\32\32\129\32\129\32\129\130\129\129\32\32\32\32\130\129\130\129\32\32\32\32\32\32\32\32\133\32\32\32\32\32\129\32\129\32\32", "\150\156\148\136\149\32\134\131\148\134\131\148\159\134\149\136\140\129\152\131\32\135\131\149\150\131\148\150\131\148\32\148\32\32\148\32\32\152\129\143\143\144\130\155\32\134\131\148", "\157\129\149\32\149\32\152\131\144\144\131\148\141\140\149\144\32\149\151\131\148\32\150\32\150\131\148\130\156\133\32\144\32\32\144\32\130\155\32\143\143\144\32\152\129\32\134\32", "\130\131\32\131\131\129\131\131\129\130\131\32\32\32\129\130\131\32\130\131\32\32\129\32\130\131\32\130\129\32\32\129\32\32\133\32\32\32\129\32\32\32\130\32\32\32\129\32", "\150\140\150\137\140\148\136\140\132\150\131\132\151\131\148\136\147\129\136\147\129\150\156\145\138\143\149\130\151\32\32\32\149\138\152\129\149\32\32\157\152\149\157\144\149\150\131\148", "\149\143\142\149\32\149\149\32\149\149\32\144\149\32\149\149\32\32\149\32\32\149\32\149\149\32\149\32\149\32\144\32\149\149\130\148\149\32\32\149\32\149\149\130\149\149\32\149", "\130\131\129\129\32\129\131\131\32\130\131\32\131\131\32\131\131\129\129\32\32\130\131\32\129\32\129\130\131\32\130\131\32\129\32\129\131\131\129\129\32\129\129\32\129\130\131\32", "\136\140\132\150\131\148\136\140\132\153\140\129\131\151\129\149\32\149\149\32\149\149\32\149\137\152\129\137\152\129\131\156\133\149\131\32\150\32\32\130\148\32\152\137\144\32\32\32", "\149\32\32\149\159\133\149\32\149\144\32\149\32\149\32\149\32\149\150\151\129\138\155\149\150\130\148\32\149\32\152\129\32\149\32\32\32\150\32\32\149\32\32\32\32\32\32\32", "\129\32\32\130\129\129\129\32\129\130\131\32\32\129\32\130\131\32\32\129\32\129\32\129\129\32\129\32\129\32\131\131\129\130\131\32\32\32\129\130\131\32\32\32\32\140\140\132", "\32\154\32\159\143\32\149\143\32\159\143\32\159\144\149\159\143\32\159\137\145\159\143\144\149\143\32\32\145\32\32\32\145\149\32\144\32\149\32\143\159\32\143\143\32\159\143\32", "\32\32\32\152\140\149\151\32\149\149\32\145\149\130\149\157\140\133\32\149\32\154\143\149\151\32\149\32\149\32\144\32\149\149\153\32\32\149\32\149\133\149\149\32\149\149\32\149", "\32\32\32\130\131\129\131\131\32\130\131\32\130\131\129\130\131\129\32\129\32\140\140\129\129\32\129\32\129\32\137\140\129\130\32\129\32\130\32\129\32\129\129\32\129\130\131\32", "\144\143\32\159\144\144\144\143\32\159\143\144\159\138\32\144\32\144\144\32\144\144\32\144\144\32\144\144\32\144\143\143\144\32\150\129\32\149\32\130\150\32\134\137\134\134\131\148", "\136\143\133\154\141\149\151\32\129\137\140\144\32\149\32\149\32\149\154\159\133\149\148\149\157\153\32\154\143\149\159\134\32\130\148\32\32\149\32\32\151\129\32\32\32\32\134\32", "\133\32\32\32\32\133\129\32\32\131\131\32\32\130\32\130\131\129\32\129\32\130\131\129\129\32\129\140\140\129\131\131\129\32\130\129\32\129\32\130\129\32\32\32\32\32\129\32", "\32\32\32\32\149\32\32\149\32\32\32\32\32\32\32\32\149\32\32\149\32\32\32\32\32\32\32\32\149\32\32\149\32\32\32\32\32\32\32\32\149\32\32\149\32\32\32\32", "\32\32\32\32\32\32\32\32\32\32\32\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\32\32\32\32\32\32\32\32\32\32\32", "\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32", "\32\32\32\32\149\32\32\149\32\32\32\32\32\32\32\32\149\32\32\149\32\32\32\32\32\32\32\32\149\32\32\149\32\32\32\32\32\32\32\32\149\32\32\149\32\32\32\32", "\32\32\32\32\32\32\32\32\32\32\32\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\32\32\32\32\32\32\32\32\32\32\32", "\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32", "\32\32\32\32\145\32\159\139\32\151\131\132\155\143\132\134\135\145\32\149\32\158\140\129\130\130\32\152\147\155\157\134\32\32\144\144\32\32\32\32\32\32\152\131\155\131\131\129", "\32\32\32\32\149\32\149\32\145\148\131\32\149\32\149\140\157\132\32\148\32\137\155\149\32\32\32\149\154\149\137\142\32\153\153\32\131\131\149\131\131\129\149\135\145\32\32\32", "\32\32\32\32\129\32\130\135\32\131\131\129\134\131\132\32\129\32\32\129\32\131\131\32\32\32\32\130\131\129\32\32\32\32\129\129\32\32\32\32\32\32\130\131\129\32\32\32", "\150\150\32\32\148\32\134\32\32\132\32\32\134\32\32\144\32\144\150\151\149\32\32\32\32\32\32\145\32\32\152\140\144\144\144\32\133\151\129\133\151\129\132\151\129\32\145\32", "\130\129\32\131\151\129\141\32\32\142\32\32\32\32\32\149\32\149\130\149\149\32\143\32\32\32\32\142\132\32\154\143\133\157\153\132\151\150\148\151\158\132\151\150\148\144\130\148", "\32\32\32\140\140\132\32\32\32\32\32\32\32\32\32\151\131\32\32\129\129\32\32\32\32\134\32\32\32\32\32\32\32\129\129\32\129\32\129\129\130\129\129\32\129\130\131\32", "\156\143\32\159\141\129\153\140\132\153\137\32\157\141\32\159\142\32\150\151\129\150\131\132\140\143\144\143\141\145\137\140\148\141\141\144\157\142\32\159\140\32\151\134\32\157\141\32", "\157\140\149\157\140\149\157\140\149\157\140\149\157\140\149\157\140\149\151\151\32\154\143\132\157\140\32\157\140\32\157\140\32\157\140\32\32\149\32\32\149\32\32\149\32\32\149\32", "\129\32\129\129\32\129\129\32\129\129\32\129\129\32\129\129\32\129\129\131\129\32\134\32\131\131\129\131\131\129\131\131\129\131\131\129\130\131\32\130\131\32\130\131\32\130\131\32", "\151\131\148\152\137\145\155\140\144\152\142\145\153\140\132\153\137\32\154\142\144\155\159\132\150\156\148\147\32\144\144\130\145\136\137\32\146\130\144\144\130\145\130\136\32\151\140\132", "\151\32\149\151\155\149\149\32\149\149\32\149\149\32\149\149\32\149\149\32\149\152\137\144\157\129\149\149\32\149\149\32\149\149\32\149\149\32\149\130\150\32\32\157\129\149\32\149", "\131\131\32\129\32\129\130\131\32\130\131\32\130\131\32\130\131\32\130\131\32\32\32\32\130\131\32\130\131\32\130\131\32\130\131\32\130\131\32\32\129\32\130\131\32\133\131\32", "\156\143\32\159\141\129\153\140\132\153\137\32\157\141\32\159\142\32\159\159\144\152\140\144\156\143\32\159\141\129\153\140\132\157\141\32\130\145\32\32\147\32\136\153\32\130\146\32", "\152\140\149\152\140\149\152\140\149\152\140\149\152\140\149\152\140\149\149\157\134\154\143\132\157\140\133\157\140\133\157\140\133\157\140\133\32\149\32\32\149\32\32\149\32\32\149\32", "\130\131\129\130\131\129\130\131\129\130\131\129\130\131\129\130\131\129\130\130\131\32\134\32\130\131\129\130\131\129\130\131\129\130\131\129\32\129\32\32\129\32\32\129\32\32\129\32", "\159\134\144\137\137\32\156\143\32\159\141\129\153\140\132\153\137\32\157\141\32\32\132\32\159\143\32\147\32\144\144\130\145\136\137\32\146\130\144\144\130\145\130\138\32\146\130\144", "\149\32\149\149\32\149\149\32\149\149\32\149\149\32\149\149\32\149\149\32\149\131\147\129\138\134\149\149\32\149\149\32\149\149\32\149\149\32\149\154\143\149\32\157\129\154\143\149", "\130\131\32\129\32\129\130\131\32\130\131\32\130\131\32\130\131\32\130\131\32\32\32\32\130\131\32\130\131\129\130\131\129\130\131\129\130\131\129\140\140\129\130\131\32\140\140\129" }, {[[000110000110110000110010101000000010000000100101]], [[000000110110000000000010101000000010000000100101]], [[000000000000000000000000000000000000000000000000]], [[100010110100000010000110110000010100000100000110]], [[000000110000000010110110000110000000000000110000]], [[000000000000000000000000000000000000000000000000]], [[000000110110000010000000100000100000000000000010]], [[000000000110110100010000000010000000000000000100]], [[000000000000000000000000000000000000000000000000]], [[010000000000100110000000000000000000000110010000]], [[000000000000000000000000000010000000010110000000]], [[000000000000000000000000000000000000000000000000]], [[011110110000000100100010110000000100000000000000]], [[000000000000000000000000000000000000000000000000]], [[000000000000000000000000000000000000000000000000]], [[110000110110000000000000000000010100100010000000]], [[000010000000000000110110000000000100010010000000]], [[000000000000000000000000000000000000000000000000]], [[010110010110100110110110010000000100000110110110]], [[000000000000000000000110000000000110000000000000]], [[000000000000000000000000000000000000000000000000]], [[010100010110110000000000000000110000000010000000]], [[110110000000000000110000110110100000000010000000]], [[000000000000000000000000000000000000000000000000]], [[000100011111000100011111000100011111000100011111]], [[000000000000100100100100011011011011111111111111]], [[000000000000000000000000000000000000000000000000]], [[000100011111000100011111000100011111000100011111]], [[000000000000100100100100011011011011111111111111]], [[100100100100100100100100100100100100100100100100]], [[000000110100110110000010000011110000000000011000]], [[000000000100000000000010000011000110000000001000]], [[000000000000000000000000000000000000000000000000]], [[010000100100000000000000000100000000010010110000]], [[000000000000000000000000000000110110110110110000]], [[000000000000000000000000000000000000000000000000]], [[110110110110110110000000110110110110110110110110]], [[000000000000000000000110000000000000000000000000]], [[000000000000000000000000000000000000000000000000]], [[000000000000110110000110010000000000000000010010]], [[000010000000000000000000000000000000000000000000]], [[000000000000000000000000000000000000000000000000]], [[110110110110110110110000110110110110000000000000]], [[000000000000000000000110000000000000000000000000]], [[000000000000000000000000000000000000000000000000]], [[110110110110110110110000110000000000000000010000]], [[000000000000000000000000100000000000000110000110]], [[000000000000000000000000000000000000000000000000]] }}
193
194--### Genarate fonts using 3x3 chars per a character. (1 character is 6x9 pixels)
195local fonts = {}
196local firstFont = {}
197do
198 local char = 0
199 local height = #rawFont[1]
200 local lenght = #rawFont[1][1]
201 for i = 1, height, 3 do
202 for j = 1, lenght, 3 do
203 local thisChar = string.char(char)
204
205 local temp = {}
206 temp[1] = rawFont[1][i]:sub(j, j + 2)
207 temp[2] = rawFont[1][i + 1]:sub(j, j + 2)
208 temp[3] = rawFont[1][i + 2]:sub(j, j + 2)
209
210 local temp2 = {}
211 temp2[1] = rawFont[2][i]:sub(j, j + 2)
212 temp2[2] = rawFont[2][i + 1]:sub(j, j + 2)
213 temp2[3] = rawFont[2][i + 2]:sub(j, j + 2)
214
215 firstFont[thisChar] = {temp, temp2}
216 char = char + 1
217 end
218 end
219 fonts[1] = firstFont
220end
221
222local function generateFontSize(size,yeld)
223 local inverter = {["0"] = "1", ["1"] = "0"} --:gsub("[01]",inverter)
224 if size<= #fonts then return true end
225 for f = #fonts+1, size do
226 --automagicly make bigger fonts using firstFont and fonts[f-1].
227 local nextFont = {}
228 local lastFont = fonts[f - 1]
229 for char = 0, 255 do
230 local thisChar = string.char(char)
231 --sleep(0) print(f,thisChar)
232
233 local temp = {}
234 local temp2 = {}
235
236 local templateChar = lastFont[thisChar][1]
237 local templateBack = lastFont[thisChar][2]
238 for i = 1, #templateChar do
239 local line1, line2, line3, back1, back2, back3 = {}, {}, {}, {}, {}, {}
240 for j = 1, #templateChar[1] do
241 local currentChar = firstFont[templateChar[i]:sub(j, j)][1]
242 table.insert(line1, currentChar[1])
243 table.insert(line2, currentChar[2])
244 table.insert(line3, currentChar[3])
245
246 local currentBack = firstFont[templateChar[i]:sub(j, j)][2]
247 if templateBack[i]:sub(j, j) == "1" then
248 table.insert(back1, (currentBack[1]:gsub("[01]", inverter)))
249 table.insert(back2, (currentBack[2]:gsub("[01]", inverter)))
250 table.insert(back3, (currentBack[3]:gsub("[01]", inverter)))
251 else
252 table.insert(back1, currentBack[1])
253 table.insert(back2, currentBack[2])
254 table.insert(back3, currentBack[3])
255 end
256 end
257 table.insert(temp, table.concat(line1))
258 table.insert(temp, table.concat(line2))
259 table.insert(temp, table.concat(line3))
260 table.insert(temp2, table.concat(back1))
261 table.insert(temp2, table.concat(back2))
262 table.insert(temp2, table.concat(back3))
263 end
264
265 nextFont[thisChar] = {temp, temp2}
266 if yeld then yeld = "Font"..f.."Yeld"..char os.queueEvent(yeld) os.pullEvent(yeld) end
267 end
268 fonts[f] = nextFont
269 end
270 return true
271end
272
273generateFontSize(3,false)
274
275--## Use pre-generated fonts instead of old code above.
276
277--local fonts = {}
278
279local tHex = {[ colors.white ] = "0", [ colors.orange ] = "1", [ colors.magenta ] = "2", [ colors.lightBlue ] = "3", [ colors.yellow ] = "4", [ colors.lime ] = "5", [ colors.pink ] = "6", [ colors.gray ] = "7", [ colors.lightGray ] = "8", [ colors.cyan ] = "9", [ colors.purple ] = "a", [ colors.blue ] = "b", [ colors.brown ] = "c", [ colors.green ] = "d", [ colors.red ] = "e", [ colors.black ] = "f"}
280
281--# Write data on terminal in specified location.
282local function stamp(tTerminal, tData, nX, nY)
283
284 local oX, oY = tTerminal.getSize()
285 local cX, cY = #tData[1][1], #tData[1]
286 nX = nX or math.floor((oX - cX) / 2) + 1
287 nY = nY or math.floor((oY - cY) / 2) + 1
288
289 for i = 1, cY do
290 if i > 1 and nY + i - 1 > oY then term.scroll(1) nY = nY - 1 end
291 tTerminal.setCursorPos(nX, nY + i - 1)
292 tTerminal.blit(tData[1][i], tData[2][i], tData[3][i])
293 end
294end
295
296--# Generate data from strings for data and colors.
297local function makeText(nSize, sString, nFC, nBC, bBlit)
298 if not type(sString) == "string" then error("Not a String") end
299 local cFC = type(nFC) == "string" and nFC:sub(1, 1) or tHex[nFC] or error("Wrong Front Color")
300 local cBC = type(nBC) == "string" and nBC:sub(1, 1) or tHex[nBC] or error("Wrong Back Color")
301 local font = fonts[nSize] or error("Wrong font size selected")
302
303 local input = {}
304 for i in sString:gmatch('.') do table.insert(input, i) end
305
306 local tText = {}
307 local height = #font[input[1]][1]
308
309
310 for nLine = 1, height do
311 local outLine = {}
312 for i = 1, #input do
313 outLine[i] = font[input[i]] and font[input[i]][1][nLine] or ""
314 end
315 tText[nLine] = table.concat(outLine)
316 end
317
318 local tFront = {}
319 local tBack = {}
320 local tFrontSub = {["0"] = cFC, ["1"] = cBC}
321 local tBackSub = {["0"] = cBC, ["1"] = cFC}
322
323 for nLine = 1, height do
324 local front = {}
325 local back = {}
326 for i = 1, #input do
327 local template = font[input[i]] and font[input[i]][2][nLine] or ""
328 front[i] = template:gsub("[01]", bBlit and {["0"] = nFC:sub(i, i), ["1"] = nBC:sub(i, i)} or tFrontSub)
329 back[i] = template:gsub("[01]", bBlit and {["0"] = nBC:sub(i, i), ["1"] = nFC:sub(i, i)} or tBackSub)
330 end
331 tFront[nLine] = table.concat(front)
332 tBack[nLine] = table.concat(back)
333 end
334
335 return {tText, tFront, tBack}
336end
337
338--# Writing in big font using current terminal settings.
339bigFont.bigWrite = function(sString)
340 stamp(term, makeText(1, sString, term.getTextColor(), term.getBackgroundColor()), term.getCursorPos())
341 local x, y = term.getCursorPos()
342 term.setCursorPos(x, y - 2)
343end
344
345bigFont.bigBlit = function(sString, sFront, sBack)
346 stamp(term, makeText(1, sString, sFront, sBack, true), term.getCursorPos())
347 local x, y = term.getCursorPos()
348 term.setCursorPos(x, y - 2)
349end
350
351bigFont.bigPrint = function(sString)
352 stamp(term, makeText(1, sString, term.getTextColor(), term.getBackgroundColor()), term.getCursorPos())
353 print()
354end
355
356--# Writing in huge font using current terminal settings.
357bigFont.hugeWrite = function(sString)
358 stamp(term, makeText(2, sString, term.getTextColor(), term.getBackgroundColor()), term.getCursorPos())
359 local x, y = term.getCursorPos()
360 term.setCursorPos(x, y - 8)
361end
362
363bigFont.hugeBlit = function(sString, sFront, sBack)
364 stamp(term, makeText(2, sString, sFront, sBack, true), term.getCursorPos())
365 local x, y = term.getCursorPos()
366 term.setCursorPos(x, y - 8)
367end
368
369bigFont.hugePrint = function(sString)
370 stamp(term, makeText(2, sString, term.getTextColor(), term.getBackgroundColor()), term.getCursorPos())
371 print()
372end
373
374--# Write/blit string on terminal in specified location
375bigFont.writeOn = function(tTerminal, nSize, sString, nX, nY)
376 stamp(tTerminal, makeText(nSize, sString, tTerminal.getTextColor(), tTerminal.getBackgroundColor()), nX, nY)
377end
378
379bigFont.blitOn = function(tTerminal, nSize, sString, sFront, sBack, nX, nY)
380 stamp(tTerminal, makeText(nSize, sString, sFront, sBack, true), nX, nY)
381end
382
383--# Generate blittle object in blittle format for printing with that api.
384bigFont.makeBlittleText = function(nSize, sString, nFC, nBC)
385 local out = makeText(nSize, sString, nFC, nBC)
386 out.height = #out[1]
387 out.width = #out[1][1]
388 return out
389end
390
391--# Calculate higher size fonts
392bigFont.generateFontSize = function(size)
393 if type(size) ~= "number" then error("Size need to be a number") end
394 if size > 6 then return false end
395 return generateFontSize(math.floor(size),true)
396end
397
398--### Finalizing big font api
399
400
401args = {...}
402entity = {
403 nbsp = " ",
404 lt = "<",
405 gt = ">",
406 quot = "\"",
407 amp = "&",
408}
409
410-- keep unknown entity as is
411setmetatable(entity, {
412 __index = function (t, key)
413 return "&" .. key .. ";"
414 end
415})
416
417block = {
418 "address",
419 "blockquote",
420 "center",
421 "dir", "div", "dl",
422 "fieldset", "form",
423 "h1", "h2", "h3", "h4", "h5", "h6", "hr",
424 "isindex",
425 "menu",
426 "noframes",
427 "ol",
428 "p",
429 "pre",
430 "table",
431 "ul",
432}
433
434inline = {
435 "a", "abbr", "acronym", "applet",
436 "b", "basefont", "bdo", "big", "br", "button",
437 "cite", "code",
438 "dfn",
439 "em",
440 "font",
441 "i", "iframe", "img", "input",
442 "kbd",
443 "label",
444 "map",
445 "object",
446 "q",
447 "s", "samp", "select", "small", "span", "strike", "strong", "sub", "sup",
448 "textarea", "tt",
449 "u",
450 "var",
451}
452
453tags = {
454 a = { empty = false },
455 abbr = {empty = false} ,
456 acronym = {empty = false} ,
457 address = {empty = false} ,
458 applet = {empty = false} ,
459 area = {empty = true} ,
460 b = {empty = false} ,
461 base = {empty = true} ,
462 basefont = {empty = true} ,
463 bdo = {empty = false} ,
464 big = {empty = false} ,
465 blockquote = {empty = false} ,
466 body = { empty = false, },
467 br = {empty = true} ,
468 button = {empty = false} ,
469 caption = {empty = false} ,
470 center = {empty = false} ,
471 cite = {empty = false} ,
472 code = {empty = false} ,
473 col = {empty = true} ,
474 colgroup = {
475 empty = false,
476 optional_end = true,
477 child = {"col",},
478 },
479 dd = {empty = false} ,
480 del = {empty = false} ,
481 dfn = {empty = false} ,
482 dir = {empty = false} ,
483 div = {empty = false} ,
484 dl = {empty = false} ,
485 dt = {
486 empty = false,
487 optional_end = true,
488 child = {
489 inline,
490 "del",
491 "ins",
492 "noscript",
493 "script",
494 },
495 },
496 em = {empty = false} ,
497 fieldset = {empty = false} ,
498 font = {empty = false} ,
499 form = {empty = false} ,
500 frame = {empty = true} ,
501 frameset = {empty = false} ,
502 h1 = {empty = false} ,
503 h2 = {empty = false} ,
504 h3 = {empty = false} ,
505 h4 = {empty = false} ,
506 h5 = {empty = false} ,
507 h6 = {empty = false} ,
508 head = {empty = false} ,
509 hr = {empty = true} ,
510 html = {empty = false} ,
511 i = {empty = false} ,
512 iframe = {empty = false} ,
513 img = {empty = true} ,
514 input = {empty = true} ,
515 ins = {empty = false} ,
516 isindex = {empty = true} ,
517 kbd = {empty = false} ,
518 label = {empty = false} ,
519 legend = {empty = false} ,
520 li = {
521 empty = false,
522 optional_end = true,
523 child = {
524 inline,
525 block,
526 "del",
527 "ins",
528 "noscript",
529 "script",
530 },
531 },
532 link = {empty = true} ,
533 map = {empty = false} ,
534 menu = {empty = false} ,
535 meta = {empty = true} ,
536 noframes = {empty = false} ,
537 noscript = {empty = false} ,
538 object = {empty = false} ,
539 ol = {empty = false} ,
540 optgroup = {empty = false} ,
541 option = {
542 empty = false,
543 optional_end = true,
544 child = {},
545 },
546 p = {
547 empty = false,
548 optional_end = true,
549 child = {
550 inline,
551 "del",
552 "ins",
553 "noscript",
554 "script",
555 },
556 } ,
557 param = {empty = true} ,
558 pre = {empty = false} ,
559 q = {empty = false} ,
560 s = {empty = false} ,
561 samp = {empty = false} ,
562 script = {empty = false} ,
563 select = {empty = false} ,
564 small = {empty = false} ,
565 span = {empty = false} ,
566 strike = {empty = false} ,
567 strong = {empty = false} ,
568 style = {empty = false} ,
569 sub = {empty = false} ,
570 sup = {empty = false} ,
571 table = {empty = false} ,
572 tbody = {empty = false} ,
573 td = {
574 empty = false,
575 optional_end = true,
576 child = {
577 inline,
578 block,
579 "del",
580 "ins",
581 "noscript",
582 "script",
583 },
584 },
585 textarea = {empty = false} ,
586 tfoot = {
587 empty = false,
588 optional_end = true,
589 child = {"tr",},
590 },
591 th = {
592 empty = false,
593 optional_end = true,
594 child = {
595 inline,
596 block,
597 "del",
598 "ins",
599 "noscript",
600 "script",
601 },
602 },
603 thead = {
604 empty = false,
605 optional_end = true,
606 child = {"tr",},
607 },
608 title = {empty = false} ,
609 tr = {
610 empty = false,
611 optional_end = true,
612 child = {
613 "td", "th",
614 },
615 },
616 tt = {empty = false} ,
617 u = {empty = false} ,
618 ul = {empty = false} ,
619 var = {empty = false} ,
620}
621
622setmetatable(tags, {
623 __index = function (t, key)
624 return {empty = false}
625 end
626})
627
628-- string buffer implementation
629function newbuf ()
630 local buf = {
631 _buf = {},
632 clear = function (self) self._buf = {}; return self end,
633 content = function (self) return table.concat(self._buf) end,
634 append = function (self, s)
635 self._buf[#(self._buf) + 1] = s
636 return self
637 end,
638 set = function (self, s) self._buf = {s}; return self end,
639 }
640 return buf
641end
642
643-- unescape character entities
644function unescape (s)
645 function entity2string (e)
646 return entity[e]
647 end
648 return s.gsub(s, "&(#?%w+);", entity2string)
649end
650
651-- iterator factory
652function makeiter (f)
653 local co = coroutine.create(f)
654 return function ()
655 local code, res = coroutine.resume(co)
656 return res
657 end
658end
659
660-- constructors for token
661function Tag (s)
662 return string.find(s, "^</") and
663 {type = "End", value = s} or
664 {type = "Start", value = s}
665end
666
667function Text (s)
668 local unescaped = unescape(s)
669 return {type = "Text", value = unescaped}
670end
671
672-- lexer: text mode
673function text (f, buf)
674 local c = f:read(1)
675 if c == "<" then
676 if buf:content() ~= "" then coroutine.yield(Text(buf:content())) end
677 buf:set(c)
678 return tag(f, buf)
679 elseif c then
680 buf:append(c)
681 return text(f, buf)
682 else
683 if buf:content() ~= "" then coroutine.yield(Text(buf:content())) end
684 end
685end
686
687-- lexer: tag mode
688function tag (f, buf)
689 local c = f:read(1)
690 if c == ">" then
691 coroutine.yield(Tag(buf:append(c):content()))
692 buf:clear()
693 return text(f, buf)
694 elseif c then
695 buf:append(c)
696 return tag(f, buf)
697 else
698 if buf:content() ~= "" then coroutine.yield(Tag(buf:content())) end
699 end
700end
701
702function parse_starttag(tag)
703 local tagname = string.match(tag, "<%s*(%w+)")
704 local elem = {_attr = {}}
705 elem._tag = tagname
706 for key, _, val in string.gmatch(tag, "(%w+)%s*=%s*([\"'])(.-)%2", i) do
707 local unescaped = unescape(val)
708 elem._attr[key] = unescaped
709 end
710 return elem
711end
712
713function parse_endtag(tag)
714 local tagname = string.match(tag, "<%s*/%s*(%w+)")
715 return tagname
716end
717
718-- find last element that satisfies given predicate
719function rfind(t, pred)
720 local length = #t
721 for i=length,1,-1 do
722 if pred(t[i]) then
723 return i, t[i]
724 end
725 end
726end
727
728function flatten(t, acc)
729 acc = acc or {}
730 for i,v in ipairs(t) do
731 if type(v) == "table" then
732 flatten(v, acc)
733 else
734 acc[#acc + 1] = v
735 end
736 end
737 return acc
738end
739
740function optional_end_p(elem)
741 if tags[elem._tag].optional_end then
742 return true
743 else
744 return false
745 end
746end
747
748function valid_child_p(child, parent)
749 local schema = tags[parent._tag].child
750 if not schema then return true end
751
752 for i,v in ipairs(flatten(schema)) do
753 if v == child._tag then
754 return true
755 end
756 end
757
758 return false
759end
760
761-- tree builder
762function parse(f)
763 local root = {_tag = "#document", _attr = {}}
764 local stack = {root}
765 for i in makeiter(function () return text(f, newbuf()) end) do
766 if i.type == "Start" then
767 local new = parse_starttag(i.value)
768 local top = stack[#stack]
769
770 while
771 top._tag ~= "#document" and
772 optional_end_p(top) and
773 not valid_child_p(new, top)
774 do
775 stack[#stack] = nil
776 top = stack[#stack]
777 end
778
779 top[#top+1] = new -- appendchild
780 if not tags[new._tag].empty then
781 stack[#stack+1] = new -- push
782 end
783 elseif i.type == "End" then
784 local tag = parse_endtag(i.value)
785 local openingpos = rfind(stack, function(v)
786 if v._tag == tag then
787 return true
788 else
789 return false
790 end
791 end)
792 if openingpos then
793 local length = #stack
794 for j=length,openingpos,-1 do
795 table.remove(stack, j)
796 end
797 end
798 else -- Text
799 local top = stack[#stack]
800 top[#top+1] = i.value
801 end
802 end
803 return root
804end
805
806function parsestr(s)
807 local handle = {
808 _content = s,
809 _pos = 1,
810 read = function (self, length)
811 if self._pos > string.len(self._content) then return end
812 local ret = string.sub(self._content, self._pos, self._pos + length - 1)
813 self._pos = self._pos + length
814 return ret
815 end
816 }
817 return parse(handle)
818end
819function getTable(filename)
820 file = fs.open(filename, "r")
821 local str = file.readAll()
822 file.close()
823 return parsestr(str)
824end
825--End of html parser
826
827cssom = {}
828
829local logger = new{func = function(self, level, message)
830 --print(level..": "..message)
831end,
832file = "log.log"}
833logger:info("Opened log file")
834
835
836function string.starts(String,Start)
837 return string.sub(String,1,string.len(Start))==Start
838end
839
840
841function css_select(selector, attr, check_with)
842 if not type(selector) == 'string' then error('selector must be string') end
843 if not type(attr) == 'string' then error('attr must be string') end
844 if not type(check_with) == 'string' then error('check_with must be string') end
845
846 if string.starts(selector, "#") and attr == "id"then
847 if selector:sub(2) == check_with then
848 return true
849 else
850 return false
851 end
852 end
853 if string.starts(selector, ".") and attr == "class"then
854 if selector:sub(2) == check_with then
855 return true
856 else
857 return false
858 end
859 end
860 if selector and attr == "element"then
861 if selector == check_with then
862 return true
863 else
864 return false
865 end
866 end
867 return false
868end
869
870
871function mysplit (inputstr, sep)
872 if sep == nil then
873 sep = "%s"
874 end
875 local t={}
876 for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
877 table.insert(t, str)
878 end
879 return t
880end
881--End of css parser
882
883
884function dumpTable(args)
885 if type(args) == 'table' then
886 local output = ""
887 for i,v in ipairs(args) do
888 if type(v) == 'table' then
889 output = output..dumpTable(v)
890 else
891 output = output .. v
892 end
893 end
894 return output
895 else
896 local x = 1
897 end
898end
899
900lines = 0
901local offset = 1
902function parse_page(b)
903 lines = 0
904 for i=1,#b do
905 local element = b[i]
906 if type(element) == 'table' then
907 if i>1 then
908 element._prevElement = b[i-1]
909 end
910 -- print(element._tag)
911 -- print(element._attr.id)
912 if element._tag == "script" then
913 local x = 1
914 end
915 if element._tag == "style" then
916 -- local css = element[1]--string.gsub(element[1], " ", "")
917 for selector, css in element[1]:gmatch("(.-)%{(.-)%}") do
918 --print("css for "..selector.." >")
919 local csscom = mysplit(css, ";")
920 local elm = {}
921 local attr = {}
922 elm.selector = selector
923 for k,v in pairs(csscom) do
924 local vals = mysplit(v, ":")
925 attr[vals[1]] = vals[2]
926 end
927 elm._attr = attr
928 table.insert(cssom, elm)
929 -- print(w)
930 --print(string.gsub(element[1], w, ""))
931 end
932 --print(textutils.serialize(cssom))
933 else
934 local fg_col = "black"
935 local bg_col = "white"
936 for _,style in pairs(cssom) do
937 if type(style) == 'table' then
938 local selector = ""
939 for _,v in pairs(style) do
940 if type(v) == 'string' then selector = v end
941 end
942 --print(selector, element._tag)
943 if css_select(selector, "element", element._tag) then
944 fg_col = style._attr['color']
945 bg_col = style._attr['background-color']
946 end
947 if css_select(selector, "id", element._attr.id) then
948 fg_col = style._attr['color']
949 bg_col = style._attr['background-color']
950 end
951 if css_select(selector, "class", element._attr.style) then
952 fg_col = style._attr['color']
953 bg_col = style._attr['background-color']
954 end
955 --print(selector..":",style._attr)
956 end
957 end
958 if type(element[1]) == 'string' then
959 if type(colors[fg_col]) == 'nil' then fg_col = 'black' end
960 if type(colors[bg_col]) == 'nil' then bg_col = 'white' end
961
962 myWindow.setTextColor(colors[fg_col])
963 myWindow.setBackgroundColor(colors[bg_col])
964 if element._tag == 'script' then
965 --JAVASCRIPT TIME!!
966 else
967 if element._tag == 'h1' then
968 lines = lines + 1
969 local xPos, yPos = myWindow.getCursorPos()
970 myWindow.clearLine()
971 bigFont.bigPrint(element[1])
972 else
973 lines = lines + 1
974 local xPos, yPos = myWindow.getCursorPos()
975 print(element[1])
976 end
977 end
978 end
979 parse_page(element)
980 end
981 else
982 --print(element)
983 end
984 end
985end
986
987_term = term.current()
988myWindow = nil
989
990local termw, termh = term.getSize()
991term.setBackgroundColor(colors.gray)
992term.clear()
993myWindow = window.create(term.current(),1,3,termw-1,termh-2)
994myWindow.setBackgroundColor(colors.white)
995myWindow.clear()
996
997myWindow.setTextColor(colors['black'])
998myWindow.setBackgroundColor(colors['white'])
999myWindow.clear()
1000myWindow.setCursorPos(1,offset)
1001
1002function restart()
1003
1004 term.setCursorPos(1,1)
1005 term.setBackgroundColor(colors.gray)
1006 term.clearLine()
1007 term.write("URL : ")
1008
1009 local myInput = read()
1010 local sExample, err = http.get(myInput) --Get contents of page
1011
1012 logger:info("Opening url "..myInput)
1013
1014 if sExample then
1015 logger:info("Opened page")
1016 s = sExample.readAll() --Read contents of page
1017 sExample.close() --Just in case
1018 else
1019 logger:error("HTTP error: "..err)
1020 --s = "<style>#bob{background-color:blue;color:red}div{background-color:red;}</style><div><p id='bob'>HI? ya?</p><h1>?</h1></div>"
1021 s = "<style>h1{color:red;} div{background-color:red;</style>"
1022 if err == 'Not Found' then
1023 s = s.."<h1>404</h1> <div> <p>"..err.."</p> </div>"
1024 else
1025 s = s.."<p>"..err.."</p>"
1026 end
1027 end
1028 dom = parsestr(s)
1029
1030 term.redirect(myWindow)
1031
1032 myWindow.setBackgroundColor(colors.white)
1033 myWindow.clear()
1034
1035 myWindow.setTextColor(colors['black'])
1036 myWindow.setBackgroundColor(colors['white'])
1037 myWindow.clear()
1038 myWindow.setCursorPos(1,offset)
1039
1040 parse_page(dom)
1041 term.redirect(_term)
1042
1043 if not sExample then
1044 restart()
1045 end
1046
1047 running = true
1048 while running do
1049 term.redirect(_term)
1050 term.setCursorPos(termw, 1)
1051 term.setBackgroundColor(colors.red)
1052 term.setTextColor(colors.white)
1053 term.write('X')
1054 term.redirect(_term)
1055 e, p1, p2, p3, p4 = os.pullEvent()
1056 local termw, termh = myWindow.getSize()
1057 if e == "mouse_click" then
1058 if p2 >= termw and p3 == 1 then
1059 running = false
1060 else
1061 if p3 == 1 and p2 < termw then
1062 term.setTextColor(colors.black)
1063 restart()
1064 end
1065 end
1066 end
1067 if e == "mouse_scroll" then
1068 if p1 == 1 and offset < (termh - lines) then
1069 offset = offset + 1
1070 myWindow.setTextColor(colors['black'])
1071 myWindow.setBackgroundColor(colors['white'])
1072 myWindow.clear()
1073 myWindow.setCursorPos(1, offset)
1074 term.redirect(myWindow)
1075 parse_page(dom)
1076 term.redirect(_term)
1077 end
1078 if p1 == -1 and offset > 1 then
1079 offset = offset - 1
1080 myWindow.setTextColor(colors['black'])
1081 myWindow.setBackgroundColor(colors['white'])
1082 myWindow.clear()
1083 myWindow.setCursorPos(1, offset)
1084 term.redirect(myWindow)
1085 parse_page(dom)
1086 term.redirect(_term)
1087 end
1088 end
1089 if e == 'key' and p1 == keys.q then
1090 running = false
1091 end
1092 end
1093end
1094restart()
1095
1096term.setTextColor(colors['white'])
1097term.setBackgroundColor(colors['black'])
1098term.clear()
1099
1100
1101