Zum Inhalt springen

„Modul:Pinging“ – Versionsunterschied

aus Wikipedia, der freien Enzyklopädie
[gesichtete Version][gesichtete Version]
Inhalt gelöscht Inhalt hinzugefügt
2015-12-22
2022-11-01
Zeile 1: Zeile 1:
local Serial = "2015-12-22"
local Pinging = { suite = "Pinging",
serial = "2022-11-01",
--[=[
item = 108829575,
Module:Pinging -- Support templates for pings and user lists
maxEcho = 50, -- currently permitted number of recipients
]=]
maxName = 99, -- length of a user name

start = "@",

suffix = ":" }

--[==[
-- local globals
Support templates for pings and user lists
local MaxEcho = 50 -- currently permitted number of recipients
]==]
local MaxName = 34 -- length of a user name
local Start = "@"
local Failsafe = Pinging
local Stick = "<span style='white-space:nowrap'> </span>| "
local Suffix = ":"




Zeile 52: Zeile 50:
-- Returns:
-- Returns:
-- string, HTML span
-- string, HTML span
return string.format( "<span %s %s>%s</span>",
local e = mw.html.create( "span" )
"class=\"error\"",
:addClass( "error" )
"style='white-space:nowrap'",
:css( "white-space", "nowrap" )
alert );
:wikitext( alert )
return tostring( e )
end -- fault()
end -- fault()






local function fetch( analyse )
local function fetch( analyse, alt )
-- Retrieve target table
-- Retrieve target table
-- Parameter:
-- Parameter:
-- analyse -- string with page content
-- analyse -- string with page content
-- alt -- string with additional nick pattern, or not
-- Returns:
-- Returns:
-- sequence of targets, even empty
-- sequence of targets, might be empty
local i = 1
local i = 1
local loop = true
local loop = true
Zeile 98: Zeile 98:
end
end
end -- while loop
end -- while loop
if alt then
local n, sn, sns, st, sub, tns
i = 1
loop = true
while ( loop ) do
j, i = mw.ustring.find( s, alt, i )
if j then
loop = true
sub = mw.ustring.sub( s, j, i )
st, sn = mw.ustring.match( sub, alt )
if st then
st = mw.text.trim( st )
if st == "" then
st = false
end
end
if st then
n = 2
if sn then
sn = mw.text.trim( sn )
if sn == "-" then
n = false
elseif sn ~= "" then
sns = sn:match( "^(%d+)$" )
if sns then
n = tonumber( sns )
else
tns = mw.site.namespaces[ sns ]
if tns then
n = tns.id
end
end
end
end
if n then
st = string.format( "%s:%s",
mw.site.namespaces[ n ].name,
st )
end
table.insert( r, st )
end
else
loop = false
end
end -- while loop
end
return r
return r
end -- fetch()
end -- fetch()
Zeile 123: Zeile 169:
s, site = assign:match( "^%s*(.+)%s*|%s*(.*)%s*$" )
s, site = assign:match( "^%s*(.+)%s*|%s*(.*)%s*$" )
if s then
if s then
site = mw.text.trim( site )
site = mw.text.trim( site )
if site == "" then
if site == "" then
site = false
site = false
else
else
long = true
long = true
end
end
r = s
r = s
end
end
end
end
Zeile 144: Zeile 190:
( not abroad and
( not abroad and
( r:find( "[/@]" ) or
( r:find( "[/@]" ) or
mw.ustring.len( r ) > MaxName) ) then
mw.ustring.len( r ) > Pinging.maxName ) ) then
lapsus = true
lapsus = true
end
end
Zeile 170: Zeile 216:
show = mw.text.trim( show )
show = mw.text.trim( show )
if show:find( "%s" ) then
if show:find( "%s" ) then
show = string.format( "<span %s>%s</span>",
local e = mw.html.create( "span" )
"style='white-space:nowrap'",
:css( "white-space", "nowrap" )
show )
:wikitext( show )
show = tostring( e )
end
end
end
end
Zeile 188: Zeile 235:
r = string.format( "[%s %s]", r, show )
r = string.format( "[%s %s]", r, show )
if live then
if live then
r = string.format( "<span class='plainlinks'>%s</span>",
local e = mw.html.create( "span" )
r )
:addClass( "plainlinks" )
:wikitext( r )
r = tostring( e )
end
end
elseif alter or r == show then
elseif alter or r == show then
Zeile 215: Zeile 264:
local limit = ( not area and
local limit = ( not area and
active.max ~= "0" and assume.max ~= "0" )
active.max ~= "0" and assume.max ~= "0" )
local max = MaxEcho
local max = Pinging.maxEcho
local n = 0
local n = 0
local stick = Stick
local lapsus, light, lost, r, s, stick
local lapsus, light, lost, r, s
if alter then
if alter then
stick = " "
stick = " "
Zeile 225: Zeile 273:
elseif assume[ "/" ] then
elseif assume[ "/" ] then
stick = assume[ "/" ]
stick = assume[ "/" ]
else
if Pinging.stick then
stick = Pinging.stick
else
local ltr = not mw.language.getContentLanguage():isRTL()
local e = mw.html.create( "span" )
:wikitext( "&#124;" )
local s
if ltr then
s = "left"
else
s = "right"
end
e:css( "margin-" .. s, "0.5em" )
stick = tostring( e ) .. " "
Pinging.stick = stick
end
end
end
if limit and ( active.max or assume.max ) then
if limit and ( active.max or assume.max ) then
Zeile 252: Zeile 317:
if n > max then
if n > max then
local scream = "Echo-notification-count"
local scream = "Echo-notification-count"
scream = mw.message.new( scream )
local tell = mw.message.new( scream )
scream = scream:numParams( max )
if tell:exists() then
:plain()
scream = tell:numParams( max ):plain()
else
scream = string.format( "count %d &gt; %d",
n, max )
end
scream = string.format( "&#32;!! %s !!&#32;",
scream = string.format( "&#32;!! %s !!&#32;",
scream )
scream )
Zeile 267: Zeile 336:
end -- for k
end -- for k
if r then
if r then
local start = Start
local start = Pinging.start
local suffix = Suffix
local suffix = Pinging.suffix
if active[ "@" ] then
if active[ "@" ] then
start = active[ "@" ]
start = active[ "@" ]
Zeile 288: Zeile 357:
:gsub( "_$", " " )
:gsub( "_$", " " )
if alter and not lapsus then
if alter and not lapsus then
r = string.format( "<span %s>%s</span>%s",
local e = mw.html.create( "span" )
"style='display:none'",
:css( "display", "none" )
r, alter )
:wikitext( r )
r = tostring( e ) .. alter
end
end
r = string.format( "%s%s%s", start, r, suffix )
r = string.format( "%s%s%s", start, r, suffix )
Zeile 305: Zeile 375:
-- Parameter:
-- Parameter:
-- assume -- table with default options
-- assume -- table with default options
-- [ 1 ] -- page speification
-- [ 1 ] -- page specification
-- [ 2 ] -- title; none for "_", list if omitted
-- [ 2 ] -- title; none for "_", list if omitted
-- active -- table with current options
-- active -- table with current options
Zeile 325: Zeile 395:
end
end
if story then
if story then
targets = fetch( story )
targets = fetch( story, assume.pattern )
else
else
r = fault( source )
r = fault( source )
Zeile 331: Zeile 401:
end
end
if targets then
if targets then
-- if =0
local show = assume[ 2 ]
local show = assume[ 2 ]
if show then
if show then
Zeile 367: Zeile 438:
return friends( active, false, area, assume, active, false )
return friends( active, false, area, assume, active, false )
end -- userlist()
end -- userlist()



Pinging.f = function ( assigned, area, assume, active )
-- Make list of entries
-- Parameter:
-- assigned -- table with pages or users
-- area -- true: always wide area = URL
-- assume -- table with default options
-- active -- table with current options
-- Returns:
-- string
local defaults = assume or { }
local current = active or { }
local r
if type( assigned ) == "table" and
type( defaults ) == "table" and
type( current ) == "table" then
r = friends( assigned, false, area, defaults, current, false )
else
local s
if type( assigned ) ~= "table" then
s = "assigned"
end
if type( defaults ) ~= "table" then
if s then
s = s .. ", assume"
else
s = "assume"
end
end
if type( current ) ~= "table" then
if s then
s = s .. ", active"
else
s = "active"
end
end
s = s .. " not 'table'"
r = fault( s )
end
end -- Pinging.f()



Failsafe.failsafe = function ( atleast )
-- Retrieve versioning and check for compliance
-- Precondition:
-- atleast -- string, with required version
-- or wikidata|item|~|@ or false
-- Postcondition:
-- Returns string -- with queried version/item, also if problem
-- false -- if appropriate
-- 2020-08-17
local since = atleast
local last = ( since == "~" )
local linked = ( since == "@" )
local link = ( since == "item" )
local r
if last or link or linked or since == "wikidata" then
local item = Failsafe.item
since = false
if type( item ) == "number" and item > 0 then
local suited = string.format( "Q%d", item )
if link then
r = suited
else
local entity = mw.wikibase.getEntity( suited )
if type( entity ) == "table" then
local seek = Failsafe.serialProperty or "P348"
local vsn = entity:formatPropertyValues( seek )
if type( vsn ) == "table" and
type( vsn.value ) == "string" and
vsn.value ~= "" then
if last and vsn.value == Failsafe.serial then
r = false
elseif linked then
if mw.title.getCurrentTitle().prefixedText
== mw.wikibase.getSitelink( suited ) then
r = false
else
r = suited
end
else
r = vsn.value
end
end
end
end
end
end
if type( r ) == "nil" then
if not since or since <= Failsafe.serial then
r = Failsafe.serial
else
r = false
end
end
return r
end -- Failsafe.failsafe()




Zeile 372: Zeile 543:
-- Export
-- Export
local p = { }
local p = { }

function p.failsafe()
return Serial
end


function p.massmessage( a1, a2 )
function p.massmessage( a1, a2 )
Zeile 401: Zeile 568:


function p.maxecho()
function p.maxecho()
return tostring( MaxEcho )
return tostring( Pinging.maxEcho )
end
end


Zeile 411: Zeile 578:
return userlist( frame.args, frame:getParent().args, false )
return userlist( frame.args, frame:getParent().args, false )
end
end

p.failsafe = function ( frame )
-- Versioning interface
local s = type( frame )
local since
if s == "table" then
since = frame.args[ 1 ]
elseif s == "string" then
since = frame
end
if since then
since = mw.text.trim( since )
if since == "" then
since = false
end
end
return Failsafe.failsafe( since ) or ""
end -- p.failsafe

setmetatable( p, { __call = function ( func, ... )
setmetatable( p, nil )
return Failsafe
end } )


return p
return p

Version vom 14. November 2022, 22:44 Uhr

Vorlagenprogrammierung Diskussionen Lua Test Unterseiten
Modul Deutsch English

Modul: Dokumentation

Diese Seite enthält Code in der Programmiersprache Lua. Einbindungszahl Cirrus

Dies ist die (produktive) Mutterversion eines global benutzten Lua-Moduls.
Wenn die serial-Information nicht übereinstimmt, müsste eine Kopie hiervon in das lokale Wiki geschrieben werden.
Versionsbezeichnung auf WikiData: 2022-11-14

Updating notwendig

(lokal: 2022-11-01)

local Pinging = { suite   = "Pinging",
                  serial  = "2022-11-01",
                  item    = 108829575,
                  maxEcho = 50,    -- currently permitted number of recipients
                  maxName = 99,    -- length of a user name
                  start   = "@",
                  suffix  = ":" }
--[==[
Support templates for pings and user lists
]==]
local Failsafe = Pinging



local function fair( analyse, add )
    -- Create link target or link title
    -- Parameter:
    --     analyse  -- string with page name or nick
    --     add      -- true, if user namespace to be added
    --                 false, if user namespace to be stripped
    -- Returns:
    --     string
    local ns = false
    local r  = analyse
    local space, spec = r:match( "^([^:]+):(.+)$" )
    if space then
        local tns = mw.site.namespaces[ space ]
        if type( tns ) == "table" then
            ns = tns.id
        end
    end
    if add then
        if not ns then
            r = string.format( "%s:%s", mw.site.namespaces.User.name, r )
        end
    elseif ns then
        if ns == 2  or  ns == 3 then
            r = spec
        end
    end
    return r
end -- fair()



local function fault( alert )
    -- Format error message by class=error
    -- Parameter:
    --     alert  -- string, error message
    -- Returns:
    --     string, HTML span
    local e = mw.html.create( "span" )
                     :addClass( "error" )
                     :css( "white-space", "nowrap" )
                     :wikitext( alert )
    return tostring( e )
end -- fault()



local function fetch( analyse, alt )
    -- Retrieve target table
    -- Parameter:
    --     analyse  -- string with page content
    --     alt      -- string with additional nick pattern, or not
    -- Returns:
    --     sequence of targets, might be empty
    local i    = 1
    local loop = true
    local r    = { }
    local s    = analyse
    local j, k
    while ( loop ) do
        loop = false
        j    = s:find( "<!--", i, true )
        if j then
            k = s:find( "-->",  j + 4,  true )
            if k then
                loop = true
                s    = s:sub( 1,  j )  ..  s:sub( k + 3 )
                i    = j
            end
        end
    end -- while loop
    i    = 1
    loop = true
    while ( loop ) do
        loop = false
        j    = s:find( "{{#target:", i, true )
        if j then
            j = j + 10
            k = s:find( "}}", j, true )
            if k then
                loop = true
                table.insert( r,  s:sub( j,  k - 1 ) )
                i = k + 1
            end
        end
    end -- while loop
    if alt then
        local n, sn, sns, st, sub, tns
        i    = 1
        loop = true
        while ( loop ) do
            j, i = mw.ustring.find( s, alt, i )
            if j then
                loop   = true
                sub    = mw.ustring.sub( s, j, i )
                st, sn = mw.ustring.match( sub, alt )
                if st then
                    st = mw.text.trim( st )
                    if st == "" then
                        st = false
                    end
                end
                if st then
                    n = 2
                    if sn then
                        sn = mw.text.trim( sn )
                        if sn == "-" then
                            n = false
                        elseif sn ~= "" then
                            sns = sn:match( "^(%d+)$" )
                            if sns then
                                n = tonumber( sns )
                            else
                                tns = mw.site.namespaces[ sns ]
                                if tns then
                                    n = tns.id
                                end
                            end
                        end
                    end
                    if n then
                        st = string.format( "%s:%s",
                                            mw.site.namespaces[ n ].name,
                                            st )
                    end
                    table.insert( r, st )
                end
            else
                loop = false
            end
        end -- while loop
    end
    return r
end -- fetch()



local function friend( assign, abroad, area, another, alter )
    -- Format single link
    -- Parameter:
    --     assign   -- user or page name
    --     abroad   -- page names came from #target
    --     area     -- true: always wide area = URL
    --     another  -- display alternative link title
    --     alter    -- link is hidden
    -- Returns:
    --     1. string or false,
    --     2. true: string is internal
    --     3. true: error occurred
    local long = area
    local r    = assign
    local site = false
    local lapsus
    if abroad then
        local s
        s, site = assign:match( "^%s*(.+)%s*|%s*(.*)%s*$" )
        if s then
            site = mw.text.trim( site )
            if site == "" then
                site = false
            else
                long = true
            end
            r = s
        end
    end
    if r then
        r = mw.text.trim( r )
        if r == "" then
            r = false
        end
    end
    if r then
        local live = not alter
        local show
        if r:find( "[#<>%[%]%{%}]" )    or
           ( not abroad   and
             ( r:find( "[/@]" )   or
               mw.ustring.len( r ) >  Pinging.maxName ) ) then
            lapsus = true
        end
        if not lapsus then
            if another then
                show = mw.text.trim( another )
                if show == "" then
                    show = false
                end
            elseif live then
                show = fair( r, false )
            end
        end
        if not show then
            show = r
        end
        if abroad then
            if site then
                show = string.format( "%s@%s", show, site )
            end
        else
            r = fair( r, true )
        end
        if show:find( "%s" )  and  live then
            show = mw.text.trim( show )
            if show:find( "%s" ) then
                local e = mw.html.create( "span" )
                                 :css( "white-space", "nowrap" )
                                 :wikitext( show )
                show = tostring( e )
            end
        end
        if lapsus then
            r    = fault( show )
            long = true
        elseif long then
            if site then
                r = string.format( "//%s/wiki/%s",
                                   site,
                                   mw.uri.encode( r, "WIKI" ) )
            else
                r = tostring( mw.uri.canonicalUrl( r ) )
            end
            r = string.format( "[%s %s]", r, show )
            if live then
                local e = mw.html.create( "span" )
                                 :addClass( "plainlinks" )
                                 :wikitext( r )
                r = tostring( e )
            end
        elseif alter  or  r == show then
            r = string.format( "[[%s]]", r )
        else
            r = string.format( "[[%s|%s]]", r, show )
        end
    end
    return r,  not long,  lapsus
end -- friend()



local function friends( assigned, abroad, area, assume, active, alter )
    -- Make list of entries
    -- Parameter:
    --     assigned  -- table with pages or users
    --     abroad    -- true: page names came from #target
    --     area      -- true: always wide area = URL
    --     assume    -- table with default options
    --     active    -- table with current options
    --     alter     -- string: hide result, show this
    -- Returns:
    --     string
    local limit  = ( not area  and
                     active.max ~= "0"  and  assume.max ~= "0" )
    local max    = Pinging.maxEcho
    local n      = 0
    local lapsus, light, lost, r, s, stick
    if alter then
        stick = " "
    elseif active[ "/" ] then
        stick = active[ "/" ]
    elseif assume[ "/" ] then
        stick = assume[ "/" ]
    else
        if Pinging.stick then
            stick = Pinging.stick
        else
            local ltr = not mw.language.getContentLanguage():isRTL()
            local e   = mw.html.create( "span" )
                               :wikitext( "&#124;" )
            local s
            if ltr then
                s = "left"
            else
                s = "right"
            end
            e:css( "margin-" .. s,  "0.5em" )
            stick = tostring( e ) .. " "
            Pinging.stick = stick
        end
    end
    if limit  and  ( active.max or assume.max ) then
        if tonumber( assume.max ) then
            max = tonumber( assume.max )
        end
        if tonumber( active.max ) then
            max = tonumber( active.max )
        end
    end
    stick = stick:gsub( "^_", " " )
                 :gsub( "_$", " " )
    for k, v in pairs( assigned ) do
        if type( k ) == "number" then
            s = assigned[ "label" .. tostring( k ) ]
            s, light, lost = friend( v, abroad, area, s, alter )
            if s then
                if r then
                    r = r .. stick
                else
                    r = ""
                end
                if lost then
                    lapsus = true
                elseif limit and light then
                    n = n + 1
                    if n > max then
                        local scream = "Echo-notification-count"
                        local tell   = mw.message.new( scream )
                        if tell:exists() then
                            scream = tell:numParams( max ):plain()
                        else
                            scream = string.format( "count %d &gt; %d",
                                                    n, max )
                        end
                        scream = string.format( "&#32;!! %s !!&#32;",
                                                scream )
                        r      = r .. fault( scream )
                        lapsus = true
                        limit  = false
                    end
                end
                r = r .. s
            end
        end
    end -- for k
    if r then
        local start  = Pinging.start
        local suffix = Pinging.suffix
        if active[ "@" ] then
            start = active[ "@" ]
        elseif assume[ "@" ] then
            start = assume[ "@" ]
        elseif active.a then
            start = active.a
        end
        if active[ ":" ] then
            suffix = active[ ":" ]
        elseif active.p then
            suffix = active.p
        elseif assume[ ":" ] then
            suffix = assume[ ":" ]
        end
        start = start:gsub( "^_", " " )
                     :gsub( "_$", " " )
        suffix = suffix:gsub( "^_", " " )
                       :gsub( "_$", " " )
        if alter  and  not lapsus then
                local e = mw.html.create( "span" )
                                 :css( "display", "none" )
                                 :wikitext( r )
                r = tostring( e ) .. alter
        end
        r = string.format( "%s%s%s", start, r, suffix )
    else
        r = fault( "? . ? . ? . ?" )
    end
    return r
end -- friends()



local function massmessage( assume, active )
    -- Create text from massmessage distribution page
    -- Parameter:
    --     assume  -- table with default options
    --                [ 1 ]  -- page specification
    --                [ 2 ]  -- title; none for "_", list if omitted
    --     active  -- table with current options
    -- Returns:
    --     string
    local source = assume[ 1 ]
    local page, r, targets
    if source then
        local id, story
        id = source:match( "^%s*#(%d+)%s*$" )
        if id then
            page = tonumber( id )
        else
            page = source
        end
        page = mw.title.new( page )
        if page then
            story = page:getContent()
        end
        if story then
            targets = fetch( story, assume.pattern )
        else
            r = fault( source )
        end
    end
    if targets then
        --  if   =0
        local show = assume[ 2 ]
        if show then
            show = mw.text.trim( show )
            if show == "" then
                show = false
            end
        end
        if show == "_" then
            show = ""
        elseif show then
            show = string.format( "[[%s|%s]]", page.prefixedText, show )
        end
        r = friends( targets, true, false, assume, active, show )
    elseif not r then
        r = fault( source or "massmessage|namespace:title" )
    end
    if assume.subst  and  not mw.isSubsting() then
        local s = string.format( "&#123;&#123;subst:%s&#125;&#125;",
                                 assume.subst )
        r = fault( s )
    end
    return r
end -- massmessage()



local function userlist( assume, active, area )
    -- Create text from template transclusion
    -- Parameter:
    --     assume  -- table with default options
    --     active  -- table with current options
    --     area    -- true: always wide area = URL
    -- Returns:
    --     string
    return friends( active, false, area, assume, active, false )
end -- userlist()



Pinging.f = function ( assigned, area, assume, active )
    -- Make list of entries
    -- Parameter:
    --     assigned  -- table with pages or users
    --     area      -- true: always wide area = URL
    --     assume    -- table with default options
    --     active    -- table with current options
    -- Returns:
    --     string
    local defaults = assume  or  { }
    local current  = active  or  { }
    local r
    if type( assigned ) == "table"  and
       type( defaults ) == "table"  and
       type( current ) == "table" then
        r = friends( assigned, false, area, defaults, current, false )
    else
        local s
        if type( assigned ) ~= "table" then
            s = "assigned"
        end
        if type( defaults ) ~= "table" then
            if s then
                s = s .. ", assume"
            else
                s = "assume"
            end
        end
        if type( current ) ~= "table" then
            if s then
                s = s .. ", active"
            else
                s = "active"
            end
        end
        s = s .. " not 'table'"
        r = fault( s )
    end
end -- Pinging.f()



Failsafe.failsafe = function ( atleast )
    -- Retrieve versioning and check for compliance
    -- Precondition:
    --     atleast  -- string, with required version
    --                         or wikidata|item|~|@ or false
    -- Postcondition:
    --     Returns  string  -- with queried version/item, also if problem
    --              false   -- if appropriate
    -- 2020-08-17
    local since  = atleast
    local last   = ( since == "~" )
    local linked = ( since == "@" )
    local link   = ( since == "item" )
    local r
    if last  or  link  or  linked  or  since == "wikidata" then
        local item = Failsafe.item
        since = false
        if type( item ) == "number"  and  item > 0 then
            local suited = string.format( "Q%d", item )
            if link then
                r = suited
            else
                local entity = mw.wikibase.getEntity( suited )
                if type( entity ) == "table" then
                    local seek = Failsafe.serialProperty or "P348"
                    local vsn  = entity:formatPropertyValues( seek )
                    if type( vsn ) == "table"  and
                       type( vsn.value ) == "string"  and
                       vsn.value ~= "" then
                        if last  and  vsn.value == Failsafe.serial then
                            r = false
                        elseif linked then
                            if mw.title.getCurrentTitle().prefixedText
                               ==  mw.wikibase.getSitelink( suited ) then
                                r = false
                            else
                                r = suited
                            end
                        else
                            r = vsn.value
                        end
                    end
                end
            end
        end
    end
    if type( r ) == "nil" then
        if not since  or  since <= Failsafe.serial then
            r = Failsafe.serial
        else
            r = false
        end
    end
    return r
end -- Failsafe.failsafe()



-- Export
local p = { }

function p.massmessage( a1, a2 )
    local r
    if type( a1 ) == "table" then
        local p1, p2
        if type( a1.getParent ) == "function" then
            -- a1 is supposed to be a frame
            p1 = a1.args
            p2 = a1:getParent().args
        else
            p1 = a1
            if type( a2 ) == "table" then
                p2 = a2
            else
                p2 = { }
            end
        end
        r = massmessage( p1, p2 )
    else
        r = fault( "Pinging::massmessage()" )
    end
    return r
end

function p.maxecho()
    return tostring( Pinging.maxEcho )
end

function p.noping( frame )
    return userlist( frame.args, frame:getParent().args, true )
end

function p.ping( frame )
    return userlist( frame.args, frame:getParent().args, false )
end

p.failsafe = function ( frame )
    -- Versioning interface
    local s = type( frame )
    local since
    if s == "table" then
        since = frame.args[ 1 ]
    elseif s == "string" then
        since = frame
    end
    if since then
        since = mw.text.trim( since )
        if since == "" then
            since = false
        end
    end
    return Failsafe.failsafe( since )  or  ""
end -- p.failsafe

setmetatable( p,  { __call = function ( func, ... )
                                 setmetatable( p, nil )
                                 return Failsafe
                             end } )

return p