mirror of
https://github.com/mehah/otclient
synced 2026-08-15 16:29:06 -04:00
- cleanup and organization - add support tag link to import external css ex: <link href="module/any.css" /> - fix display block alignment - add textarea css
136 lines
3.9 KiB
Lua
136 lines
3.9 KiB
Lua
local parser = {}
|
|
parser.__index = parser
|
|
|
|
parser.new = function()
|
|
parser.imports = {}
|
|
parser.objects = {
|
|
type = "cssroot",
|
|
children = {}
|
|
}
|
|
parser.strings = {}
|
|
return setmetatable({}, parser)
|
|
end
|
|
|
|
-- what to get the correct result
|
|
-- call parser:parse on all input names
|
|
-- check for imported files using parser:get_imports
|
|
-- recursively load all imported files until you get no more imports
|
|
-- load imported files using parser:import
|
|
function parser:parse(text, filename)
|
|
-- parse the CSS text
|
|
self.text = text
|
|
-- list of imported CSS files
|
|
self:parse_blocks()
|
|
return self.cssom
|
|
end
|
|
|
|
function parser:get_imports()
|
|
-- get list of imported CSS files. each of them should be parsed by a separate parser and imported using parser:import()
|
|
local imports = {}
|
|
for filename, _ in pairs(self.imports) do
|
|
imports[#imports + 1] = filename
|
|
end
|
|
return imports
|
|
end
|
|
|
|
--- Import CSS declarations from another file
|
|
-- The CSS file should be another instacte of the CSS parser
|
|
function parser:import(filename, CSS)
|
|
self.imports[filename] = CSS:get_objects()
|
|
end
|
|
|
|
function parser:get_objects()
|
|
return self.objects
|
|
end
|
|
|
|
function parser:parse_blocks()
|
|
-- for efficiency
|
|
local objects = self.objects
|
|
-- first parse "" and '' strings
|
|
self.text = self:parse_strings(self.text)
|
|
-- then delete comments
|
|
self.text = self:delete_comments(self.text)
|
|
local newobjects = self:parse_statements(self.text)
|
|
for _, obj in ipairs(newobjects) do
|
|
table.insert(objects, obj)
|
|
end
|
|
end
|
|
|
|
function parser:parse_strings(text)
|
|
-- delete strings from the CSS in order to safely remove the comments
|
|
-- strings will be saved to a table, where they will be retrieved lately
|
|
local function replace_string(s)
|
|
local count = #self.strings + 1
|
|
-- save the string and remove the quotations
|
|
self.strings[count] = s:sub(2, -2)
|
|
return "%%" .. count
|
|
end
|
|
local text = text:gsub("(%b'')", replace_string)
|
|
text = text:gsub('(%b"")', replace_string)
|
|
return text
|
|
end
|
|
|
|
function parser:delete_comments(text)
|
|
return text:gsub("/%*.-%*/", " ")
|
|
end
|
|
|
|
function parser:parse_statements(text)
|
|
local pos = 0
|
|
local objects = {}
|
|
local startpos, obj, at
|
|
while pos do
|
|
-- find start of the next statement and detect whether it is an at-rule
|
|
startpos, pos, at = string.find(text, "(@?)%w+", pos + 1)
|
|
if startpos then
|
|
-- these parse commands should parse next statement and return object representing it and
|
|
-- the end position of the statement in the parsed text
|
|
if at == "@" then
|
|
obj, pos = self:parse_at_rule(text, startpos - 1)
|
|
else
|
|
obj, pos = self:parse_rule(text, startpos - 1)
|
|
end
|
|
table.insert(objects, obj)
|
|
end
|
|
end
|
|
return objects
|
|
end
|
|
|
|
function parser:parse_at_rule(text, startpos)
|
|
-- detect if the statement contains block or is ended just by semicolon
|
|
local _, _, brace_or_semicolon = string.find(text, "([;{])", startpos)
|
|
if brace_or_semicolon == ";" then
|
|
local startpos, pos, statement = string.find(text, "(.-);", startpos)
|
|
return { type = "at_statement", statement = statement }, pos
|
|
else
|
|
local startpos, pos, statement, block = string.find(text, "(.-)(%b{})", startpos)
|
|
return { type = "at_statement", statement = statement, block = block }, pos
|
|
end
|
|
end
|
|
|
|
function parser:parse_rule(text, startpos)
|
|
local startpos, pos, selector, block = string.find(text, "(.-)(%b{})", startpos)
|
|
local obj = {
|
|
type = "rule",
|
|
selector = self:parse_selector(selector),
|
|
declarations = self:parse_declarations(block:sub(2, -2)) -- remove the brackets using string.sub
|
|
}
|
|
return obj, pos
|
|
end
|
|
|
|
function parser:parse_selector(selector)
|
|
return selector
|
|
end
|
|
|
|
function parser:parse_declarations(declarations)
|
|
local rules = string.explode(declarations, ";")
|
|
local properties = {}
|
|
for _, rule in ipairs(rules) do
|
|
local property, value = rule:match("%s*(.+)%s*:%s*(.+)%s*")
|
|
if property then
|
|
properties[property] = value
|
|
end
|
|
end
|
|
return properties
|
|
end
|
|
|
|
CssParse = parser
|