Documentation for this module may be created at Module:Sandbox/DrMeepster/C yes yes/doc
local p = {}
--------------------------------
-----Code from other people-----
--------------------------------
TableTools = require("Module:TableTools")
--https://stackoverflow.com/a/1283608
--All stack overflow code is CC BY-SA 3.0
local function tableMerge(t1, t2)
for k,v in pairs(t2) do
if type(v) == "table" then
if type(t1[k] or false) == "table" then
_tableMerge(t1[k] or {}, t2[k] or {})
else
t1[k] = v
end
else
t1[k] = v
end
end
return t1
end
--This function is public domain. See http://lua-users.org/wiki/OrderedTable
--Modified to be able to accept args
local function Ordered(t)
--nextkey and firstkey are used as markers; nextkey[firstkey] is
--the first key in the table, and nextkey[nextkey] is the last key.
--nextkey[nextkey[nextkey]] should always be nil.
local key2val, nextkey, firstkey = {}, {}, {}
nextkey[nextkey] = firstkey
local function onext(self, key)
while key ~= nil do
key = nextkey[key]
local val = self[key]
if val ~= nil then return key, val end
end
end
-- To save on tables, we use firstkey for the (customised)
-- metatable; this line is just for documentation
local selfmeta = firstkey
-- record the nextkey table, for routines lacking the closure
selfmeta.__nextkey = nextkey
-- setting a new key (might) require adding the key to the chain
function selfmeta:__newindex(key, val)
rawset(self, key, val)
if nextkey[key] == nil then -- adding a new key
nextkey[nextkey[nextkey]] = key
nextkey[nextkey] = key
end
end
out = setmetatable(key2val, selfmeta)
if t then
tableMerge(out, t)
end
return out
end
p.Ordered = Ordered
--------------------------------
----------Code from me----------
--------------------------------
--metatable of mw.html
p.htmlMeta = getmetatable(mw.html.create())
p.orderedMeta = getmetatable(p.Ordered())
function p.filter(tabl, typ)
local typeCheck = (type(typ) == "string" and type) or (type(typ) == "table" and getmetatable) or error("Invalid type!")
local out = {}
for k,v in pairs(tabl) do
if typeCheck(v) == typ then
out[k] = v
end
end
return out
end
--------------------------------
--an HTML Element
local Element = {parent=nil, id = nil}
Element.__index = Element
--Create a new Element
function Element.new(type)
o = setmetatable({}, Element)
o.type = type
o.children = {}
o.style = Ordered()
o.classes = {}
return o
end
--Add Element or mw.html as child
function Element:add(child)
if getmetatable(child) == Element then
child.parent = self
elseif getmetatable(child) ~= htmlMeta then
error("Inavlid child!")
end
table.insert(self.children, child)
return child
end
--Add string as child
function Element:wikitext(child)
if type(child) ~= "string" then
error("Inavlid child!")
end
table.insert(self.children, child)
return self
end
--create a new Element then add it
function Element:tag(name)
elem = Element.new(name)
return self:add(elem)
end
--add a newline (br)
function Element:newline()
self:tag("br")
return self
end
--add a css property
function Element:css(k, v)
if type(k) == "table" then
for _,kv in ipairs(k) do
self:css(kv[1], kv[2])
end
elseif type(k) == "string" and type(v) == "string" then
self.style[k] = v
else
error("CSS keys and values should be string!")
end
return self
end
--add classes
function Element:class(class)
if type(class) == "table" then
tableMerge(self.classes, class)
elseif type(class) == "string" then
table.insert(self.classes, class)
else
error("Invalid class type!")
end
return self
end
--set the id
function Element:setId(id)
if type(id) ~= "string" then
error("Id must be string!")
end
self.id = id
return self
end
--returns parent. used for function chaining
function Element:done()
return self.parent or self
end
--returns the root Element
function Element:allDone()
local parent = self.parent
if parent == nil then return self end
while parent.parent do
parent = parent.parent
end
return parent
end
--turn this Element into an mw.html object
function Element:bake()
local root = mw.html.create(self.type):css(self.style)
for _,class in ipairs(self.classes) do
root:addClass(class)
end
for _, child in ipairs(self.children) do
if getmetatable(child) == Element then
--must bake Element
root:node(child:bake())
elseif getmetatable(child) == htmlMeta then
--do not need to bake mw.html
root:node(child)
elseif type(child) == "string" then
--if string add as wikitext
root:wikitext(child)
else
error("Unknown child of element!")
end
end
return root
end
--bakes the root Element
function Element:bakeAll()
return self:allDone():bake()
end
--wip dont use
function Element:stylesheet(css)
for _,c in ipairs(css) do
for _,s in ipairs(c.selectors) do
if s:check(self) then
self:css(c.properties)
break
end
end
end
for _,child in pairs(p.filter(self.children, Element)) do
child:stylesheet(css)
end
return self
end
--------------------------------
local Selector = {
yes = false,
id = nil,
type = nil,
class = {},
directParent = nil,
parent = nil,
next = nil
}
--[[
selections ordered roughly by price, cheapest to most expensive
--basic selections--
yes - Makes the selector always be true (*)
id - Element must have this id (#id)
type - Element must be of this type (type)
class - Element must have all these classes (.class1.class2 etc)
--combinator selections--
directParent- Element must have its parent element match this selector (parentElement > element)
parent - Element must be inside an element matching this selector (parentElement element)
]]
Selector.__index = Selector
function Selector.new(s)
s = s or {}
if type(s.class) == "string" then
s.class = {s.class}
elseif type(s.class) ~= "table" then
s.class = {}
end
return setmetatable(s, Selector)
end
function Selector:check(e)
if getmetatable(e) ~= Element then
return false
end
-- all selector
if self.yes then
return true
end
-- id selector
if self.id and e.id ~= self.id then
return false
end
-- type selector
if self.type and e.type ~= self.type then
return false
end
-- class selector
if #self.class > 0 then
local class = true
for _,v in pairs(self.class) do
if not TableTools.inArray(e.classes, v) then
class = false
break
end
end
if not class then
return false
end
end
-- direct parent check
if self.directParent and not self.directParent:check(e.parent) then
return false
end
-- parent check
if self.parent then
local nextParent = e.parent
while true do
if nextParent == nil then
return false
end
if self.parent:check(nextParent) then
break
end
nextParent = nextParent.parent
end
end
-- all selectors passed
return true
end
--------------------------------
p.Element = Element
p.Selector = Selector
return p