Module:SimpleHTMLBuilder: Difference between revisions
Jump to navigation
Jump to search
Content added Content deleted
AnataBakka (talk | contribs) No edit summary |
1Dawn2Dusk3 (talk | contribs) m (Protected "Module:SimpleHTMLBuilder": Fundamental module ([Edit=Allow only administrators] (indefinite) [Move=Allow only administrators] (indefinite))) |
||
(25 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 and tags (and thus nodes) can not be re-edited once closed. |
|||
local sub = string.sub |
|||
--2. Attributes and tags are closed with :done and tostring(). |
|||
local nowiki = mw.text.nowiki |
|||
-- 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 tostring() does. |
|||
-- However that's not guaranteed. Sometimes tostring does a better job. |
|||
-- Still, you should always use :done after finishing with a tag, as that |
|||
-- has a much higher chance of lowering memory. |
|||
--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() |
|||
--6. :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 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) |
function f:node(node) |
||
node["recent_parent"] = self |
|||
self:wikitext(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 |
return self |
||
end |
end |
||
function f:wikitext(text) |
function f:wikitext(text, text2) |
||
if |
if text2 ~= nil then |
||
error("Wikitext does not work with multiple arguments.") |
|||
self[11] = {["count"] = 0} |
|||
end |
end |
||
local t = self |
local t = return_table(self, 11) |
||
t.count = t.count + 1 |
t.count = t.count + 1 |
||
Line 46: | Line 47: | ||
end |
end |
||
function f:tag(tag) |
function f:tag(tag, args) |
||
local node = p.create(tag) |
local node = p.create(tag, args) |
||
self:node(node) |
self:node(node) |
||
Line 53: | Line 54: | ||
end |
end |
||
function |
function attrTable(self, inputTable) |
||
for i,v in pairs(inputTable) do |
|||
if self[1] == "" then |
|||
attrFlat(self, i,v) |
|||
error("Attempting to create attribute without a tag") |
|||
end |
end |
||
return self |
|||
end |
|||
self[9] = {["count"] = 0} |
|||
function attrFlat(self, name, value) |
|||
if (value == nil) then |
|||
return self |
|||
end |
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.count = t.count + 1 |
||
Line 68: | Line 77: | ||
t.count = t.count + 1 |
t.count = t.count + 1 |
||
t[t.count] = name |
t[t.count] = nowiki(name) |
||
t.count = t.count + 1 |
t.count = t.count + 1 |
||
Line 74: | Line 83: | ||
t.count = t.count + 1 |
t.count = t.count + 1 |
||
t[t.count] = value |
t[t.count] = nowiki(value) |
||
t.count = t.count + 1 |
t.count = t.count + 1 |
||
Line 80: | Line 89: | ||
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[1] |
if self[1] ~= "<" then |
||
error("Attempting to create class without a tag") |
error("Attempting to create class without a tag") |
||
end |
end |
||
local t = return_table(self, 4) |
|||
if self[4] == "" then |
|||
self[4] = {["count"] = 0} |
|||
end |
|||
local t = self[4] |
|||
t.count = t.count + 1 |
t.count = t.count + 1 |
||
t[t.count] = value |
t[t.count] = nowiki(value) |
||
t.count = t.count + 1 |
t.count = t.count + 1 |
||
Line 103: | Line 116: | ||
function f:cssText(value) |
function f:cssText(value) |
||
if self[1] |
if self[1] ~= "<" then |
||
error("Attempting to create style without a tag") |
error("Attempting to create style without a tag") |
||
end |
end |
||
local t = return_table(self, 7) |
|||
if self[7] == "" then |
|||
self[7] = {["count"] = 0} |
|||
end |
|||
local t = self[7] |
|||
t.count = t.count + 1 |
t.count = t.count + 1 |
||
t[t.count] = value |
t[t.count] = nowiki(value) |
||
t.count = t.count + 1 |
t.count = t.count + 1 |
||
Line 123: | 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 without a tag") |
|||
self:css(i,v) |
|||
end |
|||
return self |
|||
end |
end |
||
if self[ |
if self[1] ~= "<" then |
||
error("Attempting to create style without a tag") |
|||
self[7] = {["count"] = 0} |
|||
end |
end |
||
local t = self |
local t = return_table(self, 7) |
||
t.count = t.count + 1 |
t.count = t.count + 1 |
||
t[t.count] = name |
t[t.count] = nowiki(name) |
||
t.count = t.count + 1 |
t.count = t.count + 1 |
||
Line 140: | Line 153: | ||
t.count = t.count + 1 |
t.count = t.count + 1 |
||
t[t.count] = value |
t[t.count] = nowiki(value) |
||
t.count = t.count + 1 |
t.count = t.count + 1 |
||
Line 149: | Line 162: | ||
function f:done() |
function f:done() |
||
local parent = self. |
local parent = self.parents and self.parents[self.parents.count].parent or nil |
||
tostring(self) |
|||
if parent ~= nil then |
|||
parent[11][parent[11].count] = tostring(self) |
|||
end |
|||
return parent |
return parent or self |
||
end |
end |
||
Line 161: | Line 171: | ||
local node = self |
local node = self |
||
while node |
while node ~= node:done() do |
||
node = node:done() |
node = node:done() |
||
end |
end |
||
Line 171: | Line 181: | ||
__index = f, |
__index = f, |
||
__tostring = function(self) |
__tostring = function(self) |
||
if self.result then |
|||
return self.result |
|||
end |
|||
if self[4] ~= "" then |
if self[4] ~= "" then |
||
self[3] = ' class="' |
self[3] = ' class="' |
||
Line 190: | Line 204: | ||
for i = 1, self[11].count do |
for i = 1, self[11].count do |
||
if type(self[11][i]) == "table" then |
if type(self[11][i]) == "table" then |
||
self[11][i] = tostring(self[11][i]) |
|||
end |
end |
||
end |
end |
||
Line 196: | Line 210: | ||
self[11] = concat(self[11]) |
self[11] = concat(self[11]) |
||
end |
end |
||
self.result = concat(self) |
|||
if self.parents ~= nil then |
|||
return concat(self) |
|||
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, |
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 |
local tag_table |
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