Module:Sandbox/Jackmcbarn/mw.html.lua explained

--

local HtmlBuilder =

local util = require 'libraryUtil'local checkType = util.checkType

local function checkTypeMulti(name, argIdx, arg, expectTypes) local argType = type(arg) for _, expectType in ipairs(expectTypes) do if argType

expectType then return end end local n = #expectTypes local typeList if n > 1 then typeList = table.concat(expectTypes, ', ', 1, n - 1) .. ' or ' .. expectTypes[n] else typeList = expectTypes[1] end local msg = string.format("bad argument #%d to '%s' (%s expected, got %s)", argIdx, name, typeList, type(arg) ) error(msg, 3)end

local metatable = local methodtable =

local selfClosingTags =

local htmlencodeMap =

metatable.__index = methodtable

metatable.__tostring = function(t) local ret = t:_build(ret) return table.concat(ret)end

-- Get an attribute table (name, value) and its index---- @param namelocal function getAttr(t, name) for i, attr in ipairs(t.attributes) do if attr.name

name then return attr, i end endend

-- Is this a valid attribute name?---- @param slocal function isValidAttributeName(s) -- Good estimate: http://www.w3.org/TR/2000/REC-xml-20001006#NT-Name return s:match('^[a-zA-Z_:][a-zA-Z0-9_.:-]*$')end

-- Is this a valid tag name?---- @param slocal function isValidTag(s) return s:match('^[a-zA-Z0-9]+$')end

-- Escape a value, for use in HTML---- @param slocal function htmlEncode(s) -- The parentheses ensure that there is only one return value return (string.gsub(s, '[<>&"]', htmlencodeMap))end

local function cssEncode(s) -- XXX: I'm not sure this character set is complete. -- bug #68011: allow delete character (\127) return (s:find('[^%z\1-\127]') and mw.ustring or string) .gsub(s, '[^\32-\57\60-\127]', function (m) return string.format('\\%X ', mw.ustring.codepoint(m)) end)end

-- Create a builder object. This is a separate function so that we can show the-- correct error levels in both HtmlBuilder.create and metatable.tag.---- @param tagName-- @param argslocal function createBuilder(tagName, args) if tagName ~= nil and tagName ~= and not isValidTag(tagName) then error(string.format("invalid tag name '%s'", tagName), 3) end

args = args or local builder = setmetatable(builder, metatable) builder.nodes = builder.attributes = builder.styles =

if tagName ~= then builder.tagName = tagName end

builder.parent = args.parent builder.selfClosing = selfClosingTags[tagName] or args.selfClosing or false return builderend

-- Append a builder to the current node. This is separate from methodtable.node-- so that we can show the correct error level in both methodtable.node and-- methodtable.wikitext.---- @param builderlocal function appendBuilder(t, builder) if t.selfClosing then error("self-closing tags can't have child nodes", 3) end

if builder then table.insert(t.nodes, builder) end return tend

methodtable._build = function(t, ret) if t.tagName then table.insert(ret, '<' .. t.tagName) for i, attr in ipairs(t.attributes) do table.insert(ret, -- Note: Attribute names have already been validated ' ' .. attr.name .. '="' .. htmlEncode(attr.val) .. '"' ) end if #t.styles > 0 then table.insert(ret, ' style="') local css = for i, prop in ipairs(t.styles) do if type(prop) ~= 'table' then -- added with cssText table.insert(css, htmlEncode(prop)) else -- added with css table.insert(css, htmlEncode(cssEncode(prop.name) .. ':' .. cssEncode(prop.val)) ) end end table.insert(ret, table.concat(css, ';')) table.insert(ret, '"') end if t.selfClosing then table.insert(ret, ' />') return 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 then table.insert(ret, '') endend

-- Append a builder to the current node---- @param buildermethodtable.node = function(t, builder) return appendBuilder(t, builder)end

-- Appends some markup to the node. This will be treated as wikitext.methodtable.wikitext = function(t, ...) local vals = for i = 1, #vals do checkTypeMulti('wikitext', i, vals[i],) appendBuilder(t, vals[i]) end return tend

-- Appends a newline character to the node.methodtable.newline = function(t) t:wikitext('\n') return tend

-- Appends a new child node to the builder, and returns an HtmlBuilder instance-- representing that new node.---- @param tagName-- @param argsmethodtable.tag = function(t, tagName, args) checkType('tag', 1, tagName, 'string') checkType('tag', 2, args, 'table', true) args = args or

args.parent = t local builder = createBuilder(tagName, args) t:node(builder) return builderend

-- Get the value of an html attribute---- @param namemethodtable.getAttr = function(t, name) checkType('getAttr', 1, name, 'string')

local attr = getAttr(t, name) if attr then return attr.val end return nilend

-- Set an HTML attribute on the node.---- @param name Attribute to set, alternative table of name-value pairs-- @param val Value of the attribute. Nil causes the attribute to be unsetmethodtable.attr = function(t, name, val) if type(name)

'table' then if val ~= nil then error("bad argument #2 to 'attr' " .. '(if argument #1 is a table, argument #2 must be left empty)', 2 ) end

local callForTable = function for attrName, attrValue in pairs(name) do t:attr(attrName, attrValue) end end

if not pcall(callForTable) then error("bad argument #1 to 'attr' " .. '(table keys must be strings, and values must be strings or numbers)', 2 ) end

return t end

checkType('attr', 1, name, 'string') checkTypeMulti('attr', 2, val,)

-- if caller sets the style attribute explicitly, then replace all styles -- previously added with css and cssText if name

'style' then t.styles = return t end

if not isValidAttributeName(name) then error(string.format("bad argument #1 to 'attr' (invalid attribute name '%s')", name ), 2) end

local attr, i = getAttr(t, name) if attr then if val ~= nil then attr.val = val else table.remove(t.attributes, i) end elseif val ~= nil then table.insert(t.attributes,) end

return tend

-- Adds a class name to the node's class attribute. Spaces will be-- automatically added to delimit each added class name.---- @param classmethodtable.addClass = function(t, class) checkTypeMulti('addClass', 1, class,)

if class

nil then return t end

local attr = getAttr(t, 'class') if attr then attr.val = attr.val .. ' ' .. class else t:attr('class', class) end

return tend

-- Set a CSS property to be added to the node's style attribute.---- @param name CSS attribute to set, alternative table of name-value pairs-- @param val The value to set. Nil causes it to be unsetmethodtable.css = function(t, name, val) if type(name)

'table' then if val ~= nil then error("bad argument #2 to 'css' " .. '(if argument #1 is a table, argument #2 must be left empty)', 2 ) end

local callForTable = function for attrName, attrValue in pairs(name) do t:css(attrName, attrValue) end end

if not pcall(callForTable) then error("bad argument #1 to 'css' " .. '(table keys and values must be strings or numbers)', 2 ) end

return t end

checkTypeMulti('css', 1, name,) checkTypeMulti('css', 2, val,)

for i, prop in ipairs(t.styles) do if prop.name

name then if val ~= nil then prop.val = val else table.remove(t.styles, i) end return t end end

if val ~= nil then table.insert(t.styles,) end

return tend

-- 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---- @param cssmethodtable.cssText = function(t, css) checkTypeMulti('cssText', 1, css,) if css ~= nil then table.insert(t.styles, css) end return tend

-- 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.methodtable.done = function(t) return t.parent or tend

-- Like .done, but traverses all the way to the root node of the tree and-- returns it.methodtable.allDone = function(t) while t.parent do t = t.parent end return tend

-- Create a new instance---- @param tagName-- @param argsfunction HtmlBuilder.create(tagName, args) checkType('mw.html.create', 1, tagName, 'string', true) checkType('mw.html.create', 2, args, 'table', true) return createBuilder(tagName, args)end

mw_interface = nil

-- Register this library in the "mw" globalmw = mw or mw.html = HtmlBuilder

package.loaded['mw.html'] = HtmlBuilder

return HtmlBuilder