Module:HtmlBuilder
40x40px | This Lua module is used on 10,000,000 pages. To avoid large-scale disruption and unnecessary server load, any changes to this module should first be tested in its /sandbox or /testcases subpages. The tested changes can then be added to this page in one single edit. Please consider discussing any changes on the talk page before implementing them. |
40x40px | This module is subject to page protection. It is a highly visible module in use by a very large number of articles, or is substituted very frequently. Because vandalism or mistakes would affect many pages, and even trivial editing might cause substantial load on the servers, it is is protected from editing. |
HtmlBuilder provides a way to construct complex HTML and CSS markup by creating a tree of nodes, similar to the Document Object Model. The result is a list of codes that are more comprehensible and maintainable than if you simply concatenated strings together. It offers a fluent interface that should look familiar to any user of jQuery.
Contents
Usage
First, you need to load the module:
local HtmlBuilder = require('Module:HtmlBuilder')
Next, create the root HtmlBuilder instance:
local builder = HtmlBuilder.create()
Then, you can build HTML using the methods of the HtmlBuilder instance, listed below.
Finally, get the resulting HTML markup as a string:
local s = tostring(builder)
Methods
To allow chaining, all methods return a reference to the builder, unless otherwise stated.
tag
local div = builder.tag('div')
Appends a new child node to the builder, and returns an HtmlBuilder instance representing that new node.
done
builder = div.done()
Returns the parent node under which the current node was created. Like jQuery.end, this is a convenience function to allow the construction of several child nodes to be chained together into a single statement.
allDone
builder = div.allDone()
Like .done()
, but traverses all the way to the root node of the tree and returns it.
wikitext
div.wikitext('This is some [[example]] text.')
Appends some markup to the node. It may include plain text, wiki markup, and even HTML markup.
newline
div.newline()
Appends a newline character to the node. Equivalent to .wikitext('\n')
.
attr
div.attr('title', 'Attr value')
Set an HTML attribute on the node.
css
div.css('color', '#f00')
Set a CSS property to be added to the node's style
attribute.
cssText
div.cssText('color:#f00; font-size:1.5em')
Add some raw CSS to the node's style
attribute. This is typically used when a template allows some CSS to be passed in as a parameter, such as the liststyle
parameter of {{Navbox}}.
addClass
div.addClass('even')
Adds a class name to the node's class
attribute. Spaces will be automatically added to delimit each added class name.
Examples
local HtmlBuilder = require('Module:HtmlBuilder') local root = HtmlBuilder.create() root .wikitext('Lorem ') .tag('span') .css('color', 'red') .attr('title', 'ipsum dolor') .wikitext('sit amet') .done() .tag('div') .wikitext('consectetur adipisicing') local s = tostring(root) -- s = 'Lorem <span style="color:red;" title="ipsum dolor">sit amet</span><div>consectetur adipisicing</div>'
For more examples, please see the test cases page and the test cases results.
-- Module for building complex HTML (e.g. infoboxes, navboxes) using a fluent interface. local HtmlBuilder = {} local metatable = {} metatable.__index = function(t, key) local ret = rawget(t, key) if ret then return ret end ret = metatable[key] if type(ret) == 'function' then return function(...) return ret(t, ...) end else return ret end end metatable.__tostring = function(t) local ret = {} t._build(ret) return table.concat(ret) end metatable._build = function(t, ret) if t.tagName then table.insert(ret, '<' .. t.tagName) for i, attr in ipairs(t.attributes) do table.insert(ret, ' ' .. attr.name .. '="' .. attr.val .. '"') end if #t.styles > 0 then table.insert(ret, ' style="') for i, prop in ipairs(t.styles) do if type(prop) == 'string' then -- added with cssText() table.insert(ret, prop .. ';') else -- added with css() table.insert(ret, prop.name .. ':' .. prop.val .. ';') end end table.insert(ret, '"') end if t.selfClosing then table.insert(ret, ' /') end table.insert(ret, '>') end for i, node in ipairs(t.nodes) do if node then if type(node) == 'table' then node._build(ret) else table.insert(ret, tostring(node)) end end end if t.tagName and not t.unclosed and not t.selfClosing then table.insert(ret, '</' .. t.tagName .. '>') end end metatable.node = function(t, builder) if builder then table.insert(t.nodes, builder) end return t end metatable.wikitext = function(t, ...) local vals = {...} for i = 1, #vals do if vals[i] then table.insert(t.nodes, vals[i]) end end return t end metatable.newline = function(t) table.insert(t.nodes, '\n') return t end metatable.tag = function(t, tagName, args) args = args or {} args.parent = t local builder = HtmlBuilder.create(tagName, args) table.insert(t.nodes, builder) return builder end local function getAttr(t, name) for i, attr in ipairs(t.attributes) do if attr.name == name then return attr end end end metatable.attr = function(t, name, val) if type(val) == 'string' or type(val) == 'number' then -- if caller sets the style attribute explicitly, then replace all styles previously added with css() and cssText() if name == 'style' then t.styles = {val} return t end local attr = getAttr(t, name) if attr then attr.val = val else table.insert(t.attributes, {name = name, val = val}) end end return t end metatable.addClass = function(t, class) if class then local attr = getAttr(t, 'class') if attr then attr.val = attr.val .. ' ' .. class else t.attr('class', class) end end return t end metatable.css = function(t, name, val) if type(val) == 'string' or type(val) == 'number' then for i, prop in ipairs(t.styles) do if prop.name == name then prop.val = val return t end end table.insert(t.styles, {name = name, val = val}) end return t end metatable.cssText = function(t, css) if css then table.insert(t.styles, css) end return t end metatable.done = function(t) return t.parent or t end metatable.allDone = function(t) while t.parent do t = t.parent end return t end function HtmlBuilder.create(tagName, args) args = args or {} local builder = {} setmetatable(builder, metatable) builder.nodes = {} builder.attributes = {} builder.styles = {} builder.tagName = tagName builder.parent = args.parent builder.unclosed = args.unclosed or false builder.selfClosing = args.selfClosing or false return builder end return HtmlBuilder