Module:Mw.html extension

Module documentation
This documentation is transcluded from Module:Mw.html extension/doc. [edit] [history] [purge]
Module:Mw.html extension requires libraryUtil.

This module is a helper module to be used by other modules; it may not designed to be invoked directly. See Brighter Shores:Lua/Helper modules for a full list and more information. For a full list of modules using this helper click here

FunctionTypeUse
addClassIf(cond, class)boolean, stringIf cond = true it behaves the same as the normal addClass function, otherwise it's a no-op. Ex.: mw.html.create('div'):addClassIf(true, 'align-left-1')
attrIf(cond, name, value)boolean, string/table, string/nilSimilar to addClassIf
cssIf(cond, name, value)boolean, string/table, string/nilSimilar to addClassIf
doneIf(cond)booleanSimilar to addClassIf
tagIf(cond, tag)boolean, stringSimilar to addClassIf
wikitextIf(cond, text)boolean, stringSimilar to addClassIf
na()N/AShortcut for :tag('td'):attr('data-sort-value', 0):attr('class','table-na'):wikitext('<small>N/A</small>'):done()
naIf(cond)booleanSimilar to addClassIf
tr([settings])table/nilShortcut for :tag('tr') but also auto closes the previous 'tr', 'th' or 'td' tag (so you don't need to add :done() before it). settings is a table with keys:
  • class or addClass - A string passed to :addClass()
  • attr - A table passed to :attr()
  • css - A table passed to :css()
  • cssText - A string passed to :cssText()
th([settings])string/table/nilShortcut for :tag('th'):wikitext(settings) if settings is a string. Also auto closes the previous 'th' or 'td' tag. settings can also be a table with keys:
  • [1] (array) or wikitext - A string passed to :wikitext()
  • class or addClass - A string passed to :addClass()
  • attr - A table passed to :attr()
  • css - A table passed to :css()
  • cssText - A string passed to :cssText()
td([settings])string/table/nilSame as :th(). Example:
local tbl = mw.html.create('table')
tbl:tr{ class='sortable' }
        :th{'foo', attr={'data-sort-type', 'number'}}
        :th('bar')
    :tr()
        :td('buz')
            :attr('data-sort-value', 10)
        :td{'N/A', class='table-na'}
IF(cond)booleanAllows for if-blocks without breaking the chain. If the condition is true it is a no-op, if false everything inside the balanced IF-END block will be ignored. Can be nested. Ex.:
mw.html.create('div')
    :IF(true)
        :wikitext('Conditional text')
    :END()
    :...

Note: This only prevents elements from being added to your html object, it does not protect against statements that throw errors. I.e

mw.html.create('div')
    :IF(false)
        :wikitext(5 * nil) -- This will still throw an error
    :END()
ELSEIF(cond)booleanUsed together with IF().
ELSE()N/AUsed together with IF().
END()N/AUsed together with IF(). Make sure the IF-END tags are balanced, it wont throw an error if they are not.
exec(func, ...)function, anyCall a function without breaking the chain. See module docs for more info.
addFunction(func, name)function, stringAdd a function to the mw.html class that can then be used on mw.html object. See module docs for more info.

Usage

For all functions other than addFunction() all you need to do is simply require this module:

require('Module:Mw.html extension')
local p = {}

function p.main()
    ...
    local tbl = mw.html.create('div')
        :IF(true)
            :wikitext('Conditional text')
        :ELSE()
            :wikitext('something else')
        :END()
        :addClassIf(true, 'wikitable')
        :tag('span)'
            :wikitext('normal wikitext')
        :done()
    ...
end

return p

You can mix the normal old functions with the newly added ones.

attrIf

This accepts either a name-value pair or a table

  • :attrIf(true, 'data-sort-value', '0')
  • :attrIf(true, {'data-sort-value' = '0', ...})

cssIf

This accepts either a name-value pair or a table similar to attrIf

exec

The first parameter of the function will have the current state of the mw.html object passed into it, usually we call this parameter self, the rest of the parameters can be anything you want. To not break the chaining the function must also return a mw.html object. Example:

local function repNa(self, times)
    for i = 1,times do
        self:na()
    end
    return self
end

This function can then be used as follows

mw.html.create('div'):exec(repNa, 5)

addFunction

The function you want to add has to be of the same structure as in exec(). Example:

local htmlExtension = require('Module:Mw.html extension')
local p = {}

local function repNa(self, times)
    for i = 1,times do
        self:na()
    end
    return self
end
htmlExtension.addFunction(repNa, 'repNaName')

function p.main()
    ...
    local tbl = mw.html.create('div'):repNaName(5)
    ...
end

return p

tr, th and td

The following three tables are the same:

local tbl = mw.html.create('table')
tbl:tr{ class='sortable' }
        :th{'foo', attr={['data-sort-type']='number'}} -- or attr={'data-sort-type', 'number'}
        :th('bar')
            :IF(expression)
                :addClass('table-na')
            :END()
    :tr()
        :td('buz')
        :td{'N/A', class='table-na'}

local tbl2 = mw.html.create('table')
tbl2:tag('tr')
        :addClass('sortable')
        :tag('th')
            :attr('data-sort-type', 'number')
            :wikitext('foo')
        :done()
        :tag('th')
            :wikitext('bar')
            :IF(expression)
                :addClass('table-na')
            :END()
        :done() -- This is needed because "tag('tr')" is used after this instead of "tr()"
    :done()
    :tag('tr')
        :tag('td')
            :wikitext('buz')
        :done()
        :tag('td')
            :wikitext('N/A')
            :addClass('table-na')

local tbl3 = mw.html.create('table')
tbl3:tag('tr')
        :addClass('sortable')
        :tag('th')
            :attr('data-sort-type', 'number')
            :wikitext('foo')
        :th('bar')
            :IF(expression)
                :addClass('table-na')
            :END()
        :done()
    :done()
    :tag('tr')
        :td('buz')
        :td('N/A')
            :addClass('table-na')

-- <nowiki>
local p = {}
local checkType = require( 'libraryUtil' ).checkType
local mwHtml = getmetatable( mw.html.create() ).__index  -- Trick to get acces to the mw.html class
local stack = {}  -- Used to keep track of nested IF-END tags
local noOp = {}  -- This object is returned by IF(false) tag

function mwHtml:addClassIf( cond, class )
    if cond then
        return self:addClass( class )
    else
        return self
    end
end

function mwHtml:tagIf( cond, tagname )
    if cond then
        return self:tag( tagname )
    else
        return self
    end
end

function mwHtml:wikitextIf( cond, text )
    if cond then
        return self:wikitext( text )
    else
        return self
    end
end

function mwHtml:doneIf( cond )
    if cond then
        return self:done()
    else
        return self
    end
end

function mwHtml:attrIf( cond, name, value )
    if cond then
        return self:attr( name, value )
    else
        return self
    end
end

function mwHtml:cssIf( cond, name, value )
    if cond then
        return self:css( name, value )
    else
        return self
    end
end

function mwHtml:na()
    return self:tag( 'td' )
            :attr( 'data-sort-value', 0 )
            :attr( 'class', 'table-na' )
            :wikitext( '<small>N/A</small>' )
        :done()
end

function mwHtml:naIf( cond )
    if cond then
        return self:na()
    else
        return self
    end
end

local function addValues( self, settings )
    -- wikitext and addClass are no-ops when their argument is nil
    self:wikitext( settings[1] or settings.wikitext )
    self:addClass( settings.class or settings.addClass )

    if settings.attr then
        if settings.attr[1] and settings.attr[2] then
            self:attr( settings.attr[1], settings.attr[2] )
        else
            self:attr( settings.attr )
        end
    end

    if settings.css then
        if settings.css[1] and settings.css[2] then
            self:css( settings.css[1], settings.css[2] )
        else
            self:css( settings.css )
        end
    end

    if settings.cssText then
        self:cssText( settings.cssText )
    end
end

function mwHtml:tr( settings )
    if self.tagName == 'tr' then
        self = self:done():tag( 'tr' )
    elseif self.tagName == 'th' or self.tagName == 'td' then
        self = self:done():done():tag( 'tr' )
    else
        self = self:tag( 'tr' )
    end

    if type( settings ) == 'table' then
        addValues( self, settings )
    end

    return self
end

function mwHtml:th( settings )
    if self.tagName == 'th' or self.tagName == 'td' then
        self = self:done():tag( 'th' )
    else
        self = self:tag( 'th' )
    end

    if type( settings ) == 'table' then
        addValues( self, settings )
    else
        self = self:wikitext( settings )
    end

    return self
end

function mwHtml:td( settings )
    if self.tagName == 'th' or self.tagName == 'td' then
        self = self:done():tag( 'td' )
    else
        self = self:tag( 'td' )
    end

    if type( settings ) == 'table' then
        addValues( self, settings )
    else
        self = self:wikitext( settings )
    end

    return self
end

function mwHtml:IF( cond )
    if cond then
        table.insert( stack, { obj=noOp, trueCaseCompleted=true } )
        return self
    else
        table.insert( stack, { obj=self, trueCaseCompleted=false } )
        return noOp
    end
end

function mwHtml:ELSEIF( cond )
    if #stack == 0 then error( 'Missing IF tag', 2 ) end
    local last = stack[#stack]

    if cond and not last.trueCaseCompleted then
        last.trueCaseCompleted = true
        local res = last.obj
        last.obj = noOp
        return res
    else
        if self ~= noOp then
            last.obj = self
        end
        return noOp
    end
end

function mwHtml:ELSE()
    return self:ELSEIF( true )
end

function mwHtml:END()
    if #stack == 0 then error( 'Missing IF tag', 2 ) end

    local res = table.remove( stack )  -- Pop element from the end
    if res.obj == noOp then
        return self
    else
        return res.obj
    end
end

function mwHtml:exec( func, ... )
    checkType( 'exec', 1, func, 'function' )
    return func( self, ... )
end

function p.addFunction( func, name )
    checkType( 'addFunction', 1, func, 'function' )
    checkType( 'addFunction', 2, name, 'string' )

    if mwHtml[name] then
        error( 'Function "' .. name .. '" already exists', 2 )
    end

    mwHtml[name] = func
end

noOp.IF = mwHtml.IF
noOp.ELSEIF = mwHtml.ELSEIF
noOp.ELSE = mwHtml.ELSE
noOp.END = mwHtml.END
setmetatable( noOp, {
    __index = function( self )
        return self
    end,
    __call = function( self )
        return self
    end,
    __tostring = function()
        error( 'Attempting to convert no-op object into a string. Check for unbalanced IF-END tags', 2 )
    end,
    __concat = function()
        error( 'Attempting to concatenate a no-op object. Check for unbalanced IF-END tags', 2 )
    end
} )

return p
-- </nowiki>