-- 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