Open main menu
Home
Random
Donate
Recent changes
Special pages
Community portal
Preferences
About Stockhub
Disclaimers
Search
User menu
Talk
Contributions
Create account
Log in
Editing
Module:Sandbox/Jackmcbarn/mw.html.lua
Warning:
You are not logged in. Your IP address will be publicly visible if you make any edits. If you
log in
or
create an account
, your edits will be attributed to your username, along with other benefits.
Anti-spam check. Do
not
fill this in!
--[[ A module for building complex HTML from Lua using a fluent interface. Originally written on the English Wikipedia by Toohool and Mr. Stradivarius. Code released under the GPL v2+ as per: https://en.wikipedia.org/w/index.php?diff=next&oldid=581399786 https://en.wikipedia.org/w/index.php?diff=next&oldid=581403025 @license GNU GPL v2+ @author Marius Hoch < hoo@online.de > ]] 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 = { area = true, base = true, br = true, col = true, command = true, embed = true, hr = true, img = true, input = true, keygen = true, link = true, meta = true, param = true, source = true, track = true, wbr = true, } 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 name local function getAttr( t, name ) for i, attr in ipairs( t.attributes ) do if attr.name == name then return attr, i end end end -- Is this a valid attribute name? -- -- @param s local 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 s local function isValidTag( s ) return s:match( '^[a-zA-Z0-9]+$' ) end -- Escape a value, for use in HTML -- -- @param s local 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 args local 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 builder end -- 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 builder local 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 t end 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, '</' .. t.tagName .. '>' ) end end -- Append a builder to the current node -- -- @param builder methodtable.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], { 'string', 'number' } ) appendBuilder( t, vals[i] ) end return t end -- Appends a newline character to the node. methodtable.newline = function( t ) t:wikitext( '\n' ) return t end -- Appends a new child node to the builder, and returns an HtmlBuilder instance -- representing that new node. -- -- @param tagName -- @param args methodtable.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 builder end -- Get the value of an html attribute -- -- @param name methodtable.getAttr = function( t, name ) checkType( 'getAttr', 1, name, 'string' ) local attr = getAttr( t, name ) if attr then return attr.val end return nil end -- 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 unset methodtable.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, { 'string', 'number', 'nil' } ) -- 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 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, { name = name, val = val } ) end return t end -- Adds a class name to the node's class attribute. Spaces will be -- automatically added to delimit each added class name. -- -- @param class methodtable.addClass = function( t, class ) checkTypeMulti( 'addClass', 1, class, { 'string', 'number', 'nil' } ) 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 t end -- 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 unset methodtable.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, { 'string', 'number' } ) checkTypeMulti( 'css', 2, val, { 'string', 'number', 'nil' } ) 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, { name = name, val = val } ) end return t end -- 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 css methodtable.cssText = function( t, css ) checkTypeMulti( 'cssText', 1, css, { 'string', 'number', 'nil' } ) if css ~= nil then table.insert( t.styles, css ) end return t end -- 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 t end -- 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 t end -- Create a new instance -- -- @param tagName -- @param args function 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" global mw = mw or {} mw.html = HtmlBuilder package.loaded['mw.html'] = HtmlBuilder return HtmlBuilder
Summary:
Please note that all contributions to Stockhub may be edited, altered, or removed by other contributors. If you do not want your writing to be edited mercilessly, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource (see
Stockhub:Copyrights
for details).
Do not submit copyrighted work without permission!
Cancel
Editing help
(opens in new window)
Template used on this page:
Module:Sandbox/Jackmcbarn/mw.html.lua/doc
(
edit
)