Only you can help us build a free scout media repository!
Please create an account to start uploading your images now.

TemplateBox

From ScoutMedia, the free scout media repository
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

--[[

   @exports
       usagesample( frame )
       argcount( frame )
       args2table( args, onGetKey, forCustom )
       paramtable( frame )
       description( frame )
       templatedata( frame )

]]

local p = {}

-- Helper function, not exposed function tobool(st)

   if type( st ) == 'string' then
       return st == 'true'
   else
       return not not st
   end

end


-- Required to determine in which languages the interface texts without langcode are local contentLangcode = mw.language.getContentLanguage():getCode() -- Forward declaration local msg, langIsInit, userLang local messagePrefix = "templatedata-doc-" local i18n = {} i18n['params'] = "Template parameters" i18n['param-name'] = "Parameter" i18n['param-desc'] = "Description" i18n['param-type'] = "Type" i18n['param-default'] = "Default" i18n['param-status'] = "Status" i18n['param-status-optional'] = "optional" i18n['param-status-required'] = "required" i18n['param-status-suggested'] = "suggested" i18n['param-status-deprecated'] = "deprecated" i18n['param-default-empty'] = "empty"

function initLangModule()

   if langIsInit then
       return
   end
   --! From de:Modul:Expr; by de:User:PerfektesChaos; 
   --! Derivative work: Rillke
   userLang = mw.getCurrentFrame():preprocess( '⧼lang⧽' )
   msg = function( key )
       -- Retrieve localized message string in content language
       -- Precondition:
       --     key  -- string; message ID
       -- Postcondition:
       --     Return some message string
       -- Uses:
       --     >  messagePrefix
       --     >  i18n
       --     >  userLang
       --     mw.message.new()
       local m = mw.message.new( messagePrefix .. key )
       local r = false
       if m:isBlank() then
           r = i18n[ key ]
       else
           m:inLanguage( userLang )
           r = m:plain()
       end
       if not r then
           r = '((('.. key .. ')))'
       end
       return r
   end -- msg()
   
   langIsInit = true

end

-- A "hash" / table of everything TemplateData takes -- to ease maintenance.

-- The type is automatically determined if t is omitted. -- If the type does not match or can't be converted, an error will be thrown! -- Available types (LUA-Types with exceptions): -- InterfaceText, boolean, number, selection, table, string -- selection*: - requires a selection-string of pipe-separated possibilities to be supplied -- InterfaceText*: A free-form string (no wikitext) in the content-language of the wiki, or, -- an object containing those strings keyed by language code. local paraminfoTemplate = {

   description = {
       default = ,
       t = 'InterfaceText',
       alias = 'desc'
   }

} local paraminfoTLParams = {

   label = {
       default = ,
       t = 'InterfaceText'
   },
   required = {
       default = false,
       extract = function(pargs, number, paramVal)
           local req = (pargs[number .. 'stat'] == 'required')
           return tobool( paramVal or req )
       end
   },
   suggested = {
       default = false,
       extract = function(pargs, number, paramVal)
           local sugg = (pargs[number .. 'stat'] == 'suggested')
           return tobool( paramVal or sugg )
       end
   },
   description = {
       default = ,
       t = 'InterfaceText',
       alias = 'd'
   },
   deprecated = {
       default = false,
       extract = function(pargs, number, paramVal)
           local depr = (pargs[number .. 'stat'] == 'deprecated')
           return tobool( paramVal or depr )
       end
   },
   aliases = {
       default = ,
       t = 'table',
       extract = function(pargs, number, paramVal)
           local key = number .. 'aliases'
           local tdkey = key .. '-td'
           local aliases = pargs[tdkey] or pargs[key]
           if aliases then
               aliases = mw.text.split( aliases, '/', true )
           end
           return aliases
       end
   },
   default = {
       default = ,
       t = 'string',
       alias = 'def'
   },
   type = {
       default = 'unknown',
       t = 'selection',
       selection = 'unknown|number|string|string/wiki-user-name|string/wiki-page-name|string/line|line|wiki-page-name|wiki-file-name|wiki-user-name|content|unbalanced-wikitext'
   },
   inherits = {
       default = nil,
       t = 'string'
   }
   -- sets will be treated differently because we can only have a plain structure in wikitext

} local tableLayout = {

   {
       col = 'param-name',
       width = '15%',
       extract = function(item, renderCell, monolingual)
           local alias, param = , item.key
           local aliasTT = ''
           param = '' .. param .. ''
           if item.aliases then
               alias = aliasTT .. table.concat(item.aliases, '
' .. aliasTT) .. ''

param = table.concat({param, '

', alias, '

'})

           end
           renderCell(param, colspan)
       end
   },  {
       col = 'param-desc',
       cols = 2,
       width = '65%',
       extract = function(item, renderCell, monolingual)
           local label = item.label or 
           label = monolingual(label)
           local labelLen = #label
           local colspan = 2 - labelLen
       
           if labelLen > 0 then
               renderCell(label)
           end
       
           renderCell(monolingual(item.description), colspan)
       end
   },  {
       col = 'param-default',
       width = '10%',
       extract = function(item, renderCell, monolingual)
           local def = monolingual(item.default) or 
           if #def == 0 then
               def = '' .. msg('param-default-empty') .. ''
           end
           renderCell(def)
       end
   },  {
       col = 'param-status',
       width = '10%',
       extract = function(item, renderCell, monolingual)
           local stat = msg('param-status-optional')
           if item.required then
               stat = '' .. msg('param-status-required') .. ''
           elseif item.deprecated then
               stat = msg('param-status-deprecated')
           elseif item.suggested then
               stat = msg('param-status-suggested')
           end
           renderCell(stat)
       end
   }

}

-- Initialize param info -- Avoids having to add redundant information to the preceding tables function init( which )

   local setDefault = function(v)
       if v.t == nil and v.default ~= nil then
           v.t = type( v.default )
       end
       if v.selection then
           v.selection = '|' .. v.selection .. '|'
       end
   end
   for a, v in pairs( which ) do
       setDefault(v)
   end

end function initParamTables()

   init( paraminfoTemplate )
   init( paraminfoTLParams )

end



USAGE PART ----------------------


function p.argcount( frame )

   local pargs = ( frame:getParent() or {} ).args or {}
   local ac = 0
   for i, arg in pairs( pargs ) do
       if ('number' == type(i)) then
           ac = ac + 1
       end
   end
   return ac

end

function p.usagesample( frame )

   local pargs = ( frame:getParent() or {} ).args or {}
   local multiline = (pargs.lines == 'multi' or pargs.print == 'multi' or pargs.print == 'infobox')
   local align = pargs.print == 'infobox'
   if not pargs.lines and not pargs.print and pargs.type == 'infobox' then
       multiline = true
       align = true
   end
   local sepStart = ' |'
   local sepEnd = multiline  and '\n' or 
   local sep = sepEnd
   local subst = #(pargs.mustbesubst or ) > 0 and 'subst:' or 
   local beforeEqual = multiline  and ' ' or 
   local equal = beforeEqual .. '= '
   local templateTitle = pargs.name or 
   local args, argName, result = {}
   local maxArgLen, eachArg = 0
   sep = sep .. sepStart
   
   local comapareLegacyVal = function(val)
       return val == 'optional-' or val == 'deprecated'
   end
   local shouldShow = function(i)
       if comapareLegacyVal(pargs[i .. 'stat']) or
           comapareLegacyVal(pargs[i .. 'stat-td']) or
           pargs[i .. 'deprecated'] == true then 
               return false
           end
       return true
   end
   
   eachArg = function(cb)
       for i, arg in pairs( pargs ) do
           if ('number' == type(i)) then
               argName = mw.text.trim( arg or  )
               if #argName == 0 then
                   argName = tostring(i)
               end
               
               if shouldShow(i) then
                   cb(argName)
               end
           end
       end
   end
   
   if align then
       eachArg(function( arg )
           local argL = #arg
           maxArgLen = argL > maxArgLen and argL or maxArgLen
       end)
   end
   
   eachArg(function( arg )
       local space = 
       if align then
           space = (' '):rep(maxArgLen - #arg)
       end
       table.insert( args, argName .. space .. equal )
   end)
   
   if #args == 0 then
       sep = 
       sepEnd = 
       sepStart = 
   end
   if #templateTitle == 0 then
       templateTitle = mw.title.getCurrentTitle().text
   end
   result = table.concat( args, sep )
   result = table.concat({ mw.text.nowiki('Template:'), subst, templateTitle, sep, result, sepEnd, '' })
   if multiline then
       -- Preserve whitespace in front of new lines
       result = frame:callParserFunction{ name = '#tag', args = { 'poem', result } }
   end
   return result

end



GENERAL PART ---------------------


function p.args2table(args, onGetKey, consumer)

   initParamTables()
   
   local sets, asParamArray, laxtype, processParams, processDesc
   if 'paramtable' == consumer then
       asParamArray = true
       processParams = true
       laxtype = true
   elseif 'templatedata' == consumer then
       sets = true
       processParams = true
       processDesc = true
       unstrip = true
   elseif 'description' == consumer then
       processDesc = true
       laxtype = true
   end
   -- All kind of strange stuff with the arguments is done, so play safe and make a copy
   local pargs = mw.clone( args )
   -- Array-like table containing all parameter-numbers that were passed
   local templateArgs = {}
   -- Arguments that are localized (i.e. the user passed  1desc-en=English description of parameter one)
   local i18nTemplateArgs = {}
   -- Ensure that tables end up as array/object (esp. when they are empty)
   local tdata = {description="", params={}, sets={}}
   local isArray  = { __tostring = function() return "JSON array"  end }    isArray.__index  = isArray
   setmetatable(tdata.sets, isArray)
   onGetKey = onGetKey or function( prefix, alias, param )
       local key, key2, tdkey, tdkey2
       key = prefix .. (alias or param)
       key2 = prefix .. param
       tdkey = key .. '-td'
       tdkey2 = key2 .. '-td'
       return tdkey, tdkey2, key, key2
   end
   
   local extractData = function( pi, number )
       local prefix = number or 
       local ppv, paramVal
       local key1, key2, key3, key4
       local paramKey, paramTable, processKey
       if number then
           paramKey = mw.text.trim( pargs[number] )
           if  == paramKey then
               paramKey = tostring( number )
           end
           
           paramTable = {}
           if asParamArray then
               paramTable.key = paramKey
               table.insert(tdata.params, paramTable)
           else
               tdata.params[paramKey] = paramTable
           end
       end
       for p, info in pairs( pi ) do
           key1, key2, key3, key4 = onGetKey(prefix, info.alias, p)
           paramVal = nil
           
           processKey = function(key)
               if paramVal ~= nil then return end
               local plain, multilingual = pargs[key], i18nTemplateArgs[key]
               paramVal = multilingual or plain
           end
           processKey( key1 )
           processKey( key2 )
           processKey( key3 )
           processKey( key4 )
           
           -- Ensure presence of entry in content language
           ppv = pargs[key1] or pargs[key2] or pargs[key3] or pargs[key4] or info.default
           if 'table' == type( paramVal ) then
               if (nil == paramVal[contentLangcode]) then
                   paramVal[contentLangcode] = ppv
               end
           else
               paramVal = ppv
           end
           if 'function' == type( info.extract ) then
               if 'string' == type( paramVal ) then
                   paramVal = mw.text.trim( paramVal )
                   if  == paramVal then
                       paramVal = nil
                   end
               end
               paramVal = info.extract( pargs, number, paramVal )
           end
           
           local insertValue = function()
               if number then
                   paramTable[p] = paramVal
               else
                   tdata[p] = paramVal
               end
           end
           
           if info.selection then
               if info.selection:find( paramVal, 1, true ) then
                   insertValue()
               end
           elseif 'InterfaceText' == info.t then
               if ({ table=1, string=1 })[type( paramVal )] then
                   insertValue()
               end
           else
               local paramType = type( paramVal )
               if 'string' == info.t and 'string' == paramType then
                   paramVal = mw.text.trim( paramVal )
                   if  ~= paramVal then
                       insertValue()
                   end
               elseif 'boolean' == info.t then
                   paramVal = tobool(paramVal)
                   insertValue()
               elseif 'number' == info.t then
                   paramVal = tonumber(paramVal)
                   insertValue()
               elseif paramType == info.t then
                   insertValue()
               elseif paramType == 'nil' then
                   -- Do nothing
               elseif not laxtype and 'string' == info.t and 'table' == paramType then
                   -- Convert multilingual object into content language string
                   paramVal = paramVal[contentLangcode]
                   insertValue()
               else
                   if laxtype then
                       insertValue()
                   else
                       error( p .. ': Is of type ' ..  paramType .. ' but should be of type ' .. (info.t or 'unknown'), 1 )
                   end
               end
           end
       end
       -- Now, treat sets
       if sets then
           key1 = prefix .. 'set-td'
           key2 = prefix .. 'set'
           paramVal = pargs[key1] or pargs[key2]
           if paramVal then
               local found = false
               for i, s in ipairs( tdata.sets ) do
                   if s.label == paramVal then
                       table.insert( s.params, p )
                       found = true
                   end
               end
               if not found then
                   table.insert( tdata.sets, {
                       label = paramVal, 
                       params = { p }
                   } )
               end
           end
       end
   end
   
   -- First, analyse the structure of the provided arguments
   for a, v in pairs( pargs ) do
       if unstrip then
           v = mw.text.unstrip( v )
           pargs[a] = v
       end
       if type( a ) == 'number' then
           table.insert( templateArgs, a )
       else
           local argSplit = mw.text.split( a, '-', true )
           local argUnitl = {}
           local argAfter = {}
           local isTDArg = false
           local containsTD = a:find( '-td', 1, true )
           for i, part in ipairs( argSplit ) do
               if isTDArg or (containsTD == nil and i > 1) then
                   -- This is likely a language version
                   table.insert( argAfter, part )
               else
                   table.insert( argUnitl, part )
               end
               if part == 'td' then
                   isTDArg = true
               end
           end
           if #argAfter > 0 then
               argUnitl = table.concat( argUnitl, '-' )
               argAfter = table.concat( argAfter, '-' )
               i18nTemplateArgs[argUnitl] = i18nTemplateArgs[argUnitl] or {}
               i18nTemplateArgs[argUnitl][argAfter] = v
           end
       end
   end
   -- Then, start building the actual template
   if processDesc then
       extractData( paraminfoTemplate )
   end
   if processParams then
       for i, number in pairs( templateArgs ) do
           extractData( paraminfoTLParams, number )
       end
   end
   return tdata, #templateArgs

end




CUSTOM PARAMETER TABLE PART -------------


-- A custom key-pref-function local customOnGetKey = function( prefix, alias, param )

   local key, key2, tdkey, tdkey2
   key = prefix .. (alias or param)
   key2 = prefix .. param
   tdkey = key .. '-td'
   tdkey2 = key2 .. '-td'
   return key2, key, tdkey2, tdkey

end local toUserLanguage = function(input, frame)

   if type(input) == 'table' then
       input = frame:expandTemplate{ title = 'LangSwitch', args = input }
   end
   return input

end

function p.description(frame)

   local pargs = ( frame:getParent() or {} ).args or {}
   local tdata, paramLen
   local monolingual = function(input)
       return toUserLanguage(input, frame)
   end
   tdata, paramLen = p.args2table(pargs, customOnGetKey, 'description')
   return monolingual(tdata.description)

end


function p.paramtable(frame)

   local pargs = ( frame:getParent() or {} ).args or {}
   local tdata, paramLen
   
   if 'only' == pargs.useTemplateData then
       return 'param table - output suppressed'
   end
   
   -- Initialize the language-related stuff
   initLangModule()
   local monolingual = function(input)
       return toUserLanguage(input, frame)
   end
   tdata, paramLen = p.args2table(pargs, customOnGetKey, 'paramtable')
   
   
   if 0 == paramLen then
       return 
   end
   
   local row, rows = , {}
   local renderCell = function(wikitext, colspan)

local colspan, oTd = colspan or 1, ''

       if colspan > 1 then

oTd = ''

       end

row = table.concat({ row, oTd, wikitext, '' }) end -- Create the header for i, field in ipairs( tableLayout ) do local style = ' style="width:' .. field.width .. '"' local colspan = if field.cols then colspan = ' colspan="' .. field.cols .. '"' end local th = '<th' .. style .. colspan .. '>' row = row .. th .. msg(field.col) .. '' end table.insert(rows, row) -- Now transform the Lua-table into an HTML-table for i, item in ipairs( tdata.params ) do row = for i2, field in ipairs( tableLayout ) do field.extract(item, renderCell, monolingual) end table.insert(rows, row) end return '

' .. table.concat(rows, '') .. '

'

end




TEMPLATEDATA PART ------------------


-- A real parser/transformer would look differently but it would likely be much more complex -- The TemplateData-portion for Template:TemplateBox function p.templatedata(frame)

   local tdata
   local args = frame.args or {}
   local formatting = args.formatting
   local pargs = ( frame:getParent() or {} ).args or {}
   local useTemplateData = pargs.useTemplateData
   if  (formatting == 'pretty' and useTemplateData ~= 'export') or
       (not useTemplateData) or
       (useTemplateData == 'export' and formatting ~= 'pretty') then
           local warning = "Warning: Module:TemplateBox - templatedata invoked but not requested by user (setting useTemplateData=1)."
           mw.log(warning)
           tdata = '{"description":"' .. warning .. '","params":{},"sets":[]}'
           return tdata
   end
   
   -- Load the JSON-Module which will convert LUA tables into valid JSON
   local JSON = require('Module:JSON')
   JSON.strictTypes = true
   -- Obtain the object containing info
   tdata = p.args2table(pargs, nil, 'templatedata')
   -- And finally return the result
   if formatting == 'pretty' then
       return JSON:encode_pretty(tdata)
   else
       return JSON:encode(tdata)
   end

end

return p