Module:SimpleHTMLBuilder: Difference between revisions
Jump to navigation
Jump to search
Content added Content deleted
AnataBakka (talk | contribs) (Created page with "--Differences to the original mw.html --1. Attributes can only be created before wikitext has been called. --2. Attributes and tags (and thus nodes) can not be re-edited once...") |
1Dawn2Dusk3 (talk | contribs) m (Protected "Module:SimpleHTMLBuilder": Fundamental module ([Edit=Allow only administrators] (indefinite) [Move=Allow only administrators] (indefinite))) |
||
(26 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
local concat = table.concat |
|||
--Differences to the original mw.html |
|||
local gsub = string.gsub |
|||
--1. Attributes can only be created before wikitext has been called. |
|||
local sub = string.sub |
|||
--2. Attributes and tags (and thus nodes) can not be re-edited once closed. |
|||
local nowiki = mw.text.nowiki |
|||
--3. Calling the root node will not add stuff to it, but will keep on adding |
|||
-- to the deepest opened node. |
|||
--4. Tags need to be manually closed (though tostring() automatically does that). |
|||
--5. Since adding stuff always adds to the deepest opened node, :allDone() and :done() |
|||
-- have been changed to close tags, and they always return the root node. |
|||
--6. There's no second argument to p.create() and :tag() |
|||
--7. :wikitext() does not allow multiple arguments (though it can be added, |
|||
-- but it consumes more memory) |
|||
--8. There's no :getAttr() |
|||
--9. :attr() and :css() do not allow a table paramater (this could be added, |
|||
-- but i'm not sure if it's needed) |
|||
local userError = require('Dev:User error') |
|||
local concat = table.concat |
|||
local p = {} |
local p = {} |
||
local f = {} |
local f = {} |
||
local function |
local function return_table(self, index) |
||
if self |
if self[index] == "" then |
||
self |
self[index] = {count = 0} |
||
elseif self[index] == nil then |
|||
error("Tag cannot be edited once closed.") |
|||
self.class_open = false |
|||
self.class_has_been_opened = true |
|||
end |
end |
||
return self[index] |
|||
end |
end |
||
function f:node(node) |
|||
self:wikitext(node) |
|||
self.s_len = self.s_len + 1 |
|||
self.s[self.s_len] = '"' |
|||
self.css_open = false |
|||
self.css_has_been_opened = true |
|||
end |
|||
end |
|||
if node.parents == nil then |
|||
local function closeOpeningTag(self) |
|||
node.parents = {count = 0} |
|||
if self.opening_tag_open then |
|||
closeClass(self) |
|||
closeCss(self) |
|||
self.s_len = self.s_len + 1 |
|||
self.s[self.s_len] = '>' |
|||
self.opening_tag_open = false |
|||
self.css_has_been_opened = false |
|||
self.class_has_been_opened = false |
|||
end |
end |
||
end |
|||
node.parents.count = node.parents.count+1 |
|||
node.parents[node.parents.count] = {parent = self, nodePtr = self[11].count} |
|||
return self |
|||
function f:node(node) |
|||
return self:wikitext(tostring(node)) |
|||
end |
end |
||
function f:wikitext(text) |
function f:wikitext(text, text2) |
||
if text2 ~= nil then |
|||
closeOpeningTag(self) |
|||
error("Wikitext does not work with multiple arguments.") |
|||
end |
|||
self.s_len = self.s_len + 1 |
|||
self.s[self.s_len] = text |
|||
local t = return_table(self, 11) |
|||
t.count = t.count + 1 |
|||
t[t.count] = text |
|||
return self |
return self |
||
Line 67: | Line 47: | ||
end |
end |
||
function f:tag(tag) |
function f:tag(tag, args) |
||
local node = p.create(tag, args) |
|||
closeOpeningTag(self) |
|||
self:node(node) |
|||
return node |
|||
self.s_len = self.s_len + 1 |
|||
end |
|||
self.s[self.s_len] = "<" |
|||
self.s_len = self.s_len + 1 |
|||
self.s[self.s_len] = tag |
|||
self.opening_tag_open = true |
|||
function attrTable(self, inputTable) |
|||
self.tag_table_len = self.tag_table_len + 1 |
|||
for i,v in pairs(inputTable) do |
|||
self.tag_table[self.tag_table_len] = tag |
|||
attrFlat(self, i,v) |
|||
end |
|||
return self |
return self |
||
end |
end |
||
function |
function attrFlat(self, name, value) |
||
if |
if (value == nil) then |
||
return self |
|||
error("Attempting to create attribute when tag is not open") |
|||
end |
end |
||
closeClass(self) |
|||
closeCss(self) |
|||
self |
if self[1] ~= "<" then |
||
error("Attempting to create attribute without a tag") |
|||
self.s[self.s_len] = " " |
|||
end |
|||
local t = return_table(self, 9) |
|||
self.s[self.s_len] = name |
|||
t.count = t.count + 1 |
|||
t[t.count] = ' ' |
|||
t.count = t.count + 1 |
|||
t[t.count] = nowiki(name) |
|||
t.count = t.count + 1 |
|||
t[t.count] = '="' |
|||
t.count = t.count + 1 |
|||
t[t.count] = nowiki(value) |
|||
t.count = t.count + 1 |
|||
t[t.count] = '"' |
|||
return self |
return self |
||
end |
|||
function f:attr(name, value) |
|||
if type(name) == "table" then |
|||
return attrTable(self, name) |
|||
else |
|||
return attrFlat(self, name, value) |
|||
end |
|||
end |
end |
||
function f:addClass(value) |
function f:addClass(value) |
||
if self |
if self[1] ~= "<" then |
||
error("Attempting to create class |
error("Attempting to create class without a tag") |
||
end |
end |
||
local t = return_table(self, 4) |
|||
if self.class_has_been_opened == true then |
|||
error("Attempting to create class when one has already been created") |
|||
end |
|||
t.count = t.count + 1 |
|||
closeCss(self) |
|||
t[t.count] = nowiki(value) |
|||
if self.class_open == false then |
|||
self.class_open = true |
|||
self.s_len = self.s_len + 1 |
|||
self.s[self.s_len] = ' class="' |
|||
end |
|||
self.s_len = self.s_len + 1 |
|||
self.s[self.s_len] = value |
|||
t.count = t.count + 1 |
|||
t[t.count] = ' ' |
|||
return self |
return self |
||
Line 136: | Line 116: | ||
function f:cssText(value) |
function f:cssText(value) |
||
if self |
if self[1] ~= "<" then |
||
error("Attempting to create style |
error("Attempting to create style without a tag") |
||
end |
end |
||
local t = return_table(self, 7) |
|||
if self.css_has_been_opened == true then |
|||
error("Attempting to create style when one has already been created") |
|||
end |
|||
t.count = t.count + 1 |
|||
closeClass(self) |
|||
t[t.count] = nowiki(value) |
|||
if self.css_open == false then |
|||
self.css_open = true |
|||
self.s_len = self.s_len + 1 |
|||
self.s[self.s_len] = ' style="' |
|||
end |
|||
self.s_len = self.s_len + 1 |
|||
self.s[self.s_len] = value |
|||
t.count = t.count + 1 |
|||
t[t.count] = ';' |
|||
return self |
return self |
||
Line 163: | Line 132: | ||
function f:css(name, value) |
function f:css(name, value) |
||
if |
if type(name) == "table" then |
||
for i,v in pairs(name) do |
|||
error("Attempting to create style when tag is not open") |
|||
self:css(i,v) |
|||
end |
|||
return self |
|||
end |
end |
||
if self |
if self[1] ~= "<" then |
||
error("Attempting to create style |
error("Attempting to create style without a tag") |
||
end |
end |
||
local t = return_table(self, 7) |
|||
t.count = t.count + 1 |
|||
if self.css_open == false then |
|||
t[t.count] = nowiki(name) |
|||
self.css_open = true |
|||
self.s_len = self.s_len + 1 |
|||
self.s[self.s_len] = ' style="' |
|||
end |
|||
self.s_len = self.s_len + 1 |
|||
self.s[self.s_len] = name |
|||
t.count = t.count + 1 |
|||
t[t.count] = ':' |
|||
t.count = t.count + 1 |
|||
t[t.count] = nowiki(value) |
|||
t.count = t.count + 1 |
|||
t[t.count] = ';' |
|||
return self |
return self |
||
Line 196: | Line 162: | ||
function f:done() |
function f:done() |
||
local parent = self.parents and self.parents[self.parents.count].parent or nil |
|||
if self.tag_table_len < 1 then |
|||
tostring(self) |
|||
error("Attempting to close tag when no tag is open") |
|||
end |
|||
return parent or self |
|||
self.s_len = self.s_len + 1 |
|||
self.s[self.s_len] = '</' |
|||
self.s_len = self.s_len + 1 |
|||
self.s[self.s_len] = self.tag_table[self.tag_table_len] |
|||
self.s_len = self.s_len + 1 |
|||
self.s[self.s_len] = '>' |
|||
self.tag_table_len = self.tag_table_len - 1 |
|||
return self |
|||
end |
end |
||
function f:allDone() |
function f:allDone() |
||
local node = self |
|||
while self.tag_table_len ~= 0 do |
|||
self:done() |
|||
while node ~= node:done() do |
|||
node = node:done() |
|||
end |
end |
||
return |
return node |
||
end |
end |
||
Line 226: | Line 181: | ||
__index = f, |
__index = f, |
||
__tostring = function(self) |
__tostring = function(self) |
||
if self.result then |
|||
return concat(self:allDone().s) |
|||
return self.result |
|||
end, |
|||
end |
|||
if self[4] ~= "" then |
|||
self[3] = ' class="' |
|||
self[4] = concat(self[4]) |
|||
self[5] = '"' |
|||
end |
|||
if self[7] ~= "" then |
|||
self[6] = ' style="' |
|||
self[7] = concat(self[7]) |
|||
self[8] = '"' |
|||
end |
|||
if self[9] ~= "" then |
|||
self[9] = concat(self[9]) |
|||
end |
|||
if self[11] ~= "" then |
|||
for i = 1, self[11].count do |
|||
if type(self[11][i]) == "table" then |
|||
self[11][i] = tostring(self[11][i]) |
|||
end |
|||
end |
|||
self[11] = concat(self[11]) |
|||
end |
|||
self.result = concat(self) |
|||
if self.parents ~= nil then |
|||
for i = 1, self.parents.count do |
|||
self.parents[i].parent[11][self.parents[i].nodePtr] = self.result |
|||
end |
|||
self.parents = nil |
|||
end |
|||
for i = 1, 11 do |
|||
self[i] = nil |
|||
end |
|||
return self.result |
|||
end, |
|||
} |
} |
||
function p.create(tag) |
function p.create(tag, args) |
||
if args ~= nil then |
|||
error("Tag creation does not accept multiple arguments.") |
|||
end |
|||
--struct to give to functions. |
--struct to give to functions. |
||
local tag_table |
|||
--"has been opened" is there just to warn the user that these |
|||
--attributes are not re-editable, unlike the original mw.html function |
|||
local main = { |
|||
["s"] = {}, |
|||
["s_len"] = 0, |
|||
["tag_table"] = {}, |
|||
["tag_table_len"] = 0, |
|||
["opening_tag_open"] = false, |
|||
["css_open"] = false, |
|||
["css_has_been_opened"] = false, |
|||
["class_open"] = false, |
|||
["class_has_been_opened"] = false |
|||
} |
|||
if tag == nil then |
|||
setmetatable(main, meta_main) |
|||
tag_table = {"", "", "", "", "", "", "", "", "", "", ""} |
|||
else |
|||
if(tag ~= nil) then |
|||
-- 3 = class start, 4 = class table, 5 = class end, 6 = css start, 7 = css table, 8 = css end, 9 = attributes table, 11 = text table |
|||
main:tag(tag) |
|||
tag_table = {"<", tag, "", "", "", "", "", "", "", ">", "", "</", tag, ">"} |
|||
end |
end |
||
setmetatable(tag_table, meta_main) |
|||
return main |
|||
return tag_table |
|||
end |
end |
||
Latest revision as of 04:38, 9 November 2024
- Module:SimpleHTMLBuilder has the following documentation.
Simple HTML Builder (SHB) intends to maintain the readability provided by the mw.html
interface while reducing the Lua memory usage when building large wikitext tables. It is designed as a near drop-in replacement for mw.html
, the HTML library included with the Scribunto Extension.
mw.html
|
SHB
|
Differences |
---|---|---|
mw.html.create( tagName, args )
|
✔ | all tags are closed (</tagName> )args ignored in SHB
|
mw.html:node( builder )
|
✔ | cannot be called after done() or tostring()
|
mw.html:wikitext( ... )
|
✔ | only uses first argument in SHB cannot be called after done() or tostring()
|
mw.html:newline()
|
✔ | cannot be called after done() or tostring()
|
mw.html:tag( tagName, args )
|
✔ | all tags are closed (</tagName> )args ignored in SHB cannot be called after done() or tostring()
|
mw.html:attr( name, value ) mw.html:attr( table )
|
✔ ✔ |
cannot be called after done() or tostring() passing nil for value is a no-op instead of an unset operation
|
mw.html:getAttr( name )
|
✖ | |
mw.html:addClass( class )
|
✔ | cannot be called after done() or tostring()
|
mw.html:css( name, value ) mw.html:css( table )
|
✔ ✔ |
cannot be called after done() or tostring()
|
mw.html:cssText( css )
|
✔ | cannot be called after done() or tostring()
|
mw.html:done()
|
✔ | |
mw.html:allDone()
|
✔ |
Differences to the original mw.html 1. Attributes and tags (and thus nodes) can not be re-edited once closed. 2. Attributes and tags are closed with :done and tostring(). Note: the garbage collector is kinda bugged, possible reason for why mw.html is expensive. The collector only collects stuff in specific undefined cases. The tag is locked upon using :done for this reason, because after trial and error i found out locking it as soon as possible greatly lowered memory consumption, even though it otherwise does the same thing as a late tostring(). However, this is also inconsistent, and leaving it to a late tostring() may sometimes do a better job. Still, since doing it early seems to have a higher chance, it should be the prioritized method. 3. There's no second argument to p.create() and :tag() 4. :wikitext() does not allow multiple arguments (though it can be added, but it consumes more memory) 5. There's no :getAttr()
local concat = table.concat
local gsub = string.gsub
local sub = string.sub
local nowiki = mw.text.nowiki
local p = {}
local f = {}
local function return_table(self, index)
if self[index] == "" then
self[index] = {count = 0}
elseif self[index] == nil then
error("Tag cannot be edited once closed.")
end
return self[index]
end
function f:node(node)
self:wikitext(node)
if node.parents == nil then
node.parents = {count = 0}
end
node.parents.count = node.parents.count+1
node.parents[node.parents.count] = {parent = self, nodePtr = self[11].count}
return self
end
function f:wikitext(text, text2)
if text2 ~= nil then
error("Wikitext does not work with multiple arguments.")
end
local t = return_table(self, 11)
t.count = t.count + 1
t[t.count] = text
return self
end
function f:newline()
return self:wikitext("\n")
end
function f:tag(tag, args)
local node = p.create(tag, args)
self:node(node)
return node
end
function attrTable(self, inputTable)
for i,v in pairs(inputTable) do
attrFlat(self, i,v)
end
return self
end
function attrFlat(self, name, value)
if (value == nil) then
return self
end
if self[1] ~= "<" then
error("Attempting to create attribute without a tag")
end
local t = return_table(self, 9)
t.count = t.count + 1
t[t.count] = ' '
t.count = t.count + 1
t[t.count] = nowiki(name)
t.count = t.count + 1
t[t.count] = '="'
t.count = t.count + 1
t[t.count] = nowiki(value)
t.count = t.count + 1
t[t.count] = '"'
return self
end
function f:attr(name, value)
if type(name) == "table" then
return attrTable(self, name)
else
return attrFlat(self, name, value)
end
end
function f:addClass(value)
if self[1] ~= "<" then
error("Attempting to create class without a tag")
end
local t = return_table(self, 4)
t.count = t.count + 1
t[t.count] = nowiki(value)
t.count = t.count + 1
t[t.count] = ' '
return self
end
function f:cssText(value)
if self[1] ~= "<" then
error("Attempting to create style without a tag")
end
local t = return_table(self, 7)
t.count = t.count + 1
t[t.count] = nowiki(value)
t.count = t.count + 1
t[t.count] = ';'
return self
end
function f:css(name, value)
if type(name) == "table" then
for i,v in pairs(name) do
self:css(i,v)
end
return self
end
if self[1] ~= "<" then
error("Attempting to create style without a tag")
end
local t = return_table(self, 7)
t.count = t.count + 1
t[t.count] = nowiki(name)
t.count = t.count + 1
t[t.count] = ':'
t.count = t.count + 1
t[t.count] = nowiki(value)
t.count = t.count + 1
t[t.count] = ';'
return self
end
function f:done()
local parent = self.parents and self.parents[self.parents.count].parent or nil
tostring(self)
return parent or self
end
function f:allDone()
local node = self
while node ~= node:done() do
node = node:done()
end
return node
end
local meta_main = {
__index = f,
__tostring = function(self)
if self.result then
return self.result
end
if self[4] ~= "" then
self[3] = ' class="'
self[4] = concat(self[4])
self[5] = '"'
end
if self[7] ~= "" then
self[6] = ' style="'
self[7] = concat(self[7])
self[8] = '"'
end
if self[9] ~= "" then
self[9] = concat(self[9])
end
if self[11] ~= "" then
for i = 1, self[11].count do
if type(self[11][i]) == "table" then
self[11][i] = tostring(self[11][i])
end
end
self[11] = concat(self[11])
end
self.result = concat(self)
if self.parents ~= nil then
for i = 1, self.parents.count do
self.parents[i].parent[11][self.parents[i].nodePtr] = self.result
end
self.parents = nil
end
for i = 1, 11 do
self[i] = nil
end
return self.result
end,
}
function p.create(tag, args)
if args ~= nil then
error("Tag creation does not accept multiple arguments.")
end
--struct to give to functions.
local tag_table
if tag == nil then
tag_table = {"", "", "", "", "", "", "", "", "", "", ""}
else
-- 3 = class start, 4 = class table, 5 = class end, 6 = css start, 7 = css table, 8 = css end, 9 = attributes table, 11 = text table
tag_table = {"<", tag, "", "", "", "", "", "", "", ">", "", "</", tag, ">"}
end
setmetatable(tag_table, meta_main)
return tag_table
end
return p