Module:SimpleHTMLBuilder: Difference between revisions

From League of Legends Wiki, LoL Wiki
Jump to navigation Jump to search
Content added Content deleted
(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...")
 
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 closeClass(self)
local function return_table(self, index)
if self.class_open then
if self[index] == "" then
self.s_len = self.s_len + 1
self[index] = {count = 0}
self.s[self.s_len] = '"'
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


local function closeCss(self)
function f:node(node)
if self.css_open then
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 f:attr(name, value)
function attrFlat(self, name, value)
if self.opening_tag_open == false then
if (value == nil) then
return self
error("Attempting to create attribute when tag is not open")
end
end
closeClass(self)
closeCss(self)


self.s_len = self.s_len + 1
if self[1] ~= "<" then
error("Attempting to create attribute without a tag")
self.s[self.s_len] = " "
end


self.s_len = self.s_len + 1
local t = return_table(self, 9)
self.s[self.s_len] = name
self.s_len = self.s_len + 1
t.count = t.count + 1
self.s[self.s_len] = '="'
t[t.count] = ' '

t.count = t.count + 1
t[t.count] = nowiki(name)
self.s_len = self.s_len + 1
t.count = t.count + 1
self.s[self.s_len] = value
t[t.count] = '="'

t.count = t.count + 1
t[t.count] = nowiki(value)
self.s_len = self.s_len + 1
t.count = t.count + 1
self.s[self.s_len] = '"'
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.opening_tag_open == false then
if self[1] ~= "<" then
error("Attempting to create class when tag is not open")
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
self.s_len = self.s_len + 1
t.count = t.count + 1
self.s[self.s_len] = ' '
t[t.count] = ' '
return self
return self
Line 136: Line 116:


function f:cssText(value)
function f:cssText(value)
if self.opening_tag_open == false then
if self[1] ~= "<" then
error("Attempting to create style when tag is not open")
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
self.s_len = self.s_len + 1
t.count = t.count + 1
self.s[self.s_len] = ';'
t[t.count] = ';'
return self
return self
Line 163: Line 132:


function f:css(name, value)
function f:css(name, value)
if self.opening_tag_open == false then
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.css_has_been_opened == true then
if self[1] ~= "<" then
error("Attempting to create style when one has already been created")
error("Attempting to create style without a tag")
end
end
closeClass(self)
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
self.s_len = self.s_len + 1
t.count = t.count + 1
self.s[self.s_len] = ':'
t[t.count] = ':'
self.s_len = self.s_len + 1
t.count = t.count + 1
self.s[self.s_len] = value
t[t.count] = nowiki(value)
self.s_len = self.s_len + 1
t.count = t.count + 1
self.s[self.s_len] = ';'
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


closeOpeningTag(self)
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 self
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 valueis 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