Difference between revisions of "Module:Unsubst"
m (1 revision imported) |
(Update to last version) |
||
Line 1: | Line 1: | ||
+ | local checkType = require('libraryUtil').checkType | ||
+ | |||
local p = {} | local p = {} | ||
+ | |||
+ | local BODY_PARAM = '$B' | ||
local specialParams = { | local specialParams = { | ||
− | ['$ | + | ['$params'] = 'parameter list', |
− | ['$B'] = 'template content' | + | ['$aliases'] = 'parameter aliases', |
+ | ['$flags'] = 'flags', | ||
+ | ['$B'] = 'template content' | ||
} | } | ||
− | p | + | function p.main(frame, body) |
− | if not | + | -- If we are substing, this function returns a template invocation, and if |
− | error( ' | + | -- not, it returns the template body. The template body can be specified in |
+ | -- the body parameter, or in the template parameter defined in the | ||
+ | -- BODY_PARAM variable. This function can be called from Lua or from | ||
+ | -- #invoke. | ||
+ | |||
+ | -- Return the template body if we aren't substing. | ||
+ | if not mw.isSubsting() then | ||
+ | if body ~= nil then | ||
+ | return body | ||
+ | elseif frame.args[BODY_PARAM] ~= nil then | ||
+ | return frame.args[BODY_PARAM] | ||
+ | else | ||
+ | error(string.format( | ||
+ | "no template content specified (use parameter '%s' from #invoke)", | ||
+ | BODY_PARAM | ||
+ | ), 2) | ||
+ | end | ||
end | end | ||
− | if | + | |
− | error( ' | + | -- Sanity check for the frame object. |
+ | if type(frame) ~= 'table' | ||
+ | or type(frame.getParent) ~= 'function' | ||
+ | or not frame:getParent() | ||
+ | then | ||
+ | error( | ||
+ | "argument #1 to 'main' must be a frame object with a parent " .. | ||
+ | "frame available", | ||
+ | 2 | ||
+ | ) | ||
end | end | ||
− | + | ||
− | + | -- Find the invocation name. | |
− | + | local mTemplateInvocation = require('Module:Template invocation') | |
− | + | local name = mTemplateInvocation.name(frame:getParent():getTitle()) | |
− | + | ||
+ | -- Combine passed args with passed defaults | ||
+ | local args = {} | ||
+ | if string.find( ','..(frame.args['$flags'] or '')..',', ',%s*override%s*,' ) then | ||
+ | for k, v in pairs( frame:getParent().args ) do | ||
+ | args[k] = v | ||
+ | end | ||
+ | for k, v in pairs( frame.args ) do | ||
+ | if not specialParams[k] then | ||
+ | if v == '__DATE__' then | ||
+ | v = mw.getContentLanguage():formatDate( 'F Y' ) | ||
+ | end | ||
+ | args[k] = v | ||
+ | end | ||
+ | end | ||
+ | else | ||
for k, v in pairs( frame.args ) do | for k, v in pairs( frame.args ) do | ||
if not specialParams[k] then | if not specialParams[k] then | ||
Line 29: | Line 75: | ||
args[k] = v | args[k] = v | ||
end | end | ||
+ | end | ||
− | + | -- Trim parameters, if not specified otherwise | |
− | + | if not string.find( ','..(frame.args['$flags'] or '')..',', ',%s*keep%-whitespace%s*,' ) then | |
− | + | for k, v in pairs( args ) do args[k] = mw.ustring.match(v, '^%s*(.*)%s*$') or '' end | |
− | + | end | |
− | + | ||
− | + | -- Pull information from parameter aliases | |
− | + | local aliases = {} | |
− | + | if frame.args['$aliases'] then | |
− | + | local list = mw.text.split( frame.args['$aliases'], '%s*,%s*' ) | |
− | + | for k, v in ipairs( list ) do | |
+ | local tmp = mw.text.split( v, '%s*>%s*' ) | ||
+ | aliases[tonumber(mw.ustring.match(tmp[1], '^[1-9][0-9]*$')) or tmp[1]] = ((tonumber(mw.ustring.match(tmp[2], '^[1-9][0-9]*$'))) or tmp[2]) | ||
+ | end | ||
+ | end | ||
+ | for k, v in pairs( aliases ) do | ||
+ | if args[k] and ( not args[v] or args[v] == '' ) then | ||
+ | args[v] = args[k] | ||
end | end | ||
+ | args[k] = nil | ||
+ | end | ||
− | + | -- Remove empty parameters, if specified | |
− | local | + | if string.find( ','..(frame.args['$flags'] or '')..',', ',%s*remove%-empty%s*,' ) then |
+ | local tmp = 0 | ||
for k, v in ipairs( args ) do | for k, v in ipairs( args ) do | ||
− | if | + | if v ~= '' or ( args[k+1] and args[k+1] ~= '' ) or ( args[k+2] and args[k+2] ~= '' ) then |
− | + | tmp = k | |
+ | else | ||
break | break | ||
end | end | ||
− | |||
− | |||
end | end | ||
for k, v in pairs( args ) do | for k, v in pairs( args ) do | ||
− | + | if v == '' then | |
+ | if not (type(k) == 'number' and k < tmp) then args[k] = nil end | ||
+ | end | ||
+ | end | ||
+ | end | ||
+ | |||
+ | -- Order parameters | ||
+ | if frame.args['$params'] then | ||
+ | local params, tmp = mw.text.split( frame.args['$params'], '%s*,%s*' ), {} | ||
+ | for k, v in ipairs(params) do | ||
+ | v = tonumber(mw.ustring.match(v, '^[1-9][0-9]*$')) or v | ||
+ | if args[v] then tmp[v], args[v] = args[v], nil end | ||
end | end | ||
− | + | for k, v in pairs(args) do tmp[k], args[k] = args[k], nil end | |
− | + | args = tmp | |
− | |||
− | |||
− | |||
− | |||
end | end | ||
+ | |||
+ | return mTemplateInvocation.invocation(name, args) | ||
end | end | ||
+ | |||
+ | p[''] = p.main -- For backwards compatibility | ||
return p | return p |
Latest revision as of 16:21, 18 June 2019
Helper module to facilitate a substituted template transform into a template transclusion.
Usage
To turn a template into a self‐substituting template, wrap the existing template code with:
{{SAFESUBST:<noinclude />#invoke:Unsubst||$B= [ ... existing template code ... ] }}
The wikitext to display when not substed must be given as "$B". All other parameters passed to the #invoke will be copied to the generated template invocation as default values. If the value of any of these default parameters is __DATE__
, that value in the generated template invocation will be the current month and year.
Some templates have a but no matching
at the end of the template. In such cases the missing
must be added before the ending
}}
.
Advanced
{{SAFESUBST:<noinclude />#invoke:Unsubst||$params=[ parameters ]|$aliases=[ aliases ]|$flags=[ flags ]|$B= [ ... existing template code ... ] }}
Due to Lua limitations, parameters are normally ordered randomly when the template is substituted. |$params=
can be used in #invoke:Unsubst to list template parameters in order, comma‐separated (e.g. egg,bacon,sausage,cheese,spam
). Numbered parameters should be before others on the list. Any remaining parameters are tacked onto the end of the generated invocation.
Parameter aliases can be listed in |$aliases=
(and shouldn’t be listed in |$params=
), and will be replaced automatically. Each alias and its replacement should be formatted as alias>replacement
, and each of those pairs should be comma‐separated (e.g. œuf>egg,melt>cheese
). Note that this parameter can function with or without |$params=
.
Parameter |$flags=
can be used to modify other facets of the module’s behaviour; entries are comma‐separated. Valid flags are override
(allows parameters in the #invoke: to take precedence over parameters in the original template invocation); keep-whitespace
(prevents whitespace from being trimmed from unnamed parameters); and remove-empty
(removes empty parameters).
These parameters can be manipulated using parser functions to provide more complicated options (note that in the parameters any parser function, or template or module invocation, should also have SAFESUBST:<noinclude />
).
Example
Consider a template Template:Example containing the following code:
{{SAFESUBST:<noinclude />#invoke:Unsubst||foo=bar |date=__DATE__ |$B= [ ... Template code goes here ... ] }}
Original | Result |
---|---|
{{subst|example}}
|
{{Example|foo=bar|date=November 2024}}
|
{{subst|example|foo=X}}
|
{{Example|foo=X|date=November 2024}}
|
{{subst|example|baz=X}}
|
{{Example|baz=X|date=November 2024}}
|
{{subst|example|date=January 2001}}
|
{{Example|foo=bar|date=January 2001}}
|
local checkType = require('libraryUtil').checkType local p = {} local BODY_PARAM = '$B' local specialParams = { ['$params'] = 'parameter list', ['$aliases'] = 'parameter aliases', ['$flags'] = 'flags', ['$B'] = 'template content' } function p.main(frame, body) -- If we are substing, this function returns a template invocation, and if -- not, it returns the template body. The template body can be specified in -- the body parameter, or in the template parameter defined in the -- BODY_PARAM variable. This function can be called from Lua or from -- #invoke. -- Return the template body if we aren't substing. if not mw.isSubsting() then if body ~= nil then return body elseif frame.args[BODY_PARAM] ~= nil then return frame.args[BODY_PARAM] else error(string.format( "no template content specified (use parameter '%s' from #invoke)", BODY_PARAM ), 2) end end -- Sanity check for the frame object. if type(frame) ~= 'table' or type(frame.getParent) ~= 'function' or not frame:getParent() then error( "argument #1 to 'main' must be a frame object with a parent " .. "frame available", 2 ) end -- Find the invocation name. local mTemplateInvocation = require('Module:Template invocation') local name = mTemplateInvocation.name(frame:getParent():getTitle()) -- Combine passed args with passed defaults local args = {} if string.find( ','..(frame.args['$flags'] or '')..',', ',%s*override%s*,' ) then for k, v in pairs( frame:getParent().args ) do args[k] = v end for k, v in pairs( frame.args ) do if not specialParams[k] then if v == '__DATE__' then v = mw.getContentLanguage():formatDate( 'F Y' ) end args[k] = v end end else for k, v in pairs( frame.args ) do if not specialParams[k] then if v == '__DATE__' then v = mw.getContentLanguage():formatDate( 'F Y' ) end args[k] = v end end for k, v in pairs( frame:getParent().args ) do args[k] = v end end -- Trim parameters, if not specified otherwise if not string.find( ','..(frame.args['$flags'] or '')..',', ',%s*keep%-whitespace%s*,' ) then for k, v in pairs( args ) do args[k] = mw.ustring.match(v, '^%s*(.*)%s*$') or '' end end -- Pull information from parameter aliases local aliases = {} if frame.args['$aliases'] then local list = mw.text.split( frame.args['$aliases'], '%s*,%s*' ) for k, v in ipairs( list ) do local tmp = mw.text.split( v, '%s*>%s*' ) aliases[tonumber(mw.ustring.match(tmp[1], '^[1-9][0-9]*$')) or tmp[1]] = ((tonumber(mw.ustring.match(tmp[2], '^[1-9][0-9]*$'))) or tmp[2]) end end for k, v in pairs( aliases ) do if args[k] and ( not args[v] or args[v] == '' ) then args[v] = args[k] end args[k] = nil end -- Remove empty parameters, if specified if string.find( ','..(frame.args['$flags'] or '')..',', ',%s*remove%-empty%s*,' ) then local tmp = 0 for k, v in ipairs( args ) do if v ~= '' or ( args[k+1] and args[k+1] ~= '' ) or ( args[k+2] and args[k+2] ~= '' ) then tmp = k else break end end for k, v in pairs( args ) do if v == '' then if not (type(k) == 'number' and k < tmp) then args[k] = nil end end end end -- Order parameters if frame.args['$params'] then local params, tmp = mw.text.split( frame.args['$params'], '%s*,%s*' ), {} for k, v in ipairs(params) do v = tonumber(mw.ustring.match(v, '^[1-9][0-9]*$')) or v if args[v] then tmp[v], args[v] = args[v], nil end end for k, v in pairs(args) do tmp[k], args[k] = args[k], nil end args = tmp end return mTemplateInvocation.invocation(name, args) end p[''] = p.main -- For backwards compatibility return p